@cyanheads/openstreetmap-mcp-server
Provides tools for geocoding, reverse geocoding, and spatial queries against OpenStreetMap data, enabling agents to search places, query nearby features, and execute Overpass QL queries.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@cyanheads/openstreetmap-mcp-serverGeocode the address of the White House"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://openstreetmap.caseyjhand.com/mcp
Overview
OpenStreetMap data through Nominatim and the Overpass API. Geocode places and addresses, resolve coordinates to the nearest address, look up known OSM objects, and query features by tag around a point, inside a bounding box, or inside an OSM boundary. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Convert a place name or address to coordinates and structured place data |
| Convert latitude/longitude to the nearest address or place name |
| Fetch address records for known OSM objects by ID |
| Find OSM features within a radius of a point |
| Find OSM features inside a bounding box or an OSM boundary such as a city, park, or region |
| Run a raw Overpass QL query for anything the other tools cannot express |
Related MCP server: Overture Maps MCP
Capability reference
openstreetmap_search_places tool
Free-form
queryor the structured fieldsstreet,city,county,state,country,postalcode, never both; up to 40 results (limit, default 5), narrowed bycountrycodes,layer,featureType, and aviewboxthat biases ranking or, withbounded: true, restricts itResults carry coordinates, structured
address,boundingbox,importance, andosm_type+osm_id;extratagsadds the object's contact and physical-attribute tags; an empty first page fails asno_resultstruncatedis confirmed by a same-call probe pastlimit, and any full page returnsnextExcludeIdsto pass back asexclude_place_ids; an exhausted walk returns zero results with a notice, not an error
openstreetmap_reverse_geocode tool
lat/lonpluszoomfrom 3 (country) to 18 (building, the default);layerdefaults toaddress,poiReturns the nearest indexed object with structured
address,osm_type+osm_id, andboundingbox; coordinates with no OSM data fail asno_coverage
openstreetmap_lookup_objects tool
Up to 50
osm_idsper call, each prefixedN,W, orR(["N240109189"]); a malformed ID fails asinvalid_id_formatReturns address records for the objects found and lists the rest under
not_found
openstreetmap_query_nearby tool
lat/lonwithradius_metersup to 50,000 (default 1,000); one primary tag,amenityortag_keywith an optionaltag_value, plus up to five ANDedfilters; up to 500 features per pageFeatures carry
osm_type+osm_id, coordinates,distance_meters, and the full tag set, sorted nearest-first;effectiveTagechoes the whole filter chain
openstreetmap_query_bbox tool
One scope per call: the corners
south,west,north,east, orwithinand oneR/Wboundary ref (R237385) built from a geocoding result'sosm_type+osm_id; the same tag filters and 500-per-page cap asopenstreetmap_query_nearbyeffectiveAreaechoes howwithinresolved andareasTimestamphow old the Overpass area data is; a ref that maps to no area returns an empty page whose notice names the causeA
westgreater thaneastis a box crossing the antimeridian; onlysouthgreater thannorthfails, asinvalid_bbox
openstreetmap_query_raw tool
Any Overpass QL containing
[out:json], with[timeout:N]injected fromtimeout_seconds(up to 180, default 30) when the query has none; up to 500 elements per pageReturns raw elements whose shape varies by type (ways carry
nodes[], relationsmembers[]), plustotal_elementsand theeffectiveQueryas sent; malformed QL fails asquery_errorwith the parse error Overpass reportedmax_element_bytes(default 20,000, ceiling 10,000,000) withholds an over-budget element'smembers,nodes, orgeometryarrays whole, listed underwithheld_keys;withheldElementsandwithheldNoticegive the offset and budget that fetch it back
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
OpenStreetMap-specific:
Nominatim request starts are spaced 1,050 ms apart to respect its one-request-per-second policy, and
OSM_USER_AGENTidentifies the server to both upstreamsOverpass submissions are capped per endpoint by
OSM_OVERPASS_MAX_CONCURRENCYor an entry's|N, with opt-in failover acrossOSM_OVERPASS_ENDPOINTS(see Overpass endpoint failover)The Overpass tools page with
limitandoffsetand reporttotalFound,truncated, andnextOffset; a match set of up to 100,000 elements is cached for 10 minutes, so re-paging makes no new upstream request (Nominatim responses are cached for 60), and identical queries in flight at once share one submissionUpstream rejections keep their cause: an Overpass 5xx carries its
runtime errorremark, a Nominatim 400 fails asinvalid_parametersnaming the rejected parameterOSM_NOMINATIM_BASE_URLandOSM_OVERPASS_BASE_URLpoint the server at a self-hosted or mirror instance
Agent-friendly output:
Provenance:
effectiveQuery,effectiveTag, andeffectiveAreaecho what was sent, Overpass responses adddata_timestampandservingEndpoint, and every response carries the ODbLattributionCross-tool chaining:
osm_type+osm_idfrom any tool feedopenstreetmap_lookup_objects, and a geocoded relation or way becomes awithinref foropenstreetmap_query_bboxTag semantics stated outright: the Nominatim tools pick objects by name, address, proximity, or ID, never by tag, and
tagSelectionCaveatsays so (on everyopenstreetmap_search_placesresponse, and on the other two whenextratagsis set). Overpass filters take literal text; regex, alternation, and negation go throughopenstreetmap_query_rawCommunity-edited names and tag values are escaped in the Markdown surface so they cannot inject headings or formatting;
structuredContentkeeps the raw text
Getting started
Public Hosted Instance
A public instance is available at https://openstreetmap.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"openstreetmap-mcp-server": {
"type": "streamable-http",
"url": "https://openstreetmap.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"openstreetmap-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/openstreetmap-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"openstreetmap-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/openstreetmap-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with Docker:
{
"mcpServers": {
"openstreetmap-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/openstreetmap-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
No API key. Nominatim and Overpass are public services; for heavy use, point
OSM_NOMINATIM_BASE_URLandOSM_OVERPASS_BASE_URLat your own instances.
Installation
Clone the repository:
git clone https://github.com/cyanheads/openstreetmap-mcp-server.gitNavigate into the directory:
cd openstreetmap-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env to override endpoints or the User-Agent as neededConfiguration
Variable | Description | Default |
| Nominatim base URL. A path prefix such as |
|
| Pins every query to one Overpass endpoint and disables failover, for a private instance. Sized by | unset |
| Comma-separated, ordered Overpass endpoints; more than one enables failover. Append |
|
| Max Overpass queries in flight per endpoint, for the pin and every entry without |
|
| User-Agent sent to Nominatim and Overpass. Required by usage policy. |
|
| Transport: |
|
| HTTP server port. |
|
| HTTP session mode: |
|
| Authentication: |
|
| Log level ( |
|
| Storage backend: |
|
| Enable OpenTelemetry. |
|
See .env.example for the full list of optional overrides. An invalid value for any OSM_* variable stops the server at startup with a message naming the variable.
Overpass endpoint failover
By default every Overpass query goes to one endpoint, the FOSSGIS-operated main instance, so a degraded endpoint fails the call. Listing more in OSM_OVERPASS_ENDPOINTS lets a failure advance to the next entry inside the same call, in list order:
OSM_OVERPASS_ENDPOINTS="https://overpass-api.de/api/interpreter,https://overpass.private.coffee/api/interpreter"An endpoint that throttles, answers 401/403/404/501 or a redirect, refuses the connection or fails DNS, reports an instance fault, or never answers is skipped for the rest of the call. Redirects are never followed. Any other 5xx, a 408, or a 425 stays eligible for a retry.
A host that cannot be connected to, or stays silent through a full attempt window, is also skipped by later calls while it cools down: 30 seconds, doubling with each repeat up to 10 minutes, after which one call tries it again. Any HTTP response from it ends the cooldown. Error messages name a skipped host's last outcome with
(cooling down), and when every endpoint is cooling a call still tries the one cooling longest.When every endpoint has refused, the call fails with their shared error if they all failed the same way (
rate_limitedfor throttling,upstream_errorfor an instance fault,endpoints_rejectedfor an HTTP refusal such as a wrong path or a missing key),endpoints_exhaustedif none answered, orendpoints_unavailableif none could be reached or they failed in a mix of ways, with each endpoint's outcome in the message.Endpoints are reported by origin alone (
https://overpass-api.de) inservingEndpoint, error messages, and logs. Where an endpoint's own error text quotes an entry's whole path, query string, or userinfo verbatim, the server repeats each as…, so a key in the URL path or query reaches a response or a log only if the endpoint echoes it in another form, such as the key alone or re-encoded. Two entries on one origin read as(entry 1),(entry 2)by list position.A failure moves to an entry the call has not tried at once; a return to one it already tried waits out a backoff. One call gets a time budget of at least 120 seconds, widened by the query's
[timeout:N].A listed mirror is failover only, so a longer list never adds queries in flight: calls run on the first entry,
OSM_OVERPASS_MAX_CONCURRENCYat a time. To add capacity, size a mirror with|N—https://mirror.example/api/interpreter|4gives it four slots of its own, and new calls take the first entry with a free slot. Write a|inside a URL as%7C.Calls past the slots wait in line in arrival order, for at most 30 seconds in total; a call still waiting then fails with
pacer_shedand aretryAfterof at least 30 seconds.
Adding an endpoint sends your queries to a third party. Before listing one, check its usage policy on the OSM wiki instance list, confirm it holds global data (a regional instance answers a query outside its extract with an empty HTTP 200), and expect mirrors to lag the main instance; servingEndpoint and data_timestamp on every response show which host answered and how fresh its data was.
Running the server
Local development
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdioRun checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Docker
docker run --rm -p 3010:3010 ghcr.io/cyanheads/openstreetmap-mcp-server:latest
# or build locally
docker build -t openstreetmap-mcp-server .
docker run --rm -p 3010:3010 openstreetmap-mcp-serverThe image serves HTTP at http://localhost:3010/mcp in stateless session mode and logs to /var/log/openstreetmap-mcp-server. OpenTelemetry peer dependencies are installed by default; build with --build-arg OTEL_ENABLED=false to omit them. Runtime telemetry stays off until you pass -e OTEL_ENABLED=true.
Project structure
Directory | Purpose |
| Tool definitions ( |
| Nominatim service: paced API client for search, reverse, and lookup, plus error parsing. |
| Overpass service: query builder, endpoint failover and executor, element normalizer, error parsing. |
| Server-specific environment variable parsing and validation with Zod. |
| Unit and integration tests for tools, services, and security. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor logging,ctx.statefor storageRegister new tools in the
createApp()arrays insrc/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
Map data from OpenStreetMap contributors, available under the Open Database License (ODbL).
This server cannot be deployed
Maintenance
Related MCP Connectors
Nominatim MCP — wraps OpenStreetMap Nominatim geocoding API (free, no auth)
Geo MCP — geographic utilities from free public APIs
OpenStreetMap Overpass MCP — programmatic queries against the OSM database
Geocodio MCP — wraps the Geocodio geocoding API (geocod.io)
Related MCP Servers
- AlicenseAqualityFmaintenanceAn MCP server providing geocoding and place discovery services via Nominatim and OpenStreetMap. It enables users to perform forward and reverse geocoding, extract bounding boxes, and find nearby places or administrative hierarchies.10Apache 2.0
- FlicenseNot gradedqualityFmaintenancePython MCP server for querying Overture Maps building footprint data via address search, building lookups, and nearby building listings.-
- AlicenseBqualityDmaintenanceAn MCP server for forward geocoding via the Nominatim API (OpenStreetMap) with no API key required.19 npmMIT
- AlicenseNot gradedqualityBmaintenanceMCP server for geocoding, reverse geocoding, place/POI search, and distance calculation using OpenStreetMap Nominatim, with no API key required.50 npm1MIT