Skip to main content
Glama

geocode

Turn a place (address, POI, town) into coordinates. Ask two ways, and they combine: query is free text — one run-together string, the way a person types into a search box — and street, housenumber, city, postcode and country name the parts of an address separately. At least one of the two is required. Pass the parts whenever you already hold the address in parts (a form, a CRM row, a manifest): components are REQUIREMENTS, not hints, so city: "London" means a result outside London cannot come back at all, where "London" inside query only reorders. country takes an ISO 3166-1 alpha-2 code or a country name ("GB", "United Kingdom"); a value naming no country is refused rather than silently matching nothing. Returns up to limit (default 10) candidates with name, one-line label, lat/lon, type and address parts. Pass focus {lat, lon} to rank results near a location higher. A bare city or town name (no comma) lists that city's airports and main railway stations directly behind it, ahead of namesakes elsewhere. Each hit also carries match: a per-component matched/inferred/unmatched verdict, the score_gap to the runner-up, and which backend answered. READ IT before acting on an address — an unmatched or inferred postcode on the top hit means the answer does not carry the address you asked for, and a small score_gap means the ranking barely chose, so show the alternatives instead of picking one. Use verify_places when the address came from a model or a user and needs checking rather than using. Results are matched in lang (default "en"), so English exonyms — "Munich", "Cologne", "Geneva" — resolve to the place meant; pass lang when querying in another language, or "default" for each place's local name.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cityNoStructured component: town or city, e.g. "London". Matches the containing city as well as the immediate locality, so a suburb name works here too.
langNoLanguage of the place names to match and return, as a two-letter code. Defaults to "en", which is what makes English exonyms ("Munich", "Cologne", "Geneva") resolve to the place meant rather than a same-named town elsewhere. Set it to the language your query is written in; "default" asks for each place's own local name. Deployments support a fixed set (this one: "en", "de", "fr"), and anything outside it is refused.
focusNoOptional location bias: results near this point rank higher.
limitNoMaximum number of results (1–50, default 10).
queryNoFree-text place query, e.g. "Dover ferry terminal". Required unless at least one structured component is supplied.
streetNoStructured component: street name, e.g. "Downing Street". Matches the street of addresses and POIs (transliterated street names included) as well as the street itself.
countryNoStructured component: ISO 3166-1 alpha-2 code or country name — "GB", "gb", "United Kingdom" and "UK" all mean the same country. A value naming no country is refused rather than quietly applied as a filter that matches nothing.
postcodeNoStructured component: postcode in any spacing or case — "SW1A 2AA" and "sw1a2aa" are one query. A bare UK outward code ("SW1A") selects the whole district.
housenumberNoStructured component: house number, e.g. "10" or "221B". Only meaningful alongside `street` — a house number on its own excludes nearly everything and identifies nothing.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultsYesMatching places, best first.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed5 schema fields changed
    • addedInput 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"
      +  }
      +]
    • changedInput 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."
    • removedInput 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"
      -  }
      -}
    • removedInput schema / $defs / LatLon / required
      Removed value: -[
      -  "lat",
      -  "lon"
      -]
    • removedInput schema / $defs / LatLon / type
      Removed value: -"object"
  2. Changed11 schema fields changed
    • addedInput schema / properties / city
      Added value: +{
      +  "description": "Structured component: town or city, e.g. \"London\". Matches the\ncontaining city as well as the immediate locality, so a suburb name\nworks here too.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedInput schema / properties / country
      Added value: +{
      +  "description": "Structured component: ISO 3166-1 alpha-2 code or country name —\n\"GB\", \"gb\", \"United Kingdom\" and \"UK\" all mean the same country.\nA value naming no country is refused rather than quietly applied as\na filter that matches nothing.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedInput schema / properties / housenumber
      Added value: +{
      +  "description": "Structured component: house number, e.g. \"10\" or \"221B\". Only\nmeaningful alongside `street` — a house number on its own excludes\nnearly everything and identifies nothing.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedInput schema / properties / postcode
      Added value: +{
      +  "description": "Structured component: postcode in any spacing or case — \"SW1A 2AA\"\nand \"sw1a2aa\" are one query. A bare UK outward code (\"SW1A\")\nselects the whole district.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • changedInput schema / properties / query / description
      Previous value: -"Free-text place query, e.g. \"Dover ferry terminal\"."New value: +"Free-text place query, e.g. \"Dover ferry terminal\". Required unless\nat least one structured component is supplied."
    • changedInput schema / properties / query / type
      Previous value: -"string"New value: +[
      +  "string",
      +  "null"
      +]
    • addedInput schema / properties / street
      Added value: +{
      +  "description": "Structured component: street name, e.g. \"Downing Street\". Matches\nthe street of addresses and POIs (transliterated street names\nincluded) as well as the street itself.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • removedInput schema / required
      Removed value: -[
      -  "query"
      -]
    • addedOutput schema / $defs / GeocodeHit / properties / match
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/GeocodeMatch"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "How far this hit can be trusted to be the place that was asked\nfor — see [`GeocodeMatch`]. Present whenever the MapMap gateway\nanswered; absent on a deployment falling back to the direct Photon\ngeocoder, and absent on the gateway's own fast paths (a pasted\ncoordinate pair, a bare UK outward code, a category browse), which\nanswer without a ranking to report on."
      +}
    • addedOutput schema / $defs / GeocodeMatch
      Added value: +{
      +  "description": "How well one geocoding result answers what was actually asked.\n\nGeocoding's real failure mode is not \"no answer\" but a confident answer\nto a different question: a plausible row on the wrong street, with\nnothing in the response to say so. This object is that missing say-so,\nand an agent should read it before acting on an address.\n\nHow to read it:\n\n* Any component `unmatched` or `inferred` on the TOP hit means the\n  answer does not carry the address that was asked for — an `unmatched`\n  postcode means the result has no postcode at all, `inferred` means it\n  has a different one. Neither is a match. Say so rather than presenting\n  the hit as the address, and reach for `verify_places` when the address\n  came from a model or a user and needs checking rather than using.\n* A small `score_gap` means the ranking barely chose between this hit\n  and the runner-up, which is exactly when to show the alternatives\n  instead of picking one for the user.",
      +  "properties": {
      +    "components": {
      +      "$ref": "#/$defs/GeocodeMatchComponents",
      +      "description": "Per-component verdict on this hit: one entry for each structured\ncomponent supplied, and empty when the query was free text only."
      +    },
      +    "score_gap": {
      +      "description": "The top result's score minus the runner-up's, rounded to 3 decimal\nplaces. `0` for a single result, and `0` from the `photon` source,\nwhich publishes no per-result score — so a `0` is \"no signal\", not\n\"a tie\".",
      +      "format": "double",
      +      "type": "number"
      +    },
      +    "source": {
      +      "description": "Which backend answered: \"mapmap-index\" (the first-party index) or\n\"photon\".",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "components",
      +    "score_gap",
      +    "source"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / GeocodeMatchComponents
      Added value: +{
      +  "description": "Per-component verdicts inside a [`GeocodeMatch`]. Each is one of\n\"matched\", \"inferred\" or \"unmatched\"; a component that was not supplied\nis absent entirely.\n\n* \"matched\" — the result's own field carries the value asked for (case-\n  and accent-insensitive, and by containment, so `city: \"London\"`\n  matches \"City of London\").\n* \"inferred\" — the result carries a value for that component, but not\n  the one asked for. It reached the page through ranking, as when a\n  street is found by its transliterated name and displayed under its\n  canonical one.\n* \"unmatched\" — the result carries no value for that component at all.",
      +  "properties": {
      +    "city": {
      +      "description": "Verdict on the supplied `city`.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "country": {
      +      "description": "Verdict on the supplied `country`.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "housenumber": {
      +      "description": "Verdict on the supplied `housenumber`.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "postcode": {
      +      "description": "Verdict on the supplied `postcode`.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "street": {
      +      "description": "Verdict on the supplied `street`.",
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    }
      +  },
      +  "type": "object"
      +}
  3. First observed

TDQS

A4.8/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 richly: components are REQUIREMENTS not hints, an unknown `country` is refused rather than silently matching nothing, `lang` outside the supported set is refused, and unmatched/inferred `match` verdicts plus small `score_gap` signal unreliable answers. This is far beyond what the schema states.

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?

Dense and largely front-loaded — the core action and the two input modes come first, then behavioral caveats. It runs long, with the `lang`/exonym material at the end feeling somewhat expendable, but nearly every sentence carries operational information.

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 9-parameter, zero-required tool with an output schema, this covers the gaps well: mutual requirement of query/components, return count and fields, ranking bias, and how to interpret `match`/`score_gap`. An agent could invoke and correctly act on results without opening the schema.

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 meaning the schema lacks: the requirement-vs-hint distinction between structured components and `query`, the 'at least one of the two is required' rule, and the ranking effect of `focus`. It stops short of fully decoupling from the schema's own parameter text.

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: 'Turn a place (address, POI, town) into coordinates.' It implicitly distinguishes itself from reverse_geocode (coordinates→place) by naming the inverse direction, and from siblings like nearby_places/places_in_view by framing itself as the forward geocoder.

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 routing to the alternative: 'Use `verify_places` when the address came from a model or a user and needs checking rather than using.' It also gives a concrete selection rule for the two input modes: 'Pass the parts whenever you already hold the address in parts (a form, a CRM row, a manifest).'

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