name: ov-nl-travel description: | Dutch rail travel helper for NS Reisinformatie via the ovNlGateway MCP tool. Use this for stations, departures, arrivals, trips, journey details, and disruptions. license: MIT metadata: author: ovnl-codex-plugin version: "0.1.0" purpose: ov-nl-travel-assistant
OV-NL Travel
Use ovNlGateway first for Dutch railway and NS questions.
Preferred behavior
- Prefer
ovNlGatewayfor anything that can change minute-to-minute: departures, delays, cancellations, platforms, disruptions, or best current trip options. - Do not use web search for live rail data when
ovNlGatewaycan answer it. - If a station name is ambiguous, ask a short disambiguation question or show the candidates from the tool result.
- Never claim something is live unless
ovNlGatewaywas used in this turn. - For requests asking for all train legs, changes, or intermediate stations on an itinerary, use
trips.searchfirst and thentrips.detailwith the selected trip'sctxRecon. - Do not fall back to web search for intermediate stops when
trips.detailis available. - Use
journey.detailfor a specific train orjourneyDetailRef, not for describing a whole itinerary. When passing ajourneyDetailRef, map it toargs.id.
Tool contract
The tool expects:
{ "action": "<action>", "args": { /* action-specific */ } }
Supported actions:
stations.search->{ query, limit?, countryCodes?, intent? }stations.nearest->{ latitude/longitude or lat/lng, limit?, intent? }departures.list->{ station? | stationCode? | uicCode?, dateTime?, maxJourneys?, lang?, intent? }departures.window->{ station? | stationCode? | uicCode?, fromDateTime+toDateTime OR date+fromTime+toTime, maxJourneys?, lang?, intent? }arrivals.list->{ station? | stationCode? | uicCode?, dateTime?, maxJourneys?, lang?, intent? }trips.search->{ from, to, via?, dateTime?, searchForArrival?, limit?, lang?, intent? }trips.detail->{ ctxRecon, date?, lang? }journey.detail->{ id? | train?, dateTime?, departureUicCode?, transferUicCode?, arrivalUicCode?, omitCrowdForecast? }disruptions.list->{ type?, isActive?, lang?, intent? }disruptions.by_station->{ station, intent? }disruptions.detail->{ type, id }
Intent guidance
Use args.intent.hard for strict constraints:
directOnlymaxTransfersmaxDurationMinutesdepartureAfterdepartureBeforearrivalAfterarrivalBeforeincludeModesexcludeModesincludeOperatorsexcludeOperatorsincludeTrainCategoriesexcludeTrainCategoriesavoidStationsexcludeCancelledrequireRealtimeplatformEqualsdisruptionTypesactiveOnly
Use args.intent.soft.rankBy as a list for preferences, for example { "rankBy": ["fastest"] }:
fastestfewest_transfersearliest_departureearliest_arrivalrealtime_firstleast_walking
Use hard constraints for wording like must, only, without, geen, alleen, zonder, or niet.
Use soft ranking for wording like prefer, liefst, best, or bij voorkeur.
Output handling
The tool normally returns a matching kind, for example trips.search or departures.list.
Special cases:
kind: "disambiguation"means the tool could not uniquely resolve a station.- Show the candidates succinctly.
- Ask the user to choose one.
- Re-run the original action with the chosen station.
kind: "error"means the request failed or no valid result exists.- Surface the message directly.
- If the error is actionable, suggest the smallest next step.
Practical recipes
- “Next train from X” ->
departures.list - “Departures between 17:00 and 18:00 from X” ->
departures.window - “Arrivals at X” ->
arrivals.list - “Best route from A to B” ->
trips.search - “Show more details for this trip” ->
trips.detail - “Show all train legs and intermediate stations for this trip” ->
trips.search, thentrips.detailwith the chosenctxRecon - “What’s going on with this train?” ->
journey.detail - “Show NS disruptions” ->
disruptions.list - “Disruptions near station X” ->
disruptions.by_station