@showmeonmap/mcp
v0.6.0
Published
MCP server for ShowMeOnMap — geospatial visualization tools for any MCP-speaking agent (Claude Desktop, Cursor, Zed, ChatGPT Apps SDK)
Maintainers
Readme
@showmeonmap/mcp
Model Context Protocol server for ShowMeOnMap. Lets any MCP-speaking agent — Claude Desktop, Cursor, Zed, Windsurf, ChatGPT Apps SDK, custom LangGraph / CrewAI / Autogen agents — build and mutate geospatial maps over a typed tool surface.
Docs & keys: https://showmeonmap.com/developers · Design spec: docs/superpowers/specs/2026-04-17-mcp-server-design.md (internal).
Install
npx -y @showmeonmap/mcpThat's it. Or pin a version: npx -y @showmeonmap/[email protected].
The server speaks standard MCP/stdio; it's a thin proxy to the ShowMeOnMap HTTP API. Point it at production with your API key (below), or at a local checkout (default: http://localhost:3000).
Get an API key (production)
- Sign in at showmeonmap.com and buy a credit pack (
/pricing). - Account menu → API keys → Create key. Copy the
smo_live_…key — it's shown once. - Every map your agent builds spends your credits at the same rates as the web app (1 credit per standard map or mutation, 3 per agent-grade map; reads are free). Refunds are automatic on failures.
Prefer no local process? Use the hosted remote endpoint directly — no install:
{
"mcpServers": {
"showmeonmap": {
"url": "https://showmeonmap.com/api/mcp",
"headers": { "Authorization": "Bearer smo_live_…" }
}
}
}The hosted endpoint speaks streamable HTTP (stateless), is rate-limited to 30 requests/min per key, and works anywhere remote MCP servers do — Claude custom connectors, the OpenAI Responses API (server_url), Gemini managed agents (mcp_server), Cursor, Copilot Studio, Bedrock.
Wire into Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"showmeonmap": {
"command": "npx",
"args": ["-y", "@showmeonmap/mcp"],
"env": {
"SHOWMEONMAP_BASE_URL": "https://showmeonmap.com",
"SHOWMEONMAP_API_KEY": "smo_live_…"
}
}
}
}Restart Claude Desktop. showmeonmap appears in the tools picker.
Cursor (~/.cursor/mcp.json), Zed, Windsurf, and any other MCP host follow the same shape — command: "npx", args: ["-y", "@showmeonmap/mcp"], plus the SHOWMEONMAP_BASE_URL + SHOWMEONMAP_API_KEY env. For local dev against a checkout, drop the key and set SHOWMEONMAP_BASE_URL=http://localhost:3000.
Run a ShowMeOnMap backend locally
The package is a client; you also need a backend. From a checkout of the main repo:
SMO_DISABLE_AUTH=1 SMO_DISABLE_CREDIT_GATE=1 SMO_DISABLE_RATE_LIMIT=1 npm run dev(In production, point SHOWMEONMAP_BASE_URL at the hosted backend instead.)
Try it
Ask Claude Desktop things like:
- "Build me a map of volcanoes in Japan" → invokes
build_map, returns amap_id+ structured response - "Now filter to ones above 2000m elevation" → invokes
mutate_map, applies afilterLayerop - "Change the basemap to satellite" → another
mutate_mapwithsetBasemap - "Show me recent earthquakes" (unrelated) →
outOfScopetriggers; Claude can fall back tobuild_map - "Remove it" (with a multi-layer map) →
clarificationwith candidates
Tool surface (26 tools)
Workspace lifecycle (6): build_map, mutate_map, get_state, export_workspace, undo, redo.
Direct ops (14): add_layer, filter_layer, select_within, focus_area, query_features, remove_layer, clear_filters, rename_layer, restyle_layer, set_camera, set_time, tag, add_annotation, remove_annotation.
Multi-source analysis (3): add_fusion_layer, correlate_layers, run_analysis.
Export (2): export_layer_data, export_image.
Feedback channel (1): report_blocker.
focus_area (0.4.0) refines an existing map to a named area — no coordinates required. Pass a layerId plus an areaRef ({kind:'corridor', street, from?, to?}, {kind:'place', name}, or {kind:'aroundIntersection', street, cross}) and the server resolves the real geometry from OpenStreetMap, then applies one of three modes: filter (hide non-matching), select (extract to a new layer), or clip (geometrically trim — lines are split at the boundary). The reply carries a grounded contextAnswer such as "kept 37 of 150 features". A resolver miss returns a clarification with real nearby candidates rather than a guessed location.
run_analysis (0.5.0; knobs below 0.6.0) computes a spatial analysis server-side and appends the finished, honestly-legended result as a new layer + camera. analysis: 'hotspot' runs Getis-Ord Gi*: hot/cold significance classes (99/95/90%, Benjamini-Hochberg FDR-corrected) on a numeric valueField, or on hex-binned point density when valueField is omitted (fishnet with zero cells, auto-grown under a stated cap). The reply carries a grounded memo + stats payload (Moran's I, class counts, significant share) — every number is computed, never generated — plus resultLayerId and a contextAnswer like "Found 12 hot and 0 cold features among n=72". "No significant clustering" is a finding that keeps the map (and the credit); typed failures (unknown layer, too few features, the 10,000-feature cap) are refunded. analysis: 'zonal' aggregates a point layer into polygon zones — an existing zone layer (zoneLayerId) or a generated h3 hex fishnet (zones: {kind:'h3', resolution?}) — as count/sum/mean/min/max per zone, rendered as a quantile choropleth where a zone with zero features and a zone the data never reaches are DIFFERENT legend classes. analysis: 'enrich' joins a US Census ACS indicator (median_household_income, median_home_value, median_rent, … or a raw variable code) onto each feature by tract/county/state containment — unmatched features stay null, never zero-filled, and the joined column carries the indicator's human name. In all analyses the result replaces its input layer(s) (undoable; original attributes preserved or aggregated as stated in the memo). The catalog grows behind this one tool. Two opt-in knobs (0.6.0): annotate: true places ONE callout annotation at the analysis's headline feature (hottest cluster / top zone / highest joined value — label text only from computed values; no headline → no annotation), and story: true persists the shared map with a 2-step story rail (the grounded contextAnswer as overview + the memo as an analysis step that isolates the result layer at its real extent) so the /m/ link opens as a guided read.
Every tool returns a three-channel envelope:
{
"ok": true,
"map_id": "…",
"version": 5,
"structured": { "workspace": {…}, "mapPlan": {…} },
"shareable_url": "http://localhost:3000/m/…",
"metadata": { "title": "…", "layer_count": 2, "last_updated": "…" },
"next_actions": [
{ "label": "Filter to features where name is not empty", "argsTemplate": { "instruction": "…" } }
],
"outOfScope": { "reason": "…" },
"clarification": { "question": "…", "candidates": [ … ] }
}The calling agent reasons over structured, surfaces shareable_url to its end user for interactive drill-down, and uses next_actions as typed hints for follow-up tool calls.
For the full Op catalog and predicate grammar that the V2 tools accept, fetch the MCP resource at showmeonmap://docs/workspace-ops-spec — agents should not guess these shapes.
Environment variables
| Var | Default | Purpose |
|---|---|---|
| SHOWMEONMAP_BASE_URL | http://localhost:3000 | Where the ShowMeOnMap HTTP API lives |
| SHOWMEONMAP_API_KEY | (unset) | Your personal smo_live_… key — identifies you and spends your credits. Required against production; optional for a local dev checkout running with auth bypasses |
| MCP_BLOCKER_LOG_PATH | reports/agent-blockers/<today>.ndjson (in the backend's cwd) | Where report_blocker tool calls are appended |
| MCP_RATIONALE_LOG_PATH | reports/mcp-rationale/<today>.ndjson | Where typed-op rationales are persisted |
Run from a checkout (debugging)
If you have the main repo cloned and want to run the unbuilt source:
npm --prefix packages/mcp-server install
npm --prefix packages/mcp-server run build
node packages/mcp-server/dist/index.jsThe integration test (requires backend on localhost:3000):
npx tsx packages/mcp-server/__tests__/stdio-roundtrip.test.tsSpawns a fresh server process, exercises initialize → tools/list → every tool over NDJSON-framed JSON-RPC, asserts envelope shape. Exits 0 with a "skipping" notice if the backend isn't reachable.
Current limitations
- In-memory session cache.
map_id→ workspace mapping lives in the server process; restarts lose state. Phase I-3 (in the parent repo's plan) wires D1 persistence soshareable_urlactually resolves across sessions. - No preview PNG. The envelope's
shareable_urlpoints at the app's/m/<id>viewer, but workspaces aren't yet persisted in the gallery. Until I-3, share the raw structured response. - stdio bin + hosted remote endpoint. This package's bin is stdio; remote hosts use
https://showmeonmap.com/api/mcp(streamable HTTP, stateless). The standalone Node HTTP entry (packages/mcp-server/src/http.ts) remains for self-hosting a checkout. - Auth is per-user API keys (
smo_live_…, minted at showmeonmap.com). The key travels as a bearer on every backend call and bills the key's owner.
References
- Protocol: modelcontextprotocol.io
- SDK:
@modelcontextprotocol/sdk - Repo: github.com/FrankAIUser/showmeonmap
