Seventeen MCP tools, one URL, no key to connect

# Seventeen MCP tools, one URL, no key to connect

> **Note, updated 2 August 2026.** The server exposed seventeen tools when this was written. It exposes **32** today. The tools named below are all still live; the list has grown since. See [/docs/mcp](/docs/mcp) for the current set.

MapMap ships an MCP server, `sn-mcp`, that exposes seventeen tools to any MCP client: routing, search along a route, day planning, reachability, ADR tunnel compliance, geocoding, matrix, optimisation, errand ordering, map correction, integration feedback and map styling, each with a full JSON Schema and a structured output. You can point an agent at the hosted endpoint `https://mcp.mapmap.ai/mcp` with no API key and no install, or run the same binary yourself over stdio or streamable HTTP against your own deployment. One of the seventeen, `check_style_contrast`, lets an agent audit the accessibility of a map it just designed, which is the part we think matters most.

## What are the seventeen tools?

Seventeen tools, grouped by what they talk to. Coordinates are `{lat, lon}` objects rather than positional arrays, distances are metres and durations are seconds throughout.

| Tool | What it does |
|---|---|
| `route` | Turn-by-turn route, costing `auto` or `truck`. The truck profile `{height_m, width_m, length_m, gross_weight_t, hazmat, tunnel_code}` merges dimensional and ADR costing. Returns `{distance_m, duration_s, summary, maneuvers[], geometry_polyline6, applied_adr}`. |
| `check_adr_tunnel` | The ADR 8.6.4 tunnel-entry decision on its own: `{tunnel_code?, hazmat, tunnel_category}` returns decision, reason, explanation and the worst-case forbidden category list. No network, answers instantly. |
| `geocode` | Forward geocoding: `{query, limit?, focus?}` returns candidates with name, label, lat/lon, type and address parts. |
| `matrix` | Many-to-many travel matrix: `{origins[], destinations[], costing, truck?}` returns `durations_s[i][j]` and `distances_m[i][j]`. Null cells are unreachable pairs. |
| `optimise_routes` | Multi-vehicle, multi-stop VRP over a matrix computed by our own engine, so costing `truck` means every optimised route respects dimensional and ADR restrictions. Fair-use cap: more than 200 unique locations returns a tool error asking you to split the problem. |
| `order_stops` | The single-vehicle case, which is what agents actually ask for. `start` plus `stops` (1 to 100 entries of `{location, label?, service_s?}`), optionally an `end` or `round_trip: true`. Returns the stops in visit order with arrival offsets. |
| `search_along_route` | Places along a route priced by the real extra travel time of stopping at each, never a straight-line guess. Returns candidates sorted by detour with `detour_minutes`, `detour_km` and an along-route position. |
| `plan_day` | Turns an itinerary into one navigable multi-stop route: geocodes free-text stop names, optionally reorders them with the VROOM solver, and returns per-leg times, arrival estimates and the full geometry. |
| `reachable_area` | Reachability contours (isochrones) from an origin within one or more travel-time budgets, returned as a GeoJSON FeatureCollection, one feature per contour. |
| `report_map_issue` | Queues a first-party map correction for review, never an automatic OSM edit: a located observation such as `road_closed` or `wrong_speed_limit`, appended to the deployment's review queue. |
| `submit_integration_retro` | Files a short retro after an integration session: what worked, what fought you. Delivered to the deployment's feedback queue so the platform improves from real agent experience. |
| `list_style_layers` | Lists every palette slot, skeleton layer id and source-layer a theme can restyle. Local lookup, no network, no configuration. |
| `get_style` | Fetches a hosted style's latest theme document and compiled style URL. Public read, unmetered. |
| `create_style` | Creates a hosted style from a theme document (metered publish). |
| `set_palette` | Recolours a style by setting palette slots, published as a new immutable version (metered publish). |
| `set_layer_paint` | Overrides one paint property on one layer, for example road width or opacity (metered publish). |
| `check_style_contrast` | Audits a theme's palette against WCAG 2.1, across both light and dark variants. Pass a hosted `style_id` or an inline `theme`. Advisory only. |

Invalid inputs and upstream failures come back as MCP tool errors with actionable messages, naming the exact environment variable to set where that is the cause, so an agent can correct itself rather than guess.

## How does an agent connect?

Give it the URL. The hosted server speaks streamable HTTP at `https://mcp.mapmap.ai/mcp`, needs no key to connect and runs on fair use.

Claude Code:

```sh
claude mcp add --transport http mapmap https://mcp.mapmap.ai/mcp
```

Claude Desktop, in `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
"mcpServers": {
"mapmap": { "type": "http", "url": "https://mcp.mapmap.ai/mcp" }
}
}
```

Cursor, in `.cursor/mcp.json`:

```json
{
"mcpServers": {
"mapmap": { "url": "https://mcp.mapmap.ai/mcp" }
}
}
```

Codex, in `~/.codex/config.toml`:

```toml
[mcp_servers.mapmap]
url = "https://mcp.mapmap.ai/mcp"
```

VS Code in Copilot agent mode uses `.vscode/mcp.json` with a `servers` key, and Windsurf uses `~/.codeium/windsurf/mcp_config.json` with `serverUrl`. Both take the same URL. The [MCP connection guide](/docs/mcp) has the exact file for each, plus the Claude API MCP connector, which takes the URL directly with no local process at all.

To run it yourself, `sn-mcp` takes `--transport stdio` for a desktop client or serves streamable HTTP on port 8200 at `/mcp` under the self-host distro's `mcp` compose profile. Each upstream is one environment variable: `VALHALLA_URL` powers `route`, `matrix`, `optimise_routes` and `order_stops`, `VROOM_URL` powers the two optimisation tools, `PHOTON_URL` powers `geocode`, and `STUDIO_URL` powers the hosted style tools, with `STUDIO_API_KEY` needed only for the three publishes (style reads work without it). All are optional at startup. The server boots regardless and warns on stderr for each missing upstream, keeping stdout clean for the protocol.

## Why is `check_style_contrast` the interesting one?

Because it closes a loop that was otherwise open: an agent could already design a map, but it could not tell whether anyone could read it. `set_palette` lets an agent recolour a style in one call. Nothing stopped it choosing mid-grey labels on a mid-grey landuse fill and publishing a map that looks fine in a thumbnail and is illegible at speed.

`check_style_contrast` gives the agent the same audit a designer would run. It takes a hosted `style_id` or an inline `theme` document (exactly one of the two, never both), resolves the palette and measures every audited pair against WCAG 2.1 thresholds:

| Pair kind | Threshold | WCAG success criterion |
|---|---|---|
| `text` | 4.5:1 | SC 1.4.3 Contrast (Minimum), AA |
| `graphics` | 3:1 | SC 1.4.11 Non-text Contrast |

The maths is implemented to the WCAG 2.1 definitions of relative luminance and contrast ratio, not approximated. Both the light and the dark palette are checked, because a theme carries both and a palette override that fixes one often breaks the other.

The output is shaped for an agent rather than a report reader:

```json
{
"passes": false,
"findings": [
{
"variant": "dark",
"kind": "text",
"foreground": "textPrimary",
"background": "background",
"description": "place-label text on the map background (halo-free worst case)",
"ratio": 3.12,
"threshold": 4.5,
"passes": false
}
]
}
```

Failing pairs sort first, so the agent sees what to fix without scrolling. Each finding names the palette slots involved, which are the exact slots to pass back to `set_palette`. Generate a theme, audit it, correct the two slots that failed, publish. No human in the loop, and no human needed to notice the map was unreadable.

The audit is advisory by design. It never blocks a publish and it never fails a compile. We think a tool that refuses to ship your map because a boundary line missed 3:1 by a tenth would get routed around within a week, and an advisory one that agents actually call is worth more than a gate they learn to avoid.

## What does it cost, and what is metered?

Most of it is not metered at all, because most of it never touches the gateway.

- The routing, matrix, geocoding and optimisation tools talk **directly** to the engines inside your deployment (`VALHALLA_URL`, `VROOM_URL`, `PHOTON_URL`). Those calls are not API-key metered.
- `check_adr_tunnel`, `list_style_layers` and `check_style_contrast` on an inline `theme` are fully local. They need no upstream and no network.
- `get_style` is a public read and unmetered.
- The three style publishes (`create_style`, `set_palette`, `set_layer_paint`) authenticate with `STUDIO_API_KEY` and meter against that key at the Standard price class: 0.05p per call, falling to 0.03p beyond 1M calls a month and 0.02p beyond 10M.

## What does not work yet?

Four things, and you will hit them in this order.

**The hosted endpoint is for evaluation, not production.** It is live and open, but `geocode` and `optimise_routes` answer with tool errors until their upstreams are enabled on the hosted deployment, and `order_stops` needs the same VROOM solver so it behaves the same way. Style publishes are metered and may be unavailable. For production traffic, use the REST API with your own `snk_` key, or run the server yourself.

**The `sn-mcp` binary is not on a package registry.** You build it from the source distribution (source access on request) or run it from the self-host distro's Docker image. There is no `npx sn-mcp`, and we are not going to pretend otherwise.

**The HTTP `/mcp` endpoint is unauthenticated.** Anyone who can reach the port can call every tool, including metered style publishes if `STUDIO_API_KEY` is set in the server's environment. Run it inside your trust boundary, restrict it by network policy or add auth at your reverse proxy, and terminate TLS there exactly as you would for the gateway. Pass `--allowed-host` (repeatable) in production so Host-header validation defends against DNS rebinding. Omit it and validation is off, and the server warns you at startup.

**`order_stops` and `check_style_contrast` are newer than the rest.** They landed after the original ten and the compose file still ships with `VROOM_URL` commented out, so `order_stops` is off until you uncomment the sidecar.

## Try it

Add the hosted endpoint to your client with one command and ask your agent to plan a truck-legal route through the Dartford Crossing with a hazmat load. Full per-client setup, the environment-variable table and the self-host paths are in the [MCP connection guide](/docs/mcp).

The tools are half the story. Agent skills carry the how-to knowledge that tools cannot, covering truck and ADR patterns, web SDK integration and migrating from Mapbox: see [the agent skills write-up](/news/agent-skills). For why we built the platform this way round, with the agent as a first-class caller rather than an afterthought, read [maps for AI: what agent-native navigation requires](/news/maps-for-ai). Everything an agent needs to find, price and key the platform without a human is on the [agents page](/agents).

Routing derives from OpenStreetMap. Credit "© OpenStreetMap contributors" when you render or republish it.