Using AI agents
A map document is plain YAML checked against a published schema, so an LLM agent can write one reliably: generate a document, run the validator, and repair what it reports until the file is valid. This page gives the agent (or the developer wiring one up) the loop, the validator’s JSON output, and the resources to load.
The loop
Section titled “The loop”generate → validate -f json → repair → repeat until valid-
Generate a map document in format v1, the default: the schemas, the examples and every guide use it. An agent that writes JSON more reliably can save the same document as
map.json, with a$schemakey (JSON & TOML documents). -
Validate it with the CLI’s JSON output:
Terminal window npx maplibre-yaml@latest validate map.yaml -f json -
Repair each entry in
errors, using itspath,message,lineandcolumn. -
Repeat until the command exits with
0and prints"valid": true.
The exit code tells the loop where it stands:
| Code | Meaning |
|---|---|
0 | Every file is valid |
1 | At least one file has errors, or warnings in strict mode |
2 | The file wasn’t found or the pattern matched nothing: check the path, not the YAML |
Strict mode is on when the CI environment variable is set, so an agent
running in CI must fix warnings as well as errors. --no-strict turns it
off; --strict turns it on anywhere.
JSON and TOML
Section titled “JSON and TOML”An agent may write the document as JSON or TOML instead of YAML — whichever
its tooling emits more reliably — and run the same loop on it: validate
reads all three, reports at the same line and column, and -f json works
on a .json or .toml file exactly as on a .yaml one. These docs stay
YAML; see JSON & TOML documents for the schema
line in each format and what else changes.
To move a document between formats, use mlym convert <file> --to yaml|json|toml:
npx maplibre-yaml@latest convert map.toml --to yamlnpx maplibre-yaml@latest convert map.json --to toml --out map.toml --strict--strict exits 1 and writes nothing if the target can’t keep everything
(comments, anchors, a null); without it, what’s lost is listed on stderr
and the command still exits 0. Add -f json to read the result instead of
prose: one object on stdout, { success, from, to, text, losses, errors }
(out in place of text with --out), each loss and error with its
path, line and column as validate -f json gives them
(the convert JSON output):
npx maplibre-yaml@latest convert map.json --to toml --strict -f jsonTOML has traps a generated document can hit:
filter = null, or anynull: TOML has no null. Leave the key out.- An unquoted dotted key outside
locale:a.b = 1nests asa = { b = 1 }. - A bare
#colour:circle-color = #e74c3cstarts a comment. Quote it. - A layer’s own key written after a
[layers.paint]header: it lands inpaint, not the layer. !html: write{ "$html" = "…" }(JSON:{ "$html": "…" }).
The JSON output
Section titled “The JSON output”On success:
{ "valid": true, "files": [ { "file": "/home/you/maps/map.yaml", "valid": true, "errors": [], "warnings": [] } ], "summary": { "total": 1, "valid": 1, "invalid": 0, "errorCount": 0, "warningCount": 0, "noteCount": 0 }}On failure, here for center: -74.006, 40.7128 written without brackets:
{ "valid": false, "files": [ { "file": "/home/you/maps/map.yaml", "valid": false, "errors": [ { "path": "config.center", "message": "center needs square brackets: center: [-74.006, 40.7128]", "line": 4, "column": 11, "severity": "error" } ], "warnings": [] } ], "summary": { "total": 1, "valid": 0, "invalid": 1, "errorCount": 1, "warningCount": 0, "noteCount": 0 }}fileis an absolute path.pathpoints into the document with dots:config.center,layers.0.paint.circle-radius.lineandcolumnlocate it in the source where the YAML allows.messagesays what is wrong and usually how to write it instead.warningsentries have the same fields. Theirseverityis"warning"for something valid that probably isn’t what the author meant (a layer naming an undeclared source, a map with no basemap), or"note"for how a valid value is read. Notes never make a file invalid and aren’t counted inwarningCount.
Other formats for the same findings (sarif, vscode) are in
CLI commands › Output formats.
Resources to load
Section titled “Resources to load”| Resource | URL |
|---|---|
Agent index (llms.txt) | https://docs.maplibre-yaml.org/llms.txt |
Full reference (llms-full.txt) | https://docs.maplibre-yaml.org/llms-full.txt |
| JSON Schema for a map document | https://docs.maplibre-yaml.org/schema/latest/map.schema.json |
llms.txt is a short index. llms-full.txt holds the complete JSON Schemas
and example documents in one file. Both are built from the same sources as
these docs. The schema for story files, and how to pin a version, are in
Editor setup › Schema URLs.
To use exactly the schema of the core version a project has installed, print it with the CLI:
npx maplibre-yaml@latest schema mapUpdating a running map
Section titled “Updating a running map”A page that already shows a <ml-map> doesn’t need to replace the element
for a new document: call el.update(doc), or assign the document to
el.config, and the running map changes in place by id, moving the camera
only if the document’s camera changed. The outcome arrives as
ml-map:update-applied ({ summary }) or ml-map:update-rejected
({ kind, errors }); el.setState(values) / el.getState() read and write
the document’s state: values at runtime, and camera="host" on the
element keeps an update from moving a reader’s own view. Full rules:
<ml-map> reference.
Three things agents get wrong
Section titled “Three things agents get wrong”1. A map document is type: map with a config: block
Section titled “1. A map document is type: map with a config: block”The file <ml-map src> loads starts with type: map. The camera and the
basemap go under config:, not at the top level, and center is
[longitude, latitude]. Don’t mix in format v2’s style: and runtime:
sections.
# yaml-language-server: $schema=https://docs.maplibre-yaml.org/schema/latest/map.schema.jsontype: mapid: my-mapconfig: center: [-74.006, 40.7128] zoom: 12 mapStyle: "https://tiles.openfreemap.org/styles/liberty"2. Popups are a list of elements
Section titled “2. Popups are a list of elements”popup: is a list of single-key { tag: [items] } objects, not an HTML
string and not a { title, description } object. Each item is str: (fixed
text) or property: (a feature’s value), optionally with else: (a
fallback) and format: (a number format).
interactive: click: popup: - h3: - property: title - p: - str: "Magnitude " - property: mag format: { max-fraction-digits: 1 }3. A named source is referenced by its name
Section titled “3. A named source is referenced by its name”Declare a source once under sources:, then set each layer’s source: to
that name as a plain string.
type: mapid: quakesconfig: center: [-120, 37] zoom: 4 mapStyle: "https://tiles.openfreemap.org/styles/liberty"sources: quakes: type: geojson url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"layers: - id: quake-points type: circle source: quakes # the name, not an inline object paint: circle-radius: 6 circle-color: "#e55e5e"The same command fails a CI job on an invalid document, and its SARIF output shows findings as annotations on a pull request: CLI commands › Integration examples.