Open Reach
An MCP server for out-of-home (OOH) site-reachability tooling that provides geospatial catchment scoring and multi-site comparison for billboard/ad-site evaluation using open data.
README
Open Reach
An MCP server for out-of-home (OOH) site-reachability tooling.
Open Reach exposes geospatial reachability and catchment-scoring tools for billboard / ad-site evaluation — isochrone catchments, POI-density scoring, and multi-site comparison — to any MCP client (Claude Desktop, Cursor, etc.), built entirely on open geospatial data and public urban-analytics methodology.
The differentiator is server-side reach scoring and multi-site ranking (
site_reach_score,compare_sites). There are several public OSM-wrapper MCP servers already; none of them do catchment scoring. That gap — plus honest, public methodology — is the point of this project.
The hook (demo)
You (in Claude Desktop):
"I'm scouting 3 candidate billboard sites near KLCC, Bukit Bintang, and
Mont Kiara. Which has the best pedestrian catchment and highest POI
density within a 10-minute walk?"
Claude:
→ geocode("KLCC"), geocode("Bukit Bintang"), geocode("Mont Kiara")
→ isochrone(..., mode="walk", minutes=10) [x3]
→ catchment_pois(..., categories=["cafe","retail","transit"]) [x3]
→ site_reach_score(...) [x3]
→ compare_sites(sites=[...], minutes=10, mode="walk")
← ranked: 1. Bukit Bintang 2. KLCC 3. Mont Kiara (with per-site breakdown)
The ranking arithmetic runs server-side in compare_sites — the model's job
is orchestration and narration, not the math.
How it works
┌─────────────────────┐ MCP (JSON-RPC over stdio)
│ MCP Client │ ──────────────────────────────┐
│ (Claude Desktop / │ │
│ Cursor / Code) │ ◄── tool schemas advertised │
└─────────────────────┘ ▼
┌────────────────────────────────────┐
│ Open Reach MCP Server │
│ (FastMCP process) │
│ │
│ @mcp.tool: geocode │
│ @mcp.tool: reverse_geocode │
│ @mcp.tool: isochrone │
│ @mcp.tool: catchment_pois │
│ @mcp.tool: site_reach_score ★ │
│ @mcp.tool: compare_sites ★ │
│ │
│ TTL cache + per-host rate limiting │
└──────────────────┬───────────────────┘
│ https GET/POST
┌──────────────────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────────────┐ ┌──────────────────────┐ ┌───────────────────┐
│ OSM Nominatim │ │ Overpass API │ │ OSRM │
│ (geocoding) │ │ (POIs by category) │ │ (isochrones) │
└───────────────────┘ └──────────────────────┘ └───────────────────┘
Each scoring tool chains isochrone → POIs/junctions → a pure, unit-tested arithmetic module, then returns a typed result plus an auditable breakdown.
Tools
| Tool | Description | Units |
|---|---|---|
geocode(query) |
Forward geocode a place name. | lat/lon decimal degrees |
reverse_geocode(lat, lon) |
Coordinate → address string. | decimal degrees → string |
isochrone(lat, lon, mode, minutes) |
Reachable-area polygon within a time budget. | minutes; polygon [lon,lat]; area m² |
catchment_pois(lat, lon, minutes, mode, categories) |
POIs in the catchment, grouped by enum category. | counts |
site_reach_score(lat, lon, minutes, mode, weights?) |
Composite reach score [0,1] + breakdown. ★ |
unitless [0,1] |
compare_sites(sites, minutes, mode, weights?) |
Deterministic multi-site ranking. ★ | ranks (1 = best) |
mode ∈ {walk, drive, transit}. categories are enum-constrained — callers
never supply raw Overpass QL; all queries are built server-side.
Quick start
git clone https://github.com/kasturi/open-reach-mcp.git
cd open-reach-mcp
python -m venv .venv && .venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -e ".[dev]"
Run standalone (stdio):
open-reach-mcp # or: python -m open_reach_mcp
Claude Desktop config
Add to claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"open-reach": {
"command": "C:\\path\\to\\open-reach-mcp\\.venv\\Scripts\\open-reach-mcp.exe",
"args": []
}
}
}
Set
USER_AGENT(see.env.example) to a descriptive value — Nominatim usage policy requires it.
Tests
pytest --cov=open_reach_mcp --cov-report=term-missing
Methodology & IP boundary (important)
The reach-scoring formula in site_reach_score is derived only from public
urban-analytics literature, not from any proprietary methodology or real
campaign/device data:
- Gravity-model catchment accessibility (Hansen, 1959; classic spatial
interaction) — each POI contributes
exp(-d / D)wheredis its distance from the site andDa pedestrian decay constant. - Walkability-style density indices — POI density and road-junction density
per km², normalized against public reference benchmarks to
[0, 1]. - Category-mix diversity via normalized Shannon entropy.
This project stands for the engineering pattern (geospatial proxy scoring under rate-limited free APIs, packaged as an MCP server). It is not a claim of parity with any production reach/audience system, and it uses no proprietary formulas or real client data.
Upstream dependencies & limits
All upstream APIs are free-tier and shared/public, so they are rate-limited and occasionally fragile. Open Reach mitigates this with:
- TTL cache (
CACHE_TTL_SECONDS, default 24h) + per-host rate limiting (RATE_LIMIT_MIN_INTERVAL_SECONDS, default 1s — Nominatim's policy ceiling). - A compliant
User-Agentheader (configurable; required by Nominatim). transitmode falls back to the OSRMfootprofile — OSRM has no transit router. For real transit isochrones, self-host a transit router (e.g. RAPTOR/OTP) and pointOSRM_BASE_URLat it. This is a known limitation, stated openly rather than hidden.- For production throughput, self-host Nominatim / Overpass / OSRM (Docker
images exist for all three) and set the
*_BASE_URLenv vars. Turning upstream fragility into a documented self-host path is part of the point.
Configuration
All settings are environment-driven (see .env.example):
| Variable | Default | Purpose |
|---|---|---|
NOMINATIM_BASE_URL |
https://nominatim.openstreetmap.org |
Geocoding upstream |
OVERPASS_BASE_URL |
https://overpass-api.de |
POI upstream |
OSRM_BASE_URL |
https://router.project-osrm.org |
Routing upstream |
USER_AGENT |
open-reach-mcp/0.1.0 (...) |
Required by Nominatim policy |
CACHE_MAXSIZE / CACHE_TTL_SECONDS |
2048 / 86400 |
TTL cache sizing |
RATE_LIMIT_MIN_INTERVAL_SECONDS |
1.0 |
Per-host request spacing |
HTTP_TIMEOUT_SECONDS |
15.0 |
Upstream call timeout |
Publishing
v1 ships stdio transport. Publishing steps (run manually after build):
- PyPI:
python -m build && twine upload dist/*. - Official MCP registry: submit
server.json(verified GitHub ownership) at registry.modelcontextprotocol.io. PyPI alone no longer supports a "published to the registry" claim; the official registry (launched Sept 2025) requires the manifest + verified ownership.
SSE-only transports are deprecated since MCP spec 2025-03-26. A Streamable HTTP transport is the planned v2 stretch (no SSE).
License
MIT — see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.