An MCP server exposing Victoria's public transport (PTV) GTFS timetable data for natural-language querying by LLMs.
Four tools with complete schemas and descriptions. Naming follows verb_noun convention (search_, list_, get_, run_). Descriptions are substantive (194 - 450 chars, within baseline 34 - 392 range). All parameters have types and descriptions. However, several parameters lack enums where appropriate (e.g., mode in list_routes should be an enum, not free-form string). Error handling is present but minimal, no recovery guidance or actionable error messages. Output schemas are documented in code but not formally declared in tool definitions. No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Composition is sound: each tool has one responsibility, and outputs chain naturally (search_stops → get_next_departures). Security is strong: read-only database, SQL injection guards, no secrets in parameters.
Get upcoming scheduled departures for a given stop, across all routes serving it. Note: this reads the static GTFS schedule (not live GPS positions), so results reflect the timetable, not real-time delays.
List public transport routes, optionally filtered by mode.
Run a read-only SQL SELECT query against the GTFS database directly, for questions the other tools don't cover. Only SELECT statements are permitted. Available tables: routes, stops, trips, stop_times, calendar (each has a gtfs_mode column identifying the transport mode), plus two precomputed rollup tables for fast aggregate questions: route_trip_counts (route_id, route_short_name, gtfs_mode, trip_count) and stop_departure_counts (stop_id, stop_name, gtfs_mode, departure_count). Prefer the rollup tables over aggregating stop_times or trips directly — stop_times alone has ~12 million rows, and a raw GROUP BY over it can take several seconds to tens of seconds. Use the ptv://schema resource to see full column details. Queries that run longer than a few seconds are aborted automatically.
Search for public transport stops/stations by name.
mode parameter in list_routes accepts free-form string instead of enum. Should constrain to: metro_train, metro_tram, metro_bus, regional_train, regional_bus, regional_coach, skybus, night_bus. LLMs will hallucinate invalid values.
Error messages in run_sql_query are actionable (e.g., 'Query aborted after exceeding 5s limit...'), but other tools (search_stops, list_routes, get_next_departures) have no explicit error handling or recovery guidance. If a stop is not found, the LLM receives an empty list with no hint to try a different search term.
Output schemas are documented in docstrings but not formally declared in tool definitions. LLMs cannot parse docstring prose to infer return types. Formal schema declarations (e.g., JSON Schema for response objects) are missing.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 58 | 2026-07-28+ | v2 |
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). All four tools are read-only and idempotent, but this is not declared in the tool metadata. Agents cannot infer safety properties without explicit hints.
get_next_departures uses LIKE pattern matching on stop_name, which is fuzzy but may return unexpected results (e.g., 'Southern Cross' matches 'Southern Cross Station' and 'Southern Cross Tram Stop'). No guidance on how to disambiguate or refine results.