◆ Solution 08 · Conversational GIS & agent integration

Ask the map a question — or let an agent ask it.

A chat interface sitting on an ArcGIS map that speaks ESRI GeoServices, OGC API Features and OGC WFS interchangeably, filters across all three with one query language, and generates SQL against your own spatial database. Every capability is also exposed as an MCP tool, so Claude Desktop and your own agents can drive the whole thing.

🗣️ 3 service standards 🔌 20+ MCP tools 🧠 8+ AI providers 🏢 Air-gap capable
The problem

GIS answers are locked behind GIS skills

The data is published, the services are open, the analysis is routine. Getting an answer out still requires knowing which button to press.

🎓

The tool needs training

Everyone in the organisation has spatial questions. A small number of them know how to build a definition query, and they become the queue everyone waits in.

🧱

Standards do not interoperate

ESRI, OGC API Features and WFS answer the same questions in different dialects. A filter written for one is worth nothing against the others.

🚚

Analysis means moving data

The usual answer to "query my database spatially" is an ETL pipeline and a second copy of the data that is immediately out of date.

🤖

Agents cannot reach it

An AI assistant that can read your email but not your parcel service is not much use for the questions that actually involve land.

◆ One map, plain language

Chat on the left, ArcGIS on the right

The assistant sits beside a live ArcGIS Maps SDK view. Add a service by URL and it detects the type for you; drop in a GeoJSON or KML and it lands on the map as a layer; then ask about what is there in plain language.

  • Point it at any service URL — the type is identified automatically, and its layers or collections are listed without you knowing which standard you just pasted.
  • 2D and 3D — switch between map and scene views, with WebMap and WebScene loading by ID and multiple basemaps.
  • No-code configuration — a whole map can be described with a WebMap ID, a WebScene ID or CatalogLayer JSON instead of code.
  • Streaming answers with persistent sessions, so a line of questioning keeps its context.
ArcGIS Maps SDK for JSFastAPI SQLAlchemy asyncPydantic
Geo + Zoning AI Assistant
The assistant interface: a chat panel on the left confirming an uploaded GeoJSON, an ArcGIS map in the centre with parcel polygons drawn over a street basemap, and a map settings panel on the right.
The whole product on one screenChat, layer list, map settings, upload and analysis — no separate desktop application in the loop.
Multi-standard

Three dialects, one query language

The awkward part of standards-compliant GIS is that there are several standards. The service layer abstracts them behind one interface and converts filters between them.

StandardWhat it coversOutput formats
ESRI GeoServicesFeatureServer, MapServer and ImageServer endpoints, plus WebMap and WebScene loading by IDJSON, GeoJSON, ESRIJSON, PBF, KML
OGC API FeaturesModern RESTful collections with CQL-2 filteringJSON, GeoJSON
OGC WFSTraditional Web Feature Service with spatial operationsGML, GeoJSON
  • Automatic service detection — paste a URL and the type is worked out from it, rather than asking the user to classify their own endpoint.
  • CQL in text and JSON form — LIKE, IN, BETWEEN and comparison operators for attributes; INTERSECTS, WITHIN, CONTAINS and DWITHIN for geometry.
  • Cross-service filter conversion — one filter is translated into whatever dialect the answering service expects, so a query written once works everywhere.
  • Bounding-box queries for cheap spatial extent filtering before anything heavier runs.
one filter, any service type
# Auto-detect what kind of service this even is
curl '…/api/geo/services/detect-type?url=<service>'

# List layers or collections — same call either way
curl '…/api/geo/services/layers?url=<service>'

# Query with CQL; the proxy converts to the right dialect
curl -X POST '…/api/geo/query/features' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "<service>",
    "cql": "zoning LIKE '\''C-%'\'' AND
            INTERSECTS(geom, BBOX(-80.3,25.7,-80.1,25.9))"
  }'

# Or translate a filter between languages explicitly
curl -X POST '…/api/geo/filters/convert'
A closer look

Upload, analyse, query

Captured from a running instance.

The Upload Data panel showing an uploaded parcels.geojson file with its feature count and size, and Analyze and Remove actions.
Bring your own dataDrop in GeoJSON or KML and it becomes a layer alongside the services, with feature count and size reported back.
The Data Analysis panel reporting feature count, attribute count and coordinate presence, with the same summary written into the chat transcript.
Analysis lands in the conversationGeometry types, spatial extent and centroid are summarised into the chat, not buried in a dialog you have to reopen.
An attribute query filtering parcels where maximum height exceeds 80 feet, returning five of twelve features with their parcel IDs, zoning and land use listed in the chat.
Attribute queries, answered in placeA filter on the parcel attributes returns the matching subset and lists them — parcel ID, zoning, land use — with the selection reflected on the map.

* Screens show a development instance with a small sample parcel file; parcel identifiers and attribute values in them are illustrative.

◆ Model Context Protocol

The part an AI agent can actually use

Every API this platform exposes is also published as an MCP tool. That means an agent — Claude Desktop, an internal assistant, a scheduled automation — can query your geospatial services directly, rather than being told about them second-hand.

  • 20+ tools covering chat, ESRI service queries, database questions and map configuration — the full backend surface, not a curated subset.
  • Standardised protocol, so anything that speaks MCP works without a bespoke integration on either side.
  • Async execution and batch operations, so an agent working through a list of parcels is not making one blocking call at a time.
  • Session management — context persists across an agent's turns the same way it does for a person in the chat panel.
MCPClaude Desktop Python clientAsync
claude_desktop_config.json
{
  "mcpServers": {
    "esri-geospatial": {
      "command": "python",
      "args": ["path/to/mcp_server/server.py"],
      "env": { "BASE_URL": "http://localhost:8000" }
    }
  }
}

# …or drive it from your own code
from mcp_server.client import GeospatialMCPClient

client = GeospatialMCPClient()
await client.connect()

features = await client.query_features(
    service_url="https://services.arcgis.com/…/FeatureServer",
    where="zoning = 'C-2'",
)

result = await client.ask_database(
    "Which parcels are within 500 m of a transit stop?"
)
Database query intelligence

Query your own spatial database, without copying it

Natural language becomes SQL that runs against the database you already have — no ETL, no second copy, no synchronisation problem.

🐘

PostGIS-native

Spatial SQL is generated directly — ST_DWithin, ST_Intersects, ST_Area — rather than pulling rows out and post-processing them. PostgreSQL, MySQL, SQLite, DuckDB, Snowflake and BigQuery are all supported connections.

🔒

Read-only, enforced

Generated SQL is gated before execution: only read operations are permitted, and known injection shapes — statement chaining, comment truncation, union-based attacks — are blocked by pattern matching rather than trusted to the model.

📈

It learns your schema

The model is trained on your DDL, documentation and worked query examples, so accuracy improves with use. Repeated questions are cached, and generated SQL can be reviewed before it runs.

Generate first, execute second. SQL generation and SQL execution are separate endpoints. If you want a human to approve every statement that touches production, the surface is already shaped for it — ask for the SQL, show it, run it only on approval.
◆ Where it runs

Including on infrastructure with no internet

The AI provider is configuration, not architecture. That matters for organisations whose parcel and permit data is not allowed to leave the building.

  • Cloud providers — OpenAI, Anthropic, or OpenRouter for access to a wide model catalogue behind one key.
  • Fully local inference — Ollama, vLLM or LlamaCpp, or any OpenAI-compatible endpoint you host yourself. Nothing leaves your network.
  • Swap without code changes — the provider is an environment variable; adding a new one is a class and a registry entry.
  • Packaged for real estates of infrastructure — Docker Compose, Kubernetes, Debian and RHEL packages, Windows/IIS and systemd.
.env — pick a provider
# Cloud
AI_PROVIDER=openrouter
OPENROUTER_MODEL=anthropic/claude-3-haiku

# …or entirely on your own hardware
AI_PROVIDER=ollama
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.1:8b

# …or a vLLM inference server
AI_PROVIDER=vllm
VLLM_BASE_URL=http://localhost:8000/v1

# Query your database in place
VANNA_ENABLED=true
VANNA_DATABASE_TYPE=postgres
VANNA_ENABLE_POSTGIS=true

# Bring your own services
DEFAULT_OGC_API_FEATURES_SERVICES=https://…
DEFAULT_WFS_SERVICES=https://…
ENABLE_CQL_FILTERS=true
Two things to set before it faces the internet. API-key authentication is off by default, which leaves the admin endpoints open — turn it on. And the proxy trust setting accepts forwarded headers from any address, which is correct behind a known load balancer and wrong on a bare public host. Both are single settings, both are in the deployment documentation, and neither is a default we would ship to production for you.
◆ Source-code SDK for every solution

Point it at a service you already run

The quickest way to judge this is to give it one of your own endpoints and ask it something awkward. Tell us whether you need cloud models or fully local inference and we will demo the configuration you would actually deploy — including the MCP server against your own agent.