mcpbeat Sign in

MapMap MCP Server

answering

MapMap is answering right now. Last checked 14 min ago. It exposes 43 tools.

Maps built for agents: routing incl. truck/ADR, geocoding, matrices, isochrones — 34 tools.

Uptime history 19 days of history · worst day 99%
19 days agonow
100.0%
Uptime 24h
91 of 91 checks
43
Tools
read from the server
165 ms
Response time
average over 24h
open, no key
Access
streamable-http

What changed 22

Every tool that appeared, vanished or quietly changed what it asks for. Recorded since 31 August 2026. No other catalogue keeps this.

18 Sep 2 tool descriptions were rewritten create_style, list_style_layers
18 Sep a tool changed the parameters it asks for create_style
17 Sep a tool description was rewritten cheapest_fuel_along_route
14 Sep a tool description was rewritten optimise_routes
13 Sep a tool description was rewritten submit_integration_retro
4 Sep 4 tools appeared cluster, get_job, replan_routes and 1 more
4 Sep 2 tool descriptions were rewritten optimise_routes, route
4 Sep 2 tools changed the parameters they ask for optimise_routes, route
1 Sep 5 tools appeared cheapest_charging_along_route, get_usage, list_place_categories and 2 more
1 Sep a tool description was rewritten geocode
and 2 more, back to 31 August 2026

Nothing serious here today

Today is the operative word: we check MapMap every 15 minutes and re-read its code on every release. Watch it and you find out the day that stops being true.

Three servers free · no card

Connect this server

Endpoint below is the one we actually reach during checks — not the one copied from a README. Last verified 14 min ago.

run in your terminal
claude mcp add mapmap --transport http https://mcp.mapmap.ai/mcp
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mapmap": {
      "url": "https://mcp.mapmap.ai/mcp"
    }
  }
}
~/.codex/config.toml
[mcp_servers.mapmap]
url = "https://mcp.mapmap.ai/mcp"
.cursor/mcp.json
{
  "mcpServers": {
    "mapmap": {
      "url": "https://mcp.mapmap.ai/mcp"
    }
  }
}
.vscode/mcp.json
{
  "mcpServers": {
    "mapmap": {
      "url": "https://mcp.mapmap.ai/mcp"
    }
  }
}

Available tools 43

Read directly from the server with tools/list, grouped by what they act on. If a tool disappears, we record the date.

geo
geo_area
Area in square metres enclosed by a ring of 3+ coordinates, computed geodesically. Always positive: the answer does not depend on whether the ring is wound clockwise or anticlockwise. Intended for zones and boundaries, not for polygons covering more than half the globe. Local computation: no network call, no quota.
geo_bbox
The axis-aligned bounding box enclosing 1+ coordinates, as {min_lat, min_lon, max_lat, max_lon}. Useful for fitting a map view to a set of stops. Local computation: no network call, no quota.
geo_bearing
Initial bearing from one coordinate to another, in degrees clockwise from true north (0-360). This is the bearing at the START of the geodesic; over long distances the bearing changes en route. Local computation: no network call, no quota.
geo_centroid
The centroid (geometric mean position) of 1+ coordinates, e.g. to pick a depot location or centre a map. Local computation: no network call, no quota.
geo_destination
The coordinate reached by travelling `distance_m` metres from `from` on `bearing_deg` (degrees clockwise from true north). The inverse of `geo_distance` + `geo_bearing`. Local computation: no network call.
geo_distance
Distance in metres between two coordinates. Computed geodesically on the WGS84 ellipsoid, so it is the straight-line (as-the-crow-flies) distance, NOT a driving distance — use `route` or `matrix` for travel distance and time. Local computation: no network call, no quota.
geo_length
Total length in metres of a polyline through 2+ coordinates, summed geodesically. This measures the line you supply, NOT a driven route — use `route` for that. Local computation: no network call, no quota.
geo_nearest_point_on_line
The closest position on a polyline to a given coordinate, plus the geodesic distance to it in metres. The answer may lie between vertices, not only on them. Useful for 'how far is this address from the route?'. Local computation: no network call, no quota.
geo_point_in_polygon
Whether a coordinate lies inside a polygon: delivery zones, catchments, congestion or clean-air zones, site boundaries. Provide `point` {lat, lon} and `polygon` as 3+ {lat, lon} coordinates of the outer ring (closed automatically if the last does not repeat the first). Points exactly on the boundary count as OUTSIDE. Local computation: no network call, no quota.
geo_simplify
Reduce the number of coordinates in a polyline while keeping its shape (Douglas-Peucker). `tolerance_deg` is in DEGREES, not metres: about 0.0001 drops detail finer than roughly 10 m at the equator. Endpoints are always kept. Returns the retained points and how many were removed. Local computation: no network call, no quota.
style
check_style_contrast
Audit a map style's colour contrast against WCAG 2.1 (4.5:1 for label text, 3:1 for graphics like the route line), across both the light and dark palette variants. Pass a hosted `style_id` OR an inline `theme` document (as accepted by create_style). Advisory: failing pairs list the palette slots to adjust with set_palette; publishing is never blocked on contrast.
create_style
Create a hosted map style. Provide a name, optionally a named `base` to start from (light, dark, streets, midnight, navigator-day, navigator-night, fleet, outdoor, dataviz, backdrop, print - call list_style_layers for what each one is for), and optionally a theme document ({base: "light"|"dark", palette: {slot: colour}, layers: {layer_id: overrides}}). The named base is laid down first and the theme document is merged over it, so "streets with darker water" is one call; with neither, the style starts from the default theme. A theme may also set worldview (ISO 3166-1 alpha-2, accepted: AE, KR, SA, US) to display that jurisdiction's official names for a small curated registry of renamed features; omitted, labels keep the OSM on-the-ground names. Returns the generated style_id (pass it to set_palette / set_layer_paint) and the compiled style URL for MapLibre.
get_style
Fetch a hosted style's latest theme document (the editable source) and the URL of its latest compiled MapLibre style. Use the theme to inspect current palette and layer overrides before editing.
list_style_layers
List everything a MapMap style theme can style: the first-party named bases a style can start from (pass one as `create_style`'s `base`, or as the static-map API's `style=`), the named palette slots with their light/dark default colours, the skeleton layer ids (paint order) that `set_layer_paint` accepts, and the OpenMapTiles source-layers extra layers may reference. Attribution is enforced on every compiled style and cannot be themed away. Local lookup, no network; always works.
cheapest
cheapest_charging_along_route
Find the best EV charge points along a route, with the REAL extra travel time of stopping at each one — never a straight-line guess. Provide `origin` + `destination` (a route is computed) or an existing route's `geometry_polyline6`, plus optional `connectors` ("ccs", "type2", "chademo", "type1", "tesla", "domestic", "other"), `min_kw` (e.g. 50 for rapid only), `available_only` and `max_detour_minutes` (default 10). Charge points come from operator-published feeds, are costed through the routing engine with your costing (a `truck` profile makes detours respect dimensional/ADR restrictions) and ranked most powerful first, since minutes off the clock are bought with kilowatts. Each result carries max_power_kw, connector_standards, best_connector, evse_count, detour_minutes/detour_km and, where a live feed backs it, available_now. IMPORTANT: there is no national charge-point registry — every deployment covers only the operators it has onboarded, so ALWAYS show the returned `coverage_note` alongside the results. An empty `results` means "none from these operators within the detour budget", NEVER "there are no chargers here". Statuses are live only when availability_live is true; otherwise they are the values captured at the last ingest and must not be described as current. Requires the MapMap gateway; answers a clear error when the deployment has no charge-point dataset. Display the returned charging_attribution with the results.
cheapest_fuel_along_route
Find the cheapest fuel along a route, with the REAL extra travel time of stopping at each station — never a straight-line guess. Provide `origin` + `destination` (a route is computed) or an existing route's `geometry_polyline6`, plus a `fuel` code ("diesel" default, "petrol_95", "petrol_98", "premium_diesel", "e85", "lpg") and `max_detour_minutes` (default 10). Stations come from the live open-data price feeds (UK CMA retailer scheme and/or the statutory Fuel Finder, FR prix-carburants, DE Tankerkoenig; the response's `fuel_attribution` names the ones actually matched), are priced through the routing engine with your costing (a `truck` profile makes detours respect dimensional/ADR restrictions) and ranked freshest-priced first and cheapest within that. Each result carries price {value, currency, updated_at, stale}, detour_minutes/detour_km, and saving_per_litre vs the cheapest on-route baseline (pass `fill_litres` to also get saving_total). `stale` = not verifiably fresher than 24 h: true unless BOTH the price's own updated_at and the snapshot's fetch time are inside that window, and true whenever either is missing. A stale price is ranked below every fresh one, is never the baseline, and carries NO saving_per_litre — quote it as "last seen at X on <date>", never as a saving. Requires the MapMap gateway; answers a clear error when the deployment has no fuel-price dataset. Display the returned fuel_attribution with the prices.
plan
plan_day
Turn an itinerary into one navigable multi-stop route. Provide a `start` and `stops` (each a `location` {lat, lon} or a free-text `name` to geocode, plus optional `dwell_minutes` time at the stop), optional `depart_at` (RFC 3339) for absolute ETAs, `optimise: true` to reorder stops for the shortest day (VROOM solver), and `return_to_start`. Costing "auto", "truck" (with a `truck` profile the whole day respects dimensional/ADR restrictions), "bicycle", "pedestrian" or "motor_scooter". Returns the stops in visit order with per-leg duration/distance and arrival/departure times, totals, and the full route geometry (polyline6). Geocoded names carry a `resolution` — when `ambiguous` is true, check `alternatives` and re-run with an explicit location rather than trusting the guess.
plan_ev_route
Plan a whole electric-vehicle journey, charge stops included. Give `origin` and `destination` (plus optional `waypoints`) and a `vehicle` — a published profile ("small_hatch", "saloon", "suv", "van") and/or inline figures (battery_kwh, mass_kg, drag_area_m2, aux_kw, connectors) — with `start_soc` (default 0.9), `min_arrival_soc` (default 0.1), `reserve_soc` (default 0.1, the floor the charge must never drop below mid-route), optional `connectors` and `min_kw` filters and `ambient_temperature_c`. Energy comes from a published road-load physics model over the route's own legs; charge times are integrated over the vehicle's charging curve capped by the charge point, NOT energy divided by peak power, which is the single biggest error in naive EV planners. Returns the stops with arrive/depart state of charge, charge time and detour, a per-leg state-of-charge trace, and the journey's driving and charging time. IMPORTANT: when no plan exists — a charger desert, a connector mismatch, a gap wider than the car's range — the answer comes back with `feasible: false`, a `reason` and the furthest point on the route the car can actually reach. That is an ANSWER, not an error to retry: report the reason and never describe it as a plan. `gradient_data` says whether elevation was available: "absent" means consumption was modelled on the flat and under-reads a hilly route. There is no national charge-point registry, so ALWAYS show the returned `coverage_note` — an infeasible plan means "none from these operators", never "there are no chargers here" — and statuses are current only when `availability_live` is true. Requires the MapMap gateway; answers a clear error when the deployment has no charge-point dataset. Display the returned charging_attribution with the plan.
submit
submit_integration_retro
Send MapMap a structured integration retro (problems, gotchas, wins, docs gaps). Call at most once, after your MapMap integration works or you stop trying, and only if the developer has approved sending feedback to MapMap. Sends ONLY the structured fields in this schema to MapMap: there is no field for a transcript, a prompt, source code, file contents or coordinates. The free-text fields are short and capped, but they are still free text: do NOT paste code, credentials, customer names or personal data into them. Provide `what_built` (required), `problems` [{area: sdk|api|mcp|docs|billing|self-host|other, description, workaround_found}], `gotchas`, `wins`, `docs_gaps`, and optionally `agent_name` and `sdk_version`.
submit_optimise_job
Submit a problem too large to solve inside one request to the asynchronous lane, and get a job id back. Set `kind` to "optimise", "replan" or "matrix", and pass `problem` in EXACTLY the shape the matching synchronous tool takes — `optimise_routes` input, `replan_routes` input, or `matrix` input. Moving a working synchronous call onto this lane changes nothing but which tool you call it with. A field that tool's input does not have is REFUSED by name rather than dropped: the HTTP API accepts some the MCP tools have not surfaced yet, and a job queued without a constraint you asked for is worse than one that was never queued. The ceilings are far higher here because there is no request to hold open: 2,000 unique locations for an optimisation or re-plan against the synchronous 200, and 40,000 matrix elements against 10,000 (a deployment may set either lower, in which case its own refusal is the authority). A re-plan is counted on the REMAINING problem, after completed stops are removed, so a shift well through its day may fit where the morning's would not. This answers 202-and-a-job-id, NOT a plan: the job is queued and a worker picks it up. Poll `get_job` with the returned id until it says the status is terminal, then read the result. Polling is free — the gateway meters this submission, not the reads. Units are charged on submission and handed back in full if the job fails. The optional `webhook_url` (https only) posts a SIGNED notification when the job finishes and is for a human wiring infrastructure that must react without a process watching; it carries a pointer, never the result, and needs a webhook signing secret on the key. An agent that can poll should not use it. Requires the MapMap gateway.
adr
check_adr_tunnel
Check whether a vehicle may pass through a tunnel of a given ADR category ("A"–"E"). Provide `hazmat` and, when known, the load's ADR 8.6.4 tunnel restriction code (e.g. "B", "C5000D", "B/D", "none"). Applies the conservative worst-case reading: conditional clauses are assumed to apply, so a blocked answer may over-restrict but never under-restricts. No network access; answers instantly.
along
search_along_route
Find places (POIs) along a route with the REAL extra travel time of stopping at each — never a straight-line guess. Provide `origin` + `destination` (a route is computed) or an existing route's `geometry_polyline6`, plus a free-text `query` ("coffee", "EV charger", "truck stop") and `max_detour_minutes` (default 10). For a category intent ("fuel", "EV charger", "coffee") pass `category` instead of relying on words alone: it takes the same vocabulary as `nearby_places` (lowercased OSM tag values such as "fuel", "cafe", "charging_station", "parking", "pharmacy"), and common colloquial phrases are normalised server-side ("petrol station" and "gas station" to fuel, "coffee" to cafe, "EV charger" to charging_station). `query` alone also promotes a pure category phrase to the same browse, so "fuel" finds fuel stations rather than places whose NAME starts "Ful"; anything else stays free-text name matching. When a browse ran, the response echoes the tokens used in `matched_categories`. Candidates near the route corridor are priced through the routing engine with your costing: detour = (origin→place) + (place→destination) − (origin→destination). Costing "auto", "truck" (with a `truck` profile the detours respect dimensional/ADR restrictions), "bicycle", "pedestrian" or "motor_scooter". Returns results sorted by detour with detour_minutes, detour_km, along_route_position (0-1) and off_route_m; at most 25 candidates are priced per call (candidate_cap).
clearance
check_clearance_on_route
Measure a vehicle's overhead clearance along a route against surveyed point cloud geometry, wherever survey coverage exists. Routes with truck costing (so the search already avoids the height restrictions the map has tagged), then measures that corridor. Give `origin`, `destination` and `height_m`; optional `width_m` asks the corridor-width axis too, and optional `margin_m` adds your operating margin to the vehicle before the verdict. Returns `pass`, `fail`, `indeterminate` or `no_verdict` with the limiting point, the measured headroom, its uncertainty bound (`safe_headroom_m`, `sigma_m`, `sampling_gap_m`) and a link to that exact view in the survey viewer. An `indeterminate` carries `indeterminate_reasons` as codes to branch on and the same reasons as English inside `explanation`; read out the English. A `pass` may carry no limiting point at all, which means the survey found nothing above that corridor, and the width axis may answer `not_assessed` where the corridor edges are too sparsely surveyed while the height axis still answers. Honesty, and it matters here: this measures physical geometry from a dated survey. It is not a signed or posted height, `clearance_enforcement.route_certified` is always false, and the caveat is on every answer including the clear one. Ground the survey did not cover comes back as `not_surveyed_m` and is never judged, so a `pass` is possible over complete coverage and nowhere else; sparse or stale coverage comes back separately as `insufficient_data_m`. Reach for this when a truck route came back unchanged and you need to know whether that means anything: an unchanged route avoids what the map records, which is a different claim from measured headroom, because a structure nobody tagged is routed through like open road. Needs the MapMap gateway (GATEWAY_URL + GATEWAY_API_KEY), which holds the surveys; there is no fallback, and it will not answer from the routing step alone. The mapmap://guide/clearance resource sets out what each answer proves.
cluster
cluster
Group stops into balanced geographic clusters, so a day too large for one optimisation can be optimised one cluster at a time. This is the front half of the recipe for a thousand-stop day: cluster here, then call `optimise_routes` per cluster, where the routing engine's own matrix decides the visiting order. IMPORTANT — this is STRAIGHT-LINE clustering. Distances are measured between coordinates, not along the road network: no road, river, motorway junction or one-way system is consulted, and two stops either side of an estuary look adjacent. That makes it the right tool for deciding which stops belong TOGETHER and the wrong one for deciding what ORDER to visit them in. The answer carries a `basis` sentence saying exactly this; show it, so a centroid is never read as a plan. Provide `locations` ([{id, lat, lon, load?}], ids unique, at most 5,000) and EXACTLY ONE of `clusters` (how many groups, balanced by stop count), `max_cluster_locations` or `max_cluster_load` (a per-cluster ceiling the count is derived from). Optional `territories` keep a cluster from straddling a round: each is clustered on its own, and so are the stops inside none of them. Optional `seed` (default 42) drives the seeding — the same request with the same seed always returns the same clusters, on every deployment, so a re-run is a re-run. Returns each cluster's member ids, count, summed load, centroid and territory, plus a `balance` block naming the constraint applied and whether it had to be relaxed to place every stop: a load ceiling with lumpy loads is a bin-packing problem and may have no solution at the derived count. Requires the MapMap gateway.
elevation
elevation
Sample terrain elevation. Provide `points` (a bare list of coordinates) for point elevation, or `encoded_polyline` (optionally with `resample_distance_m`) for an along-route profile — not both. Returns one sample per point/resampled point in order; `elevation_m` is null wherever the engine's DEM tile set has no coverage at that point (never a guess). The `encoded_polyline` form also returns each sample's resampled lat/lon and cumulative `range_km` from the start.
geocode
geocode
Turn a place (address, POI, town) into coordinates. Ask two ways, and they combine: `query` is free text — one run-together string, the way a person types into a search box — and `street`, `housenumber`, `city`, `postcode` and `country` name the parts of an address separately. At least one of the two is required. Pass the parts whenever you already hold the address in parts (a form, a CRM row, a manifest): components are REQUIREMENTS, not hints, so `city: "London"` means a result outside London cannot come back at all, where "London" inside `query` only reorders. `country` takes an ISO 3166-1 alpha-2 code or a country name ("GB", "United Kingdom"); a value naming no country is refused rather than silently matching nothing. Returns up to `limit` (default 10) candidates with name, one-line label, lat/lon, type and address parts. Pass `focus` {lat, lon} to rank results near a location higher. Each hit also carries `match`: a per-component matched/inferred/unmatched verdict, the `score_gap` to the runner-up, and which backend answered. READ IT before acting on an address — an unmatched or inferred postcode on the top hit means the answer does not carry the address you asked for, and a small `score_gap` means the ranking barely chose, so show the alternatives instead of picking one. Use `verify_places` when the address came from a model or a user and needs checking rather than using. Results are matched in `lang` (default "en"), so English exonyms — "Munich", "Cologne", "Geneva" — resolve to the place meant; pass `lang` when querying in another language, or "default" for each place's local name.
job
get_job
Read an asynchronous job submitted with `submit_optimise_job`: its status, and once it has finished, its result inline — exactly the body the synchronous tool would have returned. Status is `queued`, `running`, `succeeded` or `failed`; the answer's `terminal` field says whether the job will ever leave the status it is in, so poll while that is false. POLLING IS FREE: the gateway meters the submission and not the reads, deliberately, because a poll that costs quota is a poll a caller rations, and a rationed poll is how a job that finished in ten seconds gets noticed four minutes later. Check every few seconds rather than guessing at a duration. `units_charged` is what the SUBMISSION drew, and `refunded` says whether a failure handed it back — a failed job shows both, because reporting zero would be a lie about what was charged. Webhooks are the alternative to polling and exist for humans wiring infrastructure, not for agents in a loop. A job belongs to the key that submitted it (or another key of the same identity); anyone else's id answers NOT FOUND rather than forbidden, because confirming an id exists is itself a disclosure. Requires the MapMap gateway.
layer
set_layer_paint
Set one MapLibre paint property on one skeleton layer of a hosted style (e.g. layer_id "road-major", property "line-width", value 4 or an expression array) and publish the result as a new immutable style version. Layer ids come from list_style_layers. Returns the new version and style URL.
match
match_trace
Snap a recorded GPS trace to the road network and say what it actually travelled over. Provide `shape` (2 to 2000 recorded points, oldest first) or `encoded_polyline` (the same trace as a polyline6 string) — not both — plus the `costing` it was travelled under: "auto" (default), "truck", "bicycle", "pedestrian" or "motor_scooter". Costing decides which roads the trace may match onto, so a walk matched as "auto" snaps to the carriageway rather than the footpath. Returns the matched path as `geometry_polyline6` (the snapped roads, not your raw points) with its `distance_m` and `duration_s`, then the roll-ups: `by_road_class` and `by_admin` (distance and time, longest first), `by_surface` (distance), and `toll`, `bridge` and `tunnel` totals. This is how you turn a dashcam or telematics log into a report — which country and region the driving happened in, how much of it was motorway, how much was tolled, how much was unpaved. Honesty: the roll-ups are summed per matched road segment, so they need not add up to `distance_m` exactly, and segments the map records no surface or admin area for are left out of that breakdown rather than filed under a guess — an entry in `by_admin` with null codes is exactly that, counted and not attributed. Needs the MapMap gateway (GATEWAY_URL + GATEWAY_API_KEY); there is no direct-backend fallback.
matrix
matrix
Compute a travel time/distance matrix between origins (rows) and destinations (columns). Costing "auto", "truck" (with optional `truck` profile as in `route`), "bicycle", "pedestrian" or "motor_scooter". Returns durations_s[i][j] in seconds and distances_m[i][j] in metres; null cells are unreachable pairs. Up to 10,000 cells per call (origins × destinations). Optional `exclude_polygons` for before/after scenarios ("close this bridge and recompute the matrix"): an array of polygons, each an array of [lon, lat] pairs forming one ring — longitude FIRST — whose intersecting roads are excluded from every cell's path finding. Applies to the Valhalla engine; unsupported on the GraphHopper engine, where it is ignored.
nearby
nearby_places
Find places near a point, nearest first with distance in metres — by category (cafes, fuel, EV charging, parking), by name or brand ("the nearest Lloyds bank", "nearest Sainsbury's"), or both. Use this instead of geocode whenever the question is about what is NEAR a location: geocode ranks a brand's branches everywhere and only biases by proximity, so it will happily return a Lloyds in another city over the one 100 m away. Provide `lat`, `lon` and at least one of `category` or `name`. `category` is matched against the map's lowercased OSM tag values (amenity/shop/tourism/…), e.g. "cafe", "fuel", "charging_station", "parking", "pharmacy", "supermarket", "hotel", "restaurant", "fast_food", "atm", "bakery", "hospital", "station"; common colloquial names are normalised ("coffee" -> cafe, "ev_charging" -> charging_station, "petrol" -> fuel, "chemist" -> pharmacy). `name` matches the place's name or alternative names word by word, case- and accent-insensitively, with the last word also matching as a prefix. Combine the two to disambiguate a brand — "Lloyds" plus "bank" excludes Lloyds Pharmacy. A category or name the map does not carry returns an empty list, never an error. Optional `radius_m` (default 2500, max 100000) bounds the straight-line search distance and `limit` (default 5, max 10) the result count. Each result has name, one-line label, lat/lon, address parts, distance_m, categories and a `details` object of display tags (opening_hours, website, phone, ...) when the map carries them. Every result also carries `bearing_deg` and a spoken `direction`. Pass `heading_deg` (degrees clockwise from true north, 0 = north, 90 = east) and results are described from where the user stands — "ahead and slightly to your right, about 80 metres" — with a signed `relative_bearing_deg` (negative left, positive right); without a heading the phrasing falls back to cardinals ("to the north-east"), so this works with or without a compass. Add `fov_deg` to keep only what lies within that cone of the heading — it is the FULL width of the cone, so 90 keeps what lies within 45 degrees either side of dead ahead; anything dropped is counted in `out_of_view`, so a non-zero count means there ARE matching places nearby, just not in front of the user — say that rather than "nothing nearby". Prefer reading `direction` aloud over coordinates. Requires the MapMap gateway (GATEWAY_URL + GATEWAY_API_KEY).
optimise
optimise_routes
Optimise multi-vehicle, multi-stop delivery plans (VRP). Provide `vehicles` (id, start/end, capacity, skills, time_window), `jobs` (id, location, service_s, delivery/pickup, skills, time_windows) and/or `shipments` (pickup+delivery pairs that ride the same vehicle). Costing "auto", "truck", "bicycle", "pedestrian" or "motor_scooter" (cargo-bike and courier fleets welcome): with a `truck` profile (dimensions + ADR declaration, as in `route`), the travel-time matrix respects dimensional and dangerous-goods restrictions, so every optimised route is truck-legal. Returns a summary, unassigned tasks and per-vehicle routes with ordered steps (arrival_s/duration_s in seconds, distance_m in metres). Fair use: at most 200 unique locations per problem, and no wider than the routing engine's matrix span (1,500 km on the hosted endpoint for motor costings, 400 km stock on a self-host). Past that, cluster the stops with `cluster` and optimise each group, or submit the whole problem to the asynchronous lane with `submit_optimise_job` (2,000 locations). Optional `territories` are named polygons ([{id, polygon}], GeoJSON [lon, lat] rings, LONGITUDE FIRST) that bound who serves what: a vehicle listing `territory_ids` may serve a task only if that task sits inside at least one of the territories it names, while a vehicle listing none is unrestricted and may serve anything, inside a round or outside every one. The response's `territories` block says which vehicle was eligible for what and names any task no vehicle could take. A vehicle may also declare `reloads` {max_trips 2-5, reload_time_s, depot?} to return to a depot, reload and go out again — the tipping round. It needs a `time_window`, because the shift is what gets split: it is cut into that many consecutive non-overlapping windows separated by the reload time, each trip carrying the vehicle's FULL capacity and task caps. That split is fixed BEFORE the solve, so the plan is conservative and never optimistic — it cannot put a lorry in two places at once — but it is an approximation: a trip that finishes early cannot lend its spare time to the next, so stops can come back unassigned that a truly sequential model would have served, and `max_trips` is a budget rather than a prediction (ask for five on a shift that supports three and every window shrinks to a fifth). Read the returned `reloads` block before quoting arrival times, and re-plan after each tip with `replan_routes` for the tighter answer. `relax_if_unassigned` {time_windows_by_s?, allow_overtime_s?} re-solves ONCE with those relaxations if the first plan left work unassigned, and the `relaxation` block says honestly which plan came back: at most one second solve, never beyond the caps stated, and the relaxed plan is returned ONLY if it assigns more work than the first. Breaks are never widened — a driver's rest is not a preference to trade for a fuller van — and neither are capacities, skills, territories or task caps; only time windows move. It bills as two solves when the second one runs. Always check `relaxation.relaxed_plan_used` before telling anyone the day fits: a plan produced under relaxation has had promises moved. `emissions` {vehicle_category, fuel, euro_standard} annotates the plan with the clean-air zones its own stops sit in and what this vehicle pays in each; add `avoid_zones: true` to steer the travel-time matrix out of them, which changes the plan itself. Territories, reloads, relaxation and zones are computed by the MapMap gateway; without one configured the tool refuses rather than returning a plan that quietly ignored them.
order
order_stops
Put a single run's stops in the best visiting order ("order my errands"). Provide `start` {lat, lon} and `stops` (1-100 entries of {location, label?, service_s?}); optionally an `end` destination or `round_trip`: true to return to the start. Costing "auto" = car, "truck" = lorry (pass `truck` as in `route` for a truck-legal order). Returns the stops in visit order with arrival offsets in seconds, plus total duration and distance.
palette
set_palette
Recolour one or more palette slots of a hosted style (e.g. {"water": "#0b2038", "roadMajor": "#8a6d3b"}) and publish the result as a new immutable style version. Slot names come from list_style_layers; colours are CSS (#rgb/#rrggbb/#rrggbbaa/rgb()/hsl()). Returns the new version and style URL.
place
list_place_categories
List the canonical place categories you can pass as `category` to `nearby_places` and `search_along_route` (and browse on with `geocode`). Each entry is a `category` token (the exact value to send, e.g. "fuel", "charging_station", "hgv_parking"), the `aliases` that colloquially name it ("petrol station", "EV charger", "lorry park"), and a one-line `description` of what it covers. Read this before guessing a category: a token that is not on this list matches nothing, and quietly returns an empty result rather than an error. Cuisines, brands and names are NOT categories — search those as free text. Sorted by category and identical on every call. Local lookup, no network, no quota.
reachable
reachable_area
Compute the area reachable from an origin within one or more travel-time budgets — walkability/cyclability rings. Costing "pedestrian" answers "how far can I walk in 15 minutes?", "bicycle" the cycling equivalent; "auto", "truck" and "motor_scooter" work too (e.g. delivery coverage). `contours_minutes` lists the ring boundaries in minutes (1-10 values, each up to 120); set `polygons` true for filled polygons ready to render as a map fill layer instead of contour lines. Returns a GeoJSON FeatureCollection, one feature per contour. Optional `exclude_polygons` for before/after scenarios ("close this bridge and recompute reachability"): an array of polygons, each an array of [lon, lat] pairs forming one ring — longitude FIRST — whose intersecting roads are excluded from the reachability search. Applies to the Valhalla engine; unsupported on the GraphHopper engine, where it is ignored.
replan
replan_routes
Re-plan a fleet part-way through its shift. Send the day back; nothing is stored. MapMap holds NO dispatch state — no plan, no vehicle position, no completion log — so a re-plan is not a delta against something we remember: you pass the ORIGINAL `optimise_routes` problem in full, plus `progress` (per vehicle: `completed_stop_ids` IN THE ORDER SERVED, an optional `current_position`, and `unavailable: true` for a breakdown or an end of hours) and/or `changes` (`cancel_job_ids`, `add_jobs`, `add_shipments`), and get a fresh plan for what is left. That costs bandwidth and buys the absence of a server-side plan that can go stale, leak, or fall out of step with the telematics platform that actually owns the truth — and it makes a re-plan reproducible: the same body always yields the same answer. Completed stops are LOCKED by construction: they are removed from the problem entirely and each vehicle starts from where it actually is, so the solver cannot move a stop that has already happened — a guarantee the solver cannot break, rather than a hint it is free to ignore. At least one progress entry or one change is required. Returns the same plan shape as `optimise_routes` for the REMAINING work, plus a `replan` block: the prefix locked per vehicle, where each re-plans from and how that was decided, stops released from a vehicle that can no longer serve them, tasks forced unassigned, and a note for every id that did not resolve. Read that block — a stop that vanished from the plan is named there rather than left for a dispatcher to notice at four in the afternoon; a cancelled id that matched nothing is reported there too rather than refused. Multi-trip vehicles cannot be re-planned: a completion does not say which trip it belongs to, so a problem whose vehicles declare `reloads` is refused with what to send instead. Billed on the remaining problem, not the original. Requires the MapMap gateway.
report
report_map_issue
Report that the live world disagrees with the map — a closed road, a wrong or missing restriction, a bad speed limit, a missing road, a wrong one-way, or changed access. Use it when you observe the mismatch mid-task. Provide `location` {lat, lon}, a `category` (road_closed, wrong_restriction, wrong_speed_limit, missing_road, wrong_oneway, access_changed, other) and optionally a `description`, the OSM `way_id` and an `evidence_url`. This is a first-party observation: it is QUEUED for human/agent review and NEVER changes routing immediately or edits any map. Returns the queued report_id.
reverse
reverse_geocode
Turn coordinates into the nearest places: addresses, POIs and localities with distance in metres. The inverse of geocode. Provide `lat` and `lon`; returns up to `limit` (default 5, max 10) results, nearest first, each with name, one-line label, lat/lon, type, address parts and distance_m, plus categories and a `details` object of display tags (opening_hours, website, phone, wikipedia, ...) on POI hits when the index carries them. Every result also carries `bearing_deg` and a spoken `direction`. Pass `heading_deg` (degrees clockwise from true north, 0 = north, 90 = east) and results are described from where the user stands — "ahead and slightly to your right, about 80 metres" — with a signed `relative_bearing_deg` (negative left, positive right); without a heading the phrasing falls back to cardinals ("north-east of you"), so this works with or without a compass. Add `fov_deg` to keep only what lies within that cone of the heading — it is the FULL width, so 90 keeps what lies within 45 degrees either side of dead ahead; anything dropped is counted in `out_of_view`. Prefer reading `direction` aloud over coordinates.
route
route
Compute a turn-by-turn route between origin and destination (optionally via waypoints). Costing "auto" = car, "truck" = lorry, "bicycle", "pedestrian" = walking, "motor_scooter" = moped. Pass `truck` {height_m, width_m, length_m, gross_weight_t, hazmat, tunnel_code} to apply dimensional limits and the ADR dangerous-goods tunnel matrix to the search; `pedestrian` {use_lit 0-1, type "wheelchair"|"blind", max_hiking_difficulty 1-6} for lit-street walking, accessibility and trail limits; `bicycle` {bicycle_type, use_roads 0-1, use_living_streets 0-1, avoid_bad_surfaces 0-1, use_hills 0-1} for quiet-ride and surface preferences. Returns distance (m), duration (s), maneuvers, polyline6 geometry and the ADR costing that was applied. Any of truck, auto, bicycle, pedestrian or motor_scooter routes may set `rationale: true` (opt-in, costs up to 1 + N extra routing calls) to learn which declared truck constraints or avoidance-side preferences (hills, surfaces, tolls, unlit streets, …) actually changed the route (`rationale.avoided[]`, basis route_divergence — it proves a field was binding, it does not identify the physical restriction or feature, and no live traffic or incident data is ever attributed). ADR honesty: `applied_adr.forbidden_tunnel_categories` describes the LOAD, not the returned route, and `applied_adr.tunnel_enforcement` states the boundary: roads are excluded only where the routing graph records an ADR tunnel category, so an unchanged route is not a clearance. Set `landmarks: true` for turn instructions anchored to recognisable places — each manoeuvre that passes one gains a `landmark_instruction` like "Turn right just after the Shell garage" beside the engine's own street-name `instruction`, which is never replaced. Prefer reading it aloud: it is how a passenger gives directions. Nothing is named unless it is recognisable from the road, within 40 m of the junction and not tagged as closed, so many routes return none and a `landmarks.annotated` of 0 with no `note` means this route genuinely passes nothing recognisable. Needs the MapMap gateway (GATEWAY_URL + GATEWAY_API_KEY), whose place index does the lookup. Optional `exclude_polygons` for what-if scenarios ("close this bridge and re-route"): an array of polygons, each an array of [lon, lat] pairs forming one ring — longitude FIRST — whose intersecting roads are excluded from the search. Applies to the Valhalla engine; unsupported on the GraphHopper engine, where it is ignored. Optional `avoid` and `exclude` name road features to keep off, and the difference between them is not cosmetic. `avoid` ("tolls", "highways", "ferries") is a PREFERENCE: it sets the costing's willingness to zero, and the engine's own reference says that is not guaranteed to avoid the feature — measured, avoiding tolls across the Dartford Crossing returns the same tolled route, because the untolled alternative is fifty kilometres further. `exclude` ("tolls", "highways", "ferries", "bridges", "tunnels") is a hard exclusion: it can answer NO ROUTE rather than a detour, and it depends on the routing engine's own hard-exclusion setting, which the engine does not report and this server cannot read — so it is requested, never promised. "tolls" and "highways" under `avoid` exist only on motorised costings; asking for one on a bicycle is refused rather than silently ignored. Whatever is applied comes back in `avoidance`, with the caveats — relay them, because "avoid tolls" read as a guarantee is the failure mode here. Each location also takes a kerbside approach: `preferred_side` "same" (alias "curb") stops on the door's side of the road, resolved against the locale's driving side — the left kerb in the UK, the right in Germany — with "opposite" and "either" (alias "unrestricted") for the rest. It is a snapping preference, not a manoeuvre guarantee, and it needs a coordinate genuinely offset from the road centreline; `street_side_tolerance_m` and `street_side_max_distance_m` bound the window in which it applies, and a pair leaving no window is refused rather than answered with the preference silently inert. Pass `emissions` {vehicle_category, fuel, euro_standard} for UK clean-air-zone assessment: the response's `zones` block then names every zone the route enters and what THIS vehicle pays there, with the publishing authority cited. Without it a zone can only be named, never priced. Needs the gateway, which holds the curated zone dataset; the whole dataset — every scheme, charge, boundary and provenance record — is readable at `GET /v1/zones` on the HTTP API when an agent needs to audit a figure or list zones without routing.
usage
get_usage
Check what your own API key has spent, so you can decide mid-task whether to keep going. Reading it is free: it costs no quota. Optional `from` and `to` (YYYY-MM-DD UTC, inclusive, at most 92 days apart) bound the report; omitted, it covers the current month to date. Returns `days` (per-day, per-endpoint), `totals` per endpoint over the range, `total_units`, plus `month_used_units` against `monthly_quota_units` and the prepaid `balance_millipence` (thousandths of a penny). Everything is counted in UNITS — weighted quota units, where a heavier endpoint costs more than one unit per request — so never report these figures as a number of calls. When `identity_pooled` is true the quota is shared with the other keys belonging to the same owner, so these figures are not yours alone. The key that authenticates the call is the key reported on: there is no way to read another caller's usage. Needs the MapMap gateway (GATEWAY_URL + GATEWAY_API_KEY).
validate
validate_geodata
Check whether a dataset's DECLARED coordinate reference system actually describes its own coordinates, before you draw it on a map. Catches the failures that are otherwise silent: swapped lat/lon axes, degrees labelled as metres, and Web Mercator or another projection mislabelled with a UTM or national-grid code. Pass the declared CRS (e.g. "EPSG:4326") and a sample of the raw coordinates as {x, y} in the dataset's OWN units — deliberately not named lon/lat, because whether they are degrees is the question. Returns a verdict (consistent / suspect / impossible), what is wrong in plain language, and where the numbers actually point when read another way. This is a sanity check, not a reprojection: it never transforms coordinates. Local computation: no network call, no quota.
verify
verify_places
Check whether places (and itineraries) an AI mentioned are real, findable and physically possible. Pass structured `claims` (reliable, and the only path that supports itinerary feasibility) or free `text` (best-effort quoted-phrase extraction). Each claim resolves to exactly one of three verdicts, never a boolean: "verified" (matched a real place, with its stable id and the source/date of the evidence), "contradicted" (a specific, dated, sourced fact rules it out — currently only an itinerary leg the routing engine proves cannot be driven in the stated time, with the computed travel time as evidence), or "unverified" (no evidence either way). This tool NEVER asserts that a named real business does not exist or has closed — that would be a defamation risk with no upside; a missing match is always "unverified". Claims sharing increasing `sequence` values and both carrying `claimed_time` (ISO 8601) form itinerary legs checked for feasibility via `matrix`, catching e.g. "breakfast in Bath, 10am meeting in Edinburgh". Max 20 claims per request. The response's `summary` field is a concise plain-text digest — also returned as this tool result's text content — so clients that drop structured/non-text content blocks still see the verdicts.

Endpoints

URLTransportStateLatencyChecked
https://mcp.mapmap.ai/mcp streamable-http answering 205 ms 14 min ago

Alternatives to MapMap

same job, measured the same way
C
Geo
by thinair

Geocoding, routing, isochrones, traffic, weather, and place search for AI agents. 19 MCP tools.

answering
Openrouteservice
by pipeworx-io

Routing, isochrones, matrix, snap, elevation, geocode via Openrouteservice.

43 tools answering
Mapsi MCP
by algolayertechnologies

Geocoding, routing, isochrone, H3, elevation and 13 more geospatial tools via OpenStreetMap.

21 installs/wk local only
Mapbox
by pipeworx-io

Mapbox geocode, directions, matrix, isochrones, map-matching, tilequery, static URL.

39 tools answering
Agentmap
by raymondchins

The TS/JS-accurate repo map for coding agents: ts-morph import/symbol/call graph + MCP query tools

194 installs/wk local only
Inavi MCP
by inavi-systems

MCP Server for iNavi Maps API - Geocoding, POI Search, Route Planning for Claude AI

71 installs/wk local only
Google Maps MCP Server
by cablate

18 Google Maps tools for AI agents — geocode, search, directions, weather, and more.

3 246 installs/wk local only
Agent Margin Router
by agentmarginrouter

38 x402 pay-per-call tools for AI agents (USDC/USDT on Base): web, crypto, on-chain, geo, packages

53 tools answering

MapMap — questions

Answers built from our own checks of this server.

What can MapMap do?
It exposes 43 tools, read directly from the server on our last check. Among them: cheapest_charging_along_route, cheapest_fuel_along_route, check_adr_tunnel, check_clearance_on_route, check_style_contrast, cluster and 37 more. The full list with descriptions is on this page — we take it from the server itself via tools/list, not from a README. How MCP servers expose tools in the first place →
What is MapMap mostly used for?
Its tools cluster around geo, style and submit. That is what this server is built to work with — the grouping comes from the actual tool names, not from a category we assigned.
Is MapMap working right now?
We send a real MCP handshake every 15 minutes. Over the last 24 hours 91 of 91 checks got a reply (100.0%), average response time 165 ms. The bar chart above shows every period we have measured.
How do I connect MapMap?
Copy the ready config from this page — we generate it for Claude Code, Claude Desktop, Codex, Cursor and VS Code, each with the file path that client actually reads. It is a remote server, so there is nothing to install — the client connects to the address.
Does MapMap need an API key?
No. MapMap completed a full MCP handshake with us as an anonymous client and listed its tools without asking for anything. All 43 of them are readable on this page. This is what we observed, not what the docs claim.
How fast is MapMap?
It answers our handshake in 165 ms on average, which is faster than 73% of all working MCP servers we measure. The comparison comes from our own checks across the whole registry, every 15 minutes.