feat: add bus sign MCP #7
Labels
No labels
needs/docs
needs/research
needs/upstream
type/bug
type/external
type/feature
type/refactor
urgency
high
urgency
immediate
urgency
low
urgency
medium
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
ScottyLabs/mcp-server!7
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/bus-sign"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description
Adds a bus service to the composed MCP server using live predictions from ScottyLabs Bus Sign.
The service supports current arrival lookups for the two configured Forbes Avenue and Morewood Avenue stops, including route and place filtering, human-readable stop information, and CMU PRT rider guidance. Closes #8
Changes
bus_MCP tools:bus_get_bus_predictionsbus_get_stop_predictionsbus_get_next_busbus_search_bus_predictionsbus_get_cmu_prt_rider_guidehttps://bus-sign.scottylabs.orgby default, with support for overriding the base URL throughBUS_SIGN_API_URL.BusBackendError.secondsand add ceiling-roundedminutesfor human-readable responses.America/New_Yorkretrieval timestamps without treating the MCP retrieval time as the upstream PRT generation time.4407: inbound near Tepper Quad toward Downtown/Oakland7117: outbound near the Cohon University Centerplacefilter that resolves configured neighborhoods first and otherwise searches live route destinations.61D,61d,61 d, and61-D.Squirrel Hillfrom outbound stop7117into separate destination areas: Forbes & Murray via61A/61B/61C/61D, and Wilkins & Murray via67/69.67serves Wilkins & Murray rather than Forbes & Murray.httpxand Pydantic as direct dependencies.Example questions supported
Running locally
From the
mcp-serverrepository root, install the locked dependencies and start the composed MCP server:The server runs at
http://127.0.0.1:5050, with the MCP endpoint athttp://127.0.0.1:5050/mcp. By default, the bus service fetches predictions fromhttps://bus-sign.scottylabs.org.In a second terminal, call the next-bus tool through MCP Inspector:
To use a locally running bus-sign backend instead, set
BUS_SIGN_API_URLwhen starting the MCP server:Testing
Result:
12 passed.The tests cover:
placeScope and limitations
mcp-server. Agent-side tool selection and routing will be implemented in a follow-up PR after the MCP changes are merged.Kennel Deployments
Torn down at 2026-09-20 01:01:03 UTC.
Looks good overall, just a few small nitpicks/suggestions
@ -0,0 +186,4 @@)fetched = await fetch_predictions()if normalized_neighborhood and neighborhood_rule is None:There's a small chance that the model puts "waterfront" in neighborhood instead of destination. In this case, the tool says "not in the curated bus waypoint directory" and stops, even though a 61D to WATERFRONT is on the board and listed as available in the same response. It might be better if an unknown neighborhood falls through and is tried as a destination before giving up.
Replaced separate destination and neighborhood arguments with one place argument. It checks configured neighborhoods first, then searches live destinations. For example, place="waterfront" finds the live 61D prediction.
@ -0,0 +244,4 @@if match is None:requested = normalized_route or normalized_destination or "bus"entry["message"] = (f"No current {requested} prediction is available in this direction."Currently, if someone asks for "route 67 to Squirrel Hill," the result for stop 7117 (outbound) says, "No current 67 prediction is available in this direction." But Route 67 is actually predicted at 7117 outbound, and the same response lists it under available. In this case, it was filtered out because 67 isn't on the Squirrel Hill route list, which is a different reason than it not being available. So maybe the message for it can be something like "67 isn't on the Squirrel Hill route list (61A-D)," instead of implying that no 67 is coming.
Addressed by distinguishing destination areas. Routes 61A–61D serve Forbes & Murray, while 67/69 serve Wilkins & Murray. Route-specific responses include the served area and alternatives; unqualified Squirrel Hill queries return one result per area. A future feature could be to generate this index from PRT static GTFS and Pittsburgh neighborhood GIS data instead of maintaining route mappings manually.
@ -0,0 +49,4 @@COVERAGE_NOTE = ("Coverage is currently limited to predictions observed from the bus-sign backend ""for routes 28X, 58, 61A, 61B, 61C, 61D, and 67 across two configured "The backend doesn't filter by route and instead just returns whatever PRT predicts at stops 4407 and 7117, so this route list may not always be accurate. For example, route 69 came through today that isn't listed, and since every response carries this note, the model would say 69 isn't covered. Maybe we can just describe the two stops? Or maybe you can look into it, and there might be a better option
Updated the coverage note to describe the two configured stops instead of a fixed route list. Available routes are now derived dynamically from each backend response.
Thanks! The changes address all three concerns, and everything looks good to me. Approving.