Skip to main content

rhein-ruhr-fahrplan-mcp

PyPI Python wheel Downloads MCP GTFS + EFA uv

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_feed aufrufen 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; via FAHRPLAN_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_IDS dazu.
  • Selbst-aktualisierend (mit Agencies): Daemon-Thread prüft alle FAHRPLAN_FEED_CHECK_INTERVAL Sekunden (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 openvrr@vrr.de.

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ßen stopEvents/journeys (nicht departureList/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 GTFS parent_station wird von EFA nicht akzeptiert → DHID ohne Suffix.
  • Bei Ankünften (itdTripDateTimeDepArr=arr) heißen die Zeitfelder trotzdem departureTimePlanned/-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 ignorierter departure_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.
  • calcNumberRequests greift in rapidJSON nicht zuverlässig → plan_connection kann mehr Verbindungen liefern als max_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.txt des 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).

Metadata

Release files for rhein-ruhr-fahrplan-mcp 1.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rhein-ruhr-fahrplan-mcp 1.2.2
File Size Uploaded
rhein_ruhr_fahrplan_mcp-1.2.2.tar.gz 25.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rhein-ruhr-fahrplan-mcp 1.2.2
File Interpreter ABI Platform
rhein_ruhr_fahrplan_mcp-1.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 51.2 kB

Release files / rhein_ruhr_fahrplan_mcp-1.2.2.tar.gz

Download URL rhein_ruhr_fahrplan_mcp-1.2.2.tar.gz
Size 25.9 kB
Tags Source
SHA-256 checksum
How to use checksums
58dcef5a8a325c7e8dc86b9eda6781255ec42bbe91936cec782d6d615131615e
BLAKE2b-256 checksum
How to use checksums
21e450b4726da1fc45cd0889b5d12ee5c1d913c66f7c1ab462e70af30489a73a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / rhein_ruhr_fahrplan_mcp-1.2.2-py3-none-any.whl

Download URL rhein_ruhr_fahrplan_mcp-1.2.2-py3-none-any.whl
Size 25.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e4888296d04404fa0ecb63623c81ec612115f104768d28c96af27920f479c0b
BLAKE2b-256 checksum
How to use checksums
883f64034c86527bc852070f9c50573faeabbd81dfebdb546ebd2c80d3f8b98f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

1.2.3

2 release files

This release

1.2.2 This release

2 release files

1.2.1

2 release files

1.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page