01
A question agents guess at
“When's the next train at Park Street?” and “Is there a free dock near Kendall?” have exact answers, published live and for free. A language model doesn't have them, so without a tool it makes something up that sounds right.
bostonmove gives it the tool. It answers five questions, each one as an MCP tool and as a command: the next departures from a stop, the service alerts in effect, the nearest Bluebikes stations with bikes, e-bikes and docks, what's near a place, and how to get from A to B. A place can be a station name, a stop ID, a Bluebikes station, an address or landmark, or a latitude and longitude.
02
Saying where every number came from
A tool an agent quotes has to be clear about what it knows. So every result carries its sources and an `as_of` time, and every number is one of three kinds, labelled as such.
- Live: departures are the MBTA's own predictions, and ride times come from that trip's predicted times at both stops. Bike counts are as of the feed time, about a minute old.
- Scheduled: when a stop has no predictions, late at night or on a route that doesn't publish them, the timetable is used and each time is marked as scheduled.
- Estimated: walking and cycling are straight-line distance times 1.3 for streets, at 5 km/h and 14 km/h, and say so.
- Alerts on a trip are only the ones that matter for it: severe ones on its routes, or any at a stop where it boards, changes or gets off.
03
One change, inside a rate limit
The first version planned only trips on one route, and plenty of trips need a change: from Northeastern to Harvard, no line goes the whole way. So bostonmove looks for a subway or light-rail line near the start and another near the destination that share a station, ranks the pairs by walking plus stops travelled, and prices the best two with live departures. The second train has to leave at least three minutes after the first gets in.
The first version of that hit the MBTA's limit inside a single plan. Without a key, the MBTA allows 20 requests a minute, and pricing two transfers stop by stop used them up. Each leg now costs one request: departures at both of its stops come back together and are paired by trip. A plan costs 4 to 16 requests. When the limit is hit anyway, bostonmove waits for the reset if it's under 20 seconds, and past that it leaves the train options out, says why, and still returns walking and Bluebikes.
Testing it on real trips turned up three smaller problems. When the limit hit during a transfer search, the alerts call failed too, and the direct options were lost with it, so alerts are now best effort. Green Line branches showed up as “E” and “B”, the MBTA's short names, so rail now uses the full name. And a trip that needs two changes, Harvard to the airport, was told no stop was close enough, which wasn't the reason. It now says it plans at most one change and points to the MBTA's own planner.
04
Testing against live data
Live data makes for flaky tests: the next train is different every minute. So a recorder script runs real scenarios against the real APIs once and saves every response, and the offline tests replay them with the clock frozen at the moment they were recorded. So the offline tests run on real MBTA and Bluebikes responses and give the same answer every time.
The data comes with terms. The Bluebikes licence doesn't allow redistributing its feed as a stand-alone dataset, so the recorder keeps only the stations in central Boston that the tests need. OpenStreetMap's geocoder asks for at most one lookup a second and an identifying User-Agent, and bostonmove caches what it looks up for 30 days. A separate live test, run before each release, checks the real APIs still answer in the shape the code expects.
05
Shipping it
CI runs on Python 3.10 to 3.14 on Linux, and on macOS and Windows. Pushing a version tag builds the package and uploads it to PyPI through trusted publishing. A second workflow then lists the release in the official MCP registry, signing in as the repository through GitHub Actions instead of with a login code. Neither step stores a password or token anywhere.
Making the screenshots for this page turned up two bugs. Departure rows didn't line up when one route's name was shorter than another's, and the Bluebikes feed time was printed in UTC while everything else was in Boston time. Version 0.1.1 fixes both, with a test for each that fails on 0.1.0.
| Use it | Command |
|---|---|
| Run once, no install | uvx bostonmove plan "South Station" "Kendall" |
| Next trains | bostonmove next "Park Street" |
| Add to Claude Code | claude mcp add --scope user bostonmove -- uvx bostonmove mcp |