transport-gzm
# transport_gzm
MCP server wrapping the GTFS-RT live transit feed for **GZM (Górnośląsko-Zagłębiowska Metropolia)** — the Silesian Metropolis public transport operator in Poland.
Provides a small, focused set of tools so AI agents can answer questions like:
- "What buses/trams are currently running on route X?"
- "Is vehicle Y delayed? Where is it right now?"
- "What alerts are currently active (delays, disruptions, works)?"
- "When does the next departure of line 6 arrive at stop Z?"
## How it works
GZM publishes a public GTFS-RT feed at
`https://gtfsrt.transportgzm.pl:5443/gtfsrt/gzm` (no auth, CC-BY licensed).
It exposes three logical sub-feeds:
| Path | Content |
|----------------------------|--------------------------------------|
| `/gtfsrt/gzm/all` | full bundle (all three sub-feeds) |
| `/gtfsrt/gzm/vehiclePositions` | live GPS positions of every vehicle |
| `/gtfsrt/gzm/tripUpdates` | arrival-time deltas and stop-skips |
| `/gtfsrt/gzm/alerts` | service alerts (works, delays, etc.) |
This server fetches the protobuf, decodes it, and exposes it as **9 tools** that
return JSON-friendly structures sized for LLM context windows.
### Static GTFS join (why it matters)
The GZM GTFS-RT feed is *minimal*: trip descriptors carry only a `trip_id`
(no `route_id`), and stop-time updates carry only relative delays — no
absolute times and no `stop_id`. To answer "which line is this bus" or
"when does the next tram leave stop X", the server additionally loads:
- **Static GTFS** — daily mirror at
`github.com/TransportGZM-GTFS-mirror/TransportGZM-GTFS-extended-ver`
(same id space as the RT feed; verified). Downloaded once and cached for
`GZM_STATIC_TTL_HOURS` (default 24 h).
- **SDIP live departures** — GZM's own passenger-information system behind
`rj.transportgzm.pl`, which powers the real departure boards. The stop
directory (`/api/v2/stops/data/`, ~7 150 poles) is coordinate-matched to
GTFS stops at load time.
## Quick start
### Run with Docker (recommended)
```bash
docker run -i --rm ghcr.io/wiktor102/transport-gzm:latest
```
Wire it into your MCP client (Claude Desktop, opencode, …):
```json
{
"mcpServers": {
"transport-gzm": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/wiktor102/transport-gzm:latest"]
}
}
}
```
### Run from source
```bash
pip install -e .
transport-gzm
```
…or with `uv`:
```bash
uv run --with mcp[cli] --with gtfs-realtime-bindings --with httpx \
python -m transport_gzm.server
```
## Tools
| Tool | Purpose |
|---------------------|----------------------------------------------------------|
| `health` | Check feed availability + report feed age |
| `feed_summary` | Counts of vehicles, trips, alerts currently in the feed |
| `vehicles` | Search/filter live vehicle positions (by route, stop) |
| `vehicle_by_id` | Get a single vehicle's current position |
| `trip_updates` | Arrival predictions; filter by route, stop, or trip |
| `next_departures` | **Live** departure board per stop (GZM SDIP, minutes) |
| `search_stops` | Find stops by name → `rj_stop_id` / `gtfs_stop_id` |
| `alerts` | Active service alerts (works, delays, detours) |
| `alert_by_id` | One alert by id |
See [`docs/tools.md`](docs/tools.md) for full parameter documentation and
output schemas.
## Configuration
| Env var | Default | Purpose |
|-------------------------|---------------------------------------------|--------------------------------------|
| `GZM_FEED_BASE_URL` | `https://gtfsrt.transportgzm.pl:5443/gtfsrt/gzm` | Override the base URL |
| `GZM_HTTP_TIMEOUT` | `10` | HTTP timeout in seconds |
| `GZM_CACHE_TTL` | `10` | In-process feed cache TTL in seconds |
| `GZM_MAX_VEHICLES` | `200` | Default cap for `vehicles()` |
| `GZM_MAX_TRIPS` | `200` | Default cap for `trip_updates()` |
| `GZM_MAX_ALERTS` | `100` | Default cap for `alerts()` |
| `GZM_USER_AGENT` | `transport-gzm-mcp/0.1 (+https://github.com/Wiktor102/transport-gzm)` | Outgoing UA |
| `GZM_STATIC_DATASET_URL`| GitHub mirror ZIP of the extended GTFS | Static schedule source (must share the RT trip-id space) |
| `GZM_STOP_DIRECTORY_URL`| `https://rj.transportgzm.pl/api/v2/stops/data/` | SDIP stop directory |
| `GZM_SDIP_BASE_URL` | `https://rj.transportgzm.pl/api/-` | Live departures board base URL |
| `GZM_STATIC_TTL_HOURS` | `24` | How long to cache the static GTFS index |
| `GZM_COORD_MATCH_TOLERANCE_M` | `20` | Max distance for rj→GTFS stop matching |
| `LOG_LEVEL` | `INFO` | `DEBUG` / `INFO` / `WARNING` / `ERROR` |
## License
MIT. GTFS-RT data © Górnośląsko-Zagłębiowska Metropolia, CC-BY 4.0.
TDQS
Scored across 8 tools
Each tool has a distinct purpose: alerts vs alert_by_id, vehicles vs vehicle_by_id, next_arrivals for stop-specific predictions, trip_updates for delay info, feed_summary for metadata, and health for system status. No two tools overlap in functionality.
Tools follow a clear pattern: plural names for list operations (alerts, vehicles, trip_updates) and 'by_id' for single items (alert_by_id, vehicle_by_id). feed_summary and health are unique but descriptive. next_arrivals deviates slightly but remains intuitive.
With 8 tools, the server is well-scoped for a transit data API. It covers all necessary operations without redundancy or bloat, making it efficient for agents to navigate.
The tool set covers core real-time transit data: vehicle positions, alerts, trip updates, stop arrivals, and system health. Missing static data like route or stop listings, but for live monitoring, it is sufficiently complete.