San Diego GIS MCP
Provides tools to discover, query, and explore GIS datasets from SANDAG's ArcGIS Enterprise portal, including dataset search, schema inspection, spatial point-in-polygon lookups, geocoding, and feature data retrieval with filtering and aggregation.
Click on "Install 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., "@San Diego GIS MCPsearch for floodplain datasets in San Diego"
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.
OpenContext
San Diego Regional GIS MCP — a San Diego fork of OpenContext. It serves the SANDAG-hosted regional / SanGIS catalog from SANDAG's ArcGIS Enterprise portal (geo.sandag.org) through the built-in arcgis plugin: parcels, floodplains, address points, transit, regional land use, and hundreds of other regional layers.
Scope: two San Diego servers
San Diego coverage is split across two MCP servers. This server is the regional one.
Server | Backing data | Owns |
San Diego Regional GIS (this repo) | SANDAG/SanGIS ArcGIS Enterprise — | Regional/county-wide layers: parcels, floodplain (FEMA NFHL), address points, roads, transit, regional land use & demographics |
San Diego City (separate server) | City of San Diego ArcGIS Server — | Authoritative City municipal layers: MHPA, City base zoning, community-plan land use |
If a question needs an authoritative City of San Diego municipal layer (MHPA boundaries, City base zones, community-plan land use), route it to the San Diego City server — this catalog does not hold those layers. The search_datasets tool description carries the same routing hint so models pick the right server on their own.
Data disclaimer & attribution. This server passes each layer's SanGIS/SANDAG attribution through in tool responses. Before using the data, review the SANDAG GIS Data Disclaimer (see SANDAG's Geographic Information Systems page), the SanGIS Legal Notice, and the SANDAG Open Data Terms of Use.
Related MCP server: mcp-arcgis-sanclemente
How it works
Discovery searches SANDAG's portal catalog anonymously (geo.sandag.org/portal/sharing/rest/search); if portal search is ever closed off, the plugin automatically falls back to walking the ArcGIS Server services directory (geo.sandag.org/server/rest/services), skipping auth-gated folders such as GeoDepot.
Coordinate contract: WGS84 in, WGS84 out. SANDAG stores its layers in EPSG:2230 (CA State Plane Zone VI, US feet), but that never leaks to callers: every /query request pins outSR=4326, every point/geometry input is declared as inSR=4326, and the geocoder returns outSR=4326 — so all coordinates in and out of every tool are plain WGS84 lon/lat (EPSG:4326). This matches the sibling San Diego City server, so results from the two servers compose without reprojection.
Tools exposed
Tool | Purpose |
| Discover datasets by keyword (e.g. "parcels", "floodplain"). Supports a |
| Fetch a dataset's metadata, service URL, and SanGIS attribution |
| List a dataset's fields (name, type, alias, coded values), optionally filtered by |
| List the distinct values in a field (with optional |
| Query features from a dataset (supports |
| Point-in-polygon: which polygon(s) contain a given point — by |
| Convert a street address to |
| Facet counts across the catalog (e.g. by |
Finding queryable data: type filter
The portal catalog mixes queryable Feature Services with service definitions, web maps, and apps. search_datasets takes an optional type argument that restricts results to a single ArcGIS item type — pass type: "Feature Service" to see only data you can query or map.
search_datasets arguments:
Arg | Required | Description |
| yes | Full-text search query (single keywords match best; multi-word queries fall back to the most distinctive word if the phrase finds nothing) |
| no | Restrict to one item type. Use |
| no | Max results, 1–100 (default 10) |
For example, q: "parcels" alone returns 137 items of mixed types; with type: "Feature Service" it returns the 63 queryable layers.
Dataset IDs
Portal discovery returns 32-char hex item IDs (e.g. the SanGIS Parcels layer). When running in directory-fallback mode, IDs are service paths instead (e.g. Hosted/Parcels/FeatureServer). Both forms are accepted by every tool that takes a dataset/item ID.
Writing correct queries: schema → distinct values → query
ArcGIS field names are case-sensitive — and SANDAG's hosted layers use lowercase field names (apn, situs_address, …). Rather than guess, use the discovery tools first:
get_layer_schema— see the real field names and types.keywordnarrows a wide schema:{ "item_id": "<parcels-id>", "keyword": "situs" } // -> situs_address, situs_street, situs_zip, ...get_distinct_values— confirm the exact value to filter on:{ "item_id": "<parcels-id>", "field": "situs_community" }query_data— now write thewhereclause with verified names and values.
Spatial lookup: spatial_query_point (by address or coordinates)
"Which polygon contains this location?" — against a polygon Feature Service (parcels, floodplains, districts, …). Pass either a street address (geocoded automatically via the SANDAG composite locator) or a WGS84 lon/lat (longitude first):
// arcgis__spatial_query_point — by address (202 C St = San Diego City Administration Building)
{ "item_id": "<parcels-id>", "address": "202 C St, San Diego, CA",
"out_fields": "apn,situs_address,situs_street" }
// ...or by coordinates
{ "item_id": "<parcels-id>", "lon": -117.1626, "lat": 32.7170 }Returns the attributes of every polygon containing the point (no geometry); when an address is used, the matched address is shown. You can also geocode on its own with geocode_address.
Try asking
Once the connector is added, just ask Claude in plain English — it picks the right tools:
Discovery
"What GIS layers does SANDAG publish about flooding?" (type-filtered discovery)
"Break down the regional GIS catalog by type." (aggregations)
Counts & records
"How many parcels are in the 92101 ZIP code?" (answered from
TOTAL MATCHING, no paging)"What communities appear in the parcels layer?" (
get_distinct_values)
Schema
"What fields does the parcels layer have?" (
get_layer_schema)
Spatial
"Which parcel is San Diego City Hall (202 C St) on?" (address geocoded automatically, then point-in-polygon)
"What flood zone is at latitude 32.717, longitude -117.163?" (coordinates also work)
Run locally
config.yaml is already committed for San Diego (the arcgis plugin pointed at geo.sandag.org), so no setup is needed to run the server locally:
pip install aiohttp pyyaml
python3 scripts/local_server.py # serves http://localhost:8000/mcpOn startup it connects to the live portal and registers the eight arcgis__* tools. SANDAG's portal search and Hosted services are public, so no API token is required. (On a Windows console you may need PYTHONUTF8=1 for the startup banner's emoji.)
Verify the upstream endpoints directly (services directory, portal search, a live parcel query, and a geocode) with:
python3 scripts/smoke_sandag_live.pyAnd smoke-test a running deployment end-to-end (defaults to http://localhost:8000/mcp):
python3 scripts/smoke_prod.py http://localhost:8000/mcpSee Getting Started for the generic OpenContext setup.
Connect to Claude
The server is live. Add it as a custom connector in Claude (same steps on Claude.ai and Claude Desktop):
Settings → Connectors (or Customize → Connectors on claude.ai)
Add custom connector
Name it e.g.
San Diego GISand paste the URL:https://sandiego-regional-gis.codeforanchorage.org/mcp
Quick health check from a terminal:
curl -sS -X POST https://sandiego-regional-gis.codeforanchorage.org/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
# → {"jsonrpc":"2.0","id":1,"result":{"status":"ok"}}Deploy & operate
Production runs on AWS Lambda + API Gateway behind sandiego-regional-gis.codeforanchorage.org, in us-west-2. DNS for codeforanchorage.org is managed externally at DreamHost: the ACM validation CNAME and the sandiego-regional-gis CNAME (→ terraform output -raw custom_domain_target) are created there.
First-time bootstrap (state backend, once per account):
cd terraform/bootstrap
terraform init
terraform apply \
-var="aws_region=us-west-2" \
-var="state_bucket_name=<your-tfstate-bucket>" \
-var="lock_table_name=terraform-state-lock"These values must match terraform/aws/backend.tf.
Deploy / redeploy:
./scripts/deploy.sh --environment prodFor a code-only change, that single command is all you need. The first stand-up of a new environment also creates an ACM certificate and an API Gateway custom domain — DNS is managed externally (no Route53), so on the first deploy you must:
Validate the cert. The first apply errors on
CreateDomainName("Certificate is not in an ISSUED state") — expected. Create the ACM validation CNAME (terraform output acm_validation_cname_name/_value), wait forISSUED, then re-run the deploy.Point the endpoint. Create a CNAME for your domain →
terraform output -raw custom_domain_target.
Documentation
Doc | Description |
Setup and usage | |
System design and plugins | |
AWS, Terraform, monitoring | |
Local testing (Terminal, Claude, MCP Inspector) |
Examples
Boston OpenData (CKAN): examples/boston-opendata/config.yaml
Custom plugin: examples/custom-plugin/
Contributing
Pre-commit hooks (optional):
pip install pre-commit
pre-commit installHooks: Ruff, yamllint, gofmt, plus detect-private-key and gitleaks secret scanning. Run manually: pre-commit run --all-files.
Never commit secrets.
config.yamlis tracked, so any API token belongs in an environment variable referenced via${ENV_VAR}(e.g.token: "${ARCGIS_TOKEN}"), never inline. The gitleaks hook will block accidental commits of keys/tokens.
License
MIT — see LICENSE.
Author: Srihari Raman, City of Boston Department of Innovation and Technology
San Diego fork adapted from the Worcester, MA fork of OpenContext. OpenContext is MIT-licensed; this fork retains the original attribution above. GIS data © SanGIS/SANDAG — see the disclaimer links at the top of this README.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/codeforanchorage/sandiego-gis-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server