Adelaide Metro MCP server
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Adelaide Metro MCP server
An MCP server that lets Claude (or any MCP client) answer Adelaide Metro questions, such as "when's the next tram from City South?", using the official GTFS timetable.
Tech stack: Python 3.10+, the official MCP Python SDK (v2, stdio transport), SQLite, pytest, GitHub Actions.
What it can do
Tool | Example question | What it returns |
| "Find stops on King William St" | Matching stops with the stop number from the sign and the address |
| "Which routes stop at City South?" | Every bus, train and tram route that calls there |
| "When's the next bus from stop D2?" / "Which buses go from Sefton Park to King William St?" | Scheduled departures in the next 2 hours (adjustable), optionally for one route or only trips that go directly to a destination stop, with arrival times |
| "Where does this bus go? Where do I get off for Hindley St?" | Every stop one trip calls at after your stop, in order, with times, marking the last stop and stops where you can't get off |
| "How current is your timetable?" | Timetable version, dates it's valid for, size |
All tools are read-only and validate their inputs. Departures come from the published timetable only. Live delays and service disruptions are planned (see Roadmap).
Example next_departures output (real data, timetable version 1714):
Scheduled departures from Stop D2 King William St - East side (stop 16286, id 6455) - 96 King William St, Adelaide
As of Wed 04 Nov 15:41 (timetable only, no live delays):
- 15:44 Bus T721 to Noarlunga (in 3 min), trip 1174247
- 15:54 Bus 721X to Noarlunga (in 13 min), trip 1173546
- 15:54 Bus T722 to Noarlunga (in 13 min), trip 1174304
- 15:55 Bus 719 to Flinders University (in 14 min), trip 1173415With a destination (to_stop_id), from a real test asking Claude how to get from Sefton Park to Hindley St:
Scheduled direct trips from Stop 17 Main North Rd - East side (stop 12297, id 2393) - 255 Main North Rd, Sefton Park to Stop B1 King William St - East side (stop 13301, id 3414) - 12 King William St, Adelaide
As of Wed 04 Nov 16:38 (timetable only, no live delays):
- 16:40 Bus 222 to City (in 2 min), arrives 17:00 (20 min ride), trip 1178543
- 16:41 Bus 228F to City (in 3 min), arrives 16:58 (17 min ride), trip 1178742
- 16:54 Bus 222 to City (in 16 min), arrives 17:14 (20 min ride), trip 1178540The 228F leaves a minute later but arrives two minutes earlier. Without the destination, Claude could list departures but couldn't tell which buses reach the city stop or when.
When the user only names a street ("Hindley St"), Claude may not know which stop the bus uses. trip_stops shows where a trip goes, so Claude can pick the stop nearest the destination (shortened, real data):
Bus 228F to City (trip 1178729)
Stops after Stop 17 Main North Rd - East side (stop 12297, id 2393) - 255 Main North Rd, Sefton Park, timetable times:
- 17:12 Stop 16 Main North Rd - East side (stop 12349, id 2445) - Sefton Plaza Shopping Centre, Prospect (set down only)
...
- 17:29 Stop B1 King William St - East side (stop 13301, id 3414) - 12 King William St, Adelaide
- 17:31 Stop D2 King William St - East side (stop 16286, id 6455) - 96 King William St, Adelaide
- 17:34 Stop VS3 Victoria Sq - East side (stop 13474, id 3589) - 242 Victoria Square, Adelaide (last stop)Related MCP server: gtfs-pro-mcp
Quick start (Windows, Command Prompt)
git clone https://github.com/Niranjen99/adelaide-metro-mcp.git
cd adelaide-metro-mcp
py -3.12 -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
:: Download the timetable (15 MB) and build data\gtfs.sqlite. Re-run any time: it skips the
:: download if the published version hasn't changed. Add --force to always rebuild.
.venv\Scripts\python.exe -m adelaide_metro_mcp.gtfs_loader
:: Run the tests (no network or downloaded data needed)
.venv\Scripts\python.exe -m pytestOn Linux/macOS, use python3 -m venv .venv and .venv/bin/python instead.
Connect it to Claude
Claude starts the server itself. It doesn't start it in the project folder, so you have to tell the server where the data is with ADELAIDE_METRO_DATA_DIR. Replace C:\path\to\adelaide-metro-mcp with your clone's full path.
Claude Code:
claude mcp add adelaide-metro -e ADELAIDE_METRO_DATA_DIR="C:\path\to\adelaide-metro-mcp\data" -- "C:\path\to\adelaide-metro-mcp\.venv\Scripts\python.exe" -m adelaide_metro_mcpClaude Desktop: add this to %APPDATA%\Claude\claude_desktop_config.json, then restart Claude Desktop:
{
"mcpServers": {
"adelaide-metro": {
"command": "C:\\path\\to\\adelaide-metro-mcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "adelaide_metro_mcp"],
"env": { "ADELAIDE_METRO_DATA_DIR": "C:\\path\\to\\adelaide-metro-mcp\\data" }
}
}
}Performance
Measured with benchmarks\bench_queries.py on the full Adelaide Metro timetable (9,038 stops, 717 routes, 22,208 trips, 812,881 stop times):
.venv\Scripts\python.exe benchmarks\bench_queries.py --rebuildResults on a Windows 11 desktop (AMD64, Python 3.12.10), timetable version 1714, 1,000 runs per query:
Query | Median (ms) | p95 (ms) |
| 2.66 | 2.83 |
| 1.93 | 2.05 |
| 0.69 | 0.72 |
| 0.66 | 0.91 |
| 1.34 | 1.42 |
| 0.68 | 0.74 |
| 1.93 | 2.35 |
Full database build from the 15 MB zip: 7.3 s.
Engineering decisions
Trips that run past midnight. GTFS writes a trip that leaves at 00:50 on Monday's timetable as
24:50:00. Times are stored as seconds since the start of the service day, andnext_departureschecks yesterday's, today's and tomorrow's services. Without that, late-night trips disappear after midnight. The real feed has departures as late as29:40:00.Daylight saving. All times use
Australia/Adelaide(ACST +9:30 / ACDT +10:30). GTFS counts times from "noon minus 12 hours", so on the day the clocks change, a trip at 08:00 still means 08:00 on the clock. Thetzdatapackage is a dependency because Windows has no timezone database for Python'szoneinfo. CI runs on Windows to catch this.Which trips run on a date.
calendar.txtgives weekdays and a date range, andcalendar_dates.txtadds or removes services on specific dates (e.g. public holidays). Both are applied.Only real departures. Stops where you can't board (
pickup_type = 1, 4,378 in the feed) and the last stop of each trip are excluded. Parent stations, which group platforms, are rejected with a message pointing to a platform.A to B as an option, not a new tool.
next_departurestakes an optionalto_stop_idinstead of adding a new tool: fewer tools with clear jobs make it easier for the model to pick the right one. It keeps trips that reach the destination later and let you off there (drop_off_type != 1, 3,607 pickup-only stops in the feed). For loop routes that visit a stop twice (552 trips), it uses the first arrival after you board. If nothing ever goes directly, the tool says so (wrong side of the road, or a transfer is needed), so Claude doesn't tell the user to wait. Direct trips only: this is not a journey planner.A separate tool for "where does this bus go?".
to_stop_idonly checks a destination Claude already knows. In a real test ("Sefton Park to Hindley St"), Claude guessed six city stops one by one and none were on the route: the buses run down King William St to Victoria Sq.trip_stopslists a trip's stops instop_sequenceorder, so one call shows the stop to get off at. It works per trip, not per route, because one route has several stopping patterns (express variants, short runs), and the trip being caught gives the exact stops and times.next_departuresprints each trip id so Claude can pass it on. It uses the existingstop_times(trip_id)index, so no schema change was needed.Schema versioning. The database stores a schema version. If a newer release changes the tables, the loader rebuilds even when the timetable hasn't changed, and the server asks for a rebuild instead of failing on a missing column.
Fast reads. The 15 MB zip becomes a 55 MB SQLite database with an index on
(stop_id, departure_secs), so a departures lookup is a short index range scan.Safe updates. The loader skips the download if
version.txtis unchanged. It builds the database in a temporary file and swaps it in atomically, so a failed download never leaves a half-built database.Read-only and validated. The server opens SQLite in read-only mode, uses parameterised SQL only, and validates every input. Expected errors (unknown stop, bad input, missing data) become clear messages for Claude; unexpected errors are not exposed.
Thin protocol layer.
server.pyonly formats text; queries live indb.pyand are tested with plain pytest. The current time is passed in as a parameter, so time-dependent tests are deterministic.stdio transport. stdout carries the MCP protocol, so the server never prints. Logs go to stderr.
Project layout
adelaide_metro_mcp/
gtfs_loader.py download the GTFS zip and build the SQLite database
db.py read-only queries and input validation
server.py MCP tool definitions (thin formatting layer)
benchmarks/
bench_queries.py query timings on the real database
tests/
fixtures/mini_gtfs/ small made-up GTFS feed with the real column layoutRoadmap
Journeys with a transfer, and "stops near here" using the stop coordinates (so Claude can pick the closest stop to a street).
Live delays in
next_departuresfrom the GTFS-Realtime trip updates feed (cached for its 60 s refresh interval).service_disruptionstool from the GTFS-Realtime service alerts feed.
Data and attribution
Timetable data: Adelaide Metro – Department for Infrastructure and Transport, South Australia, from the Adelaide Metro GTFS feeds. The data isn't included in this repository; the loader downloads it.
This is an independent project and is not affiliated with Adelaide Metro.
License
MIT, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only public transit departures, stop search, and city coverage for bus and train users.
Transitland MCP — global GTFS aggregator
NJ Transit rail, bus and PATH schedules via MCP (scheduled, plus live PATH)
Read-only MCP tools for AI agent discovery, structured resources, and NIULAI information.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides tools to search stops, get next departures, and retrieve service alerts from Metlink's real-time public transport data, enabling natural language queries about Wellington transit.MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that provides tools for querying live transit data (stops, departures, routes, vehicles, alerts) from any WP GTFS Pro site, enabling AI assistants to answer rider questions.14 npmGPL 2.0
- AlicenseNot gradedqualityDmaintenanceEnables natural language queries for Porto Alegre's public transport system, including route planning, stop search, and route information via Claude Desktop or any MCP-compatible client.MIT
- AlicenseBqualityDmaintenanceMCP server that integrates with the transport12 API to provide tools for searching stops, routes, arrivals, and vehicle forecasts, enabling natural language interaction with public transport data.178 npmMIT