@cyanheads/openstreetmap-mcp-server

v0.2.5 pre-1.0

Geocode, reverse geocode, and run Overpass spatial queries on OpenStreetMap data via MCP. STDIO or Streamable HTTP.

@cyanheads/openstreetmap-mcp-server
claude mcp add --transport http openstreetmap-mcp-server https://openstreetmap.caseyjhand.com/mcp
codex mcp add openstreetmap-mcp-server --url https://openstreetmap.caseyjhand.com/mcp
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "url": "https://openstreetmap.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http openstreetmap-mcp-server https://openstreetmap.caseyjhand.com/mcp
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "command": "bunx",
      "args": [
        "@cyanheads/openstreetmap-mcp-server@latest"
      ]
    }
  }
}
{
  "mcpServers": {
    "openstreetmap-mcp-server": {
      "type": "http",
      "url": "https://openstreetmap.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://openstreetmap.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

6

openstreetmap_geocode

open-world

Convert a place name or address to geographic coordinates and structured place data via Nominatim/OpenStreetMap. Accepts either a free-form query string (e.g., "Space Needle Seattle") or structured address fields (street, city, state, etc.) — the two modes are mutually exclusive. Returns results ordered by Nominatim relevance (importance score). Use countrycodes to restrict results to specific countries. For exhaustive POI lists in an area, use openstreetmap_query_nearby or openstreetmap_query_bbox instead — Nominatim search returns best matches, not all matching objects.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_geocode",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "description": "Free-form search string (e.g., \"Space Needle Seattle\" or \"1600 Pennsylvania Ave NW, Washington DC\"). Cannot be combined with structured address fields.",
      "type": "string"
    },
    "street": {
      "description": "House number and street name (structured query). Use with city/state/country fields. Cannot be combined with query.",
      "type": "string"
    },
    "city": {
      "description": "City name (structured query).",
      "type": "string"
    },
    "county": {
      "description": "County or district (structured query).",
      "type": "string"
    },
    "state": {
      "description": "State or province (structured query).",
      "type": "string"
    },
    "country": {
      "description": "Country name or ISO 3166-1 alpha-2 code (structured query).",
      "type": "string"
    },
    "postalcode": {
      "description": "Postal or ZIP code (structured query).",
      "type": "string"
    },
    "limit": {
      "default": 5,
      "description": "Maximum results to return. Nominatim may return fewer when additional results do not sufficiently match. Max 40.",
      "type": "integer",
      "minimum": 1,
      "maximum": 40
    },
    "countrycodes": {
      "description": "Restrict results to one or more countries. Comma-separated ISO 3166-1 alpha-2 codes (e.g., \"us,ca\"). Preferred over the structured country field when filtering.",
      "type": "string"
    },
    "layer": {
      "description": "Filter by data layer. Comma-separated values: address, poi, railway, natural, manmade. Default: no restriction.",
      "type": "string"
    },
    "featureType": {
      "description": "Restrict results to a geographic feature type. Automatically implies the address layer.",
      "type": "string",
      "enum": [
        "country",
        "state",
        "city",
        "settlement"
      ]
    },
    "extratags": {
      "default": false,
      "description": "Include extra OSM tags when available (e.g., phone, website, opening_hours, wikidata). Increases response size.",
      "type": "boolean"
    },
    "language": {
      "description": "Preferred language for result names (BCP 47 code or Accept-Language string, e.g., \"en\", \"de\", \"fr,en\"). Defaults to local OSM language.",
      "type": "string"
    }
  },
  "required": [
    "limit",
    "extratags"
  ],
  "additionalProperties": false
}
view source ↗

openstreetmap_reverse

open-world

Convert latitude/longitude coordinates to the nearest address or place name via Nominatim/OpenStreetMap. Returns the closest matching OSM object at the given coordinates. Note: Nominatim finds the nearest indexed OSM object — in dense areas this may differ from the address at the exact coordinate. Use zoom=18 for building-level accuracy, lower zoom values for coarser resolution (e.g., zoom=10 for city-level).

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_reverse",
    "arguments": {
      "lat": "<lat>",
      "lon": "<lon>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "lat": {
      "type": "number",
      "minimum": -90,
      "maximum": 90,
      "description": "Latitude in WGS84 decimal degrees."
    },
    "lon": {
      "type": "number",
      "minimum": -180,
      "maximum": 180,
      "description": "Longitude in WGS84 decimal degrees."
    },
    "zoom": {
      "default": 18,
      "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.",
      "type": "integer",
      "minimum": 3,
      "maximum": 18
    },
    "layer": {
      "description": "Restrict which OSM layer is matched. Comma-separated: address, poi, railway, natural, manmade. Default: address,poi.",
      "type": "string"
    },
    "extratags": {
      "default": false,
      "description": "Include extra OSM tags when available (phone, website, opening_hours, wikidata, etc.).",
      "type": "boolean"
    },
    "language": {
      "description": "Preferred language for the result (BCP 47 code or Accept-Language string).",
      "type": "string"
    }
  },
  "required": [
    "lat",
    "lon",
    "zoom",
    "extratags"
  ],
  "additionalProperties": false
}
view source ↗

openstreetmap_lookup

open-world

Fetch address details for one or more known OSM objects by their IDs via Nominatim. Each ID must be prefixed with N (node), W (way), or R (relation), e.g., "N240109189", "W50637691", "R146656". Up to 50 IDs per call. Use when an OSM ID is already known from a prior openstreetmap_query_nearby or openstreetmap_query_bbox result — this is more efficient than a geocoding round trip to get the full Nominatim address record.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_lookup",
    "arguments": {
      "osm_ids": "<osm_ids>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "osm_ids": {
      "anyOf": [
        {
          "type": "string",
          "description": "Single OSM ID string, e.g., \"N240109189\"."
        },
        {
          "minItems": 1,
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Array of OSM ID strings, up to 50."
        }
      ],
      "description": "One or more OSM IDs, each prefixed with N (node), W (way), or R (relation). E.g., \"N240109189\", [\"W50637691\", \"R146656\"]. Up to 50 IDs per call."
    },
    "extratags": {
      "default": false,
      "description": "Include extra OSM tags (phone, website, wikidata, etc.).",
      "type": "boolean"
    },
    "language": {
      "description": "Preferred language for names (BCP 47 code).",
      "type": "string"
    }
  },
  "required": [
    "osm_ids",
    "extratags"
  ],
  "additionalProperties": false
}
view source ↗

openstreetmap_query_nearby

open-world

Find OSM features within a radius around a geographic point via the Overpass API. The primary tool for "what's near X?" spatial queries. Use amenity for common POI types (hospital, pharmacy, restaurant, cafe, school, atm, etc.) or tag_key + tag_value for other OSM categories (leisure=park, shop=supermarket, natural=peak). Exactly one of amenity or tag_key/tag_value must be provided. Results include all element types specified (nodes cover standalone POIs, ways cover buildings and areas), each with its full OSM tag set, sorted nearest-first by distance_meters from the center point. The extratags flag is not needed here — it applies only to the Nominatim-backed geocode/reverse/lookup tools.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_query_nearby",
    "arguments": {
      "lat": "<lat>",
      "lon": "<lon>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "lat": {
      "type": "number",
      "minimum": -90,
      "maximum": 90,
      "description": "Center latitude in WGS84 decimal degrees."
    },
    "lon": {
      "type": "number",
      "minimum": -180,
      "maximum": 180,
      "description": "Center longitude in WGS84 decimal degrees."
    },
    "radius_meters": {
      "default": 1000,
      "description": "Search radius in meters. Max 50,000m (50km). Keep under 5,000m for dense urban POI queries to avoid slow responses.",
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 50000
    },
    "amenity": {
      "description": "OSM amenity tag value (e.g., \"hospital\", \"pharmacy\", \"restaurant\", \"school\", \"atm\"). Shortcut for tag_key=\"amenity\". Cannot be combined with tag_key/tag_value.",
      "type": "string"
    },
    "tag_key": {
      "description": "OSM tag key for non-amenity queries (e.g., \"leisure\", \"shop\", \"highway\", \"natural\"). Use with tag_value. Cannot be combined with amenity.",
      "type": "string"
    },
    "tag_value": {
      "description": "OSM tag value paired with tag_key (e.g., \"park\", \"supermarket\", \"primary\", \"peak\").",
      "type": "string"
    },
    "element_types": {
      "default": [
        "node",
        "way"
      ],
      "description": "OSM element types to search. Ways cover most buildings and areas; nodes cover most standalone POIs. Add \"relation\" for complex structures like large campuses.",
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "node",
          "way",
          "relation"
        ]
      }
    },
    "limit": {
      "default": 20,
      "description": "Maximum results to return. Applied after the Overpass query — if the area has more features, they are truncated.",
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "timeout_seconds": {
      "default": 25,
      "description": "Overpass query timeout in seconds. Increase for large radius or dense areas.",
      "type": "integer",
      "minimum": 5,
      "maximum": 60
    }
  },
  "required": [
    "lat",
    "lon",
    "radius_meters",
    "element_types",
    "limit",
    "timeout_seconds"
  ],
  "additionalProperties": false
}
view source ↗

openstreetmap_query_bbox

open-world

Find OSM features within a rectangular geographic area (bounding box) via the Overpass API. Useful for area surveys where you want everything in a region, not proximity searches. Use amenity for common POI types (hospital, pharmacy, cafe, school, etc.) or tag_key + tag_value for other OSM categories (leisure=park, shop=supermarket, natural=peak). Exactly one of amenity or tag_key/tag_value must be provided. Every feature includes its full OSM tag set; the extratags flag (used by geocode/reverse/lookup) does not apply here. For proximity searches centered on a point, use openstreetmap_query_nearby instead.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_query_bbox",
    "arguments": {
      "south": "<south>",
      "west": "<west>",
      "north": "<north>",
      "east": "<east>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "south": {
      "type": "number",
      "minimum": -90,
      "maximum": 90,
      "description": "Southern boundary latitude (minimum latitude)."
    },
    "west": {
      "type": "number",
      "minimum": -180,
      "maximum": 180,
      "description": "Western boundary longitude (minimum longitude)."
    },
    "north": {
      "type": "number",
      "minimum": -90,
      "maximum": 90,
      "description": "Northern boundary latitude (maximum latitude)."
    },
    "east": {
      "type": "number",
      "minimum": -180,
      "maximum": 180,
      "description": "Eastern boundary longitude (maximum longitude)."
    },
    "amenity": {
      "description": "OSM amenity tag value shortcut (e.g., \"cafe\", \"bench\", \"hospital\"). Cannot be combined with tag_key/tag_value.",
      "type": "string"
    },
    "tag_key": {
      "description": "OSM tag key for non-amenity queries (e.g., \"leisure\", \"shop\", \"natural\"). Use with tag_value. Cannot be combined with amenity.",
      "type": "string"
    },
    "tag_value": {
      "description": "OSM tag value paired with tag_key (e.g., \"park\", \"supermarket\", \"peak\").",
      "type": "string"
    },
    "element_types": {
      "default": [
        "node",
        "way"
      ],
      "description": "OSM element types to search. Ways cover most buildings and areas; nodes cover most standalone POIs. Add \"relation\" for complex structures.",
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "node",
          "way",
          "relation"
        ]
      }
    },
    "limit": {
      "default": 20,
      "description": "Maximum results to return. Applied after the Overpass query — if the area has more features, they are truncated.",
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "timeout_seconds": {
      "default": 25,
      "description": "Overpass query timeout in seconds. Increase for large bounding boxes or dense areas.",
      "type": "integer",
      "minimum": 5,
      "maximum": 60
    }
  },
  "required": [
    "south",
    "west",
    "north",
    "east",
    "element_types",
    "limit",
    "timeout_seconds"
  ],
  "additionalProperties": false
}
view source ↗

openstreetmap_query_raw

open-world

Execute a raw Overpass QL query for advanced spatial queries that the convenience tools do not cover. Use for multi-type queries, union queries, relation membership, historical queries, or any operation requiring full Overpass QL expressiveness. The query must include [out:json]. Example: "[out:json][timeout:15];node[\"natural\"=\"peak\"](47.5,-122.5,47.7,-122.2);out body;" Validate complex queries at overpass-turbo.eu before use. For simple "what's near X?" or "what's in this area?" queries, use openstreetmap_query_nearby or openstreetmap_query_bbox instead.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "openstreetmap_query_raw",
    "arguments": {
      "query": "<query>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Overpass QL query string. Must include [out:json]. The server sets the endpoint and User-Agent; do not include those. Example: \"[out:json][timeout:15];node[\\\"natural\\\"=\\\"peak\\\"](47.5,-122.5,47.7,-122.2);out body;\""
    },
    "timeout_seconds": {
      "default": 30,
      "description": "Query timeout in seconds. The [timeout:N] directive in the query string takes precedence if present. Max 180s.",
      "type": "integer",
      "minimum": 5,
      "maximum": 180
    }
  },
  "required": [
    "query",
    "timeout_seconds"
  ],
  "additionalProperties": false
}
view source ↗