Koskamo

openstreetmap-mcp-server

An MCP server that provides tools to geocode addresses, reverse geocode coordinates, look up OpenStreetMap objects, and perform spatial queries using Nominatim and the Overpass API.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

openstreetmap-mcp-server logo, by cyanheadsCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-06Published by cyanheads
MCP endpoint (Streamable HTTP)
https://openstreetmap.caseyjhand.com/mcp
Vendor documentation

What its tools do

6 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

6 of 6 have a description · 6 of 6 declare behavior hints · 6 of 6 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Geocode a place name or address to coordinates
  • Reverse geocode coordinates to the nearest address
  • Query OpenStreetMap features by tag or bounding box

Source and maintenance

Community listingRegistry 0.7.1
Maintenance checked

Protocol and capabilities

Server
openstreetmap-mcp-server 0.7.1
MCP protocol negotiated in check
2025-11-25
Tools
6 advertised
Resources
1 advertised
Resource templates
1 advertised
Prompts
0 advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  },
  "logging": {},
  "prompts": {
    "listChanged": true
  },
  "resources": {
    "subscribe": true,
    "listChanged": true
  },
  "extensions": {
    "io.modelcontextprotocol/skills": {
      "directoryRead": true
    }
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Resolve place names and addresses to coordinates with openstreetmap_search_places, coordinates to an address with openstreetmap_reverse_geocode, and known OSM IDs to full records with openstreetmap_lookup_objects. Survey features with openstreetmap_query_nearby (a radius around a point) or openstreetmap_query_bbox (a bounding box, or within an OSM boundary ref such as R237385, built from the osm_type and osm_id the geocoding tools return), filtering by amenity or tag_key with an optional tag_value, and drop to openstreetmap_query_raw for arbitrary Overpass QL. Data is © OpenStreetMap contributors under ODbL 1.0.

Tools

6 advertised

openstreetmap_search_places — Geocode a place name or address
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Geocode a place name or address to coordinates and structured place data via Nominatim. Send either a free-form query or the structured address fields (street, city, county, state, country, postalcode), never both; results are the best-ranked matches, not every matching object, and matching never uses an OSM attribute tag, so filter or enumerate by tag with openstreetmap_query_nearby, openstreetmap_query_bbox, or openstreetmap_query_raw.

openstreetmap_search_places input schema
{
  "type": "object",
  "anyOf": [
    {
      "type": "object",
      "required": [
        "query"
      ]
    },
    {
      "type": "object",
      "required": [
        "street"
      ]
    },
    {
      "type": "object",
      "required": [
        "city"
      ]
    },
    {
      "type": "object",
      "required": [
        "county"
      ]
    },
    {
      "type": "object",
      "required": [
        "state"
      ]
    },
    {
      "type": "object",
      "required": [
        "country"
      ]
    },
    {
      "type": "object",
      "required": [
        "postalcode"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "city": {
      "type": "string",
      "description": "City name (structured query)."
    },
    "layer": {
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\s*(?:(?:[aA][dD][dD][rR][eE][sS][sS]|[pP][oO][iI]|[rR][aA][iI][lL][wW][aA][yY]|[nN][aA][tT][uU][rR][aA][lL]|[mM][aA][nN][mM][aA][dD][eE])(?:\\s*,\\s*(?:[aA][dD][dD][rR][eE][sS][sS]|[pP][oO][iI]|[rR][aA][iI][lL][wW][aA][yY]|[nN][aA][tT][uU][rR][aA][lL]|[mM][aA][nN][mM][aA][dD][eE]))*)?\\s*$",
          "description": "One documented layer name, or a comma-separated list of them, in any casing."
        }
      ],
      "description": "Filter by data layer: one of address, poi, railway, natural, manmade, or a comma-separated list of them, in any casing. Any other name is rejected here, not upstream; an empty value counts as omitted. Default: no restriction."
    },
    "limit": {
      "type": "integer",
      "default": 5,
      "maximum": 10000,
      "minimum": 1,
      "description": "Maximum results to return, up to 40 (Nominatim's own ceiling); a larger value is served as 40 and limitNotice says so. Nominatim may return fewer at its relevance cutoff; nextExcludeIds pages past it."
    },
    "query": {
      "type": "string",
      "description": "Free-form search string, e.g. \"Space Needle Seattle\" or \"1600 Pennsylvania Ave NW, Washington DC\". Not combinable with the structured address fields. Nominatim reads commas as an address hierarchy, so give the name plus its city or region and nothing in between: \"Beinecke Library, New Haven\" matches where \"Beinecke Library, Yale University, New Haven\" returns nothing."
    },
    "state": {
      "type": "string",
      "description": "State or province (structured query)."
    },
    "county": {
      "type": "string",
      "description": "County or district (structured query)."
    },
    "street": {
      "type": "string",
      "description": "House number and street name (structured query). Use with city/state/country fields. Cannot be combined with query."
    },
    "bounded": {
      "type": "boolean",
      "description": "Restrict results to the viewbox instead of merely biasing toward it. Requires viewbox — setting it alone is rejected rather than ignored. With it, a match outside the box is dropped even when it scores higher."
    },
    "country": {
      "type": "string",
      "description": "Country name or ISO 3166-1 alpha-2 code (structured query)."
    },
    "viewbox": {
      "type": "object",
      "required": [
        "west",
        "south",
        "east",
        "north"
      ],
      "properties": {
        "east": {
          "type": "number",
          "maximum": 180,
          "minimum": -180,
          "description": "Eastern boundary longitude. Must be strictly greater than west."
        },
        "west": {
          "type": "number",
          "maximum": 180,
          "minimum": -180,
          "description": "Western boundary longitude. Must be strictly less than east."
        },
        "north": {
          "type": "number",
          "maximum": 90,
          "minimum": -90,
          "description": "Northern boundary latitude. Must be strictly greater than south."
        },
        "south": {
          "type": "number",
          "maximum": 90,
          "minimum": -90,
          "description": "Southern boundary latitude. Must be strictly less than north."
        }
      },
      "description": "Rectangular area to bias results toward, disambiguating a name that repeats worldwide (a creek in one watershed, a street in one municipality). Narrower than countrycodes. Bias only by default: a better match outside the box is still returned. Set bounded for a hard restriction. Unlike openstreetmap_query_bbox this box may not cross the antimeridian — west must be less than east and south less than north, or the call is rejected."
    },
    "language": {
      "type": "string",
      "description": "Preferred language for result names (BCP 47 code or Accept-Language string, e.g., \"en\", \"de\", \"fr,en\"). Defaults to local OSM language."
    },
    "extratags": {
      "type": "boolean",
      "default": false,
      "description": "Include the matched object's extra OSM tags — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). An absent tag describes that object, not OpenStreetMap. Increases response size."
    },
    "postalcode": {
      "type": "string",
      "description": "Postal or ZIP code (structured query)."
    },
    "featureType": {
      "enum": [
        "country",
        "state",
        "city",
        "settlement"
      ],
      "type": "string",
      "description": "Restrict results to a geographic feature type. Automatically implies the address layer."
    },
    "countrycodes": {
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\s*(?:[A-Za-z]{2}\\s*)?(?:,\\s*(?:[A-Za-z]{2}\\s*)?)*$",
          "description": "One ISO 3166-1 alpha-2 country code, or a comma-separated list of them, in any casing."
        }
      ],
      "description": "Restrict results to one or more countries: comma-separated ISO 3166-1 alpha-2 codes (e.g. \"us,ca\"), any casing, optional spaces around the commas. Any other form — alpha-3, a semicolon list, a country name — is rejected rather than silently dropped upstream. A well-formed code for a country that does not exist matches nothing. An empty value counts as omitted. Prefer this over the structured country field for filtering."
    },
    "exclude_place_ids": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "string",
            "const": ""
          },
          {
            "type": "string",
            "pattern": "^\\s*(?:[NWRnwr]\\d+|\\d+)?\\s*$",
            "description": "An OSM ref (N/W/R plus the object id) or a bare Nominatim place_id."
          }
        ],
        "description": "One exclusion token, or an empty value that excludes nothing."
      },
      "description": "OSM refs (N/W/R + id) or Nominatim place_ids to drop from results; any other token form is rejected here rather than by Nominatim. Entries are trimmed, lowercase ref prefixes uppercased, a blank entry treated as absent. Page toward further matches by passing back the previous page's nextExcludeIds, which prefers stable OSM refs over volatile place_ids; the walk ends when a page returns zero results with an exhaustion notice — a success, not an error. Best-effort, not a cursor: Nominatim ranking can reorder between calls, so already-seen results may shift."
    }
  },
  "additionalProperties": false
}
openstreetmap_search_places output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "results",
        "total",
        "attribution",
        "effectiveQuery"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "cap": {
      "type": "number",
      "description": "The limit applied to this request."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "no_results",
                "conflicting_query_mode",
                "missing_query_mode",
                "bounded_without_viewbox",
                "invalid_viewbox",
                "invalid_parameters",
                "request_too_long",
                "rate_limited",
                "upstream_error"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `no_results`: No places matched, and no exclude_place_ids were supplied — an exhausted paging walk returns success with zero results instead. `conflicting_query_mode`: query and at least one structured address field were both supplied; the two modes are mutually exclusive. `missing_query_mode`: Neither query nor any structured address field was supplied. `bounded_without_viewbox`: bounded was set without a viewbox for it to restrict results to. `invalid_viewbox`: The viewbox is inverted or degenerate: west at or beyond east, or south at or beyond north. `invalid_parameters`: Nominatim returned HTTP 400, refusing one of the forwarded parameters; its own message names which one. `request_too_long`: Nominatim returned HTTP 414: the request URL, which grows with every exclude_place_ids entry, is longer than the instance accepts. `rate_limited`: Nominatim returned HTTP 429, or HTTP 200 with a throttle document in place of JSON — the one request per second policy was exceeded. `upstream_error`: Nominatim returned a non-2xx status other than 400, 414, or 429, or HTTP 200 with a non-JSON body carrying no throttle signature, or never answered: connection refused, host not found, or timed out. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "shown": {
      "type": "number",
      "description": "Number of results returned."
    },
    "total": {
      "type": "number",
      "description": "Number of results returned."
    },
    "notice": {
      "type": "string",
      "description": "Paging guidance, in two cases: results were capped at limit with a probe confirming a further match at the query's relevance cutoff (truncated true — keep paging with nextExcludeIds), or an exclude_place_ids walk was exhausted and the page came back empty (the query matched; the walk simply ended, so no rewrite is needed). Tell them apart by truncated and the result count, not by this field's presence. Absent on every other page, which is not thereby complete: nextExcludeIds continues the walk until a page returns zero results."
    },
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "place_id",
          "lat",
          "lon",
          "display_name"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "description": "Latitude in WGS84 decimal degrees."
          },
          "lon": {
            "type": "number",
            "description": "Longitude in WGS84 decimal degrees."
          },
          "name": {
            "type": "string",
            "description": "Feature name; absent for address-only results."
          },
          "type": {
            "type": "string",
            "description": "OSM feature type within category (e.g. \"hospital\", \"tower\")."
          },
          "osm_id": {
            "type": "number",
            "description": "OSM object ID. Combine with osm_type for openstreetmap_lookup_objects, or pass \"R\"/\"W\" + this id as within on openstreetmap_query_bbox to search inside this boundary. The same scope in openstreetmap_query_raw is rel(<osm_id>);map_to_area->.a; or way(<osm_id>);map_to_area->.a; then (area.a) on each statement."
          },
          "address": {
            "type": "object",
            "description": "Structured address breakdown, keys varying by feature type and country: house_number, road, suburb, city, state, postcode, country, country_code.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "category": {
            "type": "string",
            "description": "OSM feature category (e.g. \"amenity\", \"man_made\")."
          },
          "osm_type": {
            "enum": [
              "node",
              "way",
              "relation"
            ],
            "type": "string",
            "description": "OSM object type."
          },
          "place_id": {
            "type": "number",
            "description": "Nominatim internal place ID. Stable cross-server reference: osm_type+osm_id."
          },
          "extratags": {
            "type": "object",
            "description": "Extra OSM tags this object carries — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). Present only when extratags was requested; an absent tag describes this object, not OpenStreetMap.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "importance": {
            "type": "number",
            "description": "Nominatim relevance score (0–1). Higher is more globally prominent."
          },
          "boundingbox": {
            "type": "array",
            "items": false,
            "maxItems": 4,
            "minItems": 4,
            "description": "Bounding box as [south, north, west, east] in WGS84 decimal degrees.",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "display_name": {
            "type": "string",
            "description": "Full human-readable address string."
          }
        },
        "description": "A single geocoding result.",
        "additionalProperties": false
      },
      "description": "Geocoding results, ordered by Nominatim relevance (importance score descending)."
    },
    "truncated": {
      "type": "boolean",
      "description": "True when results were capped at limit and a same-call probe confirmed another match at this query's relevance cutoff; absent otherwise. Absence is not exhaustion — excluding a page's results can still surface less accurate matches past that cutoff, which is why nextExcludeIds is offered on every page that returned results. Nominatim reports no total, so this is a confirmed observation, not an inference from page size."
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution: Data © OpenStreetMap contributors, ODbL 1.0."
    },
    "limitNotice": {
      "type": "string",
      "description": "Present when limit exceeded this tool's maximum: the value requested and the one applied."
    },
    "boundedApplied": {
      "type": "boolean",
      "description": "True when the viewbox was a hard restriction (bounded=1 was sent), false when it biased ranking only and a match outside it could still be returned. Absent when no viewbox was supplied."
    },
    "effectiveQuery": {
      "type": "string",
      "description": "The effective query sent to Nominatim — the free-form query string, or a reconstructed string from the provided structured address fields."
    },
    "nextExcludeIds": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Accumulated exclude tokens (prior excludes plus this page) to pass as exclude_place_ids on the next call. Each is a stable OSM ref (N/W/R + osm_id) when the result carries one, the Nominatim place_id otherwise. Present on every page that returned results, full or short, whether or not truncated is set: excluding them surfaces less accurate matches past Nominatim's relevance cutoff. Best-effort, not a cursor — ranking can reorder between calls, so a walk can end sooner than the page count suggests."
    },
    "effectiveViewbox": {
      "type": "object",
      "required": [
        "west",
        "south",
        "east",
        "north"
      ],
      "properties": {
        "east": {
          "type": "number",
          "description": "Eastern boundary longitude sent to Nominatim."
        },
        "west": {
          "type": "number",
          "description": "Western boundary longitude sent to Nominatim."
        },
        "north": {
          "type": "number",
          "description": "Northern boundary latitude sent to Nominatim."
        },
        "south": {
          "type": "number",
          "description": "Southern boundary latitude sent to Nominatim."
        }
      },
      "description": "The viewbox forwarded to Nominatim on this call, echoed so an ambiguous result can be read against the area that scoped it. Absent when no viewbox was supplied.",
      "additionalProperties": false
    },
    "tagSelectionCaveat": {
      "type": "string",
      "description": "Standing caveat: tag-based selection lives on the Overpass tools (openstreetmap_query_nearby, openstreetmap_query_bbox, openstreetmap_query_raw), never here. extratags decorates the returned objects rather than selecting them, so a missing tag is not evidence the tag is missing from OpenStreetMap. Present on every successful response."
    }
  },
  "additionalProperties": false
}
openstreetmap_search_places annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
openstreetmap_reverse_geocode — Reverse geocode coordinates to an address
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Convert a latitude/longitude pair to the nearest address or named place via Nominatim. The result is the closest indexed OSM object at the requested zoom (18 building, 10 city), which in dense areas can be a neighbouring feature rather than the one containing the coordinate; proximity and layer pick it, never an OSM attribute tag, so find features by tag with openstreetmap_query_nearby, openstreetmap_query_bbox, or openstreetmap_query_raw.

openstreetmap_reverse_geocode input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "lat",
    "lon"
  ],
  "properties": {
    "lat": {
      "type": "number",
      "maximum": 90,
      "minimum": -90,
      "description": "Latitude in WGS84 decimal degrees."
    },
    "lon": {
      "type": "number",
      "maximum": 180,
      "minimum": -180,
      "description": "Longitude in WGS84 decimal degrees."
    },
    "zoom": {
      "type": "integer",
      "default": 18,
      "maximum": 18,
      "minimum": 3,
      "description": "Address detail level, roughly corresponding to map zoom. 18=building, 16=street, 14=neighbourhood, 12=town, 10=city, 8=county, 5=state, 3=country."
    },
    "layer": {
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\s*(?:(?:[aA][dD][dD][rR][eE][sS][sS]|[pP][oO][iI]|[rR][aA][iI][lL][wW][aA][yY]|[nN][aA][tT][uU][rR][aA][lL]|[mM][aA][nN][mM][aA][dD][eE])(?:\\s*,\\s*(?:[aA][dD][dD][rR][eE][sS][sS]|[pP][oO][iI]|[rR][aA][iI][lL][wW][aA][yY]|[nN][aA][tT][uU][rR][aA][lL]|[mM][aA][nN][mM][aA][dD][eE]))*)?\\s*$",
          "description": "One documented layer name, or a comma-separated list of them, in any casing."
        }
      ],
      "description": "Restrict which OSM layer is matched: one of address, poi, railway, natural, manmade, or a comma-separated list of them, in any casing. Any other name is rejected here, not upstream; an empty value counts as omitted. Default: address,poi."
    },
    "language": {
      "type": "string",
      "description": "Preferred language for the result (BCP 47 code or Accept-Language string)."
    },
    "extratags": {
      "type": "boolean",
      "default": false,
      "description": "Include the matched object's extra OSM tags — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). An absent tag describes that object, not OpenStreetMap."
    }
  },
  "additionalProperties": false
}
openstreetmap_reverse_geocode output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "result",
        "attribution"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "no_coverage",
                "invalid_parameters",
                "rate_limited",
                "upstream_error"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `no_coverage`: Nominatim reported no OSM data at the coordinates — open ocean or unmapped territory. `invalid_parameters`: Nominatim returned HTTP 400, refusing one of the forwarded parameters; its own message names which one. `rate_limited`: Nominatim returned HTTP 429, or HTTP 200 with a throttle document in place of JSON — the one request per second policy was exceeded. `upstream_error`: Nominatim returned a non-2xx status other than 400 or 429, or HTTP 200 with a non-JSON body carrying no throttle signature, or never answered: connection refused, host not found, or timed out. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "result": {
      "type": "object",
      "required": [
        "place_id",
        "lat",
        "lon",
        "display_name"
      ],
      "properties": {
        "lat": {
          "type": "number",
          "description": "Latitude in WGS84 decimal degrees."
        },
        "lon": {
          "type": "number",
          "description": "Longitude in WGS84 decimal degrees."
        },
        "name": {
          "type": "string",
          "description": "Feature name when the object is named."
        },
        "type": {
          "type": "string",
          "description": "OSM feature type within category."
        },
        "osm_id": {
          "type": "number",
          "description": "OSM object ID. Combine with osm_type for openstreetmap_lookup_objects, or pass \"R\"/\"W\" + this id as within on openstreetmap_query_bbox to search inside this boundary. The same scope in openstreetmap_query_raw is rel(<osm_id>);map_to_area->.a; or way(<osm_id>);map_to_area->.a; then (area.a) on each statement."
        },
        "address": {
          "type": "object",
          "description": "Structured address, keys varying by feature type: house_number, road, suburb, city, state, postcode, country, country_code.",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "category": {
          "type": "string",
          "description": "OSM feature category (e.g. \"amenity\", \"building\")."
        },
        "osm_type": {
          "enum": [
            "node",
            "way",
            "relation"
          ],
          "type": "string",
          "description": "OSM object type."
        },
        "place_id": {
          "type": "number",
          "description": "Nominatim internal place ID."
        },
        "extratags": {
          "type": "object",
          "description": "Extra OSM tags this object carries — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). Present only when extratags was requested; an absent tag describes this object, not OpenStreetMap.",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string"
          }
        },
        "boundingbox": {
          "type": "array",
          "items": false,
          "maxItems": 4,
          "minItems": 4,
          "description": "Bounding box as [south, north, west, east] in WGS84 decimal degrees.",
          "prefixItems": [
            {
              "type": "number"
            },
            {
              "type": "number"
            },
            {
              "type": "number"
            },
            {
              "type": "number"
            }
          ]
        },
        "display_name": {
          "type": "string",
          "description": "Full human-readable address."
        }
      },
      "description": "The closest matching OSM object at the given coordinates.",
      "additionalProperties": false
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution."
    },
    "tagSelectionCaveat": {
      "type": "string",
      "description": "Standing caveat: tag-based selection lives on the Overpass tools (openstreetmap_query_nearby, openstreetmap_query_bbox, openstreetmap_query_raw), never here. extratags decorates the returned objects rather than selecting them, so a missing tag is not evidence the tag is missing from OpenStreetMap. Present when extratags was requested."
    }
  },
  "additionalProperties": false
}
openstreetmap_reverse_geocode annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
openstreetmap_lookup_objects — Look up address details for OSM objects by ID
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Fetch the Nominatim address record for known OSM objects by ID, 50 per call with any more returned under deferred_ids, each prefixed N (node), W (way), or R (relation), e.g. "N240109189". Use it for IDs already in hand from openstreetmap_query_nearby or openstreetmap_query_bbox; it returns only objects named in osm_ids and lists those with no Nominatim record under not_found (Nominatim skips unnamed and non-place objects, so such an ID may still exist). It cannot select by tag, so discover objects with those tools or openstreetmap_query_raw first.

openstreetmap_lookup_objects input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "osm_ids"
  ],
  "properties": {
    "osm_ids": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "maxItems": 500,
      "minItems": 1,
      "description": "OSM IDs to look up, each prefixed with N (node), W (way), or R (relation). Always an array, including for a single ID: [\"N240109189\"], [\"W50637691\", \"R146656\"]. Up to 500 entries; duplicates are dropped and the first 50 unique IDs are looked up per call, the rest returned under deferred_ids to pass on the next call."
    },
    "language": {
      "type": "string",
      "description": "Preferred language for names (BCP 47 code)."
    },
    "extratags": {
      "type": "boolean",
      "default": false,
      "description": "Include each looked-up object's extra OSM tags — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). An absent tag describes that object, not OpenStreetMap."
    }
  },
  "additionalProperties": false
}
openstreetmap_lookup_objects output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "results",
        "not_found",
        "total",
        "attribution"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "invalid_id_format",
                "invalid_parameters",
                "rate_limited",
                "upstream_error"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `invalid_id_format`: An array element is not a single N/W/R-prefixed OSM ID. `invalid_parameters`: Nominatim returned HTTP 400, refusing one of the forwarded parameters; its own message names which one. `rate_limited`: Nominatim returned HTTP 429, or HTTP 200 with a throttle document in place of JSON — the one request per second policy was exceeded. `upstream_error`: Nominatim returned a non-2xx status other than 400 or 429, or HTTP 200 with a non-JSON body carrying no throttle signature, or never answered: connection refused, host not found, or timed out. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "total": {
      "type": "number",
      "description": "Number of results returned."
    },
    "notice": {
      "type": "string",
      "description": "Present when IDs were deferred or not_found is non-empty: the deferral first, then what not_found means and the openstreetmap_query_raw query and limit that return whichever of those IDs exist. Absent otherwise."
    },
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "place_id",
          "lat",
          "lon",
          "display_name"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "description": "Latitude in WGS84 decimal degrees."
          },
          "lon": {
            "type": "number",
            "description": "Longitude in WGS84 decimal degrees."
          },
          "name": {
            "type": "string",
            "description": "Feature name if applicable."
          },
          "type": {
            "type": "string",
            "description": "OSM feature type within category."
          },
          "osm_id": {
            "type": "number",
            "description": "OSM object ID. Pass \"R\"/\"W\" + this id as within on openstreetmap_query_bbox to search inside this boundary. The same scope in openstreetmap_query_raw is rel(<osm_id>);map_to_area->.a; or way(<osm_id>);map_to_area->.a; then (area.a) on each statement."
          },
          "address": {
            "type": "object",
            "description": "Structured address breakdown. Keys vary by feature type.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "category": {
            "type": "string",
            "description": "OSM feature category."
          },
          "osm_type": {
            "enum": [
              "node",
              "way",
              "relation"
            ],
            "type": "string",
            "description": "OSM object type."
          },
          "place_id": {
            "type": "number",
            "description": "Nominatim internal place ID."
          },
          "extratags": {
            "type": "object",
            "description": "Extra OSM tags this object carries — contact and metadata (phone, website, opening_hours, wikidata) and physical attributes (surface, tracktype, sac_scale, ele, access). Present only when extratags was requested; an absent tag describes this object, not OpenStreetMap.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "boundingbox": {
            "type": "array",
            "items": false,
            "maxItems": 4,
            "minItems": 4,
            "description": "Bounding box as [south, north, west, east] in WGS84 decimal degrees.",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "display_name": {
            "type": "string",
            "description": "Full human-readable address string."
          }
        },
        "description": "Address details for a single OSM ID lookup result.",
        "additionalProperties": false
      },
      "description": "Address details for the requested OSM IDs that were found."
    },
    "not_found": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "OSM IDs from the request with no Nominatim record, each listed once in canonical form (prefix uppercased, leading zeros dropped). The object does not exist, or it exists and Nominatim does not index it (unnamed or non-place features such as street lamps or building parts); notice gives the openstreetmap_query_raw query that tells the two apart."
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution: Data © OpenStreetMap contributors, ODbL 1.0."
    },
    "deferred_ids": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Unique IDs past the first 50, not looked up on this call; pass them as osm_ids on the next call. Absent when every ID was looked up."
    },
    "tagSelectionCaveat": {
      "type": "string",
      "description": "Standing caveat: tag-based selection lives on the Overpass tools (openstreetmap_query_nearby, openstreetmap_query_bbox, openstreetmap_query_raw), never here. extratags decorates the returned objects rather than selecting them, so a missing tag is not evidence the tag is missing from OpenStreetMap. Present when extratags was requested."
    }
  },
  "additionalProperties": false
}
openstreetmap_lookup_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
openstreetmap_query_nearby — Find OSM features near a point
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Find OSM features within a radius of a point via the Overpass API, the tool for "what is near X?" questions. Filter with amenity, or with tag_key plus an optional tag_value, ANDing up to five more filters; every feature returns with its full OSM tag set (no extratags flag here), sorted nearest-first by distance_meters. An area containing the point, such as the park or city it lies in, matches at 0 m whatever the radius.

openstreetmap_query_nearby input schema
{
  "type": "object",
  "anyOf": [
    {
      "type": "object",
      "required": [
        "amenity"
      ]
    },
    {
      "type": "object",
      "required": [
        "tag_key"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "lat",
    "lon"
  ],
  "properties": {
    "lat": {
      "type": "number",
      "maximum": 90,
      "minimum": -90,
      "description": "Center latitude in WGS84 decimal degrees."
    },
    "lon": {
      "type": "number",
      "maximum": 180,
      "minimum": -180,
      "description": "Center longitude in WGS84 decimal degrees."
    },
    "limit": {
      "type": "integer",
      "default": 20,
      "maximum": 10000,
      "minimum": 1,
      "description": "Maximum results to return, up to 500 per call; a larger value is served as 500 and limitNotice says so. Applied after the Overpass query — if the area has more features, they are truncated."
    },
    "offset": {
      "type": "integer",
      "default": 0,
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Number of matching features to skip before applying limit, for paging through a large result set. Features are distance-sorted before paging, so higher offsets return progressively farther matches; the full set is cached ~10 minutes so re-paging costs no extra upstream request. Pass the nextOffset value from a prior truncated response."
    },
    "amenity": {
      "type": "string",
      "description": "OSM amenity tag value (e.g. \"hospital\", \"pharmacy\", \"restaurant\", \"atm\"), shortcut for tag_key=\"amenity\". Exactly one primary mode is required: this or tag_key, never both. Many categories live under other keys — tourism (museums, hotels, viewpoints), leisure (parks, pitches, playgrounds), shop (supermarkets), historic (castles, memorials), natural (beaches, peaks); query those with tag_key and tag_value."
    },
    "filters": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Literal OSM tag key. Trimmed and nonblank; must be unique across the primary tag and all filters."
          },
          "match": {
            "enum": [
              "exact",
              "list_item"
            ],
            "type": "string",
            "description": "How value matches: \"exact\" (default) or \"list_item\", one item of a semicolon list. Needs value."
          },
          "value": {
            "type": "string",
            "description": "Literal value to match. Omit for key existence; an explicitly blank value is invalid. Trimmed before matching."
          }
        },
        "description": "One additional equality, list-item, or key-existence filter.",
        "additionalProperties": false
      },
      "maxItems": 5,
      "description": "Up to five additional filters, ANDed with the required primary amenity or tag_key filter in input order. Omitted or [] adds no conditions. Exact values miss semicolon-list values; set match \"list_item\" on the entry. Keys must be unique after trimming; keys and values must not contain Overpass QL metacharacters (\" \\ [ ] ; ( ))."
    },
    "tag_key": {
      "type": "string",
      "description": "Primary OSM tag key (e.g. \"leisure\", \"shop\", \"highway\"); omit tag_value to match any feature carrying the key, or supply it for exact equality. The alternative to amenity, never both. Additional filters are ANDed with this tag."
    },
    "tag_value": {
      "type": "string",
      "description": "Literal value paired with tag_key for exact equality (e.g., \"park\", \"supermarket\"); omit for key existence. Exact equality misses semicolon-list values (cuisine=pizza;italian); tag_value_match \"list_item\" catches them. Explicit empty or whitespace-only values are invalid. Keys and values are trimmed; blank unused fields are ignored in amenity mode."
    },
    "element_types": {
      "type": "array",
      "items": {
        "enum": [
          "node",
          "way",
          "relation"
        ],
        "type": "string"
      },
      "default": [
        "node",
        "way",
        "relation"
      ],
      "minItems": 1,
      "description": "OSM element types to search, at least one. Nodes cover most standalone POIs, ways most buildings and areas, relations large parks, lakes, campuses, and boundaries. Omit the field to search all three; pass [\"node\", \"way\"] to leave relations out. An empty array is rejected because it can only match nothing."
    },
    "radius_meters": {
      "type": "number",
      "default": 1000,
      "maximum": 50000,
      "description": "Search radius in meters. Max 50,000m (50km). Keep under 5,000m for dense urban POI queries to avoid slow responses.",
      "exclusiveMinimum": 0
    },
    "tag_value_match": {
      "enum": [
        "exact",
        "list_item"
      ],
      "type": "string",
      "description": "How tag_value matches: \"exact\" (default) compares the whole value; \"list_item\" matches one item of a semicolon list, so \"pizza\" finds cuisine=pizza;italian. Needs tag_value; not available with amenity."
    },
    "timeout_seconds": {
      "type": "integer",
      "default": 25,
      "maximum": 60,
      "minimum": 5,
      "description": "Overpass query timeout in seconds. Increase for large radius or dense areas."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_nearby output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "elements",
        "attribution",
        "effectiveTag",
        "totalFound",
        "truncated"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "invalid_tag",
                "query_timeout",
                "result_too_large",
                "rate_limited",
                "upstream_error",
                "overpass_gateway_timeout",
                "overpass_unavailable",
                "endpoints_exhausted",
                "endpoints_unavailable",
                "endpoints_rejected",
                "pacer_shed"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `invalid_tag`: Tag modes conflict or are missing, a key or supplied value is blank, keys repeat after trimming, a list_item match has no value, or a filter carries Overpass QL metacharacters. `query_timeout`: The query exceeded timeout_seconds. `result_too_large`: Overpass ran out of memory on this query, or its response passed the server's 64 MiB read ceiling. `rate_limited`: Every configured endpoint refused the query as throttled — HTTP 429, or a throttle document in place of JSON. `upstream_error`: Overpass reported a runtime error that is neither a timeout nor memory exhaustion. `overpass_gateway_timeout`: Overpass answered HTTP 504 or 408 with no busy-dispatcher remark — the query exceeded the endpoint's own time budget, not timeout_seconds. `overpass_unavailable`: Overpass answered an HTTP 5xx other than 501 and 504, a 425, or a 504 or 408 whose remark says its dispatcher is too busy or turned off — the endpoint is down, restarting, shedding load, or not taking the query yet. `endpoints_exhausted`: No endpoint completed within its attempt window, or the total budget ran out first. `endpoints_unavailable`: No configured endpoint would serve the call — connections refused, DNS failures, HTTP refusals such as 401/403/404, throttling, or instance faults, in some mix. `endpoints_rejected`: Every configured endpoint refused the call by HTTP status: a 401, 403, 404, or 501, a redirect, or another non-retried status below 500 other than 400 and 429. `pacer_shed`: The query waited 30 seconds in total for an Overpass slot while other calls held every slot it could take. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "notice": {
      "type": "string",
      "description": "Why this page is empty and what to try: nothing matched (widen the radius or change the tag), or offset ran past the end (retry lower). On any page, also names the key to retry with when amenity names a value OpenStreetMap tags under another key (tourism=museum, not amenity=museum), and says when distances were measured to bounding-box centers or areas containing the point went unchecked. Absent otherwise."
    },
    "elements": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "osm_type",
          "osm_id",
          "tags"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "description": "Latitude: a node's own, or the center of a way's or relation's bounding box."
          },
          "lon": {
            "type": "number",
            "description": "Longitude: a node's own, or the center of a way's or relation's bounding box."
          },
          "name": {
            "type": "string",
            "description": "Feature name from OSM tags."
          },
          "tags": {
            "type": "object",
            "description": "All OSM tags for this feature. Values are always strings.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "osm_id": {
            "type": "number",
            "description": "OSM element ID. Use with osm_type for openstreetmap_lookup_objects."
          },
          "osm_type": {
            "enum": [
              "node",
              "way",
              "relation"
            ],
            "type": "string",
            "description": "OSM element type."
          },
          "distance_meters": {
            "type": "number",
            "description": "Great-circle distance in meters from the query center to the feature's nearest point, rounded to one decimal: 0 inside an area (a closed way, a multipolygon or boundary relation), never 0 for a route; to a way's or relation's bounding-box center when notice says so. Results are sorted ascending by it; omitted for a way or relation that arrived without geometry."
          }
        },
        "description": "A single matching OSM feature.",
        "additionalProperties": false
      },
      "description": "Matching OSM features, up to the limit."
    },
    "truncated": {
      "type": "boolean",
      "description": "True if results were cut at the limit. Reduce radius, add more specific tags, or page with offset to retrieve the rest."
    },
    "nextOffset": {
      "type": "number",
      "description": "Offset to pass on the next call to retrieve the following page of features. Present only when more features remain beyond this page."
    },
    "totalFound": {
      "type": "number",
      "description": "Total features returned by Overpass before limit truncation."
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution: Data © OpenStreetMap contributors, ODbL 1.0."
    },
    "limitNotice": {
      "type": "string",
      "description": "Present when limit exceeded this tool's maximum: the value requested and the one applied."
    },
    "effectiveTag": {
      "type": "string",
      "description": "The full ordered AND filter chain: key=value for equality, key∋value for a list-item match, key alone for existence (e.g. \"amenity=restaurant, cuisine∋pizza, name\")."
    },
    "areasTimestamp": {
      "type": "string",
      "description": "Freshness of the Overpass area database that found the relations containing the point, rebuilt on its own schedule and so lagging data_timestamp. Present only when ways or relations were searched and the endpoint ran that check."
    },
    "data_timestamp": {
      "type": "string",
      "description": "OSM data freshness timestamp from the Overpass response. Absent when the endpoint reported no freshness metadata."
    },
    "servingEndpoint": {
      "type": "string",
      "description": "Overpass endpoint that answered, named by origin alone (scheme, host, port), with \"(entry N)\" added when two configured endpoints share an origin. May name a failover mirror, or the endpoint that originally served a cached response. Read with data_timestamp when a result looks slow, sparse, or stale."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_nearby annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
openstreetmap_query_bbox — Find OSM features inside a bounding box or an OSM boundary
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Find OSM features inside an area via the Overpass API, for surveys of everything in a region (openstreetmap_query_nearby covers proximity to a point). Scope with the four corner fields south, west, north, east, or with within and a single OSM boundary ref such as a city relation or a park way, which searches the boundary polygon itself instead of an overcovering box; never both. Filter with amenity, or with tag_key plus an optional tag_value, ANDing up to five more filters; every feature returns with its full OSM tag set (no extratags flag here).

openstreetmap_query_bbox input schema
{
  "type": "object",
  "anyOf": [
    {
      "type": "object",
      "required": [
        "south",
        "west",
        "north",
        "east",
        "amenity"
      ]
    },
    {
      "type": "object",
      "required": [
        "south",
        "west",
        "north",
        "east",
        "tag_key"
      ]
    },
    {
      "type": "object",
      "required": [
        "within",
        "amenity"
      ]
    },
    {
      "type": "object",
      "required": [
        "within",
        "tag_key"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "east": {
      "type": "number",
      "maximum": 180,
      "minimum": -180,
      "description": "Eastern boundary longitude (maximum longitude). A value below west describes an antimeridian crossing rather than an inverted box."
    },
    "west": {
      "type": "number",
      "maximum": 180,
      "minimum": -180,
      "description": "Western boundary longitude (minimum longitude). A west greater than east is valid, not an error: Overpass reads it as an antimeridian-crossing box and returns the union of west..180 and -180..east."
    },
    "limit": {
      "type": "integer",
      "default": 20,
      "maximum": 10000,
      "minimum": 1,
      "description": "Maximum results to return, up to 500 per call; a larger value is served as 500 and limitNotice says so. Applied after the Overpass query — if the area has more features, they are truncated."
    },
    "north": {
      "type": "number",
      "maximum": 90,
      "minimum": -90,
      "description": "Northern boundary latitude (maximum latitude)."
    },
    "south": {
      "type": "number",
      "maximum": 90,
      "minimum": -90,
      "description": "Southern boundary latitude (minimum latitude). One of four corner fields: supply all four, or use within instead."
    },
    "offset": {
      "type": "integer",
      "default": 0,
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Number of matching features to skip before applying limit, for paging through a large result set. The full match set is fetched and cached ~10 minutes keyed by the query, so re-paging at a new offset is deterministic and costs no extra upstream request. Pass the nextOffset value from a prior truncated response."
    },
    "within": {
      "type": "string",
      "pattern": "^[RWrw]\\d+$",
      "description": "OSM boundary to search inside, as one ref: R plus a relation id (\"R237385\", Seattle) or W plus a closed-way id (\"W13800188\", a park), case-insensitive. Take it from osm_type plus osm_id on openstreetmap_search_places, openstreetmap_reverse_geocode, or openstreetmap_lookup_objects. The alternative to the four corner fields, never both. A node ref is rejected: a node is never an area. A ref that maps to no Overpass area returns an empty page whose notice names the cause."
    },
    "amenity": {
      "type": "string",
      "description": "OSM amenity tag value shortcut (e.g. \"cafe\", \"bench\", \"hospital\"). Exactly one primary mode is required: this or tag_key, never both. Many categories live under other keys — tourism (museums, hotels, viewpoints), leisure (parks, pitches, playgrounds), shop (supermarkets), historic (castles, memorials), natural (beaches, peaks); query those with tag_key and tag_value."
    },
    "filters": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Literal OSM tag key. Trimmed and nonblank; must be unique across the primary tag and all filters."
          },
          "match": {
            "enum": [
              "exact",
              "list_item"
            ],
            "type": "string",
            "description": "How value matches: \"exact\" (default) or \"list_item\", one item of a semicolon list. Needs value."
          },
          "value": {
            "type": "string",
            "description": "Literal value to match. Omit for key existence; an explicitly blank value is invalid. Trimmed before matching."
          }
        },
        "description": "One additional equality, list-item, or key-existence filter.",
        "additionalProperties": false
      },
      "maxItems": 5,
      "description": "Up to five additional filters, ANDed with the required primary amenity or tag_key filter in input order. Omitted or [] adds no conditions. Exact values miss semicolon-list values; set match \"list_item\" on the entry. Keys must be unique after trimming; keys and values must not contain Overpass QL metacharacters (\" \\ [ ] ; ( ))."
    },
    "tag_key": {
      "type": "string",
      "description": "Primary OSM tag key (e.g. \"leisure\", \"shop\", \"natural\"); omit tag_value to match any feature carrying the key, or supply it for exact equality. The alternative to amenity, never both. Additional filters are ANDed with this tag."
    },
    "tag_value": {
      "type": "string",
      "description": "Literal value paired with tag_key for exact equality (e.g., \"park\", \"supermarket\"); omit for key existence. Exact equality misses semicolon-list values (cuisine=pizza;italian); tag_value_match \"list_item\" catches them. Explicit empty or whitespace-only values are invalid. Keys and values are trimmed; blank unused fields are ignored in amenity mode."
    },
    "element_types": {
      "type": "array",
      "items": {
        "enum": [
          "node",
          "way",
          "relation"
        ],
        "type": "string"
      },
      "default": [
        "node",
        "way",
        "relation"
      ],
      "minItems": 1,
      "description": "OSM element types to search, at least one. Nodes cover most standalone POIs, ways most buildings and areas, relations large parks, lakes, campuses, and boundaries. Omit the field to search all three; pass [\"node\", \"way\"] to leave relations out. An empty array is rejected because it can only match nothing."
    },
    "tag_value_match": {
      "enum": [
        "exact",
        "list_item"
      ],
      "type": "string",
      "description": "How tag_value matches: \"exact\" (default) compares the whole value; \"list_item\" matches one item of a semicolon list, so \"pizza\" finds cuisine=pizza;italian. Needs tag_value; not available with amenity."
    },
    "timeout_seconds": {
      "type": "integer",
      "default": 25,
      "maximum": 60,
      "minimum": 5,
      "description": "Overpass query timeout in seconds. Increase for large bounding boxes or dense areas."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_bbox output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "elements",
        "attribution",
        "effectiveTag",
        "totalFound",
        "truncated"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "invalid_scope",
                "invalid_bbox",
                "invalid_tag",
                "query_timeout",
                "result_too_large",
                "rate_limited",
                "upstream_error",
                "overpass_gateway_timeout",
                "overpass_unavailable",
                "endpoints_exhausted",
                "endpoints_unavailable",
                "endpoints_rejected",
                "pacer_shed"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `invalid_scope`: The two spatial scopes conflict: within sent alongside a corner field, neither scope sent, or only part of the four-corner set sent. `invalid_bbox`: south exceeds north — the latitude bounds are inverted. `invalid_tag`: Tag modes conflict or are missing, a key or supplied value is blank, keys repeat after trimming, a list_item match has no value, or a filter carries Overpass QL metacharacters. `query_timeout`: The query exceeded timeout_seconds. `result_too_large`: Overpass ran out of memory on this query, or its response passed the server's 64 MiB read ceiling. `rate_limited`: Every configured endpoint refused the query as throttled — HTTP 429, or a throttle document in place of JSON. `upstream_error`: Overpass reported a runtime error that is neither a timeout nor memory exhaustion. `overpass_gateway_timeout`: Overpass answered HTTP 504 or 408 with no busy-dispatcher remark — the query exceeded the endpoint's own time budget, not timeout_seconds. `overpass_unavailable`: Overpass answered an HTTP 5xx other than 501 and 504, a 425, or a 504 or 408 whose remark says its dispatcher is too busy or turned off — the endpoint is down, restarting, shedding load, or not taking the query yet. `endpoints_exhausted`: No endpoint completed within its attempt window, or the total budget ran out first. `endpoints_unavailable`: No configured endpoint would serve the call — connections refused, DNS failures, HTTP refusals such as 401/403/404, throttling, or instance faults, in some mix. `endpoints_rejected`: Every configured endpoint refused the call by HTTP status: a 401, 403, 404, or 501, a redirect, or another non-retried status below 500 other than 400 and 429. `pacer_shed`: The query waited 30 seconds in total for an Overpass slot while other calls held every slot it could take. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "notice": {
      "type": "string",
      "description": "Why this page is empty and what to try: the within ref resolved to no Overpass area, nothing matched (change the scope or tag), or offset ran past the end (retry lower). On any page, also names the key to retry with when amenity names a value OpenStreetMap tags under another key (tourism=museum, not amenity=museum). Absent otherwise."
    },
    "elements": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "osm_type",
          "osm_id",
          "tags"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "description": "Latitude: a node's own, or a way's or relation's bounding-box center."
          },
          "lon": {
            "type": "number",
            "description": "Longitude: a node's own, or a way's or relation's bounding-box center."
          },
          "name": {
            "type": "string",
            "description": "Feature name from OSM tags."
          },
          "tags": {
            "type": "object",
            "description": "All OSM tags for this feature. Values are always strings.",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "osm_id": {
            "type": "number",
            "description": "OSM element ID. Use with osm_type for openstreetmap_lookup_objects."
          },
          "osm_type": {
            "enum": [
              "node",
              "way",
              "relation"
            ],
            "type": "string",
            "description": "OSM element type."
          }
        },
        "description": "A single matching OSM feature.",
        "additionalProperties": false
      },
      "description": "Matching OSM features inside the requested scope — the bounding box, or the within boundary — up to the limit."
    },
    "truncated": {
      "type": "boolean",
      "description": "True if results were cut at the limit. Reduce bbox area, add more specific tags, or page with offset to retrieve the rest."
    },
    "nextOffset": {
      "type": "number",
      "description": "Offset to pass on the next call to retrieve the following page of features. Present only when more features remain beyond this page."
    },
    "totalFound": {
      "type": "number",
      "description": "Total features returned by Overpass before limit truncation."
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution: Data © OpenStreetMap contributors, ODbL 1.0."
    },
    "limitNotice": {
      "type": "string",
      "description": "Present when limit exceeded this tool's maximum: the value requested and the one applied."
    },
    "effectiveTag": {
      "type": "string",
      "description": "The full ordered AND filter chain: key=value for equality, key∋value for a list-item match, key alone for existence (e.g. \"amenity=restaurant, cuisine∋pizza, name\")."
    },
    "effectiveArea": {
      "type": "string",
      "description": "The boundary scope as resolved: the within ref, the boundary it reached (its name, boundary, and admin_level tags, those it carries), and the Overpass area it mapped to. A mistyped ref resolves too, so check the name. Absent when the call used the four corner fields."
    },
    "areasTimestamp": {
      "type": "string",
      "description": "Freshness of the Overpass area database, rebuilt on its own schedule and so lagging data_timestamp — a boundary edited since is scoped against its older polygon. Present only on a within call whose endpoint reported it."
    },
    "data_timestamp": {
      "type": "string",
      "description": "OSM data freshness timestamp from the Overpass response. Absent when the endpoint reported no freshness metadata."
    },
    "servingEndpoint": {
      "type": "string",
      "description": "Overpass endpoint that answered, named by origin alone (scheme, host, port), with \"(entry N)\" added when two configured endpoints share an origin. May name a failover mirror, or the endpoint that originally served a cached response. Read with data_timestamp when a result looks slow, sparse, or stale."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_bbox annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
openstreetmap_query_raw — Execute a raw Overpass QL query
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Run an arbitrary Overpass QL query for anything the convenience tools cannot express: multi-type or union queries, relation membership, historical queries, regex tag matching. The query must include [out:json], e.g. "[out:json][timeout:15];node[\"natural\"=\"peak\"](47.5,-122.5,47.7,-122.2);out body;"; scope to an OSM boundary with rel(<id>);map_to_area->.a; or way(<id>);map_to_area->.a; then (area.a) on each statement (the 2400000000 way-area offset is gone since Overpass 0.7.57; openstreetmap_query_bbox takes the same scope as within, without QL). The response is one page: page with limit and offset, read totalFound and truncated for the whole match, and an element over max_element_bytes arrives with its members, nodes or geometry array withheld whole and withheldNotice saying how to fetch it back. For plain "near X" or "in this area" questions use openstreetmap_query_nearby or openstreetmap_query_bbox.

openstreetmap_query_raw input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "query"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "default": 20,
      "maximum": 10000,
      "minimum": 1,
      "description": "Maximum elements to return, up to 500 per call; a larger value is served as 500 and limitNotice says so. Applied after the Overpass query — if the query matched more, they are truncated."
    },
    "query": {
      "type": "string",
      "description": "Overpass QL query string. Must include [out:json]. The server sets the endpoint and User-Agent; do not include those. With no [maxsize:N] it adds [maxsize:268435456] (256 MiB), which a busy endpoint admits sooner than its 512 MiB default; write your own to change it. Example: \"[out:json][timeout:15];node[\\\"natural\\\"=\\\"peak\\\"](47.5,-122.5,47.7,-122.2);out body;\""
    },
    "offset": {
      "type": "integer",
      "default": 0,
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Elements to skip before applying limit, for paging a large result set. The full match set is cached ~10 minutes keyed by the query, so re-paging at a new offset is deterministic and costs no extra request; a result over 100000 elements is served uncached, so paging that far re-queries and depends on the endpoint returning the same order. Pass the nextOffset from a prior truncated response."
    },
    "timeout_seconds": {
      "type": "integer",
      "default": 30,
      "maximum": 180,
      "minimum": 5,
      "description": "How long Overpass may spend on the query. A [timeout:N] directive in the query string wins over this. The client waits the full value rather than cutting a long query off early, but the endpoint enforces its own budget and may answer HTTP 504 first."
    },
    "max_element_bytes": {
      "type": "integer",
      "default": 20000,
      "maximum": 9007199254740991,
      "minimum": 1000,
      "description": "Serialized-byte budget for one element, in UTF-8 bytes, applied per element after limit and offset — the dimension limit cannot bound, a single relation or geometry-heavy way. An over-budget element keeps every scalar and its tags but has its members, nodes and geometry arrays withheld whole, never truncated to a prefix, and lists each under withheld_keys with its item count and byte size; withheldElements and withheldNotice then give the offset and raised budget that fetch it back whole in one more call. That disclosure is not counted against the budget, so a bounded element runs ~60 bytes per withheld key above it."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_raw output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "elements",
        "total_elements",
        "attribution",
        "effectiveQuery",
        "totalFound",
        "truncated"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "query_error",
                "query_timeout",
                "result_too_large",
                "rate_limited",
                "upstream_error",
                "overpass_gateway_timeout",
                "overpass_unavailable",
                "endpoints_exhausted",
                "endpoints_unavailable",
                "endpoints_rejected",
                "pacer_shed"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `query_error`: Overpass returned HTTP 400 — malformed query syntax. `query_timeout`: The query exceeded its timeout. `result_too_large`: Overpass ran out of memory on this query, or its response passed the server's 64 MiB read ceiling. `rate_limited`: Every configured endpoint refused the query as throttled — HTTP 429, or a throttle document in place of JSON. `upstream_error`: Overpass reported a runtime error that is neither a timeout nor memory exhaustion. `overpass_gateway_timeout`: Overpass answered HTTP 504 or 408 with no busy-dispatcher remark — the query exceeded the endpoint's own time budget, not the [timeout:N] directive. `overpass_unavailable`: Overpass answered an HTTP 5xx other than 501 and 504, a 425, or a 504 or 408 whose remark says its dispatcher is too busy or turned off — the endpoint is down, restarting, shedding load, or not taking the query yet. `endpoints_exhausted`: No endpoint completed within its attempt window, or the total budget ran out first. `endpoints_unavailable`: No configured endpoint would serve the call — connections refused, DNS failures, HTTP refusals such as 401/403/404, throttling, or instance faults, in some mix. `endpoints_rejected`: Every configured endpoint refused the call by HTTP status: a 401, 403, 404, or 501, a redirect, or another non-retried status below 500 other than 400 and 429. `pacer_shed`: The query waited 30 seconds in total for an Overpass slot while other calls held every slot it could take. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "notice": {
      "type": "string",
      "description": "Why this page is empty and what to try: nothing matched (check the query syntax or broaden the filter), or offset ran past the end (retry lower). Absent when results were returned."
    },
    "elements": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Raw Overpass elements for this page, up to the limit. Shape varies by type: nodes carry lat/lon, ways nodes[], relations members[]. An element over max_element_bytes swaps those heavy arrays for withheld_keys, each naming the key, its item count, and its byte size."
    },
    "truncated": {
      "type": "boolean",
      "description": "True if elements were cut at the limit. Narrow the query, or page with offset to retrieve the rest."
    },
    "nextOffset": {
      "type": "number",
      "description": "Offset to pass on the next call to retrieve the following page of elements. Present only when more elements remain beyond this page."
    },
    "totalFound": {
      "type": "number",
      "description": "Total elements returned by Overpass before limit truncation."
    },
    "attribution": {
      "type": "string",
      "description": "Required data attribution: Data © OpenStreetMap contributors, ODbL 1.0."
    },
    "limitNotice": {
      "type": "string",
      "description": "Present when limit exceeded this tool's maximum: the value requested and the one applied."
    },
    "data_timestamp": {
      "type": "string",
      "description": "OSM data freshness timestamp from the Overpass response. Absent when the endpoint reported no freshness metadata."
    },
    "effectiveQuery": {
      "type": "string",
      "description": "The Overpass QL string as sent to the API (after any timeout or maxsize injection)."
    },
    "total_elements": {
      "type": "number",
      "description": "Number of elements returned on this page. See totalFound for the full match count."
    },
    "withheldNotice": {
      "type": "string",
      "description": "How to retrieve the withheld arrays, one element per call. Absent when every element fit."
    },
    "servingEndpoint": {
      "type": "string",
      "description": "Overpass endpoint that answered, named by origin alone (scheme, host, port), with \"(entry N)\" added when two configured endpoints share an origin. May name a failover mirror, or the endpoint that originally served a cached response. Read with data_timestamp when a result looks slow, sparse, or stale."
    },
    "withheldElements": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "type",
          "id",
          "keys",
          "offset",
          "maxElementBytes"
        ],
        "properties": {
          "id": {
            "type": "number",
            "description": "OSM id of the bounded element."
          },
          "keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Keys withheld whole from this element: members, nodes, or geometry."
          },
          "type": {
            "type": "string",
            "description": "OSM element type of the bounded element."
          },
          "offset": {
            "type": "number",
            "description": "Absolute offset of this element in the full match set. Pass it with limit 1 to fetch this element alone."
          },
          "maxElementBytes": {
            "type": "number",
            "description": "Serialized UTF-8 byte size of this element whole, which is the smallest max_element_bytes that returns it. Pass it on the retrieval call."
          }
        },
        "additionalProperties": false
      },
      "description": "Elements on this page that exceeded max_element_bytes, each with the arguments that fetch it back whole. Absent when every element fit."
    }
  },
  "additionalProperties": false
}
openstreetmap_query_raw annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}

Prompts

0 advertised

No prompt definitions available.

Resources

1 advertised

openstreetmap-guide

Advertised by the server during the last successful anonymous check.

Use openstreetmap_* tools for OSM feature surveys, boundary-scoped searches, tag-based discovery, or Overpass QL. Resolve object references and check scope, coverage, and pagination before drawing absence or completeness conclusions.

openstreetmap-guide URI
skill://openstreetmap-guide/SKILL.md

MIME type: text/markdown

io.modelcontextprotocol/skills

Advertised by the server during the last successful anonymous check.

io.modelcontextprotocol/skills URI template
skill://{+path}

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (2 of 2 answered).

  1. 2026-11-07: 2 of 2 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
591 ms
95th percentile latency
591 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 591 ms · metadata changed · 6 tool(s), 1 resource(s), 1 resource template(s), 0 prompt(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http openstreetmap-mcp-server https://openstreetmap.caseyjhand.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add openstreetmap-mcp-server --url https://openstreetmap.caseyjhand.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable openstreetmap-mcp-server in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "url": "https://openstreetmap.caseyjhand.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "type": "streamableHttp",
      "url": "https://openstreetmap.caseyjhand.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start openstreetmap-mcp-server and inspect its tools.
VS Code configuration
{
  "servers": {
    "openstreetmap-mcp-server": {
      "type": "http",
      "url": "https://openstreetmap.caseyjhand.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http openstreetmap-mcp-server https://openstreetmap.caseyjhand.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://openstreetmap.caseyjhand.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "serverUrl": "https://openstreetmap.caseyjhand.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like openstreetmap-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.