Skip to main content
Glama
atlasfetch-dev

atlasfetch-mcp

Official

atlasfetch-mcp

No signup needed to try it: without a key it falls back to a shared public demo key, capped at 1,000 lookups a month across everyone using it. Get your own limits with a free sign-up at atlasfetch.xyz.

An MCP server for AtlasFetch — reverse geocoding to administrative boundaries and streets, and geofencing, as tools your AI assistant can call directly.

Ask "which municipality is -33.9249, 18.4241 in?" and get Cape Town, Western Cape, South Africa with ISO 3166 codes — from a real point-in-polygon lookup, not the model's memory.

country: United Kingdom (GB) · region: England (GB-ENG) · municipal: City of Westminster (GB-WSM)

What it does

Tool

What it does

lookup_location

A coordinate becomes the country, region and municipality containing it, with ISO 3166-1 / 3166-2 codes, plus matches from your own boundary sets. Optionally the nearest street (ask for the street layer), and the point as an H3 cell or Google Plus Code.

geocode_address

An address becomes a point, in South Africa today, with a precision that says whether the house, the street or only the suburb was found — and what to do about it.

get_route

A route between 2 and 25 coordinates by car, bike, foot or truck, with turn-by-turn steps — optionally around your own zones, or measuring how far it runs inside them.

list_boundary_sets

Lists your boundary sets, their counts, and which API keys may query them.

create_boundary_set

Creates an empty set for your own polygons.

add_boundary

Adds one GeoJSON polygon, with properties returned on every match.

update_boundary

Moves or reshapes one boundary, keeping its id, name and properties.

Good for: reverse geocoding to administrative areas, finding the street a point is on, geofencing against your own delivery zones or service areas, jurisdiction and region checks, tagging location data with region codes, geocoding delivery addresses in South Africa, and routing that keeps out of the areas you name.

Not for: geocoding outside South Africa (yet), searching for places by name, traffic-aware ETAs, distance matrices, isochrones, map tiles, or downloading boundary geometry.

Directions

get_route returns distance, duration and turn-by-turn steps between 2 and 25 coordinates, for car, bike, foot or truck.

car: 3.5 km · 5 min · 11 steps · a location moved 62 m to reach a road — free-flow, no traffic

Three things worth knowing before you wire it up:

  • A route costs 10 lookups, not one. Everything it returns is included in that, but it is ten times a lookup.

  • Durations are free-flow — the network at its own speed limits, with no live or historical traffic. They are not ETAs. For those, use a traffic-aware service.

  • The route line is off by default (routeGeometry: "none"). An encoded polyline is thousands of characters an assistant cannot read; ask for polyline6 or geojson only when something will draw it.

Your own zones, in a route. avoidSets names boundary sets to route around; exposureSets names sets to measure against, leaving the route alone and reporting how many metres of it ran inside each zone. A zone containing one of your locations cannot be avoided for that route — it is skipped, and the answer says so.

Plan limits apply: 2 locations on the free plans and up to 25 on Pro, truck is Pro only, and alternatives need exactly 2 locations. Over a cap the answer is a 403 and nothing is billed; a location with no road near it is a 422, and that one is billed, because the route was attempted.

Geocoding

geocode_address turns one address into a point — q on one line, or address already split into parts. South Africa today.

address · confidence 0.96 · 25 Adderley Street, Cape Town, ZA-WC — dispatch
street · confidence 0.47 · ambiguous · Long Street, Cape Town, ZA-WC — ask the customer before dispatching

Read the precision before you use the point. address is the house; interpolated is estimated between the numbers either side; intersection is a street corner ("Cnr Main Rd and Belmont Rd"); site is a named place such as a shopping centre, office park or hospital (pass the shop or unit number on to the driver); street is the right street but not the house; locality is only the suburb. The result line applies the API's own advice for deliveries: dispatch only on a house-, corner- or site-level answer that is not ambiguous, ask the recipient for a pin on a street-level one, and ask the customer when it is ambiguous, listing the other candidates by the suburb each lies in (five Long Streets in Cape Town: City Centre, Observatory, Maitland…). In South Africa most answers are street-level, because OpenStreetMap has house numbers on about 13% of street names.

One address per call, at 1 lookup each, found or not. That is deliberate: a delivery list goes through one row at a time, where you can see what it spends. Programs with thousands of rows should call POST /geocode/batch on the API directly.

Streets

Add the street layer and the answer includes the nearest street, and the house number where one is mapped. It is worldwide, and opt-in — not part of the default country, region, municipal.

You always get three answers — for 5 m, 20 m and unlimited — because "the nearest street" depends on how far you are willing to look:

"street": [
  { "radiusMeters": 5,    "streetNumber": null, "streetName": "Adderley Street", "postcode": null,   "distanceMeters": 2.7 },
  { "radiusMeters": 20,   "streetNumber": "25", "streetName": "Adderley Street", "postcode": "8001", "distanceMeters": 9.6 },
  { "radiusMeters": null, "streetNumber": "25", "streetName": "Adderley Street", "postcode": "8001", "distanceMeters": 9.6 }
]

Two nulls that mean different things: "street": null means there is no street data for that country, while an entry whose fields are null means nothing was within that radius. streetNumber and postcode are strings (44A, 12-14, 0181); house numbers are rare outside well-mapped areas. Set streetGeometry and each answer also carries the street's line, trimmed to about 250 m — leave it off unless something will draw it.

Related MCP server: Location MCP Server

Install

Claude Code

claude mcp add atlasfetch -- npx -y atlasfetch-mcp

Claude Desktop, Cursor, VS Code and other clients

Add to your MCP config (claude_desktop_config.json, .cursor/mcp.json, or your client's equivalent):

{
  "mcpServers": {
    "atlasfetch": {
      "command": "npx",
      "args": ["-y", "atlasfetch-mcp"],
      "env": {
        "ATLASFETCH_API_KEY": "your-key-here"
      }
    }
  }
}

Requires Node 20 or newer.

Getting a key

The demo key is shared by every anonymous user on the internet — one pooled quota and rate limit. Fine for a first look, wrong for anything real.

Create your own at atlasfetch.xyz/dashboard and set ATLASFETCH_API_KEY. The free Personal plan needs no card. You can copy a key from the dashboard at any time — so restrict it to your own domains there. Keys made before 12 October 2026 were revoked; if yours stops working, copy a live one.

Variable

Default

Purpose

ATLASFETCH_API_KEY

(demo key)

Your API key.

ATLASFETCH_API_URL

https://api.atlasfetch.xyz

Override only for local or staging APIs.

Two things that surprise people

municipal is the finest unit available, not a consistent kind of thing. Los Angeles returns a city; rural Kansas returns a county; some places return a district. Where a country's fine tier is patchy, a coarser complete tier answers instead, because a county beats null for most uses. Don't assume the value names a city.

A new boundary set cannot be made queryable from here. Sets are created switched off and granted to no key, and granting is addressed by API key id — which only a signed-in browser session can list. So this server can create a set and fill it, but the last step happens in the dashboard. create_boundary_set says so in its response rather than leaving you with a set that silently matches nothing.

Billing

A lookup or a geocoded address is one lookup; a route is 10 on the standard plans. A lookup is one, however many layers, sets or grid codes it touches. Metering happens before matching, so a lookup that matches nothing still counts. Requests rejected as invalid or rate-limited are not billed.

Conventions

A few rules hold across all seven tools, so an input means the same thing wherever it appears.

  • set names one set, sets names several. add_boundary and update_boundary act on one set (set). lookup_location and geocode_address match against any number of sets (sets).

  • get_route flattens two API objects. The API takes avoid: { sets: [...] } and exposure: { sets: [...] }. The tool takes avoidSets and exposureSets, because a one-key wrapper object is a step a model gets wrong.

  • …Geometry is always a line you ask to get back, and always off by default. routeGeometry on get_route, and streetGeometry on lookup_location and geocode_address. A plain geometry input is a polygon you send, on add_boundary and update_boundary. (get_route still accepts geometry as an old name for routeGeometry; it goes in 1.0.)

  • Where a default differs from the API's, it is on purpose:

    Tool

    Input

    This server

    API

    Why

    get_route

    steps

    true

    false

    Turn-by-turn is what someone asks an assistant for

    lookup_location

    streetGeometry

    adds street to base

    400 without street

    Asking for a street's line means asking for the street

  • The first line of every answer is for a person, and is not a contract. It summarises the answer and says what to do with it (— dispatch, — ask the customer), and its wording will keep improving. The JSON below it is the API's answer, unchanged, and that is what to parse.

Stability

Before 1.0, minor versions (0.3 → 0.4) may rename or remove an input, as 0.4.0 did, though always with a deprecated alias first.

From 1.0:

  • Frozen: tool names, input names, input types and defaults.

  • May change in a minor release: new tools, new optional inputs, new values an output can take (as intersection and site were added to precision), and the wording of descriptions and of the first line of each answer.

  • Breaking changes go through deprecation. The old form keeps working for at least one minor release, its description says what replaces it, and it is removed only in the next major version.

  • The JSON in each answer follows the AtlasFetch API, which versions its own contract in its OpenAPI spec.

Every release is described in CHANGELOG.md.

Development

npm install
npm run build
node scripts/smoke.mjs   # drives the built server with a real MCP client, against the live API

Licence

MIT for this server. Reference boundaries derive from OpenStreetMap under the ODbL; attribution and share-alike obligations pass through to you. Coordinates you look up are not stored.

Available Tools

7 tools
add_boundaryAdd a polygon to a boundary setA
Destructive

Adds ONE polygon to an existing boundary set on this account, so that lookup_location reports when a point falls inside it — delivery zones, service areas, sales territories. Each call creates a new boundary: calling twice with the same name stores two.

Before calling: the set must already exist (create_boundary_set). A set matches nothing in lookups until it is switched on and granted to an API key in the dashboard at https://atlasfetch.xyz/dashboard, which this server cannot do.

geometry must be a single GeoJSON Polygon — not a MultiPolygon, Feature or FeatureCollection. Positions are [longitude, latitude]. Each ring needs at least 4 positions and must be closed (last position equals the first); extra rings are holes. Self-intersecting shapes are rejected. To store a MultiPolygon, add each part separately.

A polygon may have at most 50 points on EVERY plan, the closing one included; more is refused, unbilled, with advice to simplify the shape or split it into several boundaries. Plan limits cap properties per boundary and boundaries per set. WITHOUT YOUR OWN KEY, the shared demo key is on the Public plan: properties only category (zone, area, route, place, other), color (green, blue, red, yellow, purple, orange) and priority (low, medium, high), and one set shared with every demo user, where adding to a full set silently deletes its oldest boundary. Never upload anything private with the demo key.

Returns the stored boundary: id, set, name, properties, pointCount and createdAt.

NameRequiredDescriptionDefault
setYesName of an existing set on this account (see list_boundary_sets).
nameYesLabel returned when a looked-up point falls inside this polygon, e.g. "Zone A".
geometryYesA GeoJSON Polygon, e.g. {"type":"Polygon","coordinates":[[[18.40,-33.93],[18.40,-33.90],[18.44,-33.90],[18.44,-33.93],[18.40,-33.93]]]}.
propertiesNoOptional flat key/value pairs returned with every match: keys up to 32 characters, values a string (up to 256 characters), number or boolean. The demo key accepts only category, color and priority, from fixed lists.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and idempotentHint=false, and the description explains exactly why: repeated calls with the same name store duplicates, and adding to a full demo set silently deletes the oldest boundary. It also discloses rejection behavior (self-intersecting shapes, >50 points refused unbilled) that the annotations cannot express.

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?

Front-loaded with the core action and the lookup consequence, then proceeds through prerequisites, geometry rules, plan limits and return shape in a logical order. It is long, but nearly every sentence carries a constraint the agent needs; the demo-key paragraph is the only slightly bloated section.

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?

There is no output schema, yet the description names the returned fields (id, set, name, properties, pointCount, createdAt). Combined with prerequisites, geometry limits and failure modes, an agent has everything needed to call it correctly and interpret the result.

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

Parameters4/5

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

Schema coverage is 100%, so the schema already documents ring closure and [longitude, latitude] ordering. The description nonetheless adds constraints not present in the schema: geometry must be a single Polygon (not MultiPolygon/Feature/FeatureCollection), a hard 50-point cap on every plan including the closing point, and demo-key restrictions on properties values.

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?

States a specific verb and resource ('Adds ONE polygon to an existing boundary set') and ties it to the downstream effect on lookup_location. It is clearly distinguished from create_boundary_set and update_boundary by the 'each call creates a new boundary' wording.

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

Usage Guidelines5/5

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

Explicitly names the prerequisite tool (create_boundary_set), the dashboard step required before lookups match, and the alternative action for MultiPolygons (add each part separately) or oversized shapes (simplify or split). It also scopes demo-key usage with a 'never upload anything private' warning.

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

create_boundary_setCreate a boundary setA

Creates an empty named set to hold your own polygons: delivery zones, service areas, sales territories.

IMPORTANT, and not a bug: a new set is created unavailable and granted to no key, so it matches nothing until both are changed. Granting is addressed by API key id, and nothing this server can call tells it its own key's id. So this tool can create the set and add boundaries to it, but the FIRST grant has to happen in the dashboard: tell the user to switch the set on and grant it to the key this server uses, at https://atlasfetch.xyz/dashboard.

NameRequiredDescriptionDefault
nameYesA short name, used to reference the set in lookups: 1-32 letters, digits, hyphens or underscores, starting with a letter or digit.

TDQS

A4.3/5.0
Behavior5/5

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

Discloses a non-obvious behavioral trait that no annotation conveys: a new set is created unavailable and ungranted, so it matches nothing until both are changed. It explains the root cause (grants are addressed by API key id, which the server cannot discover about itself) and prescribes the exact remediation and dashboard URL. This is consistent with readOnlyHint=false and openWorldHint=true.

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 purpose is front-loaded in the first clause and the critical caveat follows, with the actionable instruction to relay to the user placed last. It is a bit long, but nearly every sentence carries decision-relevant information; the 'not a bug' framing is slightly defensive but justified given the surprising behavior.

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

Completeness4/5

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

Covers creation semantics, the grant gap, and the required user follow-up thoroughly for a mutating one-parameter tool. With no output schema, it does not say whether the new set's identifier is returned or how an agent should reference the set in a subsequent add_boundary call, which is the remaining gap.

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 coverage is 100% and the single 'name' parameter is fully documented in the schema, including the pattern constraint, so the description adds no syntax or format detail beyond it. The phrase 'named set' only echoes what the schema already says, making baseline 3 correct.

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?

States a specific verb and resource ('Creates an empty named set to hold your own polygons') and illustrates the kind of content it holds (delivery zones, service areas, sales territories). This clearly separates it from add_boundary, which populates an existing set.

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?

Implies the create-then-add workflow ('this tool can create the set and add boundaries to it') and gives concrete use cases, so an agent knows when to reach for it. It stops short of explicitly naming an alternative or stating a when-not condition relative to list_boundary_sets or add_boundary.

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

geocode_addressGeocode an address to a coordinate (South Africa)A
Read-only

Turn ONE address into a point, with an honest account of how much of it was found. Built for deliveries. South Africa today; more countries as their data loads. 1 lookup per address, found or not.

Send either q (one line) or address (housenumber, street, suburb, city, postcode, unit) — never both. If the address is already split, send it split: it matches better.

READ precision BEFORE using the point. "address" means the house was found; "interpolated" means it was estimated between the numbers either side; "intersection" means a street corner ("Cnr Main Rd and Belmont Rd", "Bree & Strand Streets"), at the point where the two meet, with the second street in matched.crossStreet; "site" means a named site — a shopping centre, office park, complex or estate, hospital, school, airport or hotel — at a point inside it, named in matched.site; "street" means the right street but NOT the house; "locality" means only the suburb or town. NEVER present a street- or locality-level point as a door: a driver sent there will be at the wrong house. In South Africa most answers are street-level, because OpenStreetMap has house numbers on about 13% of street names — that is the data being honest, not a failure.

What to do with an answer — the result line says which applies:

  • address, interpolated, intersection or site, not ambiguous, confidence 0.5 or more: dispatch. For a site, pass on the unit or shop number from the original address ("Shop 12") — the answer does not include it, and the driver needs it.

  • street: ask the recipient for a pin or a landmark.

  • ambiguous, or locality: ask the customer before dispatching. When the advice is to ask the customer, ASK — do not pick one. result.alternatives holds up to 4 other candidates the answer was chosen from, and the result line lists them with the suburb each lies in and its distance and direction from the first, because they often share a street name AND a town (five "Long Street, Cape Town"s: City Centre, Observatory, Maitland…). Put the first answer and those to the user, by suburb. Free: still one lookup. matched.locality is the place the caller wrote; matched.suburb is the suburb the point actually lies in, absent at locality precision and where no suburb is near. confidence ranks answers; it is NOT a probability. ambiguous means a same-named street elsewhere scored about as well, or the street was a near-miss spelling with no house number to confirm it. result null means nothing trustworthy was found, and that still costs the lookup.

Some addresses name something that cannot be placed: an erf, stand, plot or farm portion (land parcels — there is no cadastre), a township stand, a PO box or private bag. Those parts come back in unmatched and the answer is the place named, at locality: ask the customer for a street address.

country is a hard filter; region (ZA-WC, or "Western Cape") only ranks streets inside it first, so a wrong province does not lose the address. An uncovered country is refused, unbilled, with the list of covered countries in the message — that message is the live coverage list; there is no other. sets and base answer "which delivery zone is it in" in the same call, at no extra cost.

One address per call, deliberately. For a delivery list, call this once per row so the user sees what is being spent; programs with thousands of rows should use the API’s POST /geocode/batch directly. Not for: searching for a business or landmark by name, or a coordinate to an address (that is lookup_location with the street layer).

NameRequiredDescriptionDefault
qNoThe whole address on one line, e.g. "25 Adderley Street, Cape Town".
baseNoAdministrative layers to resolve at the found point.
setsNoBoundary sets on this account to match at the found point, e.g. delivery zones.
regionNoISO 3166-2 ("ZA-WC") or a name ("Western Cape"). A preference, not a filter.
addressNoThe address already split into parts. Needs at least a street. Use instead of q.
countryNoISO 3166-1 alpha-2, e.g. "ZA". A hard filter: only that country is searched.
streetGeometryNoThe matched street’s line, trimmed to about 250 m. Only if something will draw it.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only declare readOnlyHint and openWorldHint; the description carries the real behavioral load: billing (one lookup per address, found or not, null still billed), unbilled refusal for uncovered countries, country as a hard filter vs region as a preference, what confidence and ambiguous actually mean, and that sets/base are free in the same call. Far beyond what the annotations supply.

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?

Front-loaded correctly with the one-address-per-call constraint and the precision warning, and nearly every paragraph carries operational information. It is nevertheless very long and repeats itself slightly ("1 lookup per address" vs "One address per call, deliberately", plus the multi-paragraph "Long Street, Cape Town" illustration), which costs it the top mark.

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?

No output schema exists, so the description must describe returns — and it does: precision values, matched.crossStreet, matched.site, matched.locality vs matched.suburb, result.alternatives with suburb/distance/direction, and result null. Combined with the precision-to-action mapping, an agent has everything needed to call this and interpret the response.

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?

Schema coverage is already 100%, yet the description still adds non-obvious semantics: q vs address are mutually exclusive and pre-split input "matches better", country is a hard filter, region "only ranks streets inside it", and sets/base answer zone questions at no extra cost. It also explains that streetGeometry is trimmed and only worth requesting if something will draw it.

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?

Opens with a specific verb+resource+scope: "Turn ONE address into a point, with an honest account of how much of it was found. Built for deliveries. South Africa today." It also distinguishes itself from the reverse-geocoding sibling at the end ("a coordinate to an address — that is lookup_location"). An agent can tell what this does and what it does not without opening the schema.

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

Usage Guidelines5/5

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

Explicit when-to-use routing per precision level (dispatch / ask for a pin / ask the customer), explicit when-not (business or landmark name search, reverse geocoding, bulk rows which should use the API's batch endpoint), and explicit exclusivity between q and address. This is about as complete a routing guide as a tool description gets.

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

get_routeRoute between coordinates, optionally avoiding your own zonesA
Read-only

A driving, cycling, walking or truck route from the first location to the last, via any in between, with distance, duration and turn-by-turn steps.

COST: a route is 10 LOOKUPS on the standard plans, not one (a custom plan may charge less; X-Lookups-Remaining is always the truth). Everything it returns is included in that, so do not call it in a loop to compare options without saying so.

Durations are FREE-FLOW: the road network at its own speed limits, with NO live or historical traffic. Never present one as an arrival time in traffic — for that, say a traffic-aware service (Google, Mapbox) is needed.

The route line is NOT returned unless routeGeometry is set, because an encoded polyline is thousands of characters that no one reads. Ask for it only when something will draw it.

Locations are SNAPPED to the nearest routable road, and locations[] in the response says how far each one moved. A large snapDistanceMeters means the answer starts somewhere else than asked — worth repeating to the user.

avoidSets and exposureSets name BOUNDARY SETS on the account (list_boundary_sets), not ad-hoc shapes. avoid routes around every zone in those sets; exposure leaves the route alone and reports how far it ran inside each zone. A zone containing one of the locations cannot be avoided for that route: it is skipped and reported in avoid.skipped. Along very many exposure zones (over 1,000, or over 5 s to measure) exposure is skipped: it comes back empty with a note in errors, and the route is still answered and billed.

TRUCKS (Pro only): a truck over 9 t, or with no weightTonnes, is capped at 80 km/h, as South African law caps goods vehicles; a lighter truck keeps car speeds. The vehicle’s height, width, length, weight, axle load and hazmat are checked against road limits where OpenStreetMap records them, which is not everywhere — a good start, not a legal clearance.

Limits by plan: 2 locations and 1 alternative on the free plans, up to 25 and 2 on Pro; alternatives need exactly 2 locations. Over a cap the answer is 403, and nothing is billed. A location with no road near it, or two places with no road between them, is 422 — and that one IS billed, because the route was attempted.

Not for: distance matrices, isochrones or live traffic (none exist yet), and not for which-area-is-this questions — that is lookup_location, at one lookup.

NameRequiredDescriptionDefault
modeNoTravel mode. Defaults to car. "truck" is Pro only; see TRUCKS above.
stepsNoTurn-by-turn steps per leg. Defaults to true here; set false for distance and time only.
vehicleNoTruck dimensions and load. Only with mode "truck"; rejected otherwise. Give weightTonnes: without it the truck is treated as heavy (over 9 t) and capped at 80 km/h.
geometryNoDEPRECATED: use routeGeometry, which takes the same values. Removed in 1.0.
languageNoLanguage for the step instructions, e.g. "en-GB", "de-DE", "fr-FR". Defaults to en-US.
avoidSetsNoNames of boundary sets on this account to route AROUND. Every zone in them is excluded.
locationsYesOrigin, any stops in order, then destination. 2 on the free plans, up to 25 on Pro.
alternativesNoAlso return up to this many other routes. Exactly 2 locations only. Plan-capped.
exposureSetsNoNames of boundary sets to MEASURE against: the route is unchanged, and the answer says how many metres of it ran inside each zone.
routeGeometryNoThe route line. Defaults to none. Ask for polyline6 or geojson only when something will draw it: it is long, and an assistant cannot read it usefully.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only declare readOnlyHint/openWorldHint, so the description carries real behavioral weight and does: cost of 10 LOOKUPS with X-Lookups-Remaining as truth, free-flow (no traffic) durations, 403 caps unbilled vs 422 failures billed, snapping with snapDistanceMeters, exposure skipping above 1,000 zones, and truck speed capping. This is exactly the non-obvious context an agent needs.

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?

Well front-loaded with purpose then COST, and organized into scannable labeled paragraphs. It is long, and a few points (geometry rationale, free-flow warning) are restated across description and schema descriptions, costing some density.

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 10-parameter, nested-object, no-output-schema routing tool, the description covers billing, failure modes, plan caps, snapping, boundary-set semantics, and result contents (distance, duration, steps). An agent could invoke it correctly without reading anything else.

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?

Schema coverage is 100%, so baseline is 3, but the description adds meaning beyond the schema: avoidSets/exposureSets are confirmed to be account boundary sets resolved via list_boundary_sets, zones containing a location are skipped and reported in avoid.skipped, and missing weightTonnes triggers an 80 km/h cap. That is substantive semantics the schema alone does not convey.

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?

Opens with a specific verb+resource+scope: 'A driving, cycling, walking or truck route from the first location to the last, via any in between, with distance, duration and turn-by-turn steps.' It also draws hard boundaries against siblings ('Not for: distance matrices, isochrones or live traffic ... that is lookup_location').

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

Usage Guidelines5/5

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

Explicit when-to-use and when-not-to-use: names lookup_location for area questions, warns against looping to compare options, tells the agent to request routeGeometry only when something will draw it, and explains avoidSets vs exposureSets selection criteria. Nothing is left to inference.

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

list_boundary_setsList the boundary sets on this accountA
Read-only

Lists the boundary sets on this account with their boundary counts and the API keys (id and label) each is granted to. A set only matches during a lookup when it is available AND granted to the key making the call. On the shared demo key, grantedKeys is always empty.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds genuine value beyond that: it discloses the response contents (counts and granted keys per set) and a non-obvious caveat that grantedKeys is always empty on the shared demo key. It stops short of describing pagination, ordering, or error behavior.

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

Conciseness5/5

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

Three sentences, front-loaded with the core purpose, followed by only the semantics needed to interpret the output. Every sentence earns its place and there is no repetition of the schema or annotations.

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

Completeness4/5

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

For a zero-parameter read tool with annotations and no output schema, the description is nearly complete: it covers what is returned and a key edge case. Minor gaps remain around response ordering, empty-state behavior, and pagination, but an agent has enough to call and interpret it correctly.

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

Parameters4/5

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

There are zero parameters, so the baseline is 4 and there is nothing for the description to disambiguate at the input level. The description does explain the meaning of return fields, which is useful because no output schema exists, but parameter semantics themselves are not applicable.

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?

The description states a specific verb and resource ('Lists the boundary sets on this account') and even enumerates the returned fields (boundary counts, granted API keys). It is clearly distinct from the mutating siblings like create_boundary_set and add_boundary, though it does not explicitly name an alternative tool.

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?

The description supplies interpretive context ('a set only matches during a lookup when it is available AND granted to the key making the call'), which implies why an agent would call it. However, it never explicitly says when to use this tool versus lookup_location or how to act on the results, leaving routing to inference.

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

lookup_locationLook up the boundaries containing a coordinateA
Read-only

Reverse geocode a coordinate to the administrative areas that contain it — country, region (state or province) and municipality — as names plus ISO 3166 codes, optionally the nearest street, and matches from boundary sets the account has uploaded: all in one call.

Use for: which country, region or municipality a point is in; which street a point is on (add "street" to base); geofence checks against your own polygons; tagging data with region codes. For the other direction, an address to a coordinate, use geocode_address. For a route, a travel distance or a travel time, use get_route — 10 lookups on the standard plans to this one’s 1. Do NOT use for place search by name or for fetching boundary geometry.

Caveats worth repeating to the user: municipal is the finest unit AVAILABLE, not a consistent kind of thing — Los Angeles returns a city, rural Kansas returns a county, so do not assume it names a city. A layer that matched nothing comes back null. The errors array is always present: a boundary set that is unavailable or not granted to this key is skipped and reported there, while the call itself still succeeds.

STREETS (opt-in via base, worldwide). base.street is ALWAYS three answers, for 5 m, 20 m and unlimited in that order, each with radiusMeters, streetNumber, streetName, postcode and distanceMeters. A numbered address within the radius wins over a nearer street (for the unlimited entry the address must still be within 20 m); otherwise the nearest named street; unnamed roads never answer. Two different nulls: base.street === null means there is no street data for the point’s country, while an entry whose fields are null means nothing was within that radius — never report them the same way. streetNumber and postcode are STRINGS (44A, 12-14, 0181), house numbers are rare outside well-mapped areas. Street geometry comes back only when streetGeometry is set — leave it off unless something will draw the line.

One call is one billed lookup however many layers, sets or grid codes it touches, and a call that matches nothing still bills.

NameRequiredDescriptionDefault
latYesLatitude, -90 to 90.
lngYesLongitude, -180 to 180.
baseNoWhich reference layers to resolve. Defaults to country, region and municipal. "street" is opt-in: add it explicitly to get the nearest street.
setsNoNames of the boundary sets on this account to match against. Defaults to none.
h3resNoH3 resolution 0-15. 9 is about 400 m across.
encodeNoAlso return the point as an H3 cell index and/or Google Plus Code. Output only — neither can be used AS a location, because both name areas rather than points.
pluslenNoPlus Code length. 9 is not a valid length. 10 is about 14 m.
streetGeometryNoGive each street answer the line of the street it names, trimmed to about 250 m either side of the point. Implies the street layer. Off by default, and leave it off unless something will draw it: a line is hundreds of characters nobody reads.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only declare readOnlyHint and openWorldHint, so the description carries the behavioral burden and does so richly: it discloses billing semantics (one call = one billed lookup regardless of layers, and a no-match call still bills), that the errors array is always present and reports skipped/ungranted boundary sets while the call still succeeds, and the distinction between the two kinds of null for streets. This is far beyond what the annotations supply.

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 definition is front-loaded (purpose, then Use for / alternatives, then caveats, then a dedicated STREETS section) and nearly every sentence carries information that changes agent behavior. It is dense and long, and lines like 'caveats worth repeating to the user' are slightly meta/marketing-toned, but no section is genuinely filler.

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?

With eight parameters, no output schema, and only two required params, the description compensates thoroughly: it explains the shape of street answers, the always-present errors array, the meaning of each null variant, unit and code formats, and the billing model. An agent has everything needed to call it correctly and interpret the response without a schema.

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?

Although schema coverage is 100%, the description adds meaning the schema does not: base.street always returns exactly three answers (5 m, 20 m, unlimited) with named fields, streetNumber and postcode are STRINGS with examples, streetGeometry is trimmed to ~250 m and implies the street layer, and 'add street to base' is the opt-in mechanic. Null-vs-null-field semantics for street results are spelled out, which the schema cannot express.

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 first sentence states a specific verb (reverse geocode) plus the exact resource (administrative areas containing the coordinate) and enumerates the outputs: country, region, municipality with ISO 3166 codes, optional street, plus uploaded boundary-set matches. It also explicitly names the sibling it is not (geocode_address for the opposite direction, get_route for routes), so an agent can disambiguate without opening any schema.

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

Usage Guidelines5/5

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

A dedicated 'Use for:' block lists the concrete tasks (country/region/municipality lookup, street identification, geofence checks against own polygons, tagging with region codes) and names the alternatives with their conditions. It adds explicit exclusions: 'Do NOT use for place search by name or for fetching boundary geometry,' and cites a cost differential versus get_route. Nothing is left to inference.

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

update_boundaryMove or reshape a boundary, keeping its name and propertiesA
DestructiveIdempotent

Replace the shape of ONE existing boundary. Its id, name, properties and set stay as they are, so every lookup that matches it keeps returning the same properties.

Use this to fix or move a zone. Do NOT delete it and add it again: that gives it a new id and loses the properties unless they are re-sent.

Name the boundary by set and name; this finds its id. If a set holds two boundaries with that name, the answer lists their ids and asks for one. The new shape gets the same checks as add_boundary, including the 50-point cap (refused, unbilled). Only boundaries on this account can be changed.

NameRequiredDescriptionDefault
idNoThe boundary’s id (a UUID), when it is known. Use this or name.
setYesThe set the boundary is in.
nameNoThe boundary’s name. Use this or id.
geometryYesThe new shape: a GeoJSON Polygon.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructiveHint, idempotentHint and openWorldHint, but the description goes further: it explains what survives the replacement (identity and properties), how ambiguous name resolution behaves (ids returned, caller asked to pick one), the 50-point cap, that a rejected shape is unbilled, and the account boundary. That is exactly the context annotations cannot carry.

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

Conciseness5/5

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

Five short sentences, front-loaded with the operation and its invariant (id/name/properties preserved), then the when-to-use, then the error path. No sentence restates the name or title without adding information.

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

Completeness4/5

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

For a mutation with a nested GeoJSON payload and no output schema, the description covers validation limits, ambiguity handling and preservation semantics. The one gap is what a successful call returns or how to confirm the new shape landed, which the agent has to discover at runtime.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real resolving semantics beyond the schema: naming by set+name performs an id lookup, and duplicate names produce an id list rather than an arbitrary pick. It does not add syntax detail for the geometry rings, which is left entirely to the schema.

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?

States a specific verb and resource ('Replace the shape of ONE existing boundary') and immediately scopes it by what is preserved: id, name, properties, set. The capitalization of ONE disambiguates it from bulk or create operations, so an agent can tell it apart from add_boundary without opening either schema.

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

Usage Guidelines5/5

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

Gives the positive case ('Use this to fix or move a zone') and an explicit anti-pattern with the reason ('Do NOT delete it and add it again: that gives it a new id and loses the properties'). It also states the account restriction ('Only boundaries on this account can be changed'), leaving nothing to inference.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.4.1
    • Addedgeocode_address
    • Addedget_route
    • Changedlookup_location1 field changed
      • addedInput schema / properties / streetGeometry
        Added value: +{
        +  "description": "Give each street answer the line of the street it names, trimmed to about 250 m either side of the point. Implies the street layer. Off by default, and leave it off unless something will draw it: a line is hundreds of characters nobody reads.",
        +  "enum": [
        +    "polyline6",
        +    "geojson"
        +  ],
        +  "type": "string"
        +}
    • Addedupdate_boundary
  2. 1 tool updatev0.1.6
    • Changedlookup_location2 fields changed
      • changedInput schema / properties / base / description
        Previous value: -"Which reference layers to resolve. Defaults to all three."New value: +"Which reference layers to resolve. Defaults to country, region and municipal. \"street\" is opt-in: add it explicitly to get the nearest street."
      • changedInput schema / properties / base / items / enum
        Previous value: -[
        -  "country",
        -  "region",
        -  "municipal"
        -]New value: +[
        +  "country",
        +  "region",
        +  "municipal",
        +  "street"
        +]
  3. 2 tool updatesv0.1.4
    • Changedadd_boundary13 fields changed
      • removedInput schema / properties / geometry / additionalProperties
        Removed value: -{}
      • changedInput schema / properties / geometry / description
        Previous value: -"GeoJSON geometry, e.g. {\"type\":\"Polygon\",\"coordinates\":[[[lng,lat],...]]}. Longitude comes first. A Feature or FeatureCollection is not accepted."New value: +"A GeoJSON Polygon, e.g. {\"type\":\"Polygon\",\"coordinates\":[[[18.40,-33.93],[18.40,-33.90],[18.44,-33.90],[18.44,-33.93],[18.40,-33.93]]]}."
      • addedInput schema / properties / geometry / properties
        Added value: +{
        +  "coordinates": {
        +    "description": "Rings of [longitude, latitude] positions. The first ring is the outline; any further rings are holes. Each ring is closed: its last position repeats its first.",
        +    "items": {
        +      "items": {
        +        "items": {
        +          "type": "number"
        +        },
        +        "maxItems": 3,
        +        "minItems": 2,
        +        "type": "array"
        +      },
        +      "minItems": 4,
        +      "type": "array"
        +    },
        +    "minItems": 1,
        +    "type": "array"
        +  },
        +  "type": {
        +    "const": "Polygon",
        +    "type": "string"
        +  }
        +}
      • removedInput schema / properties / geometry / propertyNames
        Removed value: -{
        -  "type": "string"
        -}
      • addedInput schema / properties / geometry / required
        Added value: +[
        +  "type",
        +  "coordinates"
        +]
      • changedInput schema / properties / name / description
        Previous value: -"A name for this boundary, returned on a match."New value: +"Label returned when a looked-up point falls inside this polygon, e.g. \"Zone A\"."
      • addedInput schema / properties / properties / additionalProperties / anyOf
        Added value: +[
        +  {
        +    "maxLength": 256,
        +    "type": "string"
        +  },
        +  {
        +    "type": "number"
        +  },
        +  {
        +    "type": "boolean"
        +  }
        +]
      • changedInput schema / properties / properties / description
        Previous value: -"Flat key/value pairs returned with every match of this boundary."New value: +"Optional flat key/value pairs returned with every match: keys up to 32 characters, values a string (up to 256 characters), number or boolean. The demo key accepts only category, color and priority, from fixed lists."
      • addedInput schema / properties / properties / propertyNames / maxLength
        Added value: +32
      • changedInput schema / properties / set / description
        Previous value: -"The set to add it to. It must already exist."New value: +"Name of an existing set on this account (see list_boundary_sets)."
      • removedInput schema / properties / set / maxLength
        Removed value: -32
      • removedInput schema / properties / set / minLength
        Removed value: -1
      • addedInput schema / properties / set / pattern
        Added value: +"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$"
    • Changedcreate_boundary_set4 fields changed
      • changedInput schema / properties / name / description
        Previous value: -"A short name, used to reference the set in lookups."New value: +"A short name, used to reference the set in lookups: 1-32 letters, digits, hyphens or underscores, starting with a letter or digit."
      • removedInput schema / properties / name / maxLength
        Removed value: -32
      • removedInput schema / properties / name / minLength
        Removed value: -1
      • addedInput schema / properties / name / pattern
        Added value: +"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$"
  4. 4 tool updatesv0.1.2
    • First observedadd_boundary
    • First observedcreate_boundary_set
    • First observedlist_boundary_sets
    • First observedlookup_location

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct action and resource: forward geocode (geocode_address) vs reverse geocode (lookup_location), routing (get_route), and boundary management (create/add/update/list). Descriptions explicitly cross-reference the correct alternative (e.g., 'For the other direction... use geocode_address'), leaving no ambiguity even between inverse geocoding tools.

Naming Consistency5/5

All tools follow a clean snake_case verb_noun pattern (lookup_location, geocode_address, get_route, add_boundary, create_boundary_set, list_boundary_sets, update_boundary). Multi-word nouns are handled consistently, and no mixed conventions appear.

Tool Count5/5

Seven tools is well-scoped for a geocoding/routing/boundary service: three geospatial query tools and four boundary-management tools. Each earns its place without feeling bloated or thin.

Completeness3/5

Forward/reverse geocoding and routing are covered, and boundary management has create/add/update/list, but there is no delete_boundary, delete_boundary_set, or get_boundary for a single record. Batch geocoding is also explicitly deferred to the raw API, leaving a notable lifecycle gap that will force workarounds.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables IP address location and latitude/longitude address queries, providing detailed geographic information such as country, province, city, and district.
    2
    10 npm
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Provides forward/reverse geocoding, bounding box extraction, nearby places discovery, batch geocoding, route waypoints, and administrative boundary lookup using OpenStreetMap data.
    10
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables forward and reverse geocoding using the OpenCage API, allowing conversion between addresses and coordinates.
    8 npm
    MIT