# gdal-mcp MCP server exposing GDAL/Rasterio operations to AI agents, with a reflection middleware that requires structured justification before executing operations whose methodology matters (CRS choice, resampling method, query extent). [![CI](https://github.com/Wayfinder-Foundry/gdal-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Wayfinder-Foundry/gdal-mcp/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [![FastMCP 2.0](https://img.shields.io/badge/FastMCP-2.0-blue.svg)](https://github.com/jlowin/fastmcp) [![PyPI Downloads](https://static.pepy.tech/personalized-badge/gdal-mcp?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/gdal-mcp) ## Install ### Via uvx (recommended) ```bash uvx --from gdal-mcp gdal --transport stdio ``` ### Via Docker ```bash docker build -t gdal-mcp . docker run -i gdal-mcp gdal --transport stdio ``` ### Local development ```bash git clone https://github.com/Wayfinder-Foundry/gdal-mcp.git cd gdal-mcp uv sync uv run gdal --transport stdio ``` ## Configure your MCP client ### Claude Desktop Add to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`, Linux: `~/.config/Claude/`): ```json { "mcpServers": { "gdal-mcp": { "command": "uvx", "args": ["--from", "gdal-mcp", "gdal", "--transport", "stdio"], "env": { "GDAL_MCP_WORKSPACES": "/path/to/your/geospatial/data" } } } } ``` Restart Claude Desktop. The MCP server indicator should appear, and the `raster_*` and `vector_*` tools become available. ### Workspace scoping `GDAL_MCP_WORKSPACES` is a colon-separated list of directories the server is allowed to touch. If unset, all paths are allowed and a warning is logged. Optional tool-surface flags: `RASTER=true`, `VECTOR=true`. See [docs/ENVIRONMENT_VARIABLES.md](docs/ENVIRONMENT_VARIABLES.md) for the full set. ## Tools - **Raster:** `raster_info`, `raster_convert`, `raster_reproject`, `raster_stats`, `raster_query` - **Vector:** `vector_info`, `vector_convert`, `vector_reproject`, `vector_clip`, `vector_buffer`, `vector_simplify`, `vector_query` - **Resources:** catalog (`workspace://...`), metadata (`metadata://...`), reference (`reference://...`), query results (`query://result/{id}`) - **Prompts:** `justify_crs_selection`, `justify_resampling_method`, `justify_query_extent` (and more under `src/prompts/`) See [TOOLS.md](TOOLS.md) for parameters, return shapes, and worked examples. ## The reflection middleware Tools whose methodology matters refuse to execute until the calling agent produces a structured justification. The flow is: 1. Agent calls e.g. `raster_reproject(dst_crs="EPSG:3857", resampling="cubic", ...)`. 2. Middleware checks `.preflight/justifications/{domain}/` for a matching hash. 3. On miss, the call raises `ToolError` with a hint pointing at the relevant prompt (e.g. `justify_crs_selection`). 4. Agent calls the prompt, fills out the `Justification` schema (intent, alternatives considered, choice, tradeoffs, confidence), and re-invokes the tool with a `__reflection` payload. 5. The justification is cached domain-keyed, so a CRS rationale for EPSG:3857 satisfies both `raster_reproject` and `vector_reproject` on subsequent calls. See [docs/REFLECTION.md](docs/REFLECTION.md) for the schema and cache layout, and [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for why this exists. ## Documentation - [TOOLS.md](TOOLS.md) — tool reference - [docs/ENVIRONMENT_VARIABLES.md](docs/ENVIRONMENT_VARIABLES.md) — runtime config - [docs/REFLECTION.md](docs/REFLECTION.md) — reflection middleware internals - [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) — design philosophy and direction - [docs/ROADMAP.md](docs/ROADMAP.md) — release history and direction - [CHANGELOG.md](CHANGELOG.md) — versioned changelog - [CONTRIBUTING.md](CONTRIBUTING.md) — contribution guide ## Troubleshooting **`Access denied: path outside allowed workspaces`** — set `GDAL_MCP_WORKSPACES` to include the directory in question (see "Workspace scoping"). **MCP client doesn't see the server** — verify `uvx --from gdal-mcp gdal --help` runs on its own, then restart the client after editing its config file. ## License MIT — see [LICENSE](LICENSE). Built on [FastMCP](https://github.com/jlowin/fastmcp), [Rasterio](https://github.com/rasterio/rasterio), [pyogrio](https://github.com/geopandas/pyogrio), and [Shapely](https://github.com/shapely/shapely).