uestra-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@uestra-mcpWhen is the next departure from Kröpcke and is it delayed?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
uestra-mcp
MCP-Server (stdio) für Fahrplandaten von ÜSTRA und GVH in der Region Hannover. Er stellt drei Tools bereit: Haltestellensuche, Live-Abfahrten mit Echtzeit und Verbindungssuche.
Repository: https://github.com/Schneggox/uestra-mcp
Datenquelle
Die Daten kommen von der EFA-Schnittstelle der Region Hannover (https://www.efa.de/efa, Betreiber HannIT/Region Hannover, Software MENTZ EFA). Abgefragt wird outputFormat=rapidJSON, ohne API-Key und ohne Anmeldung. Es wird nichts gescrapt.
Die Schnittstelle ist offen erreichbar, aber nicht offiziell dokumentiert. Parameter, Fehlercodes und Antwortformate können sich ohne Ankündigung ändern. Das Verhalten in diesem Server ist gegen die Live-Schnittstelle gemessen, nicht gegen eine Spezifikation.
Related MCP server: RMV MCP Server
Voraussetzungen
Node.js 20 oder neuer
Internetzugang zu
www.efa.de
Setup
git clone https://github.com/Schneggox/uestra-mcp.git
cd uestra-mcp
npm install
npm run buildnpm run build schreibt nach dist/ (nicht eingecheckt). Der Server startet mit npm start bzw. node dist/index.js; im Normalbetrieb startet ihn der MCP-Client selbst.
Smoke-Test
npm run smokeDer Smoke-Test ruft ohne MCP-Client die Tool-Logik gegen die Live-EFA auf: Suche nach "Kröpcke", Abfahrten an de:03241:11 (Kröpcke) und eine Verbindung von de:03241:11 nach de:03241:31 (Hauptbahnhof). Er gibt die Ergebnisse als Text aus und endet mit Exit-Code 1, wenn eine Antwort leer ist oder ein Aufruf fehlschlägt.
Einbindung in Claude Code
claude mcp add -s user uestra -- node <pfad-zum-repo>/dist/index.jsBeispiel unter Windows (Vorwärts-Slashes):
claude mcp add -s user uestra -- node C:/Users/sk8er/ClaudeWorkspace/uestra-mcp/dist/index.jsOhne -s user gilt der Standard-Scope local: der Server ist dann nur in dem Projektverzeichnis verfügbar, in dem der Befehl ausgeführt wurde. Kontrolle:
claude mcp listDie Zeile zum Server muss so aussehen:
uestra: node C:/Users/sk8er/ClaudeWorkspace/uestra-mcp/dist/index.js - ✔ ConnectedEntfernen: claude mcp remove uestra -s user.
Andere MCP-Clients
Für Clients mit mcpServers-Konfiguration (z. B. Claude Desktop, claude_desktop_config.json):
{
"mcpServers": {
"uestra": {
"command": "node",
"args": ["<pfad-zum-repo>/dist/index.js"]
}
}
}Unter Windows den Pfad mit Vorwärts-Slashes oder doppelten Backslashes schreiben.
Tools
Jedes Tool liefert einen Text-Block (lesbar, Zeiten in Berliner Zeit) und zusätzlich structuredContent (maschinenlesbar). Zeitstempel im structuredContent sind ISO 8601 in UTC (...Z), nicht Berliner Zeit. Bei Fehlern kommt isError: true mit dem Text Fehler: <Meldung>.
Eine Haltestelle kann überall als Name oder als globale ID im Format de:03241:11 angegeben werden. Namen werden über die Haltestellensuche automatisch zur ID des besten Treffers aufgelöst (Treffer aus der Region Hannover vor allen anderen) und führen ohne Treffer zu Fehler: Keine Haltestelle zu "…" gefunden; eine unbekannte de:-ID bei departures zu Fehler: Haltestelle <ID> unbekannt; wo es auf Genauigkeit ankommt, vorher mit stop_search die ID bestimmen.
stop_search
Sucht Haltestellen nach Name. Liefert Name, Stop-ID, Ort, Typ und Koordinaten. Zurückgegeben werden nur Treffer vom Typ stop, sortiert nach Region (IDs de:03241:… zuerst), dann EFA-Bestwert. Ohne Treffer antwortet das Tool mit Keine Haltestellen gefunden.
Parameter | Typ | Default | Beschreibung |
| string, min. 1 Zeichen | (Pflicht) | Haltestellenname oder Teil davon |
| Ganzzahl, 1 bis 25 | 8 | Maximale Trefferzahl |
Aufruf: { "query": "Kröpcke", "limit": 2 }
1. Hannover, Kröpcke [de:03241:11] Ort: Hannover Typ: stop Koord (lat, lon): 52.37448, 9.73858
2. Hannover, Kröpcke/Theaterstraße [de:03241:1] Ort: Hannover Typ: stop Koord (lat, lon): 52.37381, 9.74221structuredContent: { "stops": [{ "id", "name", "locality", "type", "lat", "lon" }] }
departures
Live-Abfahrten an einer Haltestelle: Sollzeit, Echtzeit (bei Abweichung mit Verspätung in Minuten), Linie, Produkt, Ziel und Steig.
Parameter | Typ | Default | Beschreibung |
| string, min. 1 Zeichen | (Pflicht) | Haltestellenname oder ID wie |
| Ganzzahl, 1 bis 40 | 10 | Maximale Zahl Abfahrten |
Aufruf: { "stop": "Hauptbahnhof", "limit": 4 }
Abfahrten Hannover, Hauptbahnhof (de:03241:31)
12:31 -> 12:44 (+13 min) ICE 857 InterCityExpress (Zug) -> S+U Berlin Hauptbahnhof [Gleis 1-8] Steig 9
12:31 -> 12:44 (+13 min) ICE 847 InterCityExpress (Zug) -> S+U Berlin Hauptbahnhof [Gleis 1-8] Steig 9
12:42 Stadtbahn 13 -> Hannover/Fasanenkrug Steig UB2
12:44 Stadtbahn 7 -> Hannover/Wettbergen Steig UB1Lesart: Soll -> Ist (Differenz) bei Abweichung, sonst nur die Sollzeit. Hinter der Linie steht das Produkt in Klammern, wenn es nicht schon im Linien-Namen vorkommt. ENTFAELLT markiert ausfallende Fahrten, (nur Fahrplan) Fahrten ohne Echtzeitdaten.
structuredContent: { "stop": { "id", "name" }, "departures": [{ "planned", "estimated", "delayMinutes", "line", "product", "destination", "platform", "cancelled", "realtime" }] }
journey_planner
Verbindungssuche zwischen zwei Haltestellen, mit Echtzeitdaten (GVH, ÜSTRA, DB). Standard ist Abfahrt jetzt.
Parameter | Typ | Default | Beschreibung |
| string, min. 1 Zeichen | (Pflicht) | Start: Haltestellenname oder |
| string, min. 1 Zeichen | (Pflicht) | Ziel: Haltestellenname oder |
| string, optional |
| Abfahrtszeit, ISO 8601 (z. B. |
| string, optional | nicht gesetzt | Gewünschte Ankunftszeit, ISO 8601. Schließt |
| Ganzzahl, 1 bis 6 | 3 | Anzahl Verbindungen |
Zeitangaben:
Ohne Zeitzone (
2026-10-19T08:15) gilt die Eingabe als Berliner Ortszeit.Mit Zone (
Z,+02:00,+0200oder+02) wird nach Berliner Zeit umgerechnet.Erlaubt ist nur
YYYY-MM-DD[THH:MM[:SS]][Zone](TrennerT,toder Leerzeichen, Stunde und Minute zweistellig). Alles andere wird mitFehler: Zeitangabe nicht lesbar, erwartet YYYY-MM-DD[THH:MM][Zone]abgelehnt.Nur ein Datum (
2026-10-19) bedeutet 00:00 Uhr.Leere oder nur aus Leerzeichen bestehende Werte zählen als nicht angegeben.
departureundarrivalnie zugleich angeben; der Server antwortet sonst mitFehler: Entweder "departure" oder "arrival" angeben, nicht beides.
Aufruf: { "from": "Kröpcke", "to": "Flughafen Hannover", "departure": "2026-10-19T08:15", "results": 2 }
Verbindungen Hannover, Kröpcke -> Langenhagen (Han), Hannover Flughafen
1) Mo., 19.10. Abfahrt 07:57, Ankunft 08:36, Dauer 39 min, Umstiege 1
Stadtbahn 1 -> Langenhagen: Hannover/Kröpcke 07:57 bis Langenhagen/Zentrum 08:16
Bus 470 -> Flughafen: Langenhagen Zentrum 08:21 bis Flughafen 08:36
2) Mo., 19.10. Abfahrt 08:24, Ankunft 08:53, Dauer 29 min, Umstiege 1
Stadtbahn 13 -> Hannover/Fasanenkrug: Hannover/Kröpcke 08:24 bis Hannover Hauptbahnhof 08:26
S-Bahn S5 -> Langenhagen Flughafen: Hannover Hauptbahnhof 08:35 bis Langenhagen Flughafen 08:53Das Datum steht nur, wenn die Abfahrt nicht heute ist. Fußwege erscheinen als Fussweg A -> B (Dauer). Die Verbindungen werden nach Abfahrtszeit sortiert ausgegeben.
structuredContent: { "from": { "id", "name" }, "to": { "id", "name" }, "journeys": [{ "departure", "arrival", "durationSeconds", "interchanges", "legs": [{ "mode": "walk" | "transit", "line", "from", "to", "departure", "arrival", "destination", "durationSeconds" }] }] }. departure und arrival sind hier die Sollzeiten in UTC.
Bekannte Eigenheiten der EFA
Der Stopfinder meldet bei mehrdeutigen Namen den Fehler
-8011mit leerem Text und liefert trotzdem Treffer. Der Server behandelt das nicht als Fehler, solange Treffer vorhanden sind. Die Verbindungssuche löst Namen deshalb immer zuerst über den Stopfinder auf, weil Freitext direkt als Ziel mit-8011scheitert.Zeiten kommen mit Sekunden (z. B. Ankunft
10:39:50Z). Der Server kappt auf volle Minuten, damit keine scheinbaren Verspätungen von einer Minute entstehen, wo Soll und Ist in derselben Minute liegen.Verbindungen sind von der EFA nach Rating sortiert, nicht nach Zeit, und es kommen teils mehr oder früher abfahrende Verbindungen zurück als angefragt (im Beispiel oben: Wunschzeit 08:15, erste Verbindung 07:57). Der Server sortiert nach Abfahrt und kappt dann auf
results; der Zeitpunkt ist aber nur ein Richtwert.Reine Fußweg-Verbindungen zählen als Verbindung und belegen einen der
results-Plätze.Die EFA gewichtet nicht nach Region (
Messe/Ostliefert dort zuerst Friedrichshafen). Der Server bevorzugt deshalb Treffer mit IDde:03241:…(Kreisschlüssel der Region Hannover), dann den EFA-Bestwert, dann die Trefferqualität; Treffer außerhalb bleiben als Fallback erhalten (Hildesheim Hauptbahnhoffindet Hildesheim). Im Zweifel mitstop_searchdie ID prüfen.Ein Stopfinder-Aufruf ohne Treffer meldet
-8020mit leerem Text und leerer Trefferliste; der Server wertet das als leeres Ergebnis. Eine unbekanntede:-ID bei den Abfahrten liefert keine Fehlermeldung, sondern Fuzzy-Treffer fremder Haltestellen ohne Abfahrten; der Server meldet sie als unbekannt.
Lizenz
MIT, siehe LICENSE.
Available Tools
3 toolsdeparturesAbfahrten (live)B
Live departures at a stop in the Hannover region: planned time, real-time time (with delay in minutes), line, product, destination and platform. Accepts a stop name or a de: stop ID.
| Name | Required | Description | Default |
|---|---|---|---|
| stop | Yes | Stop name (resolved automatically) or ID like "de:03241:11" | |
| limit | No | Max. number of departures |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does disclose that results are 'live' with real-time delay in minutes — useful since there is no output schema. It omits error behavior for unresolvable stops, whether live data can be missing/degraded, and any rate-limit or result-ordering details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the primary action and followed by the returned fields — every clause earns its place, and the field list compensates for the absent output schema. Slightly dense but no waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations and no output schema, the description usefully enumerates return fields and input flexibility, which covers the essentials. It still leaves gaps on failure modes (invalid stop), empty-result handling, and how limit affects output ordering, so it is adequate but not complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters (stop, limit) are already documented in the schema, including the 'de:03241:11' ID format and the 1–40 limit range. The description's note that a stop name is resolved automatically adds only marginal value beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — live departures at a stop — with geographic scope (Hannover region) and the exact fields returned (planned/real-time time, delay, line, product, destination, platform). It is distinguishable from stop_search and journey_planner by implication, but never names or contrasts them explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: an agent can infer this is the tool for departure boards, and 'stop name (resolved automatically)' hints that a prior stop_search lookup may be unnecessary. However, there is no explicit when-to-use, when-not-to-use, or routing to journey_planner for actual trip planning.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
journey_plannerVerbindungssucheA
Plan public-transport journeys in the Hannover region (GVH/ÜSTRA/DB) from one stop to another, with real-time data. Names are resolved to the best matching stop automatically. Default: departing now; set departure or arrival (ISO 8601, local Berlin time if no zone) to plan for a time.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | Destination stop name or de: ID | |
| from | Yes | Origin stop name or de: ID | |
| arrival | No | Desired arrival time, ISO 8601. Mutually exclusive with departure | |
| results | No | Number of journeys | |
| departure | No | Departure time, ISO 8601 (e.g. "2026-10-19T08:15") or "now" (default) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that the data is real-time, that stop names are fuzzy-resolved to the best match automatically (so exact IDs are not required), and how timestamps are interpreted (local Berlin time when no zone). It still omits failure behavior for unresolvable names and any rate/auth constraints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with the core action, then name resolution, then timing defaults. Every clause conveys a constraint or behavior; nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter read tool with no annotations and no output schema, the description adequately covers scope, resolution behavior, and time handling. It stops short of describing the shape of a returned journey or what happens when no stop matches, which leaves a small gap given the absence of an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema does not: the timezone fallback ('local Berlin time if no zone') for departure/arrival and the fact that from/to accept free-text names rather than requiring de: IDs. This meaningfully improves how an agent fills the parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource+scope: plan public-transport journeys from one stop to another in the Hannover GVH/ÜSTRA/DB network. This is clearly distinct from the sibling stop_search (stop lookup) and departures (departure board) without naming them, because the resource is a route between two stops.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit operating context: default is depart-now, and either departure or arrival can be set to plan for a specific time, with arrival/departure being mutually exclusive. It does not, however, say when to prefer this over stop_search or departures, so there is no alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stop_searchHaltestellen suchenA
Search stops/stations in the Hannover region (GVH/ÜSTRA) by name. Returns name, stop ID (e.g. de:03241:11), locality, type and coordinates. Use it to find the exact stop ID.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max. number of results | |
| query | Yes | Stop name or part of it, e.g. "Kröpcke" or "Hauptbahnhof" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden. It usefully enumerates the response fields (name, stop ID, locality, type, coordinates) and shows the stop ID format, but says nothing about read-only semantics, result cap/default behavior, or how ambiguous name matches are ranked.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler: scope and method first, return payload second, intended use third. Everything an agent needs is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by listing the returned fields and example ID format. Parameters are fully covered by the schema, and the missing pieces (pagination/ambiguity handling) are minor for a small-limit lookup tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters are already documented there with examples ("Kröpcke", limit default 8/max 25). The description's "by name" adds nothing beyond the schema's query description, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (search) + resource (stops/stations) + geographic scope (Hannover, GVH/ÜSTRA), plus the explicit end goal of obtaining a stop ID, which cleanly separates it from the sibling tools that consume such IDs (departures, journey_planner).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use it to find the exact stop ID" tells the agent this is the discovery/prerequisite step before calling departures or journey_planner, which is real routing guidance. It stops short of naming those siblings or stating when-not to use it, so no 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
departures - First observed
journey_planner - First observed
stop_search
TDQS
Scored across 3 tools
Each tool targets a distinct task: stop_search resolves stop IDs, departures returns live departure boards, and journey_planner handles routing between stops. There is no meaningful overlap—finding a stop, checking departures, and planning a trip are clearly separate actions.
All names are snake_case, but the patterns differ: stop_search (noun_verb), departures (bare plural noun), and journey_planner (noun_noun). The lack of a single verb_noun convention makes the set less predictable, though each name is still readable and descriptive.
Three tools map cleanly onto the core transit workflow (find stop, view departures, plan journey) with no redundancy. The surface is slightly thin—no coordinate-based or nearby-stop lookup—but each tool earns its place.
The set covers the essential lifecycle: resolving stops, real-time departures, and journey planning with time parameters. Minor gaps exist (no nearby-stop-by-coordinates search or service-disruption/line-info lookup), but agents can work around them using stop_search.
Maintenance
Related MCP Connectors
Journeys, departures, line courses, disruptions in Germany; nearby sharing as currently reported.
Real-time transit stops, routes, arrivals, vehicle positions, and schedules via OneBusAway APIs.
Read-only public transit departures, stop search, and city coverage for bus and train users.
Plan journeys by train, bus, tram, metro and ferry across 42 European countries.
Related MCP Servers
- FlicenseDqualityCmaintenanceProvides access to Berlin's public transport data through the VBB API, enabling users to search stops, get departures, and plan journeys across Berlin-Brandenburg.36-
- FlicenseNot gradedqualityDmaintenanceEnables querying nearby stations and departure boards using the RMV public transport API.1-
- FlicenseNot gradedqualityDmaintenanceEnables querying Swiss public transport (trains, buses, trams, boats) for stations, connections, and station boards via the opendata.ch API.-
- FlicenseNot gradedqualityDmaintenanceMCP server for real-time German public transport data (HAFAS/VBN network). Enables querying departures, arrivals, journeys, and nearby stops using natural language.-