mcp-geo-server
Model Context Protocol · GeoServer · PostGIS

Ask your GIS stack in plain language.

An intelligent MCP server that drives a GeoServer instance through an LLM agent, plus a chat + map web UI and a fully Dockerised, domain-agnostic stack. Ask “show me the linear landslides in Molise” and the matching layers render on the map.

Chat on the left resolves a natural-language request to GeoServer layers; the map on the right shows landslide areas of the Marche region with a thematic legend.
The chat resolves a request to GeoServer layers and renders them with a thematic legend — here, the landslide areas of the Marche region, classified by movement type.

What it does

One async core — the GeoServer client, the geo_* tools and the shared ingest / catalog / styling modules — serves the MCP agent, the web UI and the data bootstrap.

Typed MCP server

38 tools over MCP 2026-07-28: the 33 geo_* GeoServer operations with annotations, high-level map tools such as geo_resolve_request, catalog resources and prompts.

Chat-first map UI

Requests resolve to published layers, rendered on Leaflet with live WMS legend, admin-area clipping and per-layer CQL.

Domain-agnostic catalog

Layer resolution reads names, titles, abstracts and keywords from WMS capabilities. There’s no vocabulary hardcoded in the code.

Idempotent data bootstrap

Drop shapefiles and GeoTIFFs in ./data: they land in PostGIS or as zero-copy coverages and are published automatically.

Config-driven styles

Thematic SLDs (categorical, line, point, raster ramps) generated from a YAML file and assigned by layer-name rules.

Terrain enrichment

Elevation, slope, aspect and curvature for any vector layer, sampled from a DTM on demand.

Pluggable LLM

Host Ollama, Ollama Cloud, Anthropic Claude, or any OpenAI-compatible endpoint (LiteLLM, vLLM, llama-server).

Safe by default

A function middleware blocks destructive tools unless GEO_ALLOW_DESTRUCTIVE=true.

Native multi-arch images

amd64 and arm64 images published to GHCR by CI, with no QEMU emulation on Apple Silicon.

Architecture

The LLM is reached lazily over the network and never gates startup. The browser only talks to the web UI, which proxies WMS.

MCP client Claude Desktop, IDEs… Browser chat + Leaflet map MCP server :9000 38 typed tools + assistant Web UI :8000 NL resolver · WMS proxy LLM provider geo core client · tools · catalog GeoServer PostGIS ./data shp · GeoTIFF

Quickstart

Requires Docker + Compose and an LLM: host Ollama by default, or Ollama Cloud, Anthropic or an OpenAI-compatible gateway.

git clone https://github.com/agent-engineering-studio/mcp-geo-server
cd mcp-geo-server
ollama serve &        # host Ollama (default provider)
make ollama-pull      # pull OLLAMA_LLM_MODEL onto the host
make build            # app images + GeoServer/PostGIS for your arch
make up               # start the stack
ServiceEndpointRole
webuihttp://localhost:8000Chat + map UI, WMS proxy, shapefile upload
mcphttp://localhost:9000/mcpIntelligent MCP server (streamable HTTP)
geoserverhttp://localhost:8080/geoserverGeoServer 2.28
postgislocalhost:5432PostGIS 16-3.4
geo-initone-shotLoads ./data and applies styles

Call it from any MCP client:

import mcp

async with mcp.Client("http://localhost:9000/mcp") as client:
    res = await client.call_tool("geo_resolve_request",
                                 {"query": "frane ad alta pericolosità in Puglia"})
    print(res.content[0].text)   # layers, cql_by_layer, bbox, clip, info…

AI roadmap

Where the AI layer goes next. The full analysis, with file references, is in docs/ai-roadmap.md; the protocol migration is covered separately in the MCP 2026-07-28 upgrade plan.

Making the current AI better

First

NL → layer evaluation

A golden set of Italian and English requests scored on layer precision and recall, CQL match, invalid-JSON rate and latency. It’s how we compare models and prompts on evidence.

Reliability

Structured outputs

Schema-constrained responses, with layer names as an enum, replace brace-scanning JSON extraction. Hallucinated layers can’t be produced, and parse failures stop surfacing as HTTP 502.

Scale

Semantic catalog retrieval

Layer embeddings in pgvector, inside the PostGIS the stack already runs. Only the top-k candidates reach the prompt, so its size stays constant as the catalog grows.

Safety

CQL guardrails

Parse LLM-generated filters to an AST and whitelist attributes and values against the selected layer before they reach WMS.

Cost

Prompt caching & model routing

Use cached static prefixes and a small local model first, then escalate to a larger one on low confidence or invalid output.

Ops

GenAI observability

OpenTelemetry spans with tokens, latency, model and validation outcome, exported to a collector in the compose stack.

New AI features

Top pick

AI metadata at ingest

When a layer is published, an LLM writes its title, abstract, bilingual keywords and column descriptions from the schema and sample values. That feeds the resolver directly.

New

Styles from natural language

“Colour landslides by movement type, red-orange.” The model emits a validated styles.yml entry instead of raw SLD.

New

Multi-turn map refinement

“Only high hazard” → “add the DTM” → “drop rockfalls”. Each turn sends a delta on the current map state, with the state kept client-side.

New

Analytical questions

Answer how many, which and where most: WFS aggregates first, then sandboxed read-only text-to-SQL on PostGIS for spatial questions.

New

Automated area reports

A workflow combines statistics, DTM metrics, the map image and the legend into a written risk sheet for a municipality or area.

New

High-level MCP tools

geo_search_layers, geo_resolve_request, geo_admin_scope, geo_describe_layer, plus MCP prompts and the catalog as resources.

New

Visual map QA

A vision model checks the rendered map for empty results, opaque rasters covering vectors, or unreadable palettes.

New

Assisted data quality

Suggests a missing SRS from the coordinates, detects encoding issues and flags invalid geometries in an ingest report.

Exploratory

Landslide susceptibility ML

The landslide inventory plus DTM derivatives (already computed) train a model whose output is published as a susceptibility raster.

Phases

  1. 0

    Close the blockers

    Bound the mcp dependency, bind MCP to localhost by default, add auth and rate limiting to the web UI, update the default Claude model, cache the catalog.

    1–2 days
  2. 1

    Measure, then constrain

    NL → layer evaluation, structured outputs, CQL guardrails.

    4–6 days
  3. 2

    Extract the resolver, feed it metadata

    Move the resolution logic into the core package, make admin levels config-driven, generate AI metadata at ingest, add OTel.

    1 week
  4. 3

    Scale the catalog

    pgvector retrieval, prompt caching, model router with fallback.

    1 week
  5. 4

    Conversational map

    Multi-turn refinement, model-based intent and places, styles from natural language.

    1–2 weeks
  6. 5

    High-level MCP tools

    Exposed alongside the MCP v2 migration from the upgrade plan.

    1 week
  7. 6

    Analysis & reports

    WFS aggregates, then text-to-SQL, area reports, visual QA.

    2–3 weeks

Rule of thumb: from phase 3 on, a feature only ships with an evaluation case that measures its effect.