GeoQuery AI
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@GeoQuery AIShow me all coffee shops within 1 mile of Central Park"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
GeoQuery AI — Conversational Spatial SQL Engine via MCP Server
Ask questions in plain English ("hospitals within 2km of downtown Seattle") and get
live PostGIS query results rendered as GeoJSON on a map. A Claude agent, embedded in
a Next.js app, translates the question into tool calls against a custom Node.js
MCP server (@modelcontextprotocol/sdk) that executes validated spatial SQL.
Architecture
apps/web— Next.js (App Router) chat UI + MapLibre GL JS map. Hosts the Claude tool-use agent loop (app/api/chat/route.ts) that acts as an MCP client.services/mcp-server— Node MCP server exposing three tools:spatial_buffer,isochrone_query, andpostgis_raw_sql, backed by a read-only Postgres role.db— PostGIS schema (OSM POIs + Census block groups) and a city-agnostic ingestion pipeline (flag-driven:--region,--pbf,--tiger-shp,--acs-csv).
Related MCP server: PostGIS Yukon MCP
Status
This project is being built incrementally across a series of sessions. See the sections below as they're filled in; this README will grow into a full setup guide.
Setup
1. Database
cp .env.example .env # fill in POSTGRES_PASSWORD, MCP_RO_PASSWORD, ANTHROPIC_API_KEY
docker compose up -d
docker compose exec postgis pg_isready -U geoquery_admin -d geoquery # wait for "accepting connections"On a fresh volume, Docker auto-applies everything in db/schema/ via
docker-entrypoint-initdb.d (extensions, tables, indexes, and the read-only
geoquery_ro role). If you're re-provisioning an existing database instead
(schema changes after the first run), apply them manually:
./db/migrate.shmigrate.sh tracks applied migrations in a schema_migrations table, so
it's safe to run any time — it only applies files that haven't run yet.
Re-running
docker compose updoes not re-applydb/schema/— those init scripts only fire on a brand-new volume. To wipe and start over:docker compose down -v(destroys all data). To apply new schema files without losing data, use./db/migrate.shinstead.
2. Data ingestion
City-agnostic and flag-driven — nothing about a specific city is hardcoded
in any ingestion script; see db/ingest/config/example-city.env
for where to download a PBF/TIGER shapefile/ACS CSV and what shape they need.
pip install -r db/ingest/requirements.txt # needed for census/join_acs.py
./db/ingest/ingest.sh \
--region seattle-wa \
--pbf ./data/seattle.osm.pbf \
--tiger-shp ./data/tl_2023_53_bg.shp \
--acs-csv ./data/seattle_acs.csv \
--db-url "$DATABASE_URL"Requires osm2pgsql (OSM loading) and ogr2ogr/GDAL (TIGER shapefile
loading) installed locally. --acs-csv is optional — without it, block
groups load with geometry only and NULL demographic columns. Re-running
ingest.sh for the same --region is safe (each step deletes that region's
existing rows before re-inserting); re-running for a different --region
just adds more rows alongside it, so multiple cities can coexist.
3. Running the app
The MCP server (services/mcp-server) is feature-complete for this phase —
all three tools (spatial_buffer, isochrone_query, postgis_raw_sql) are
registered and can be built/run/tested standalone; see
its README for how to exercise them
manually with the MCP Inspector.
The Next.js app (apps/web) has a working map now — a real MapLibre GL map
on a free vector basemap, in the split-pane layout's right pane:
npm run --workspace @geoquery/web dev # http://localhost:3000The chat UI (Session 11), and the MCP client + Claude agent loop wiring it all together (Sessions 12-15), are still to come — nothing produces real GeoJSON for the map to render yet.
Verified vs. user-verified
Some parts of this project (Docker provisioning, real OSM/Census data ingestion, live end-to-end chat queries) require a local machine with Docker and downloaded data extracts, and can't be verified in a sandboxed dev environment.
Verified so far: shell script syntax (
bash -n) for all.shfiles; Python syntax (py_compile) forjoin_acs.py; SQL DDL reviewed against PostGIS/Postgres documentation;services/mcp-serveractually builds and runs — confirmed fail-fast behavior on missing config, and used a real MCP client to confirm all three tools' Zod schemas convert to JSON Schema correctly over the wire and that DB-dependent calls fail cleanly as anisErrorresult against an unreachable Postgres rather than crashing.services/mcp-serveralso has a real automated test suite now (48 tests,npm run --workspace services/mcp-server test, no database needed — see its README): thepostgis_raw_sqlSQL guard's 34 cases (stacked queries, comment-hidden payloads, CTE-disguised writes, disallowed tables), andspatial_buffer/isochrone_query's query-construction and GeoJSON assembly logic against a mocked database.apps/webis also verified with a real headless browser (Playwright driving system Chrome), not just typecheck/build/curl: the MapLibre map actually renders and fills its pane, attribution text is correct, clicking a point feature opens a popup with the right content while clicking a polygon doesn't, and (after finding and fixing a Turbopack/maplibre-gl worker-loading issue along the way — seeapps/web/README.md) there are no console errors left besides an unrelated, pre-existingfavicon.ico404.Not verified — needs your machine:
docker compose upactually provisioning PostGIS;osm2pgsql/ogr2ogrruns against real data (the Lua flex tag-transform script in particular — its API varies across osm2pgsql versions and hasn't been run against a live import); the ACS join's SQL against real rows; actualspatial_buffer/isochrone_querycalls against real, ingested data (doST_DWithin/ST_Intersectsreturn the right rows, do the GIST indexes actually get used — check withEXPLAIN ANALYZE); and the end-to-end chat → MCP → PostGIS query flow once those pieces exist.
This server cannot be deployed
Maintenance
Related MCP Connectors
Ask in plain English, get a rendered, shareable map from live public data. 24 geospatial tools.
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Generate and run high performance queries on open and private spatial data at-scale in the cloud
Ask questions in plain language, get answers from your business database. No SQL required.
Related MCP Servers
- FlicenseBqualityDmaintenanceEnables natural language interaction with PostgreSQL databases, converting questions to safe SQL queries and executing them with result validation.1-
- AlicenseNot gradedqualityFmaintenanceEnables natural language and geospatial queries on PostGIS databases, with 32 tools for spatial analysis, geometry operations, and database management.5MIT
- AlicenseNot gradedqualityDmaintenanceEnables natural language querying of PostgreSQL databases with intelligent SQL generation using LLMs.1Apache 2.0
- FlicenseBqualityBmaintenanceProvides read-only query tools over OpenStreetMap data in PostGIS, enabling natural language queries for features, categories, and spatial analysis.7-