mcp-geo-server
Enables natural language interaction with a GeoServer instance for managing workspaces, datastores, feature types, layers, styles, and OGC services (WMS/WFS) via an LLM-powered agent.
README
mcp-geo-server
An intelligent MCP (Model Context Protocol) server that lets you drive a
GeoServer instance in natural language. It is built with the Microsoft
Agent Framework: an LLM-backed agent is given the GeoServer operations as
tools and exposed as a single MCP tool via agent.as_mcp_server(). The LLM
backend is pluggable — local Ollama, Ollama Cloud, or Anthropic
Claude. Any MCP client sends a request like "how many features in
topp:states?" and the agent decides which GeoServer operations to call.
Under the hood the agent can: manage workspaces, PostGIS datastores, feature types, layers and SLD styles via the REST API, and query/edit data via the OGC services (WMS, WFS GetFeature → GeoJSON, WFS-T insert/update/delete). It ships with a small Leaflet web UI and a Docker stack (GeoServer + PostGIS + Ollama) for local development.
The same async core (GeoServerClient + the geo_* tool functions) is shared by
the agent and the web UI — there is exactly one place that talks to GeoServer.
Prerequisites
- Python 3.11+
- Docker + Docker Compose (for the local GeoServer/PostGIS stack)
1. Start the stack (Docker)
make build # build the app image + pull GeoServer/PostGIS/Ollama
make ollama-pull # download the LLM model into the Ollama container (once)
make docker-up # start everything
The Compose project is named mcp-geo-server; containers are named coherently:
| Service | Container | Endpoint |
|---|---|---|
mcp |
mcp-geo-server-mcp |
http://localhost:9000/mcp — intelligent MCP server (streamable-HTTP) |
webui |
mcp-geo-server-webui |
http://localhost:8000 — Leaflet test UI |
geoserver |
mcp-geo-server-geoserver |
http://localhost:8080/geoserver (admin / geoserver) |
ollama |
mcp-geo-server-ollama |
http://localhost:11434 — local LLM backend |
postgis |
mcp-geo-server-postgis |
localhost:5432 (gis / gis / gis) |
GeoServer runs with CORS_ENABLED=true so the browser UI can call OGC services
directly. PostGIS has a healthcheck and GeoServer waits for it. The mcp agent
talks to GeoServer and Ollama over the internal Compose network.
2. Install
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,webui]"
cp .env.example .env # then edit as needed
3. Configuration (environment variables)
| Variable | Default | Meaning |
|---|---|---|
GEOSERVER_URL |
— (required) | Base URL, e.g. http://localhost:8080/geoserver |
GEOSERVER_USER |
— (required) | REST username |
GEOSERVER_PASSWORD |
— (required) | REST password |
GEOSERVER_DEFAULT_WORKSPACE |
(none) | Workspace used when a tool omits one |
GEOSERVER_DEFAULT_SRS |
EPSG:4326 |
SRS used when publishing without one |
GEOSERVER_TIMEOUT |
30 |
HTTP timeout (seconds) |
GEOSERVER_RETRIES |
2 |
Retry attempts on connect/timeout/502/503/504 |
GEOSERVER_RETRY_BACKOFF |
0.5 |
Linear backoff factor (seconds × attempt) |
GEOSERVER_VERIFY_TLS |
true |
Verify TLS certificates |
GEO_MAP_OUTPUT_DIR |
./maps |
Where generated maps / downloaded PNGs are saved |
WEBUI_PORT |
8000 |
Port for the test UI |
GEO_LLM_PROVIDER |
ollama |
Agent LLM backend: ollama, ollama-cloud or anthropic |
OLLAMA_HOST |
http://localhost:11434 |
Local Ollama endpoint (ollama provider) |
OLLAMA_MODEL |
qwen2.5 |
Ollama model (must support tool calling); cloud model id for ollama-cloud |
OLLAMA_CLOUD_HOST |
https://ollama.com |
Ollama Cloud endpoint (ollama-cloud provider) |
OLLAMA_API_KEY |
(none) | Ollama Cloud API key (required for ollama-cloud) |
ANTHROPIC_API_KEY |
(none) | Anthropic API key (required for anthropic) |
ANTHROPIC_MODEL |
claude-sonnet-4-6 |
Claude model (anthropic provider) |
GEO_MCP_TRANSPORT |
stdio |
MCP transport: stdio or http |
GEO_MCP_HOST |
0.0.0.0 |
Bind host when transport is http |
GEO_MCP_PORT |
9000 |
Port when transport is http |
Secrets are never hardcoded — everything is read from the environment.
4. The intelligent MCP server (Microsoft Agent Framework)
The MCP server is an agent, not a flat list of tools. server.py builds a
GeoServer agent (agent.py: a chat client + the geo_* functions as tools) and
exposes it with agent.as_mcp_server(). The MCP client therefore sees one
tool, geoserver-agent, that takes a natural-language task.
Choosing the LLM backend (GEO_LLM_PROVIDER)
| Provider | Value | Needs | Notes |
|---|---|---|---|
| Local Ollama | ollama |
the ollama container |
Default. No API key; run make ollama-pull. |
| Ollama Cloud | ollama-cloud |
OLLAMA_API_KEY |
Hosted models at https://ollama.com; set OLLAMA_MODEL to a cloud model (e.g. gpt-oss:120b). |
| Anthropic Claude | anthropic |
ANTHROPIC_API_KEY |
Uses ANTHROPIC_MODEL (default claude-sonnet-4-6). |
Examples:
# Ollama Cloud
GEO_LLM_PROVIDER=ollama-cloud OLLAMA_API_KEY=sk-... OLLAMA_MODEL=gpt-oss:120b mcp-geo-server
# Anthropic Claude
GEO_LLM_PROVIDER=anthropic ANTHROPIC_API_KEY=sk-ant-... mcp-geo-server
For Docker, set the same variables in your shell (or .env) before make docker-up; the mcp service forwards them. With ollama-cloud/anthropic the
local ollama container is not needed.
Run it over either transport (selected by GEO_MCP_TRANSPORT):
# stdio (for MCP clients that spawn the process, e.g. Claude Desktop)
mcp-geo-server
# streamable-HTTP (long-lived, visible container) at http://localhost:9000/mcp
GEO_MCP_TRANSPORT=http mcp-geo-server
In Docker the mcp service runs it over HTTP. Example MCP client call:
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
async with streamablehttp_client("http://localhost:9000/mcp") as (r, w, _):
async with ClientSession(r, w) as s:
await s.initialize()
res = await s.call_tool("geoserver-agent",
{"task": "How many features are in topp:states?"})
print(res.content[0].text)
For a stdio MCP client (claude_desktop_config.json), point command at
mcp-geo-server and set the GEOSERVER_* / OLLAMA_* env vars.
5. Run the test UI
uvicorn webui.app:app --reload --port 8000
Open http://localhost:8000. The sidebar has a form for each operation (create workspace / PostGIS datastore, publish a feature type, create + assign a style, run a WFS query) plus a connection-status indicator. The map uses an OpenStreetMap basemap and shows selected layers as WMS overlays; the "Carica come GeoJSON" button fetches features via WFS and draws them with popups.
6. Tests
pytest # unit + behavioural (no GeoServer needed)
GEO_RUN_INTEGRATION=1 pytest tests/integration # live round-trip vs real GeoServer
tests/test_formatting.py,test_styles_helpers.py,test_ogc_helpers.py,test_map_template.py— pure helpers / template rendering.tests/test_tools_behaviour.py— every tool driven with aFakeClient, asserting on the request bodies / params / WFS-T XML (no network).tests/integration/test_live.py— status + create/list/delete workspace round-trip; skipped unlessGEO_RUN_INTEGRATION=1.tests/evals/geo_eval.xml— read-only eval questions against GeoServer's standard sample data (e.g.topp:stateshas 49 features). Confirm the expected answers on your live instance.
Agent tools (28 geo_* functions)
These are the tools the agent calls internally to fulfil a request (they are not
exposed individually over MCP — the agent is). make tools lists them.
| Tool | Kind | Description |
|---|---|---|
geo_get_status |
read | Version + connectivity (/rest/about/version.json) |
geo_list_workspaces |
read | List workspaces |
geo_get_workspace |
read | Get one workspace |
geo_create_workspace |
write | Create workspace (optionally default) |
geo_delete_workspace |
destructive | Delete workspace (recurse) |
geo_list_datastores |
read | List datastores in a workspace |
geo_get_datastore |
read | Get one datastore |
geo_create_datastore_postgis |
write | Create a PostGIS datastore |
geo_delete_datastore |
destructive | Delete datastore (recurse) |
geo_list_featuretypes |
read | List feature types (or available tables) |
geo_publish_featuretype |
write | Publish a table as a layer (recalculates bbox) |
geo_list_layers |
read | List layers |
geo_get_layer |
read | Get one layer |
geo_get_layer_bbox |
read | Layer bounding boxes + SRS |
geo_update_layer |
idempotent | Set default style / enabled flag |
geo_delete_layer |
destructive | Delete layer (+ feature type cleanup) |
geo_list_styles |
read | List styles |
geo_get_style |
read | Get style SLD |
geo_create_style |
write | Create style from SLD string/file |
geo_update_style |
idempotent | Replace style SLD |
geo_assign_style_to_layer |
idempotent | Assign style to layer (default/extra) |
geo_delete_style |
destructive | Delete style (purge) |
geo_wms_get_capabilities |
read | WMS GetCapabilities |
geo_wms_get_map |
read | Build WMS GetMap URL (optionally download PNG) |
geo_wfs_get_capabilities |
read | WFS GetCapabilities |
geo_wfs_get_feature |
read | WFS GetFeature → GeoJSON (bbox or CQL) |
geo_wfs_transaction |
write | WFS-T delete / update / raw |
geo_build_web_map |
read | Generate a Leaflet HTML map (OSM + WMS overlays) |
The agent's instructions (agent.py) tell it to treat delete_* as
destructive and only run them on explicit request.
Resilience knobs
- Retry with linear backoff on connect errors, timeouts, and HTTP
502/503/504 — controlled by
GEOSERVER_RETRIESandGEOSERVER_RETRY_BACKOFF. - Actionable errors: 401/403/404/405/409/500 are translated into messages with a suggested fix.
- OGC exceptions:
ServiceExceptionReport/ServiceException(which arrive with HTTP 200) are detected and raised with the response body. - Logging on the
mcp_geo_serverlogger (setGEO_LOG_LEVEL=DEBUG).
Project layout
src/mcp_geo_server/ core: config, client, formatting, agent (Agent Framework),
server (MCP stdio/http), tools/ (geo_* functions), templates/
webui/ FastAPI backend (app.py) + static Leaflet UI (static/index.html)
tests/ unit, behavioural, integration, evals
Dockerfile app image (web UI + MCP agent)
docker-compose.yml GeoServer 2.28.0 + PostGIS 16-3.4 + Ollama + webui + mcp
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。