Reach — design doc

Reach answers one question: what can I reach from here within N minutes by walking, bike, transit or car, and what is inside that area? It is a public MCP server for agents plus a map playground.

Coverage: the 12 largest US metros (New York, Los Angeles, Chicago, Dallas–Fort Worth, Houston, Washington, Philadelphia, Atlanta, Miami, Phoenix, Boston, San Francisco Bay Area) and the 8 largest Indian metros (Delhi NCR, Mumbai, Kolkata, Bengaluru, Chennai, Hyderabad, Ahmedabad, Pune). The tool is built for dense cities, where "what's within 20 minutes" really depends on the mode and the transit network.

The primitive: a zone

A zone is an origin, a mode and a time budget, plus the assumptions behind them: departure time and how transit waits are counted. Creating one computes the travel time from the origin to every reachable street point and stores the result under a zone_id. Every other tool reads that stored zone and does no routing.

Tool Does
create_zone Origin + mode + minutes → zone_id, resolved inputs, assumptions, data dates
search_places Plain-language search inside a zone, ranked by travel time, paged
travel_times Minutes from the origin to named places or coordinates
combine_zones Intersect ("we can all reach it") or union; the result is a zone
zone_geometry GeoJSON outline, optionally in time bands, for drawing
list_regions Coverage, limits, data freshness, attribution

Data, built offline

reach-build turns two kinds of public data into compact per-city files:

Graph arrays are memory-mapped files. A city loads on its first request (seconds) and the least-used city is dropped when more than 3 are loaded, so all 20 fit on one 8 GB machine.

Routing, on demand

Why not precompute? Hub tricks like contraction hierarchies speed up single A-to-B routes. A zone needs the time to every point inside it, so the work scales with the answer, and timetables break those tricks anyway. Storing every answer for ~120k origin cells would take tens to hundreds of GB and days of transit compute, to save about a second on the first request. Identical requests already hit the cache.

Local assumptions

Honesty, privacy, limits

Known gaps

Adding a city

Add a Region (bbox, timezone, Geofabrik extracts, default origin, aliases) to regions.py and run reach-build <id>. Transit feeds are discovered automatically; feeds the catalog lists without a bounding box can be added by id. The server picks up every built city on restart.