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. Through the gateway each manoeuvre also carries the road it starts on: country, subdivision, ref (route numbers only), road_class, speed_limit_unit (mph or km/h, from the road's country), speed_limit_sign (vienna or mutcd), speed_limit_posted (the limit as the sign shows it, in the road's unit) and driving_side; each is absent when the map does not know it, never guessed. 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. Set scenic: true (auto costing only) to ask whether there is a prettier way. It is a PEER OFFER, never a substitution: the route in geometry_polyline6 is byte-for-byte what the same request returns without the flag, and the prettier way, when there is one, arrives beside it in scenic, with its own geometry in scenic.alternative_geometry_polyline6 and what it costs in scenic.spoken ("About seven minutes longer than the quick way"). EVERY OFFER STATES ITS REASON OR THERE IS NO OFFER: a route that scores well but cannot support a plain sentence is dropped rather than dressed up, so a scenic.reason of null means nothing here measured above the floor. That is an ANSWER, not a failure, and it is the one to relay. A REJECTION IS ALSO AN ANSWER: every candidate that lost says why in scenic.rejections[] (same_route, no_scenic_gain, not_scenic_enough, over_time_budget with the minutes it would have cost, nothing_to_say), so "no prettier way" is always attributable and raising the budget is an informed choice. It is a re-ranking of routes the engine already proposed, not a scenic search: a beautiful road the engine never offered was never considered, and scenic.caveats says so. Like rationale, IT COSTS EXTRA METERED COMPUTATIONS, at most two, which is why it is opt-in and never on by default. available: false with a note means this deployment could not look, which is not the same claim as nothing being there. Needs the gateway, whose basemap archive the scoring is measured against.

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.
scenicNoAsk whether there is a prettier way (default false, `auto` costing only). The route in `geometry_polyline6` is NEVER swapped: what comes back is a peer offer beside it, in `scenic`, carrying the plain sentence that justifies it. No sentence, no offer: a route that scores well but cannot support one is dropped rather than dressed up, and `scenic.reason` is then null, which is an answer. Every candidate that lost says why in `scenic.rejections[]` (`same_route`, `no_scenic_gain`, `not_scenic_enough`, `over_time_budget`, `nothing_to_say`), so "no prettier way" is always attributable. Opt-in because it costs: at most two extra metered route computations, the same billing shape as `rationale`. Needs the MapMap gateway, whose basemap archive the scoring is measured against.
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.
scenicNoThe scenic peer offer (only when `scenic: true` was requested): whether there is a prettier way, the sentence that justifies it, what the detour costs, and why every candidate that lost lost. The route above is never swapped for it.
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.

  1. Changed8 schema fields changed
    • addedOutput schema / $defs / ManeuverOut / properties / country
      Added value: +{
      +  "description": "ISO 3166-1 alpha-2 country of the road the manoeuvre starts on, e.g.\n`\"GB\"`. This and the road fields below come from the MapMap gateway's\nroad walk: present when the route was computed through the gateway\n(GATEWAY_URL + GATEWAY_API_KEY) and the map knows the road, absent\notherwise. Never inferred.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / driving_side
      Added value: +{
      +  "description": "The side of the road traffic keeps to there, `\"left\"` or `\"right\"`;\npresent only when the road's country is known.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / ref
      Added value: +{
      +  "description": "Route numbers of that road (`\"; \"` separated, e.g. `\"A27\"` or\n`\"I 5; US 101\"`); absent when it carries none, never read from a\nstreet name.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / road_class
      Added value: +{
      +  "description": "Engine road class of that road (`motorway`, `trunk`, `primary`,\n`secondary`, `tertiary`, `unclassified`, `residential`,\n`service_other`).",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / speed_limit_posted
      Added value: +{
      +  "description": "The posted limit as the sign shows it, in the road's own unit:\n`{\"type\":\"known\",\"value\":30,\"unit\":\"mph\"}` (plus `\"national\":true`\nfor a national limit), `{\"type\":\"unlimited\"}` or `{\"type\":\"unknown\"}`.\nRead from the road the manoeuvre mostly travels along."
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / speed_limit_sign
      Added value: +{
      +  "description": "The speed-limit sign family there: `\"vienna\"` (roundel) or\n`\"mutcd\"` (US and Canadian rectangle).",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / speed_limit_unit
      Added value: +{
      +  "description": "The unit speed limits are posted in on that road, `\"mph\"` or\n`\"km/h\"`, from its country (never from the request's units).",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ManeuverOut / properties / subdivision
      Added value: +{
      +  "description": "ISO 3166-2 subdivision of that road where the map carries one, e.g.\n`\"GB-ENG\"` or `\"US-CA\"`.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  2. Changed9 schema fields changed
    • addedInput schema / $defs / RouteLocation / anyOf
      Added value: +[
      +  {
      +    "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"
      +  },
      +  {
      +    "description": "GeoJSON position [lon, lat]: longitude FIRST.",
      +    "items": {
      +      "format": "double",
      +      "type": "number"
      +    },
      +    "maxItems": 2,
      +    "minItems": 2,
      +    "type": "array"
      +  }
      +]
    • removedInput schema / $defs / RouteLocation / properties / lat
      Removed value: -{
      -  "description": "Latitude in decimal degrees (−90 to 90).",
      -  "format": "double",
      -  "type": "number"
      -}
    • removedInput schema / $defs / RouteLocation / properties / lon
      Removed value: -{
      -  "description": "Longitude in decimal degrees (−180 to 180).",
      -  "format": "double",
      -  "type": "number"
      -}
    • removedInput schema / $defs / RouteLocation / required
      Removed value: -[
      -  "lat",
      -  "lon"
      -]
    • addedOutput schema / $defs / LatLon / anyOf
      Added value: +[
      +  {
      +    "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"
      +  },
      +  {
      +    "description": "GeoJSON position [lon, lat]: longitude FIRST.",
      +    "items": {
      +      "format": "double",
      +      "type": "number"
      +    },
      +    "maxItems": 2,
      +    "minItems": 2,
      +    "type": "array"
      +  }
      +]
    • changedOutput schema / $defs / LatLon / description
      Previous value: -"A WGS84 coordinate pair in decimal degrees."New value: +"A WGS84 coordinate in decimal degrees: a {lat, lon} object (preferred), or a GeoJSON [lon, lat] array with LONGITUDE FIRST, the same order as every polygon field on this server."
    • removedOutput schema / $defs / LatLon / properties
      Removed value: -{
      -  "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"
      -  }
      -}
    • removedOutput schema / $defs / LatLon / required
      Removed value: -[
      -  "lat",
      -  "lon"
      -]
    • removedOutput schema / $defs / LatLon / type
      Removed value: -"object"
  3. Changed4 schema fields changed
    • addedInput schema / properties / scenic
      Added value: +{
      +  "description": "Ask whether there is a prettier way (default false, `auto`\ncosting only). The route in `geometry_polyline6` is NEVER swapped:\nwhat comes back is a peer offer beside it, in `scenic`, carrying\nthe plain sentence that justifies it. No sentence, no offer: a\nroute that scores well but cannot support one is dropped rather\nthan dressed up, and `scenic.reason` is then null, which is an\nanswer. Every candidate that lost says why in\n`scenic.rejections[]` (`same_route`, `no_scenic_gain`,\n`not_scenic_enough`, `over_time_budget`, `nothing_to_say`), so\n\"no prettier way\" is always attributable. Opt-in because it\ncosts: at most two extra metered route computations, the same\nbilling shape as `rationale`. Needs the MapMap gateway, whose\nbasemap archive the scoring is measured against.",
      +  "type": [
      +    "boolean",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / ScenicOffer
      Added value: +{
      +  "description": "The scenic assessment beside a route: a peer offer, never a\nsubstitution.\n\nThe fields here are the ones a client must actually read before\nsaying anything. `block` keeps the gateway's whole answer beside them\nso a figure can always be audited back to the source that produced\nit, rather than to this mapping.",
      +  "properties": {
      +    "alternative_geometry_polyline6": {
      +      "description": "The scenic route's own geometry, polyline6, when there is one to\noffer. `geometry_polyline6` on the route itself is byte-for-byte\nwhat the same request returns without the flag.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "available": {
      +      "description": "Whether scenery could be assessed at all on this deployment. When\nfalse, `note` says why and nothing below was measured: that is\n\"we could not look\", never \"there is nothing there\".",
      +      "type": "boolean"
      +    },
      +    "block": {
      +      "description": "The gateway's whole `scenic` block, verbatim: candidates, feature\nshares, tile coverage, metering and budget."
      +    },
      +    "caveats": {
      +      "description": "The standing limits of the method, for relaying to users.",
      +      "type": "string"
      +    },
      +    "chosen": {
      +      "description": "Which route the assessment would offer: `\"fastest\"` (the quick way\nis already the pretty one, or nothing beat it) or `\"scenic\"`.",
      +      "type": "string"
      +    },
      +    "extra_time_s": {
      +      "description": "How much longer the offered route takes than the fastest one,\nseconds, and the sentence for it. Absent when nothing was offered.",
      +      "format": "double",
      +      "type": [
      +        "number",
      +        "null"
      +      ]
      +    },
      +    "note": {
      +      "description": "Why no assessment ran (only when `available` is false).",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "reason": {
      +      "description": "The plain, speakable sentence behind the offer. **Null is an\nanswer**: nothing on this corridor measured above the floor, so\nno claim is made rather than one being invented.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "rejections": {
      +      "description": "Why each candidate that lost lost. Empty when none did. Relay it:\nit is what makes \"no prettier way\" an attributable answer instead\nof a shrug.",
      +      "items": {
      +        "$ref": "#/$defs/ScenicRejection"
      +      },
      +      "type": "array"
      +    },
      +    "spoken": {
      +      "description": "The trade phrased for speech, e.g. \"About seven minutes longer\nthan the quick way.\" Prefer reading this aloud over the seconds.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    }
      +  },
      +  "required": [
      +    "available",
      +    "chosen",
      +    "caveats",
      +    "block"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / ScenicRejection
      Added value: +{
      +  "description": "Why one candidate route was not offered.",
      +  "properties": {
      +    "code": {
      +      "description": "The rejection code: `same_route`, `no_scenic_gain`,\n`not_scenic_enough`, `over_time_budget` or `nothing_to_say`.",
      +      "type": "string"
      +    },
      +    "detail": {
      +      "description": "The code in words, including the minutes an `over_time_budget`\ncandidate would have cost, so raising the budget is an informed\nchoice rather than a guess.",
      +      "type": "string"
      +    },
      +    "source": {
      +      "description": "Where the candidate came from: `\"fastest\"`, `\"engine_alternate\"`\nor `\"no_motorway\"`.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "source",
      +    "code",
      +    "detail"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / properties / scenic
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/ScenicOffer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The scenic peer offer (only when `scenic: true` was requested):\nwhether there is a prettier way, the sentence that justifies it,\nwhat the detour costs, and why every candidate that lost lost.\nThe route above is never swapped for it."
      +}
  4. 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."
      +}
  5. First observed

TDQS

A4/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 burden and does so exhaustively: gateway/auth requirements (GATEWAY_URL + GATEWAY_API_KEY), which flags incur extra metered route calls and their caps, the honest limits of `avoid` (Dartford Crossing example) vs `exclude`, ADR tunnel-flag caveats, and the distinction between 'no prettier way' and 'could not look'. This is unusually complete behavioral disclosure.

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

Conciseness2/5

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

A single enormous prose block covering a dozen optional flags with no headers, bullets, or ordering by importance. Individual sentences are informative, but the structure forces the reader to parse a wall of text to find the core call, and much of the detail duplicates the schema.

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?

For a 14-parameter routing tool with a nested schema and an output schema, the description leaves nothing an agent would need uncovered: costing semantics, ADR honesty, zone pricing, scenic/rationale cost models, and engine-specific limitations (Valhalla vs GraphHopper). It is complete to the point of excess.

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

Parameters3/5

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

Schema description coverage is 100%, and the schema's own $defs already document every parameter (TruckSpec, BicycleSpec, PreferredSideKind, etc.) in depth. The description largely restates that material, adding little semantic detail the schema lacks, so the baseline of 3 applies.

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

Purpose4/5

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

Opens with a specific verb+resource+scope: 'Compute a turn-by-turn route between origin and destination (optionally via waypoints).' An agent immediately knows this is the routing primitive, distinct from siblings like optimise_routes or reachable_area. It never names an alternative sibling explicitly, so it stops short of a 5.

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

Usage Guidelines4/5

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

Rich conditional guidance: 'Use `exclude` when you mean a ban', 'scenic: true (auto costing only)', eligibility rules for avoid features, and repeated warnings about opt-in metered cost. However it gives no guidance on choosing this tool over routing-adjacent siblings (plan_ev_route, optimise_routes), so it lacks an explicit alternatives clause.

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.

Resources