Cloud-Native Geospatial MCP Server with embedded DuckDB spatial compute and GeoFlow DAG validation
A Cloud-Native Geospatial MCP Server that brings spatial analytics to AI agents — not as a dumb search proxy, but as a constrained analytical engine with type-safe workflow validation.
| Feature | Typical STAC MCP | geoflow-stac-mcp |
|---|---|---|
| STAC Search | ✅ Pass-through to API | ✅ Structured search with validation |
| Spatial Compute | ❌ Download COGs manually | ✅ Embedded DuckDB spatial engine |
| Workflow Validation | ❌ Hope the LLM gets it right | ✅ GeoFlow DAG prevents type errors |
| Error Recovery | ❌ Cryptic SQL errors | ✅ LLM-readable correction hints |
AI Agent (Claude, GPT, etc.)
│
▼ MCP over stdio
┌──────────────────────────────────┐
│ geoflow-stac-mcp │
│ │
│ 1. GeoFlow DAG Validation │
│ → Acyclicity check │
│ → Spatial type compatibility │
│ │
│ 2. STAC API Search │
│ → Microsoft Planetary Computer│
│ │
│ 3. DuckDB Spatial Engine │
│ → ST_Area, ST_Intersects... │
│ → In-memory, zero-copy │
└──────────────────────────────────┘
# Build
cargo build --release
# Run (connects to MCP client via stdio)
./target/release/geoflow-stac-mcp
# Explain stdio usage
./target/release/geoflow-stac-mcp --help
# Print version
./target/release/geoflow-stac-mcp --versionWhen started from a normal shell, the server will wait for an MCP host to connect over stdio. This is expected behavior and does not indicate a hang.
| Symptom | Meaning | What to do |
|---|---|---|
MCP server ready on stdio ... and then no further output |
Server is idle and waiting for an MCP host client | Start it from Claude Desktop or another MCP host |
Exit code 0 after a disconnect |
Normal client shutdown/EOF | No action needed |
Exit code 1 with Failed to start MCP server on stdio transport |
Real startup/protocol failure | Re-run with RUST_LOG=debug and inspect stderr |
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"geoflow-stac": {
"command": "/path/to/geoflow-stac-mcp"
}
}
}| Parameter | Type | Description |
|---|---|---|
bbox |
[f64; 4] |
Bounding box [west, south, east, north] in WGS84 |
collection |
string |
STAC collection (e.g., sentinel-2-l2a) |
spatial_operation |
string |
Spatial operation description (e.g., compute ST_Area) |
Vector: ST_Area, ST_Intersects, ST_Buffer, ST_Centroid, ST_Union, ST_Contains, ST_Distance, ST_Transform
Raster: NDVI, BandMath, ZonalStats
Utility: ST_AsGeoJSON, COUNT
Tool: stac_compute_query
Input: {
"bbox": [-105.5, 39.5, -104.5, 40.5],
"collection": "sentinel-2-l2a",
"spatial_operation": "compute ST_Area of the scene footprints"
}
Before any SQL touches DuckDB, the requested workflow is parsed into a Directed Acyclic Graph and validated:
- Acyclicity — No circular dependencies (Kahn's algorithm)
- Type Compatibility — Vector ops get vector data, raster ops get raster data
If validation fails, the error message is structured for LLM self-correction:
GEOFLOW_TYPE_MISMATCH: Node 'step1' applies operator 'ST_Area' (expects Vector input)
to source data of type Raster.
To fix: ST_Area operations require Vector data. If your source data is Raster,
use a raster (e.g., NDVI, BandMath, ZonalStats) operator instead.
- Rust (Edition 2021) — Performance, safety, zero-cost abstractions
- rmcp v0.16.0 — Official Rust MCP SDK
- DuckDB v1.4.4 — Embedded analytical database with spatial extension
- reqwest — Async HTTP for STAC API calls
- tokio — Async runtime
MIT