About Rhein-Ruhr Fahrplan MCP
Rhein-Ruhr Fahrplan MCP is an MCP server for public transit data in the Rhein-Ruhr region, using VRR data. It provides live departures, arrivals, trips, and disruption information by default, with GTFS schedules available when configured.
Key features
- 01Query live departures and arrivals
- 02Plan journeys across the network
- 03Check disruptions, construction, and diversions
- 04Load GTFS schedules when configured
- 05Find stops and inspect their schedules
Use cases
- 01Check live times at a stop
- 02Plan a journey between two locations
- 03Review route disruptions and construction
MCP support
It exposes VRR regional transit data to MCP clients through tools. It supports stdio and Streamable HTTP, and adds scheduled GTFS data when configured.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
uvx rhein-ruhr-fahrplan-mcpFAHRPLAN_AGENCY_IDSComma-separated GTFS agency IDs to enable the schedule database (e.g. 'nia-25'). Without it the server runs live-only; schedule tools return a configuration hint. An unfiltered import would create a ~500 MB database and is intentionally not the default.FAHRPLAN_OPERATOROptional: filter live departures/arrivals by operator code (e.g. 'NIAG'). Empty = all operators.FAHRPLAN_DEFAULT_STARTOptional: default start stop (DHID like 'de:05154:35739', EFA ID or unique name), available via the get_default_start tool.Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.
Tools
9Saved tool list · updated 2w ago
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | |
| query | string | Yes |
Details
From the project READMErhein-ruhr-fahrplan-mcp
MCP-Server für ÖPNV-Fahrplandaten im Rhein-Ruhr-Gebiet (Datenbasis: Open Data des Verkehrsverbunds Rhein-Ruhr). Auf PyPI: https://pypi.org/project/rhein-ruhr-fahrplan-mcp/
Zwei Datenpfeiler, beide keyless:
| Pfeiler | Quelle | Inhalt |
|---|---|---|
| Sollfahrplan (optional) | VRR-GTFS (Open Data ÖPNV, CKAN-API) → SQLite | Nur die konfigurierten Verkehrsunternehmen (FAHRPLAN_AGENCY_IDS); z. B. NIAG: 123 Routen, 6.975 Fahrten, 3.869 Haltestellen |
| Echtzeit (immer aktiv) | VRR-EFA efa.vrr.de | Live-Abfahrten/-Ankünfte mit Prognose, Verspätungen, Steige, Störungshinweise – im gesamten Verbundgebiet |
Default = Live-Only: Ohne Konfiguration startet der Server sofort und liefert alle Live-Tools über die EFA-Schnittstelle. GTFS-Tools (Sollplan, Haltestellen-Index) melden dann einen klaren Hinweis mit Einrichtungsanleitung – bewusst so, weil ein ungefilterter Import die komplette Datenbank (~500 MB, ~7 Mio. Stop-Times) anlegen würde.
Tools (9)
| Tool | Quelle | Zweck |
|---|---|---|
next_departures | EFA (live) | Live-Abfahrten mit Echtzeitprognose |
next_arrivals | EFA (live) | Live-Ankünfte mit Echtzeitprognose |
plan_connection | EFA (live) | Verbindungen von A nach B im Gesamtnetz |
line_disruptions | EFA (live) | Aktuelle Störungen/Baustellen/Umleitungen |
find_stop | GTFS → EFA-Fallback | Haltestelle finden (DHID auflösen) |
stop_schedule | GTFS | Sollfahrplan (auch morgen/übermorgen) |
sync_status | GTFS | Feed-Status: Version, Gültigkeit, Zeilenzahlen |
update_feed | GTFS | Feed aktualisieren (nur bei neuer Ressource – idempotent) |
get_default_start | GTFS → EFA | Konfigurierte Standard-Start-Haltestelle (FAHRPLAN_DEFAULT_START) auflösen |
Schnellstart
Variante A: uvx (stdio, ohne Docker)
uvx rhein-ruhr-fahrplan-mcp
Oder dauerhaft installiert:
uv tool install rhein-ruhr-fahrplan-mcp
rhein-ruhr-fahrplan-mcp
- Startet sofort (Live-Only). Für den GTFS-Sollplan einmalig die Umgebungsvariablen setzen (siehe unten) und
update_feedaufrufen bzw. Server neu starten – der Import läuft dann automatisch im Hintergrund (~1–2 min, 39 MB Download). - Datenbank-Pfad fest:
~/.local/share/rhein-ruhr-fahrplan-mcp/fahrplan.db(XDG, cwd-unabhängig; viaFAHRPLAN_DB_PATHüberschreibbar).
mcphub/Client-Registrierung (stdio-Typ):
{
"fahrplan-mcp": {
"type": "stdio",
"command": "uvx",
"args": ["rhein-ruhr-fahrplan-mcp"],
"env": {"FAHRPLAN_AGENCY_IDS": "nia-25"}
}
}
(env optional – ohne Agencies reiner Live-Only-Betrieb.)
Variante B: Docker (Streamable HTTP)
docker compose up -d --build
Im Hub als Streamable-HTTP-Server registrieren (Netz-Anbindung via compose.override.yaml, siehe Deployment):
http://fahrplan-mcp:8080/mcp
Konfiguration: Verkehrsunternehmen (FAHRPLAN_AGENCY_IDS)
Der GTFS-Import filtert auf die agency_ids des VRR-Feeds. Setze eine oder mehrere:
# z. B. nur NIAG (Kreise Kleve & Wesel):
export FAHRPLAN_AGENCY_IDS=nia-25
# oder mehrere Unternehmen:
export FAHRPLAN_AGENCY_IDS=nia-25,swk-02,dvg-20
in compose.yaml:
environment:
- FAHRPLAN_AGENCY_IDS=nia-25
# - FAHRPLAN_OPERATOR=NIAG # optional: Live-Abfahrten auch filtern
Ohne FAHRPLAN_AGENCY_IDS:
- Live-Tools (next_departures, next_arrivals, plan_connection, line_disruptions) funktionieren normal (ganzes Gebiet, alle Unternehmen)
- GTFS-Tools (find_stop mit Index, stop_schedule, sync_status) und der Auto-Import antworten mit einem Konfigurationshinweis statt zu importieren
Verkehrsunternehmen (Auswahl)
Die agency_ids stammen aus dem VRR-GTFS-Feed (agency.txt, Stand August 2026). Häufige Unternehmen:
| agency_id | Unternehmen | Gebiet/Angebot |
|---|---|---|
nia-25 | Niederrheinische Verkehrsbetriebe | Bus, Kreise Kleve & Wesel |
dvg-20 | Duisburger Verkehrsgesellschaft | Bus/Tram Duisburg |
swk-02 | SWK MOBIL | Krefeld |
new-80 | NEW mobil und aktiv M'gladbach | Mönchengladbach |
vvg-65 | NEW mobil und aktiv Viersen | Viersen |
btm-70, rbg-70 | Rheinbahn | Düsseldorf |
bgs-00, bgs-30–bgs-34 | BOGESTRA | Bochum/Gelsenkirchen |
btm-13, eva-10–eva-12 | Ruhrbahn | Essen/Mülheim |
dsw-36–dsw-39 | DSW21 | Dortmund |
sto-15, btm-15, eva-15 | STOAG | Oberhausen |
wsw-16, wsw-66 | WSW mobil | Wuppertal |
hst-00, hst-50 | Hagener Straßenbahn | Hagen |
sws-18 | Stadtwerke Solingen | Solingen |
swr-16 | Stadtwerke Remscheid | Remscheid |
ver-45 | VER | Ennepe-Ruhr-Kreis |
ves-40 | Vestische | Recklinghausen/Marl |
bvr-88 | BVR Busverkehr Rheinland | Rheinland-Bus |
bsm-76 | Bahnen der Stadt Monheim | Monheim |
hcr-35 | Straßenbahn Herne-Castrop | Herne/Castrop |
swn-60 | Stadtwerke Neuss | Neuss |
ddb-8003 | DB Regio NRW | Regionalzug NRW |
ddb-NX | National Express | Regionalzug Ruhrgebiet |
ddb-RR | RheinRuhrBahn | Regionalzug |
ddb-R2 | eurobahn | Regionalzug |
ddb-W3 | WestfalenBahn | Regionalzug |
ddb-N2 | NordWestBahn | Regionalzug |
ddb-M2 | REGIOBAHN | Regionalzug |
Rail-Freight-/Charter-Einträge im Feed (Train Charter Services, Train Rental International, Ulmer Eisenbahnfreunde u. a.) sind für den Personenverkehr nicht relevant.
Komplette Liste: agency.txt aus dem aktuellen Feed (Download via CKAN-API, siehe unten) – alle 58 IDs mit Namen. Ein Blick per Python:
python3 -c "import csv; [print(r['agency_id'], '|', r['agency_name'])
for r in csv.DictReader(open('agency.txt'))]"
Architektur
┌─────────────────────── Server (stdio | http) ─────────────────────────┐
│ │
│ FastMCP │
│ ├── Live: EFA-Client (urllib, keyless) ← immer aktiv │
│ │ efa.vrr.de/standard/XML_DM_REQUEST (rapidJSON) │
│ │ XML_TRIP_REQUEST2, XML_STOPFINDER_REQUEST │
│ └── Soll: GTFS-SQLite (nur mit FAHRPLAN_AGENCY_IDS) ← Auto-Update │
│ CKAN-API (alle 6 h) → bei Neuheit: atomarer Import │
└────────────────────────────────────────────────────────────────────────┘
Eigenschaften
- Live-Only-Default: Ohne Konfiguration voll nutzbar (Live-Tools); GTFS-Teil schaltet sich mit
FAHRPLAN_AGENCY_IDSdazu. - Selbst-aktualisierend (mit Agencies): Daemon-Thread prüft alle
FAHRPLAN_FEED_CHECK_INTERVALSekunden (Default 6 h) die CKAN-API; Import nur bei neuer Ressource – atomar per.tmp+os.replace(). Initial- und Auto-Import laufen über dasselbe Lock (kein Doppel-Import). - Operator-Filter für Live-Daten:
FAHRPLAN_OPERATOR(z. B.NIAG) filtert next_departures/next_arrivals; plan_connection sortiert passend bevorzugt. Leer = alle Unternehmen. - Keine API-Keys, keine Accounts. VRR-Open-Data-Nutzungsbedingungen: Test-/Hobby-/Entwicklungsnutzung erlaubt; bei dauerhafter öffentlicher Nutzung Mail an [email protected].
Deployment (Docker)
Der HTTP-Modus lauscht nur im Container-Netz auf :8080/mcp (kein Host-Port). Für die Anbindung an einen MCP-Hub in einem bestehenden Docker-Netz eine lokale Datei compose.override.yaml neben compose.yaml anlegen:
services:
fahrplan-mcp:
networks:
- mein-hub-netz # Name des Netzes, in dem der Hub läuft
networks:
mein-hub-netz:
external: true
docker compose up -d merged das Override automatisch. Die Override-Datei ist umgebungsspezifisch und gehört nicht ins Repository (bereits in .gitignore).
Konfiguration (Env)
| Variable | Default | Bedeutung |
|---|---|---|
FAHRPLAN_AGENCY_IDS | (leer) | Kommaseparierte GTFS-Agency-IDs für den Sollplan-Import. Leer = Live-Only; GTFS-Tools antworten mit Konfigurationshinweis |
FAHRPLAN_DEFAULT_START | (leer) | Standard-Start-Haltestelle (DHID, EFA-ID oder eindeutiger Name – passende IDs liefert find_stop); abrufbar via get_default_start |
FAHRPLAN_OPERATOR | (leer) | Live-Operator-Filter (z. B. NIAG); leer = alle Unternehmen |
FAHRPLAN_DB_PATH | ~/.local/share/rhein-ruhr-fahrplan-mcp/fahrplan.db | SQLite-DB-Pfad (Dockerfile: /data/fahrplan.db) |
FAHRPLAN_MCP_TRANSPORT | stdio | stdio oder http (Dockerfile setzt http) |
FAHRPLAN_MCP_HOST / FAHRPLAN_MCP_PORT | 0.0.0.0:8080 (Dockerfile) | nur http-Modus |
FAHRPLAN_FEED_CHECK_INTERVAL | 21600 (6 h) | CKAN-Prüfung; 0 deaktiviert Auto-Update |
Verifizierte EFA-Eigenheiten (Pitfalls)
outputFormat=rapidJSON: Response-Keys heißenstopEvents/journeys(nichtdepartureList/trips).- Freie Haltestellennamen können mehrdeutig sein → Antwort mit
locations[]-Vorschlägen und leeren Events. Deshalb: IMMER per DHID/ID auflösen. …_Parent-Suffix aus GTFSparent_stationwird von EFA nicht akzeptiert → DHID ohne Suffix.- Bei Ankünften (
itdTripDateTimeDepArr=arr) heißen die Zeitfelder trotzdemdepartureTimePlanned/-Estimated. - Der EFA liefert alle Zeiten mit
Z-Suffix (UTC) – der Client konvertiert sie nach Europe/Berlin, bevor er sie ausgibt (früher zeigten die Tools versehentlich UTC, was wie ein ignorierterdeparture_time-Parameter aussah; gefixt in v1.2.1). - Verspätungen werden als UTC-Differenz berechnet – zeitzonenunabhängig korrekt.
- MS_REQUEST (Meldungs-Endpoint) liefert HTTP 400 → Störungen aus den eingebetteten
infos[]der DM-Antworten extrahieren. calcNumberRequestsgreift in rapidJSON nicht zuverlässig →plan_connectionkann mehr Verbindungen liefern alsmax_results(Ergebnisse korrekt, nur Begrenzung weich).
GTFS-Feed-Details
- CKAN-Dataset:
496eea5d-d6ef-4dc2-aeb0-d15c4fbf3178(„Soll-Fahrplandaten VRR“), API:https://opendata.ruhr/api/3/action/package_show?id=… - Ressourcen-UUID wechselt monatlich → Import ermittelt sie immer frisch per CKAN-API.
- Filter:
agency_id IN (FAHRPLAN_AGENCY_IDS); transitiv referenzierende Zeilen bleiben erhalten. stop_times.txtdes Gesamtfeeds (~7 Mio. Zeilen) wird gestreamt (erste Listen-Version wurde OOM-killed).- Ungefilterter Gesamtfeed wäre ~500 MB Datenbank – deshalb bewusst kein Default-Import.
Entwicklung
git clone … && cd rhein-ruhr-fahrplan-mcp
uv venv && source .venv/bin/activate
uv pip install -e .
rhein-ruhr-fahrplan-mcp # stdio starten (Live-Only)
FAHRPLAN_AGENCY_IDS=nia-25 rhein-ruhr-fahrplan-mcp # mit GTFS-Sollplan
python3 tests/stdio_e2e.py $(command -v rhein-ruhr-fahrplan-mcp) # stdio-E2E
FAHRPLAN_AGENCY_IDS=nia-25 python3 tests/stdio_fullcycle.py $(command -v rhein-ruhr-fahrplan-mcp) # inkl. Autoimport
Releases: Version in pyproject.toml hochziehen → Commit → git tag vX.Y.Z && git push origin vX.Y.Z – die GitHub-Action published automatisch nach PyPI (Trusted Publishing).
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.