MapSmith is an open-source MCP server that gives an agent real GIS analysis — buffers, overlays, reprojection, zonal statistics, terrain, hydrology — run by GeoPandas, DuckDB Spatial, exactextract and Whitebox. Never written by the model.
Every dataset it writes lands on disk beside a lineage manifest — the one below came with the image on the left. It is the whole argument, so it is the first thing on this page.
basins.tif.provenance.json
Not a log. A record complete enough that someone who was not there can re-run the analysis and disagree with it — which is a different and much higher bar than being able to read what happened.
The bytes, not the filename. A manifest whose input has been edited since stops matching, and says so.
Why this coordinate system, in words. Silent reprojection is the most expensive habit in the field.
Which build produced the numbers. Terrain algorithms change between releases, and so do their outputs.
Deterministic checks, recorded whether they passed or failed. Written before a critical failure raises, so the audit trail survives the error.
UTC, from the process that ran it — not from whoever is reading it later.
Install
Works with Claude Desktop, Claude Code, Cursor and VS Code over stdio. Docker is the other supported path; both are documented in the README.
{
"mcpServers": {
"mapsmith": {
"command": "uvx",
"args": ["mapsmith"]
}
}
}
Point MAPSMITH_WORKSPACE at a directory and every path argument is confined to
it. Paths come from a language model; they are treated as untrusted input.
Operations
Tool-selection accuracy degrades past a few dozen exposed tools, so capability lives behind a searchable catalogue rather than in the tool list. What is exposed is what an agent has to choose between.
| buffer_layer · clip_layer · overlay_layers | Vector geometry, with the metric-on-geographic trap handled explicitly rather than silently |
| reproject_layer | Coordinate systems, with the decision and its reason recorded |
| zonal_statistics | Raster statistics per polygon, via exactextract — partial pixels weighted, not counted or dropped |
| hillshade · slope · flow_accumulation · watershed | Terrain and hydrology on Whitebox Workflows |
| run_sql | Spatial SQL on DuckDB, sandboxed to the workspace with external access off |
| validate_plan · run_plan | A typed DAG checked before anything executes: stable error codes, simulated CRS flow |
| preview_map | An in-chat map panel that shows the verification status of every layer it draws |
Evidence
Published with the analysis that took our own positive number apart, and with a conclusion we had already published and had to correct rather than delete.
Enforcing a validated plan buys reproducibility, not accuracy. The flow-aware gate we expected to help moved nothing measurable. The one effect outside the noise came from telling the planner the argument rules up front.
Saying so is the point. A geospatial agent can pick the right tools in the right order, write a file, and hand you a number that is wrong — and every trajectory benchmark in circulation scores that run full marks.
Invariants
Geometry and statistics come from engine executions. If a model produced it, it is not an output.
An input without a CRS is refused with a message that says what to fix. A declaration MapSmith will not resolve is quoted back, not swallowed.
Every writer emits one. A dataset with no lineage beside it did not come from here.
Verification is recorded before a critical failure raises, so the diagnosis outlives the error.
Remote paths are opt-in, enforced at the GDAL driver level — a local file that makes GDAL fetch a URL is the same hole with a different name.
SQL that configures one is refused rather than redacted, because redaction is a text scan and an audit walked past it four ways.