공간 데이터셋에 대해 검증 가능한 출처와 자동화된 분석 계획을 제공하며 결정론적인 GIS 지오프로세싱을 수행합니다.
다른 이름 mapsmith-ai/MapSmith
- 스타
- 2
- Tools
- 29
- 다운로드, 30일
- 885
- 최신 버전
- 0.12.2
- 마지막 커밋
- 2026년 11월 13일
- 라이선스
- AGPL-3.0
MapSmith MCP란?
MapSmith은(는) MCP server입니다. 공간 데이터셋에 대해 검증 가능한 출처와 자동화된 분석 계획을 제공하며 결정론적인 GIS 지오프로세싱을 수행합니다. 29개 도구를 제공합니다. 예: describe_dataset, buffer_layer, clip_layer.
프로젝트 README에서 생성된 요약
MapSmith MCP server 정보
공간 분석가와 개발자는 공간 데이터셋에 대해 자연어로 질문함으로써 결정론적인 GIS 지오프로세싱을 수행할 수 있습니다. 이 MCP server는 단순히 단일 도구를 실행하는 대신, 작업 시퀀스를 자동으로 계획하고 파일이 수정되기 전에 로직을 검증하며 GeoPandas, DuckDB Spatial, exactextract, Whitebox Workflows와 같은 엔진을 사용하여 워크플로우를 실행합니다. 이러한 접근 방식은 레이어 버퍼링이나 구역 통계 계산과 같은 복잡한 다단계 작업이 구조적 무결성을 유지하며 처리되도록 보장합니다.
사용자는 모든 MCP client를 이 server에 연결하여 검증 가능한 출처가 포함된 분석을 실행할 수 있습니다. 모든 작업은 입력 체크섬, 특정 파라미터, 좌표 참조 시스템(CRS) 결정 사항 및 결정론적 확인 항목을 포함하는 리니지 매니페스트를 생성합니다. 이를 통해 특정 CRS 결정이 내려진 이유를 기록으로 설명할 수 있어 완전한 재현성이 가능합니다. 시스템은 실행 단계가 시작되기 전에 단계 순서가 잘못되었거나 인수가 유효하지 않은 경우와 같이 잘못된 계획을 거부하도록 설계되었습니다.
활용 사례
- 1복잡한 공간 필터링강과의 근접성 및 고도 임계값을 기준으로 특정 필지를 식별합니다.
- 2자동화된 분석 계획단일 자연어 요청으로부터 검증된 GIS 작업 시퀀스를 생성합니다.
- 3검증 가능한 지오프로세싱 출처감사를 위해 모든 파라미터와 좌표계 결정 사항에 대한 상세 기록을 생성합니다.
- 4데이터 무결성 검증래스터 파일의 충돌하는 지오레퍼런싱 정보로 인해 발생하는 오류를 방지합니다.
프로젝트의 README를 바탕으로 작성되었습니다. 모든 활용 사례는 해당 내용의 인용문과 대조하여 확인합니다.
MapSmith MCP 설치 방법
형식 확인: 2026년 11월 10일Cursor에 추가Goose에 추가Claude Code에 MapSmith MCP 설치
$ claude mcp add mapsmith -- uvx mapsmithCursor에 MapSmith MCP 설치
~/.cursor/mcp.json
{
"mcpServers": {
"mapsmith": {
"command": "uvx",
"args": [
"mapsmith"
]
}
}
}VS Code에 MapSmith MCP 설치
.vscode/mcp.json
{
"servers": {
"mapsmith": {
"type": "stdio",
"command": "uvx",
"args": [
"mapsmith"
]
}
}
}VS Code CLI에 MapSmith MCP 설치
$ code --add-mcp '{"name":"mapsmith","type":"stdio","command":"uvx","args":["mapsmith"]}'Claude Desktop에 MapSmith MCP 설치
claude_desktop_config.json
{
"mcpServers": {
"mapsmith": {
"command": "uvx",
"args": [
"mapsmith"
]
}
}
}Codex에 MapSmith MCP 설치
$ codex mcp add mapsmith -- uvx mapsmithCodex config.toml에 MapSmith MCP 설치
~/.codex/config.toml
[mcp_servers.mapsmith] command = "uvx" args = ["mapsmith"]
Gemini CLI에 MapSmith MCP 설치
$ gemini mcp add -s user mapsmith uvx -- mapsmithGemini settings.json에 MapSmith MCP 설치
~/.gemini/settings.json
{
"mcpServers": {
"mapsmith": {
"command": "uvx",
"args": [
"mapsmith"
]
}
}
}MapSmith MCP 도구 29
README에서 감지됨describe_datasetbuffer_layerclip_layeroverlay_layersdissolve_layernearest_joinexplode_layermeasure_areamerge_layerssimplify_layercentroid_layerconvert_format
| describe_dataset | — | |
| buffer_layer | — | |
| clip_layer | — | |
| overlay_layers | — | |
| dissolve_layer | — | |
| nearest_join | — | |
| explode_layer | — | |
| measure_area | — | |
| merge_layers | — | |
| simplify_layer | — | |
| centroid_layer | — | |
| convert_format | — |
도구 29개 모두 보기도구 수 줄이기
reproject_layerspatial_joinrun_sqlzonal_statisticshillshadeslopeaspectflow_accumulationwatershedpreview_mapvalidate_planbrief_planexecute_planget_provenancelist_operationsrun_operationserver_info
| reproject_layer | — | |
| spatial_join | — | |
| run_sql | — | |
| zonal_statistics | — | |
| hillshade | — | |
| slope | — | |
| aspect | — | |
| flow_accumulation | — | |
| watershed | — | |
| preview_map | — | |
| validate_plan | — | |
| brief_plan | — | |
| execute_plan | — | |
| get_provenance | — | |
| list_operations | — | |
| run_operation | — | |
| server_info | — |
구성
MAPSMITH_DISCOVERY_LOGMAPSMITH_WORKSPACEConfine all reads/writes to this directory (path jail + sandboxed SQL engine)
MAPSMITH_WORKSPACEConfine all reads/writes to this directory (path jail + sandboxed SQL engine)
| 이름 | 설명 | 유형 |
|---|---|---|
| MAPSMITH_DISCOVERY_LOG | — | |
| MAPSMITH_WORKSPACE | Confine all reads/writes to this directory (path jail + sandboxed SQL engine) | |
| MAPSMITH_WORKSPACE | Confine all reads/writes to this directory (path jail + sandboxed SQL engine) |
자동 검사
2026년 11월 12일에 실행됨전체 보고서 →자동 검사를 통과했습니다. 이는 보안 감사가 아닙니다. 코드를 직접 읽고 서버에 필요한 최소한의 권한만 부여하십시오. 검사 방식
- 소스 저장소 연결 및 접근 가능통과
- 오픈 소스 라이선스 (OSI)통과
- 최근 1년 내 커밋 또는 릴리스통과
- 패키지 이름이 인기 패키지와 유사하지 않음통과
- 원격 엔드포인트가 HTTPS 사용검사되지 않음
- 파괴적인 도구에 라벨 표시됨검사되지 않음
- 모델에 대한 숨겨진 지침 없음통과
- 패키지 및 해당 종속성에 중요한 보안 권고 없음통과
README
GitHub에서 보기MapSmith
Ask a question about your data. Get an analysis — and the record that proves it.
Most GIS servers hand an agent a tool to run. MapSmith takes the question, works out the analysis — usually several operations, in an order that has to be right — validates the plan before a single file is touched, runs it, and leaves a record of every step — which bytes went in, with which parameters and CRS decisions, and which checks ran — that somebody else can read afterwards and rerun to confirm.
Try it
Point any MCP client at MapSmith (one JSON block) and ask, in your own words:
Which parcels lie within 1.5 km of the river and sit at 120 m or lower? Give me the mean elevation and the ground area of each.
That is five operations, two coordinate systems and forty-eight checks. What actually happens is below — the plan, the step that gets rejected for reading something a later step produces, the CRS decision behind every metric step, and an answer you can work out on paper before MapSmith sees the files. That rejection is structural, and so is every other one: a plan that is well formed and answers the wrong question runs instead, which is measured and written down further down this page.
The operations are executed by GeoPandas, DuckDB Spatial, exactextract and Whitebox Workflows — never by the model that asked for them. Every dataset lands on disk next to a lineage manifest: inputs with checksums, the exact parameters, the CRS decisions and why, engine versions, and the deterministic checks that ran on the result.
mapsmith.dev — a real terrain analysis and the manifest that came with it. Both are build products: the figure is rendered from GeoTIFFs MapSmith writes, so the page cannot drift from what the software does.
The manifest is a specified format, not
MapSmith's private output: JSON Schema, a toolchain-free validator, a conformance suite, and a
hundred-line emitter that never imports MapSmith. Records carry spec_version, and CI validates
real MapSmith output against the spec's own validator. The specification is archived and citable
as 10.5281/zenodo.22205213, a concept DOI that
resolves to the latest archived draft. Records written by 0.8.0 to 0.12.1 declare 1.0.0-draft.10, and
0.7.x's declare 1.0.0-draft.8; if the DOI shows an earlier one, the archive has not caught up, and the
repository holds the text they are written against. The names MapSmith puts
in a record beyond the ones the specification defines — every extension check name, and every
extension field in crs_decisions with the reason it is not a synonym of a key the specification
already has — are listed in docs/manifest-vocabulary.md,
generated from the source rather than maintained by hand.
Evidence before promises. A correctness suite in its own organisation, Argleton, grades MapSmith on thirty-three traps whose answers are computed on paper before any system runs: 0.00 silent errors, nothing skipped, against 0.8788 for the obvious way of writing the same code. The runs from 2026-08-30 to 2026-09-15 said 0.00 too, and on one trap they were wrong: its truth was wrong in MapSmith's favour for 26 days, the error was ours twice over, and the erratum says so. Every run since 2026-09-26, the current one included, is scored against the corrected truth. Getting here cost seven defects the suite sent back, and they are listed. Alongside it: an A/B on GABench whose headline is a null result — with the analysis that took our own positive number apart — notebooks on a real USGS DEM of Mount St. Helens, an in-chat map panel that shows the verification status of every layer it draws, and a measurement of our own tool discovery that retracted two numbers this page had already published — including the one in the bullet list below.
A whole analysis, start to finish
This is the thing MapSmith is for, and it is not a single tool call. One question — six parcels, a river, an elevation grid — becomes a plan of five operations chosen out of 81, validated before anything runs, executed step by step, and recorded: the search that narrowed the catalogue, the arguments that mattered, the CRS decision behind each metric step, and the checks that ran on every result.
Nothing in this section is drawn. benchmarks/worked_example.py builds fixtures whose answer can
be worked out on paper, asks the catalogue in the words of the problem, validates and runs the
plan, reads the manifests, and writes what follows; tests/test_worked_example.py fails if this
page and that script disagree. The position column is BM25's rather than the default engine's,
because a published figure should not depend on whether a model download succeeded on the machine
that built the page — the narrowing, which is the point, is identical on both. Two things worth watching: the middle column, where the catalogue
goes from 81 operations to a handful the caller can read; and the CRS column, where every
metric operation says which coordinate system it moved the data into and why.
flowchart TB
ASK["<b>Parcels within 1.5 km of the river whose mean ground elevation is at most 120 m, with the elevation and the ground area of each</b>"]
ASK --> PLAN{{"plan validated<br/>before anything runs"}}
PLAN -. "rejected: FORWARD_REFERENCE" .-> BAD["'mask_path' references '$buffer' which runs later — move step 'buffer' before 'near'"]
BAD:::bad
BUFFER["<b>buffer_layer</b><br/>81 operations → 28 candidates → chosen<br/>CRS EPSG:32610<br/>9/9 checks"]
PLAN --> BUFFER
NEAR["<b>clip_layer</b><br/>81 operations → 13 candidates → chosen<br/>CRS EPSG:4326<br/>12/12 checks"]
BUFFER --> NEAR
HEIGHT["<b>zonal_statistics</b><br/>81 operations → 2 candidates → chosen<br/>CRS EPSG:4326<br/>7/7 checks"]
NEAR --> HEIGHT
AREA["<b>measure_area</b><br/>81 operations → 25 candidates → chosen<br/>CRS WGS 84 (ellipsoidal)<br/>10/10 checks"]
HEIGHT --> AREA
FILTER["<b>select_features</b><br/>81 operations → 25 candidates → chosen<br/>CRS EPSG:4326<br/>10/10 checks"]
AREA --> FILTER
OUT[["3 parcels, each with elevation and ground area"]]
FILTER --> OUT
classDef bad stroke-dasharray: 4 3
| what the agent asks for | it declares | candidates | picked | at position |
|---|---|---|---|---|
| “everything within one and a half kilometres of the river” | vector, dataset:vector, 1 dataset(s), line | 28 of 81 | buffer_layer |
2 |
| “keep only the parcels that fall inside that strip” | vector, dataset:vector, 2 dataset(s), polygon | 13 of 81 | clip_layer |
1 |
| “how high is the ground under each of these parcels” | raster, dataset:vector, 2 dataset(s), polygon | 2 of 81 | zonal_statistics |
2 |
| “how big is each one on the ground” | vector, dataset:vector, 1 dataset(s), polygon | 25 of 81 | measure_area |
1 |
| “drop the ones where the ground is above 120 metres” | vector, dataset:vector, 1 dataset(s), polygon | 25 of 81 | select_features |
1 |
| step | operation | arguments that mattered | CRS decision, recorded | checks |
|---|---|---|---|---|
| buffer | buffer_layer |
distance_meters=1500 |
EPSG:32610 — estimated UTM zone for metric buffering on a geographic CRS |
9/9 |
| near | clip_layer |
mask_path=$buffer |
EPSG:4326 — the mask is already in the input layer's CRS; nothing was reprojected |
12/12 |
| height | zonal_statistics |
zones_path=$near, stats=['mean', 'min'] |
EPSG:4326 — zones and raster share the same CRS |
7/7 |
| area | measure_area |
input_path=$height, method=geodesic |
WGS 84 (ellipsoidal) — ground area computed on the WGS 84 ellipsoid, which is where the coordinates are put first; no map plane is involved, so no projection distortion enters |
10/10 |
| filter | select_features |
input_path=$area, by=field_between, field=mean, maximum=120 |
EPSG:4326 — no CRS change: selecting rows does not touch coordinates |
10/10 |
Every step is inside the plan, the last one included: select_features took 4 rows and returned 3, with a manifest like every other write. This step used to run outside the plan, because the only operation that could answer it was run_sql — which takes its inputs inside a SQL string, declares zero datasets, and therefore cannot join the plan's dataflow. That boundary is deliberate and has not moved: substituting $step into arbitrary strings would be a grammar in which a planner assembles a path out of text. What changed is that it is no longer the only way to ask.
The answer, which can be worked out on paper before MapSmith sees the files: the parcels are squares of 0.0015° at 46.2°N, so each is about 119 m by 167 m, and the elevation ramps west to east across the fixture.
| name | mean | min | area_m2 |
|---|---|---|---|
| North Field | 104.85 | 104.14 | 19303.33 |
| Mill Meadow | 110.51 | 109.8 | 19303.33 |
| Old Orchard | 117.58 | 116.87 | 19303.33 |
The rejected plan is the honest half. Steps in the wrong order are the dominant failure class in
the agent benchmark, so the example includes one and shows what the validator says about it,
before any file is touched. It earned that place while this was being written: the first version
of the plan passed distance_m where the operation declares distance_meters, and the validator
named the argument and listed the three it accepts.
You can step through this on mapsmith.dev, one beat at a time, along with a second case that GitHub cannot show as well: a file MapSmith refuses. Both are executed while that page is built. The residue of the second one is here, because it is the half nobody demonstrates.
What it declines to answer. Asked “How high is the ground at each of these two wells?” against a GeoTIFF that opens in every viewer:
| describing the file first | |
|---|---|
georeferencing_source |
sidecar (.aux.xml) |
georeferencing_sidecar_present |
terrain.tif.aux.xml |
georeferencing_internal_would_give |
cell 10 x 10 at (500000, 5030000) |
terrain.tif is georeferenced twice and nobody chose: the GeoTIFF's own tags say cell 10 x 10 at (500000, 5030000), and terrain.tif.aux.xml beside it says something else. GDAL prefers the sidecar, which is correct — that is how an override works — but sample_raster_at_points would then report numbers from a file you did not name, and this record could not say which. describe_dataset lists both. To choose, either remove the sidecar or set GDAL_GEOREF_SOURCES=INTERNAL for a run that must use the file's own.
A number would have come back. It would have been read off a grid a hundred kilometres away and at twice the cell size, with nothing in the record able to say which of the two readings produced it.
And the analysis comes back out of the file. Given only the last output of the five-step run above — no plan, no filenames, no field pointing at another record — the chain is recovered by hashing it and following every input digest back:
5 operations recovered back to 3 original dataset(s), every one passing its critical checks.
<- select_features 10 checks
<- measure_area 10 checks
<- zonal_statistics 7 checks
<- clip_layer 12 checks
<- buffer_layer 9 checks
(original) elevation.tif
(original) parcels.gpkg
(original) river.gpkg
Quickstart
Add MapSmith to any MCP client over stdio (Claude Desktop, Claude Code, Cursor, VS Code):
{
"mcpServers": {
"mapsmith": {
"command": "uvx",
"args": ["mapsmith"]
}
}
}
Docker is the supported path, and confines the server to the directory you mount:
{
"mcpServers": {
"mapsmith": {
"command": "docker",
"args": ["run", "-i", "--rm",
"-v", "/absolute/path/to/your/data:/data",
"-e", "MAPSMITH_WORKSPACE=/data",
"ghcr.io/mapsmith-ai/mapsmith"]
}
}
}
One-click installs:
or from a terminal: code --add-mcp '{"name":"mapsmith","command":"uvx","args":["mapsmith"]}'
To check it runs before wiring a client, uvx mapsmith starts the server on stdio
(Ctrl-C to quit) — it speaks MCP, not a CLI, so a silent prompt means it is working.
This page describes 0.12.1, which is what that command installs. When main runs ahead of
the published artifact this paragraph says so and names the difference — a reader should never
have to find out by calling a tool that is not there. New in 0.12.1
(release notes): semantic verification of every primary,
secondary and native geometry CRS declaration in calculate_field. New in 0.12.0
(release notes): restricted numeric and boolean field
calculation, executable typed-DAG input contracts and corrected multi-step method-choice
benchmarks. New in 0.11.0
(release notes): the validated project brief, bounded agent
repair, vector/raster conversion and QGIS native:buffer, taking
the catalogue to 80 operations behind 29 tools. If you run 0.10.0 or earlier under
MAPSMITH_WORKSPACE, update: a coordinate reference system -- a tool argument, or the one a
file declares -- could make PROJ read a file outside the workspace or open a network share, and
in run_sql DuckDB's own PROJ could do the same; 0.10.0 closed that for tool arguments and for
GeoPackage, shapefile and GeoParquet inputs, and 0.10.1 for every other format MapSmith accepts
-- under a workspace it now reads each input's coordinate system before GDAL opens the file,
and refuses a format it does not recognise. SECURITY.md has all of it. From
0.9.0, 0.10.0 also: every operation that brings a vector input onto another CRS -- the eight
that align a second input, and the samplers, zones, masks, pour points and stations of the
raster ones -- chooses the datum transformation the way reproject_layer does, which moves
their results where PROJ's own pick was the ballpark and a covering operation or a Greenwich
twin exists (Monte Mario from Rome: up to about 80 m); a datum shift a CRS itself declares
(+towgs84) is no longer recorded as a ballpark; nearest_neighbour_index works in linear
memory and refuses a feature with no position; under a workspace run_sql runs only SELECT,
with coordinate systems and dataset paths as literals; and 103 catalogue phrasings from the QGIS
Training Manual's exercises moved the discovery figures on this page. From 0.8.0, 0.9.0 adds
apportion_by_area, which moves counts and rates from the zones they are reported in to zones
that cross them, and makes a record's transformation describe what PROJ did to the data's own
coordinates rather than to a point chosen from the CRS. That changes what reproject_layer
writes in two cases: coordinates outside every published operation's area are no longer
moved by an operation from elsewhere, and a datum counted from another meridian whose
operations are published only for its Greenwich twin -- Monte Mario from Rome, EPSG:4806 --
goes through that twin. The changelog names both. If you run 0.4.0 to 0.6.1, 0.7.0 and
later change answers you have: every position read from a point-registered raster
(AREA_OR_POINT=Point, the USGS and Copernicus DEMs) was half a cell off, and the
changelog says which operations and why. From 0.7.x, 0.8.0 adds
cluster_points_by_distance and a geometry facet for the catalogue search, digests a
shapefile with its attributes and its CRS, reads any single-layer file geodatabase, and fixes
operations that accepted the wrong geometry and answered anyway (zonal_statistics on line
zones returned their length as a pixel count).
If you read manifests, 0.10.0 renames no key: records still declare 1.0.0-draft.10.
Two keys of MapSmith's own are new inside crs_decisions.transformation:
x-mapsmith:shift_from_crs_definition, when the shift is written into the CRS itself, and
x-mapsmith:default_was_ballpark, when the engine's own choice would have applied no shift.
If you read manifests, 0.9.0 renames no key: records still declare 1.0.0-draft.10.
Three keys of MapSmith's own are new: crs_decisions.x-mapsmith:areas_measured_on, from
apportion_by_area, and x-mapsmith:ballpark_share and x-mapsmith:outside_area_of_use
inside transformation; so are three check names, all from apportion_by_area. A run that
fails where an earlier run's dataset still sits writes its record beside the earlier one, as
<output>.failed.provenance.json, instead of replacing it.
If you read manifests, 0.8.0 renames one key: records declare 1.0.0-draft.10, and a
second input's own georeferencing facts, inputs[].x-mapsmith:environment on 0.7.x, are
inputs[].environment, the name the specification gives them since draft.10. Each input of
an operation that reads more than one dataset also says which argument read it
(inputs[].argument).
If you read manifests, 0.7.x renames no key: records declare spec_version
1.0.0-draft.8, two drafts past 0.6.0's draft.6, and both drafts only narrowed where a
producer may add a key of its own — which MapSmith already did the narrower way. From 0.5.x,
0.6.0 renamed x-mapsmith:round_trip to round_trip; from 0.4.0, 0.5.0 had renamed eight
crs_decisions keys, the top-level mapsmith_version (read producer.version), two fields of
the round trip, two of repairs[] and engine.geometry_library. The changelog lists each with
what to read instead.
Then ask your agent things like:
"Take parcels.gpkg, keep only the parcels within 300 m of the river in rivers.gpkg, and give me the result with the analysis lineage."
The Docker image includes the [raster] and [whitebox] extras. With uvx, pick your
own: uvx --from "mapsmith[raster,whitebox]" mapsmith. Docker — or uvx on a machine
with working wheels — is the only supported installation path: geospatial native
dependencies across three OSes are a support black hole, and issues about broken local
environments will be redirected here.
Two things about the image, because they change what happens on your machine: it sets
MAPSMITH_WORKSPACE=/data itself (the -e above is explicit, not required) and runs as
uid 1000, so pass --user $(id -u):$(id -g) if the directory you mount belongs to another
user; and it is built for amd64 only, so on Apple Silicon it runs under emulation.
MapSmith MCP 질문
MapSmith MCP를 Claude Code에 추가하려면 어떻게 하나요?
터미널에서 claude mcp add mapsmith -- uvx mapsmith를 실행합니다. 모든 프로젝트에서 사용할 수 있게 하려면 --scope user를 추가합니다.
MapSmith MCP를 Cursor에 추가하려면 어떻게 하나요?
~/.cursor/mcp.json를 열고 mcpServers 아래에 서버를 추가합니다. 위 설치 섹션에 표시된 Cursor 구성을 사용합니다.
MapSmith MCP는 어떤 도구를 제공하나요?
29개 도구를 제공합니다. 예: describe_dataset, buffer_layer, clip_layer, overlay_layers, dissolve_layer.
MapSmith는 분석의 정확성을 어떻게 보장하나요?
MapSmith는 실행 전에 분석 계획을 검증하며 입력 체크섬, 파라미터, CRS 결정 사항 및 결정론적 확인 항목을 포함하는 리니지 매니페스트를 생성합니다.
GIS 작업을 실행하는 데 어떤 엔진이 사용되나요?
GeoPandas, DuckDB Spatial, exactextract, Whitebox Workflows를 사용하여 작업을 수행합니다.
MapSmith는 충돌하는 지오레퍼런싱 데이터를 처리할 수 있나요?
명확한 기록 없이 잘못된 수치를 보고하는 것을 방지하기 위해, 파일에 지오레퍼런싱이 중복 적용된 경우(예: 내부 태그와 사이드카 파일 모두 사용) MapSmith는 응답을 거부합니다.