MapControl MCP server
Commit Oct 26, 2026Lets AI agents create, style, and control live, shareable web maps with geospatial data and 3D terrain.
Also known as ESIPFed/mc2
- Stars
- 5
- Tools
- 13
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Oct 26, 2026
- License
- Apache-2.0
What is MapControl MCP?
MapControl lets AI agents create, style, and control live, shareable web maps with geospatial data and 3D terrain. It exposes 13 tools, such as create_map, add_geojson, add_arc.
Summary generated from the project's README
About MapControl MCP server
Creating and controlling interactive web maps through natural language or code is what MapControl enables for developers and AI agents. By connecting an MCP client like Claude Desktop or Cline to the server, users can instruct an agent to generate maps, style geospatial data, and manipulate a 3D camera in real time. The server provides a live, shareable URL for every map session, allowing humans to watch changes happen instantly in a browser via WebSockets.
This system supports diverse geospatial tasks including rendering GeoJSON features, adding great-circle arcs, and displaying GeoTIFF rasters with custom colormaps. It offers 3D terrain and globe views using MapLibre, with basemaps ranging from OpenStreetMap to Esri Satellite. Developers can interact with the map through a dedicated Python SDK, a REST API, or the MCP interface. The entire environment is easily deployed using Docker, which bundles the necessary Python, GDAL, and headless Chromium components for capturing viewport screenshots as PNG files.
What you can do with it
- 1Visualizing geospatial analysis resultsAn AI agent can take a script's output and immediately render it as a styled polygon or raster on a live map.
- 2Automated map storytellingUsers can command an agent to perform complex camera movements and basemap switches to highlight specific geographic areas.
- 3Generating map snapshots for reportsDevelopers can use tool calls to capture the current map view as a static image file.
- 4Programmatic map manipulationSoftware engineers can use the Python SDK to drive map sessions, add assets, and update styles through code.
Written from the project's README. Every use case is checked against a quote from it.
MapControl MCP tools 13
Detected from the READMEcreate_mapadd_geojsonadd_arcadd_geotiffzoomlist_assetsupdate_styleset_visibilitydelete_assetset_basemapset_themeget_viewport
| create_map | — | |
| add_geojson | — | |
| add_arc | — | |
| add_geotiff | — | |
| zoom | — | |
| list_assets | — | |
| update_style | — | |
| set_visibility | — | |
| delete_asset | — | |
| set_basemap | — | |
| set_theme | — | |
| get_viewport | — |
Show all 13 toolsShow fewer tools
take_screenshot
| take_screenshot | — |
Automated checks
Ran Nov 14, 2026Full report →Not verified yet. A server needs a score of 80, no serious finding, and enough data for us to check. How checks work · What to fix for more points
- Source repository linked and reachableesipfed/mc2 is public and reachablegithub.com/esipfed/mc2 ↗Pass
- Package names this repository as its sourceNot checked
- Build provenance (Sigstore attestation)Not checked
- Open source license (OSI)Apache-2.0spdx.org/licenses/Apache-2.0.html ↗Pass
- Commit or release in the last yearLast commit or release 2026-10-26github.com/esipfed/mc2 ↗Pass
- OpenSSF ScorecardNot checked
- Package name not close to a popular packageNot checked
- No critical advisory in the package or its dependenciesNot checked
- No hidden instructions to the model13 tool descriptions and the README read; no override phrases, credential paths or hidden charactersPass
- Destructive tools are labelledNot checked
- Remote endpoint answers the MCP handshakeNot checked
README
View on GitHubMapControl (MC²)
Map Control × Model Context Protocol — a live, shareable web map that AI agents and humans drive from code or conversation.
https://github.com/user-attachments/assets/d0adcfcd-61bb-419c-8b85-3a405d4a5453
MapControl is a controllable map for AI agents.
Agents can create maps, add and style geospatial data, move the camera, switch basemaps, render 3D terrain, capture screenshots, and update the map incrementally as they work. Humans open the same map in a browser, watch those changes happen live, interact with the result, and share it with a link.
Control it through:
- MCP — give an AI agent direct map tools (Claude, Cline, any MCP client)
- Python — drive the map from scripts, notebooks, or generated code
- REST — integrate MapControl into any application
All three interfaces operate on the same live map sessions.
Ask an agent: "Show the burn scar near Los Alamos, switch to satellite imagery, and zoom to it." — MapControl gives the agent a map it can actually operate, not just coordinates to describe or a static image to return.
Why MapControl?
- 🗺️ Live & shareable — every map is a URL. Open it anywhere; it updates in real time over WebSocket.
- 🤖 Agent-native — a first-class MCP server (Streamable HTTP) mounted at
/mcp. Point Claude Desktop, Claude.ai, Cline, or the OpenAI Agents SDK at it and start talking to your map. - 🐍 Typed Python SDK — the
mapcontrolpackage gives you sessions, assets, styles, screenshots, and camera control in a few lines. - 🌍 Real geospatial data — GeoJSON features, styled polygons/lines/points, glyph markers, arcs, and GeoTIFF rasters rendered server-side with colormaps.
- 🏔️ 3D terrain & globe — MapLibre globe with terrain tiles and sky, or classic 2D. Ballistic zoom animations included.
- 📸 Screenshots — capture the current viewport to PNG, straight from a tool call.
- 🎨 Basemaps & themes — OpenStreetMap, Esri Satellite, and Carto Dark work with zero keys; drop in a MapTiler key for vector streets, hybrid, topo, and dataviz styles. Light/dark themes throughout.
- 🔐 Optional auth — self-issued signed scoped tokens for
/mcp, plus a standalone OAuth-style authorization portal for multi-user deployments.
Architecture
Claude / Cline / any MCP client Python SDK (`mapcontrol`) curl / your app
│ │ │
▼ ▼ ▼
MCP (/mcp) ────────────────────────► REST API ◄───────────────────────┘
│
FastAPI server (sessions,
assets, events, GeoTIFFs,
screenshots, auth)
│
WebSocket fan-out
│
▼
MapLibre GL frontend (live map,
3D terrain, drawing tools)
| Directory | What it is |
|---|---|
server/ |
FastAPI server — REST API, WebSocket hub, MCP server, GeoTIFF & screenshot services, auth portal |
sdk/ |
mapcontrol — typed Python client SDK |
examples/ |
Runnable demo scripts (shapes, terrain, glyphs, GeoTIFFs) + sample data |
docs/ |
Guides — MCP integration, LLM context block, MCP Apps field guide, map-engine comparison, Puppeteer animation skills |
deploy/ |
Deployment helpers (local PyPI index for the SDK) |
Quick start
1. Run the server
All you need is Docker — the published image bundles everything else (Python, GDAL, a headless Chromium for screenshots) and is published multi-arch (linux/amd64 + linux/arm64), so it runs natively on Intel and Apple-Silicon machines alike. No clone required. Save this as docker-compose.yml:
services:
mapcontrol:
image: ghcr.io/esipfed/mc2:latest
ports: ["8000:8000"]
docker compose up -d
Port 8000 already taken on your machine? (
lsof -i :8000shows what's using it.) Remap only the host side and leave the container port unchanged —ports: ["8080:8000"]— then reach it athttp://localhost:8080. Keep the container on 8000: the image binds 8000 and self-navigates there for screenshots, so changing the container side would break them.
Verify it's up:
curl http://localhost:8000/docs # interactive API docs
That's the whole install. Want maps that survive restarts, share links that work off your machine, or premium basemaps? See docs/deployment.md — it walks from this minimal setup to a full production config and explains what every knob does. (Contributors: git clone this repo and docker compose up --build builds the image locally.)
2. Drive a map from Python
Install the SDK (pure Python — no GIS stack needed on the client):
pip install "git+https://github.com/ESIPFed/mc2.git#subdirectory=sdk"
from mapcontrol import MapControl
mc = MapControl("http://localhost:8000")
session = mc.create_map()
print(session.url) # ← open this in a browser; it updates live
# Fly to San Francisco
session.zoom_to_point(-122.42, 37.77, zoom=12)
# Add a polygon (GeoJSON dict or string)
session.add_polygon(
{
"type": "Feature",
"properties": {"name": "Mission District"},
"geometry": {"type": "Polygon", "coordinates": [[
[-122.43, 37.75], [-122.40, 37.75], [-122.40, 37.77],
[-122.43, 37.77], [-122.43, 37.75]]]},
},
style={"fill_color": "#43b4e440", "stroke_color": "#43b4e4"},
)
session.set_basemap("satellite")
session.set_theme("dark")
shot = session.take_screenshot() # PNG of the current view
Watch the browser tab while the script runs — every call lands on the shared map live.
3. Connect an AI assistant (MCP)
The MCP server is mounted in-process at /mcp (Streamable HTTP — the current MCP standard). Add it to any MCP client:
Cline / Claude Desktop (mcpServers config):
{
"mcpServers": {
"mapcontrol": {
"url": "http://localhost:8000/mcp"
}
}
}
Then just ask:
"Create a map, add a red polygon around Austin, switch to satellite, and zoom to it."
See docs/mcp-integration.md for full setup guides (Claude Desktop, Cline, Claude.ai web, OpenAI Agents SDK) and transport details.
4. Generating analysis code? Drop the SDK into your harness
If your system auto-generates analysis code — a planner, an agent loop, a notebook generator — append the context block from docs/llm-context.md to the generator's prompt. One extra block, and the SDK's usage slides right into every generated script: results land on a live, shareable map instead of dying in a stdout log.
It works interactively too — paste it into ChatGPT, Cursor, or Copilot with your script:
"Here's my NDVI script. Put the result raster on the map, outline the study area, and zoom to it."
MCP tools
| Tool | What it does |
|---|---|
create_map |
Create a live map and get its shareable URL |
add_geojson |
Add styled GeoJSON features (polygons, lines, points, glyphs) |
add_arc |
Add a great-circle arc between two points |
add_geotiff |
Upload & render a GeoTIFF raster with a colormap |
zoom |
Fly the camera — to coordinates, bounds, or assets (ballistic animation) |
list_assets |
List everything on a map |
update_style |
Restyle an existing asset |
set_visibility |
Show / hide an asset |
delete_asset |
Remove an asset |
set_basemap |
Switch basemaps (OSM, satellite, dark, MapTiler styles…) |
set_theme |
Light / dark theme |
get_viewport |
Read the current camera position |
take_screenshot |
Capture the current view to PNG |
REST API
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/maps |
Create a map |
GET |
/api/maps/{id} |
Get map info |
DELETE |
/api/maps/{id} |
Delete a map |
POST |
/api/maps/{id}/sessions |
Create a user session |
POST |
/api/maps/{id}/events |
Send a map command |
GET |
/api/maps/{id}/events |
List events |
GET |
/api/maps/{id}/assets |
List assets |
PATCH |
/api/maps/{id}/assets/{aid} |
Update an asset |
DELETE |
/api/maps/{id}/assets/{aid} |
Delete an asset |
GET |
/map/{id} |
Map frontend (iframe-ready; ?ui=none for a chromeless embed) |
WS |
/ws/{id}/{session} |
WebSocket for real-time events |
Configuration
Everything lives in server/config.toml — host/port, session TTL, storage paths, default basemap/theme/terrain, and the full basemap catalog. Keyless basemaps (OSM, Esri Satellite, Carto Dark) work out of the box; set MAPTILER_API_KEY to unlock the MapTiler vector styles. Adding a new tile provider is a config-only change.
docs/deployment.md covers the compose setups (minimal → production), what every environment variable does to the map, custom config.toml mounts, and running behind a reverse proxy.
Optional auth for /mcp is controlled via environment (MAPCONTROL_AUTH_MODE) — see server/mapcontrol_server/portal/ for the standalone authorization portal.
Demos
Runnable scripts in examples/ show the SDK end to end (start the server first):
python examples/demo.py # tour: shapes, styles, zoom, basemaps
python examples/demo_terrain_showcase.py # 3D terrain + ballistic camera moves
python examples/demo_glyphs.py # glyph markers & labels
# see also: demo_drawing.py, demo_ballistic_zoom.py, demo_multi_session.py
Sample GeoTIFFs live in examples/data/.
Want to drive the map from a browser instead of Python — for camera animations, recorded flythroughs, or screenshot capture? See the reference Puppeteer animation skills (ballistic flyTo tours, 3D terrain orbits, keyframe stills, frame-sequence recording).
Running tests
The acceptance suites run inside the same image you deploy — exactly how CI gates every push:
docker compose run --rm mapcontrol python tests/test_mcp.py # MCP conformance
docker compose run --rm mapcontrol python tests/test_mcp_auth.py # MCP authorization
(Hacking on the server internals? server/ is a standard installable Python package — pip install -e ".[dev]" && pytest tests/ -v from that directory, with system GDAL and playwright install chromium on your machine. For just running MapControl, Docker is the way.)
Troubleshooting
| Symptom | Fix |
|---|---|
port is already allocated on start |
Something else is on 8000. Change the compose mapping to e.g. "8010:8000" and point the SDK at http://localhost:8010. |
Shared map links point at localhost |
Set MAPCONTROL_PUBLIC_URL to your server's public URL so map links work off-machine. |
| Maps vanish after container restart | Keep the ./data volume mount from docker-compose.yml — SQLite and uploaded files live there. |
Community
Questions, ideas, demos to show off? Join us on Discord →
MapControl is developed within the ESIP Federation community. Issues and pull requests are welcome.
License
MapControl MCP questions
What tools does MapControl MCP provide?
It exposes 13 tools, including create_map, add_geojson, add_arc, add_geotiff, zoom.
How can I use MapControl with an AI agent?
You can connect it to any MCP client like Claude Desktop or Cline by pointing the client to the server's /mcp endpoint.
Can the AI agent take screenshots of the map?
Yes, the agent can capture the current viewport as a PNG file through a tool call.
Does it support 3D terrain?
Yes, it supports MapLibre globe with terrain tiles and sky, as well as 2D views.