mcp-postgis
A Model Context Protocol server that exposes PostGIS to MCP-aware clients (Claude Desktop, Claude Code).
Lets Claude introspect your spatial database, run safe spatial queries, and publish results as views that QGIS picks up automatically — so an analyst can describe a layer in plain language and have it land in their QGIS browser.
Install
pip install mcp-postgis # or `uv tool install mcp-postgis`
Quick start (Docker PostGIS + Claude Desktop)
- Run PostGIS:
bash docker run --name postgis -e POSTGRES_PASSWORD=postgres \ -p 5432:5432 -d postgis/postgis:16-3.4 - Create a dedicated read-only role (see docs/security.md for read_write and admin variants):
sql CREATE ROLE mcp_postgis_ro LOGIN PASSWORD 'change-me'; GRANT CONNECT ON DATABASE mydb TO mcp_postgis_ro; GRANT USAGE ON SCHEMA public, app TO mcp_postgis_ro; GRANT SELECT ON ALL TABLES IN SCHEMA public, app TO mcp_postgis_ro; ALTER DEFAULT PRIVILEGES IN SCHEMA public, app GRANT SELECT ON TABLES TO mcp_postgis_ro; - Add to your
claude_desktop_config.json:json { "mcpServers": { "postgis": { "command": "uvx", "args": ["mcp-postgis"], "env": { "MCP_POSTGIS_DATABASE_URL": "postgresql://mcp_postgis_ro:change-me@localhost:5432/mydb", "MCP_POSTGIS_MODE": "read_only" } } } } - Restart Claude Desktop. The server's tools appear in the model picker.
Using with Claude Code (Linux/macOS/Windows)
Claude Desktop is macOS/Windows only — on Linux (or anywhere you use the CLI), register the server with Claude Code instead:
claude mcp add --transport stdio \
--env MCP_POSTGIS_DATABASE_URL="postgresql://mcp_postgis_ro:change-me@localhost:5432/mydb" \
--env MCP_POSTGIS_MODE=read_only \
--scope user \
postgis \
-- uvx mcp-postgis
Restart claude, then /mcp lists the server and its tools. Use --scope user
(not project) so your connection string isn't committed to a repo.
Modes
| Mode | What it can do |
|---|---|
read_only |
Default. Introspection + SELECT + spatial analysis. No writes, no DDL. |
read_write |
Above + create/refresh/drop views in MCP_POSTGIS_LAYER_SCHEMA (mcp_layers by default). |
admin |
Anything the connected role can do. Use only for one-off admin tasks; the role still gates. |
Tools
Introspection: list_schemas, list_tables, describe_table,
list_geometry_columns, list_spatial_indexes, list_extensions
Query: execute_sql, explain, sample_table
Spatial analysis: features_in_bbox, features_in_polygon,
nearest_features, within_distance, buffer, intersect_layers
Geometry operations: transform_srid, centroid, point_on_surface,
area, length, simplify, is_valid, make_valid, bbox
Data quality: check_geometry_validity
Import / export: import_geojson (load a GeoJSON FeatureCollection into a
table), export_geojson, export_wkt
Layer publishing (QGIS bridge): create_layer (with geometry_type
filter), refresh_layer, list_layers, describe_layer, drop_layer
Resources: postgis://schemas, postgis://schema/{schema}/{table},
postgis://layers · Prompts: analyze-layer, nearest-things,
within-radius, compare-layers
QGIS integration
After running the server in read_write mode and asking Claude to "publish that as a layer named hotels_near_coast", point QGIS at the same database (or use the same role). In the QGIS Browser → PostGIS → your connection, right-click → Refresh; the mcp_layers schema appears with hotels_near_coast inside it.
See docs/qgis-setup.md for screenshots.
License
MIT. Contributions welcome — please open an issue first to discuss bigger changes.





