Skip to main content
Glama

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
avoidNoRoad features to avoid as a PREFERENCE. Rendered as the costing's willingness factor set to zero — a thumb on the scale, not a ban. The engine's own reference is explicit that a value of zero is not guaranteed to avoid the feature entirely, and it does not: asking to avoid tolls across the Dartford Crossing returns the same tolled route, because the untolled alternative is fifty kilometres further. Use `exclude` when you mean a ban.
truckNoTruck profile (dimensions + ADR declaration). Requires costing "truck"; when present, ADR dangerous-goods costing options are merged into the request.
originYesRoute origin.
bicycleNoBicycle options (bicycle type, road/surface/hill preferences). Requires costing "bicycle".
costingNoCosting model: "auto" (default), "truck", "bicycle", "pedestrian" or "motor_scooter".auto
excludeNoRoad features to exclude outright, rendered as the costing's hard exclusion flag. Two things follow from that and both matter: a hard exclusion can return NO ROUTE rather than a detour (excluding tunnels on a Rotherhithe crossing has no answer), and the flags depend on the routing engine's `allow_hard_exclusions` setting, which the engine does not expose and nothing here can read — so this is never promised, only requested.
emissionsNoThe vehicle's emission declaration, for UK clean-air / low-emission zone assessment. Given, the response's `zones` block 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 MapMap gateway, which holds the curated zone dataset; the full dataset is readable at `GET /v1/zones`.
landmarksNoName landmarks in the turn instructions (default false): each manoeuvre that passes a recognisable place — a petrol station, a supermarket, a household-name chain — gains a `landmark_instruction` like "Turn right just after the Shell garage" beside the engine's own street-name instruction, which is never replaced. Nothing is named unless it is recognisable from the road, within 40 m of the junction and not tagged as closed, so many routes come back with none: a wrong landmark is worse than no landmark. Needs the MapMap gateway, whose place index does the lookup.
rationaleNoExplain the route (default false): re-routes with each declared truck constraint (truck costing) or avoidance-side routing preference (auto, bicycle, pedestrian, motor_scooter) relaxed and reports the ones that actually changed the route as `rationale.avoided[]`. Opt-in — it costs up to 1 + N extra routing calls, one per declared field plus one combined probe, and it is billed for the ones it actually makes: at most 8 in total, typically fewer, and 1 when there is nothing to probe. Against the hosted gateway each probe is its own metered route call, which is exactly what `POST /route` with `rationale: true` charges for its own fan-out, so the two surfaces price the same explanation the same way.
waypointsNoOptional intermediate stops, visited in order between origin and destination.
pedestrianNoPedestrian options (lit-street preference, wheelchair/blind type, hiking difficulty). Requires costing "pedestrian".
destinationYesRoute destination.
exclude_polygonsNoAreas to avoid — scenario analysis ("close this bridge and re-route"): an array of polygons, each an array of `[lon, lat]` pairs forming one exterior ring (GeoJSON-style, longitude FIRST). Roads intersecting any ring are excluded from the search. Applies to the Valhalla engine; unsupported on the GraphHopper engine, where it is ignored.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
zonesNoClean-air / low-emission zones this route enters and what the declared vehicle pays in each, with the publishing authority cited (only when `emissions` was declared). Passed through verbatim from the gateway's curated dataset — the same figures `GET /v1/zones` publishes, so a charge can always be audited back to its source.
summaryYesOne-line human-readable summary of the route.
avoidanceNoWhat `avoid`/`exclude` became, and the caveats that go with it (only when either list carried something).
landmarksNoThe landmark-annotation summary (only when `landmarks: true` was requested): how many manoeuvres gained a `landmark_instruction`, and a `note` when the per-route cap was hit or the deployment has no place index. A zero with no note means this route genuinely passes nothing recognisable.
maneuversYesOrdered turn-by-turn maneuvers across all legs.
rationaleNoWhy the route goes this way (only when `rationale: true` was requested; computed for truck, auto, bicycle, pedestrian and motor_scooter costings).
distance_mYesTotal route distance in metres.
duration_sYesTotal estimated travel time in seconds.
applied_adrNoThe ADR costing merged into the request, or null when no truck profile was given.
geometry_polyline6YesFull route geometry as a Google encoded polyline with six digits of decimal precision (polyline6).

Schema Changelog

Changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. Changed17 schema fields changed
    • addedInput schema / $defs / AvoidFeature
      Added value: +{
      +  "description": "A road feature to avoid as a PREFERENCE, not a ban. \"tolls\" and \"highways\" apply to motorised costings only (auto, truck, bus, motor_scooter, motorcycle); \"ferries\" applies to every costing. Asking for one on a costing whose engine table has no field for it is refused rather than silently ignored.",
      +  "enum": [
      +    "tolls",
      +    "highways",
      +    "ferries"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / $defs / EmissionsFuel
      Added value: +{
      +  "description": "What a vehicle burns, in clean-air-zone scheme terms.",
      +  "oneOf": [
      +    {
      +      "const": "petrol",
      +      "description": "Petrol, including petrol hybrids (schemes rate a hybrid by its\ncombustion engine's approval).",
      +      "type": "string"
      +    },
      +    {
      +      "const": "diesel",
      +      "description": "Diesel, including diesel hybrids.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "electric",
      +      "description": "Battery-electric.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "hydrogen",
      +      "description": "Hydrogen fuel cell.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "gas",
      +      "description": "LPG or CNG; rated as petrol by every scheme in the dataset.",
      +      "type": "string"
      +    }
      +  ]
      +}
    • addedInput schema / $defs / EmissionsSpec
      Added value: +{
      +  "description": "A vehicle's emission declaration, for clean-air / low-emission zone\nassessment.",
      +  "properties": {
      +    "euro_standard": {
      +      "description": "Its Euro emission standard, 1–6. Heavy-duty approvals are written\nin Roman numerals (Euro VI); declare Euro VI as `6`. Required for\nany combustion fuel — without it no zone can be resolved, and a\nhalf-declared vehicle is indistinguishable from an undeclared one.\nOptional only for `electric` or `hydrogen`.",
      +      "format": "uint8",
      +      "maximum": 255,
      +      "minimum": 0,
      +      "type": [
      +        "integer",
      +        "null"
      +      ]
      +    },
      +    "fuel": {
      +      "$ref": "#/$defs/EmissionsFuel",
      +      "description": "What it burns."
      +    },
      +    "vehicle_category": {
      +      "$ref": "#/$defs/EmissionsVehicleCategory",
      +      "description": "What kind of vehicle this is, in scheme terms."
      +    }
      +  },
      +  "required": [
      +    "vehicle_category",
      +    "fuel"
      +  ],
      +  "type": "object"
      +}
    • addedInput schema / $defs / EmissionsVehicleCategory
      Added value: +{
      +  "description": "What a vehicle is, in clean-air-zone scheme terms.\n\nDeclaring this turns \"charge depends on vehicle emissions\" into an\nanswer. Without it a zone can only be named, never priced.",
      +  "oneOf": [
      +    {
      +      "const": "car",
      +      "description": "A private car.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "van",
      +      "description": "A van or light goods vehicle up to 3.5 tonnes.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "minibus",
      +      "description": "A minibus (typically 8+ passenger seats, up to 5 tonnes).",
      +      "type": "string"
      +    },
      +    {
      +      "const": "hgv",
      +      "description": "A heavy goods vehicle over 3.5 tonnes.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "bus",
      +      "description": "A bus over 5 tonnes.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "coach",
      +      "description": "A coach over 5 tonnes.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "taxi",
      +      "description": "A licensed hackney carriage.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "phv",
      +      "description": "A private hire vehicle.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "motorcycle",
      +      "description": "A motorcycle, moped or tricycle.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "motorhome",
      +      "description": "A motor caravan or campervan.",
      +      "type": "string"
      +    }
      +  ]
      +}
    • addedInput schema / $defs / ExcludeFeature
      Added value: +{
      +  "description": "A road feature to exclude outright. A hard exclusion can leave a request with no path at all — that is the honest answer, not a failure — and it depends on the routing engine's own hard-exclusion setting, which nothing here can read.",
      +  "enum": [
      +    "tolls",
      +    "highways",
      +    "ferries",
      +    "bridges",
      +    "tunnels"
      +  ],
      +  "type": "string"
      +}
    • removedInput schema / $defs / LatLon
      Removed value: -{
      -  "description": "A WGS84 coordinate pair in decimal degrees.",
      -  "properties": {
      -    "lat": {
      -      "description": "Latitude in decimal degrees (−90 to 90).",
      -      "format": "double",
      -      "type": "number"
      -    },
      -    "lon": {
      -      "description": "Longitude in decimal degrees (−180 to 180).",
      -      "format": "double",
      -      "type": "number"
      -    }
      -  },
      -  "required": [
      -    "lat",
      -    "lon"
      -  ],
      -  "type": "object"
      -}
    • addedInput schema / $defs / PreferredSideKind
      Added value: +{
      +  "description": "Side-of-street preference for arriving at or departing from a location.\n\nCarries both vocabularies: MapMap's own `same`/`opposite`/`either` and\nthe OSRM/Mapbox `approaches` words `curb`/`unrestricted`, which are\naliases for `same` and `either`. Both spell the same request, on this\nsurface and on the HTTP API.",
      +  "oneOf": [
      +    {
      +      "const": "same",
      +      "description": "The side the location itself projects to — the kerb, resolved\nagainst the locale's driving side (the left kerb in the UK, the\nright in Germany).",
      +      "type": "string"
      +    },
      +    {
      +      "const": "opposite",
      +      "description": "The far side of the road from the location.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "either",
      +      "description": "No side preference.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "curb",
      +      "description": "Alias for `same`, from the OSRM/Mapbox `approaches` vocabulary.",
      +      "type": "string"
      +    },
      +    {
      +      "const": "unrestricted",
      +      "description": "Alias for `either`, from the OSRM/Mapbox `approaches` vocabulary.",
      +      "type": "string"
      +    }
      +  ]
      +}
    • addedInput schema / $defs / RouteLocation
      Added value: +{
      +  "description": "One location of a `route` request: a coordinate, plus the optional\nkerbside approach for arriving at it.\n\nA bare `{lat, lon}` is still a complete location — every kerbside field\nis optional and omitting all of them is exactly the request that was\nmade before they existed.",
      +  "properties": {
      +    "lat": {
      +      "description": "Latitude in decimal degrees (−90 to 90).",
      +      "format": "double",
      +      "type": "number"
      +    },
      +    "lon": {
      +      "description": "Longitude in decimal degrees (−180 to 180).",
      +      "format": "double",
      +      "type": "number"
      +    },
      +    "preferred_side": {
      +      "anyOf": [
      +        {
      +          "$ref": "#/$defs/PreferredSideKind"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Which side of the street to arrive on (or depart from). `same` (or\n`curb`) puts the vehicle on the door's side of the road, resolved\nagainst the locale's driving side. Two honest limits: this is a\n**snapping preference**, not a manoeuvre guarantee — the engine\nprefers an edge on that side, it does not promise the driver never\ncrosses — and it needs a coordinate genuinely offset from the road\ncentreline, because a point on the centreline has no side."
      +    },
      +    "street_side_max_distance_m": {
      +      "description": "Metres: further than this from the road centreline, the side of\nstreet is treated as `none` and `preferred_side` does nothing.\nEngine default 1000 m. Together with `street_side_tolerance_m` this\nis a WINDOW: a pair that leaves no window (tolerance at or above\nmax distance) is refused here rather than answered with a route on\nwhich the kerbside preference was silently inert.",
      +      "format": "uint32",
      +      "minimum": 0,
      +      "type": [
      +        "integer",
      +        "null"
      +      ]
      +    },
      +    "street_side_tolerance_m": {
      +      "description": "Metres: nearer than this to the road centreline, the side of street\nis treated as `none` and `preferred_side` does nothing. Engine\ndefault 5 m.",
      +      "format": "uint32",
      +      "minimum": 0,
      +      "type": [
      +        "integer",
      +        "null"
      +      ]
      +    }
      +  },
      +  "required": [
      +    "lat",
      +    "lon"
      +  ],
      +  "type": "object"
      +}
    • addedInput schema / properties / avoid
      Added value: +{
      +  "description": "Road features to avoid as a PREFERENCE. Rendered as the costing's\nwillingness factor set to zero — a thumb on the scale, not a ban.\nThe engine's own reference is explicit that a value of zero is not\nguaranteed to avoid the feature entirely, and it does not: asking\nto avoid tolls across the Dartford Crossing returns the same tolled\nroute, because the untolled alternative is fifty kilometres\nfurther. Use `exclude` when you mean a ban.",
      +  "items": {
      +    "$ref": "#/$defs/AvoidFeature"
      +  },
      +  "type": [
      +    "array",
      +    "null"
      +  ]
      +}
    • changedInput schema / properties / destination / $ref
      Previous value: -"#/$defs/LatLon"New value: +"#/$defs/RouteLocation"
    • addedInput schema / properties / emissions
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/EmissionsSpec"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The vehicle's emission declaration, for UK clean-air / low-emission\nzone assessment. Given, the response's `zones` block names every\nzone the route enters and what THIS vehicle pays there, with the\npublishing authority cited. Without it a zone can only be named,\nnever priced. Needs the MapMap gateway, which holds the curated\nzone dataset; the full dataset is readable at `GET /v1/zones`."
      +}
    • addedInput schema / properties / exclude
      Added value: +{
      +  "description": "Road features to exclude outright, rendered as the costing's hard\nexclusion flag. Two things follow from that and both matter: a hard\nexclusion can return NO ROUTE rather than a detour (excluding\ntunnels on a Rotherhithe crossing has no answer), and the flags\ndepend on the routing engine's `allow_hard_exclusions` setting,\nwhich the engine does not expose and nothing here can read — so\nthis is never promised, only requested.",
      +  "items": {
      +    "$ref": "#/$defs/ExcludeFeature"
      +  },
      +  "type": [
      +    "array",
      +    "null"
      +  ]
      +}
    • changedInput schema / properties / origin / $ref
      Previous value: -"#/$defs/LatLon"New value: +"#/$defs/RouteLocation"
    • changedInput schema / properties / waypoints / items / $ref
      Previous value: -"#/$defs/LatLon"New value: +"#/$defs/RouteLocation"
    • addedOutput schema / $defs / AppliedAvoidance
      Added value: +{
      +  "description": "What the avoidance lists actually did, reported back so a 200 is never\nreadable as a certificate.",
      +  "properties": {
      +    "avoid": {
      +      "description": "The `avoid` values that were applied, echoed back.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "caveats": {
      +      "description": "The caveats that apply to this request, in words fit to repeat to a\nuser. Always present when anything was applied.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "costing_option_fields": {
      +      "description": "The engine costing-option fields these words became, e.g.\n`{\"use_tolls\": 0.0, \"exclude_ferries\": true}`. This is the whole of\nwhat was sent — there is no hidden second mechanism."
      +    },
      +    "exclude": {
      +      "description": "The `exclude` values that were applied, echoed back.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "required": [
      +    "costing_option_fields",
      +    "caveats"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / properties / avoidance
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/AppliedAvoidance"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "What `avoid`/`exclude` became, and the caveats that go with it\n(only when either list carried something)."
      +}
    • addedOutput schema / properties / zones
      Added value: +{
      +  "description": "Clean-air / low-emission zones this route enters and what the\ndeclared vehicle pays in each, with the publishing authority cited\n(only when `emissions` was declared). Passed through verbatim from\nthe gateway's curated dataset — the same figures `GET /v1/zones`\npublishes, so a charge can always be audited back to its source."
      +}
  2. First observed

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and meets it extensively: it discloses that avoid is a preference not a guarantee (Dartford example), exclude is requested never promised, rationale costs up to 1+N calls, preferred_side is a snapping preference needing an offset coordinate, landmarks may return none, and ADR tunnel_applied describes the load not the route. It also explains engine/GraphHopper differences and gateway dependency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but the tool has 13 parameters and many caveats, and nearly every sentence earns its place by adding behavioral nuance or a failure mode. It is front-loaded with the core computation before the option deep-dives. It could be tightened in places, but the length is defensible for this complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a 13-parameter tool, a complete output schema, and no annotations, this description covers everything an agent needs to call the tool correctly: required locations, optional waypoints, engine support, external gateway requirements, cost implications, honest limitations, and feature-specific behavior. There are no obvious gaps that would cause incorrect invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Even though schema coverage is 100%, the description adds meaning beyond the schema: it maps costing names to everyday vehicles, spells out truck/pedestrian/bicycle tuning intent, distinguishes avoid vs exclude with consequences, explains 'longitude FIRST' for polygons, and clarifies that preferred_side aliases resolve against the locale's driving side. This is substantive semantic enrichment, not restatement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Compute a turn-by-turn route between origin and destination (optionally via waypoints).' It also lists concrete outputs (distance, duration, maneuvers, polyline6 geometry) and costing options, so an agent can clearly tell this is the point-to-point routing tool rather than matrix/geocoding or other siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is rich guidance for when to use individual features (truck ADR, pedestrian accessibility, rationale, landmarks, exclude_polygons, emissions) and clear warnings about engine support. However, the description never names sibling tools or states when to use route instead of matrix, plan_ev_route, match_trace, search_along_route, or reachable_area; usage vs alternatives is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

TDQS

A4/5.0
Disambiguation4/5

Most tools target a distinct action and resource pair, and descriptions are explicit about which tool fits which scenario. The closest overlaps—plan_ev_route vs cheapest_charging_along_route, and route vs plan_day vs order_stops vs optimise_routes—are mitigated by clear guidance, so an agent can usually pick correctly.

Naming Consistency4/5

Tool names overwhelmingly follow a verb_noun snake_case pattern (plan_ev_route, set_palette, list_style_layers) with a consistent geo_ prefix for geometry helpers. Minor deviations like elevation, route, and matrix are short and readable but break the strict verb_noun convention.

Tool Count2/5

At 39 tools, this surface is well past the 25+ threshold and feels heavy even for a broad mapping platform. The set spans routing, geocoding, places, styles, EV/fuel, telematics, usage, and feedback, which would be easier for an agent to navigate if split into smaller domain-focused servers.

Completeness4/5

For the stated breadth, coverage is strong: routing, multi-stop planning, VRP, EV/fuel detours, geocoding, places, geometry, style lifecycle, and telematics all have workable primary paths. Minor gaps like no style deletion, no route alternatives, and no batch geocoding are present but do not create dead ends for core workflows.

Resources