Skip to main content

MCP Server for Swiss public transport -- OJP journey planner, real-time departures, disruptions, occupancy, ticket prices, train formations and open data from opentransportdata.swiss

Project description

๐Ÿ‡จ๐Ÿ‡ญ Part of the Swiss Public Data MCP Portfolio

๐Ÿš† swiss-transport-mcp

Version License: MIT Python 3.11+ MCP Data Source CI

MCP server connecting AI models to the Swiss public transport system โ€“ journey planning, real-time departures, disruptions, occupancy, ticket prices, train formations and open data from opentransportdata.swiss.

๐Ÿ‡ฉ๐Ÿ‡ช Deutsche Version

Demo

Demo: Claude queries disruptions and departures


Overview

swiss-transport-mcp gives AI assistants like Claude a complete Swiss travel information system โ€“ not just timetables, but also real-time disruption alerts, occupancy forecasts, ticket prices, and a full train formation view. All accessible through a single, standardised MCP interface.

The various APIs at opentransportdata.swiss speak different protocols โ€“ OJP 2.0 (XML/SOAP), SIRI-SX (XML), REST/JSON. This server translates everything into clean JSON for the AI model, acting as a multilingual protocol interpreter.

Anchor demo query: "Plan a school trip for 25 students from Zurich to the Technorama in Winterthur โ€“ check for disruptions and find the best departure." โ†’ More use cases by audience โ†’


Features

  • ๐Ÿ—บ๏ธ Journey planning (A โ†’ B with transfers, duration, transport mode) via OJP 2.0
  • ๐Ÿ• Real-time departures with delays and platform information
  • ๐Ÿ” Stop search by name or coordinates
  • ๐Ÿšจ Live disruption alerts (cancellations, closures) via SIRI-SX
  • ๐Ÿ“Š Occupancy forecasts for trains (SBB, BLS, Thurbo, SOB)
  • ๐Ÿ’ฐ Ticket prices including class selection
  • ๐Ÿšƒ Train formation โ€“ coaches, classes, amenities, accessibility
  • ๐Ÿ“ฆ Open data catalogue โ€“ ~90 transport datasets via CKAN
  • ๐Ÿ”‘ Graceful degradation โ€“ server starts with core tools even without optional API keys
  • โ˜๏ธ Dual transport โ€“ stdio for Claude Desktop, Streamable HTTP/SSE for cloud deployment

Prerequisites


Installation

# Clone the repository
git clone https://github.com/malkreide/swiss-transport-mcp.git
cd swiss-transport-mcp

# Install
pip install -e .

Or with uvx (no permanent installation):

uvx swiss-transport-mcp

Quickstart

# Set the minimum required key (OJP core tools)
export TRANSPORT_API_KEY=your_key_here

# Start the server (stdio mode for Claude Desktop)
swiss-transport-mcp

Try it immediately in Claude Desktop:

"What are the next departures from Zurich Stadelhofen?" "How do I get from Wรคdenswil to Bern by train?"


Configuration

Environment Variables

Variable API Required
TRANSPORT_API_KEY Unified key for OJP + CKAN โœ… (or individual keys)
TRANSPORT_OJP_API_KEY OJP 2.0 Journey Planner Optional (override)
TRANSPORT_CKAN_API_KEY CKAN data catalogue Optional (separate subscription)
SIRI_SX_API_KEY Disruption alerts (SIRI-SX) Optional
OCCUPANCY_API_KEY Occupancy forecast Optional
FORMATION_API_KEY Train formation Optional
OJP_FARE_API_KEY Ticket prices (OJP Fare) Optional

APIs without a key are silently disabled โ€“ the server starts fine with just the 6 core tools.

Operational / security variables:

Variable Effect Default
MCP_ENV / ENV Process environment. Must be dev/development/local/test to allow disabling TLS verification. (unset โ†’ production)
TRANSPORT_SSL_VERIFY Set to false to disable TLS certificate verification. Honoured only when MCP_ENV marks a dev environment โ€“ otherwise the request is ignored and verification stays on. true
TRANSPORT_CKAN_URL Override the CKAN base URL. Must stay on the egress allow-list (*.opentransportdata.swiss); off-site overrides are refused. https://api.opentransportdata.swiss/ckan-api
MCP_CORS_ORIGINS Comma-separated list of browser origins allowed to call the HTTP transport. Use * to allow any origin (not recommended). The Mcp-Session-Id header is exposed to these origins. https://claude.ai
LOG_FORMAT json for structured logs (RFC 5424 severity); anything else for human-readable text. Always written to stderr. text
OTEL_TRACES_ENABLED 1 to enable OpenTelemetry tracing (requires the otel extra: pip install 'swiss-transport-mcp[otel]'). No-op otherwise. (off)
MCP_STATELESS 1 to run the Streamable HTTP transport statelessly โ€” no server-side session state, so instances need no sticky load balancing. Recommended for horizontal scale-out. (off โ†’ stateful)

๐Ÿ”’ Egress allow-list: all outbound requests are restricted to https:// on opentransportdata.swiss hosts. Any other host is refused before a request is sent (SSRF / egress hardening).

Claude Desktop Configuration

Minimal (core tools only):

{
  "mcpServers": {
    "swiss-transport": {
      "command": "swiss-transport-mcp",
      "env": {
        "TRANSPORT_API_KEY": "your_key_here"
      }
    }
  }
}

Full (all 11 tools):

{
  "mcpServers": {
    "swiss-transport": {
      "command": "swiss-transport-mcp",
      "env": {
        "TRANSPORT_API_KEY": "your_ojp_key_here",
        "SIRI_SX_API_KEY": "your_siri_key_here",
        "OCCUPANCY_API_KEY": "your_occupancy_key_here",
        "FORMATION_API_KEY": "your_formation_key_here",
        "OJP_FARE_API_KEY": "your_fare_key_here"
      }
    }
  }
}

Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Cloud Deployment (Streamable HTTP)

For use via claude.ai in the browser (e.g. on managed workstations without local software). The cloud transport is Streamable HTTP (MCP_TRANSPORT=streamable-http, endpoint /mcp). SSE (/sse) is still supported but deprecated.

MCP_TRANSPORT Use Endpoint
stdio (default) Local Claude Desktop subprocess โ€“
streamable-http (or http) Cloud / container (recommended) /mcp
sse Legacy browser transport (deprecated) /sse

Docker (recommended):

# Build + run with explicit resource limits (see docker-compose.yml)
TRANSPORT_API_KEY=xxx docker compose up --build
# โ†’ http://127.0.0.1:8000/mcp

The image is a multi-stage build running as a non-root user; docker-compose.yml adds read_only, no-new-privileges and memory/CPU/PID limits.

Render.com:

  1. Push/fork the repository to GitHub
  2. On render.com: New Web Service โ†’ connect GitHub repo (Docker runtime)
  3. Set env MCP_TRANSPORT=streamable-http and MCP_HOST=0.0.0.0
  4. In claude.ai under Settings โ†’ MCP Servers, add: https://your-app.onrender.com/mcp

๐Ÿ’ก "stdio for the developer laptop, Streamable HTTP for the cloud."

Scaling horizontally: run with MCP_STATELESS=1. In stateless mode the server keeps no per-session state, so any instance can serve any request and a plain round-robin load balancer suffices โ€” no sticky sessions / Mcp-Session-Id affinity required. If you need stateful streaming instead, route by Mcp-Session-Id at the edge LB (e.g. HAProxy stick-tables) so each session stays pinned to one instance.

โš ๏ธ Binding: In a network transport the server binds to 127.0.0.1 by default so a locally started server is not exposed to your whole network (e.g. public Wi-Fi). Set MCP_HOST=0.0.0.0 only in a container/cloud environment where binding to all interfaces is intended (the Docker image does this for you).


Available Tools

Core Tools (OJP 2.0 / CKAN)

Tool Description Data Source
transport_search_stop Search stops/stations by name OJP 2.0
transport_nearby_stops Find nearby stops by coordinates OJP 2.0
transport_departures Real-time departure board with delays & platforms OJP 2.0
transport_trip_plan Plan journey A โ†’ B with transfers, duration, mode OJP 2.0
transport_search_datasets Search open data catalogue (~90 datasets) CKANยน
transport_get_dataset Get full details of a specific dataset CKANยน

ยน CKAN tools require a separate subscription in the API Manager.

Extension Tools (optional API keys)

Tool Description Data Source
get_transport_disruptions ๐Ÿšจ Live disruptions, cancellations, line closures SIRI-SX
get_train_occupancy ๐Ÿ“Š Occupancy forecast for specific trains Occupancy JSON
get_ticket_price ๐Ÿ’ฐ Ticket prices for connections OJP Fare
get_train_composition ๐Ÿšƒ Train formation, classes, accessibility Formation REST
check_transport_api_status ๐Ÿ” Health check for all configured APIs All

Example Use Cases

Query Tool
"Next trains from Zurich Stadelhofen?" transport_departures
"Plan a trip for 25 students from Zurich to Winterthur Technorama" transport_trip_plan
"Any disruptions between Zurich and Bern?" get_transport_disruptions
"How full is IC 1009 today?" get_train_occupancy
"What does a ticket from Wรคdenswil to Bern cost?" get_ticket_price
"Does IC 708 have a dining car?" get_train_composition
"Which stops are near Langstrasse 100?" transport_nearby_stops

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Claude / AI   โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚   Swiss Transport MCP     โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚  opentransportdata.swiss  โ”‚
โ”‚   (MCP Host)    โ”‚โ—€โ”€โ”€โ”€โ”€โ”‚   (MCP Server)            โ”‚โ—€โ”€โ”€โ”€โ”€โ”‚                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚                           โ”‚     โ”‚  OJP 2.0  (XML/SOAP)     โ”‚
                        โ”‚  11 Tools ยท 2 Resources   โ”‚     โ”‚  SIRI-SX  (XML)          โ”‚
                        โ”‚  Stdio | SSE              โ”‚     โ”‚  CKAN     (REST/JSON)    โ”‚
                        โ”‚                           โ”‚     โ”‚  Occupancy(REST/JSON)    โ”‚
                        โ”‚  Core:                    โ”‚     โ”‚  Formation(REST/JSON)    โ”‚
                        โ”‚   api_client + ojp_client โ”‚     โ”‚  OJP Fare (XML/SOAP)     โ”‚
                        โ”‚  Extensions:              โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ”‚   siri_sx, occupancy,     โ”‚
                        โ”‚   ojp_fare, formation     โ”‚
                        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Infrastructure Components

Component Metaphor Function
RateLimiter Bouncer Limits API calls per time window
SimpleCache Whiteboard Caches responses for repeated queries
APIClient Switchboard Handles auth, redirects, errors centrally
APIConfig Business card Key, URL, limits per API

Caching Strategy

API Cache TTL Rationale
SIRI-SX 120s Disruptions don't change every second
Occupancy 300s Forecasts are day-based
Formation 600s Train composition is stable for the day
OJP Fare 1800s Prices rarely change intraday

Project Structure

swiss-transport-mcp/
โ”œโ”€โ”€ src/swiss_transport_mcp/        # Main package
โ”‚   โ”œโ”€โ”€ server.py                   # FastMCP server, tool definitions
โ”‚   โ”œโ”€โ”€ api_client.py               # Core OJP + CKAN client
โ”‚   โ”œโ”€โ”€ ojp_client.py               # OJP 2.0 XML/SOAP parser
โ”‚   โ”œโ”€โ”€ api_infrastructure.py       # RateLimiter, SimpleCache, APIClient
โ”‚   โ”œโ”€โ”€ siri_sx.py                  # Disruption alerts
โ”‚   โ”œโ”€โ”€ occupancy.py                # Occupancy forecasts
โ”‚   โ”œโ”€โ”€ ojp_fare.py                 # Ticket prices
โ”‚   โ””โ”€โ”€ formation.py                # Train formation
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_server.py              # Unit + integration tests
โ”œโ”€โ”€ .github/workflows/ci.yml        # GitHub Actions (Python 3.11/3.12/3.13)
โ”œโ”€โ”€ claude_desktop_config.json       # Example Claude Desktop config
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ CHANGELOG.md
โ”œโ”€โ”€ CONTRIBUTING.md
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ README.md                        # This file (English)
โ””โ”€โ”€ README.de.md                     # German version

Safety & Limits

  • Read-only: All tools perform read-only requests (HTTP GET / OJP XML POST for queries only) โ€” no data is written, modified, or deleted on any upstream system.
  • No personal data: Journey queries are transient and not stored by this server. The APIs return scheduled timetable and real-time operational data. No personally identifiable information (PII) is processed or retained.
  • Rate limits: opentransportdata.swiss enforces per-key rate limits (documented in the API Manager). The server's built-in RateLimiter (SIRI-SX: 2 req/min, Formation/OJP Fare: 5 req/min) stays within these bounds automatically. Use the limit parameters conservatively for bulk queries.
  • API key required: A free key from api-manager.opentransportdata.swiss is mandatory. Keys are bound to your account's subscription โ€” only subscribe to APIs you intend to use.
  • Data freshness: Real-time tools (departures, disruptions, occupancy) reflect the upstream source at query time. The server caches responses for short TTLs (120sโ€“1800s) to reduce API load โ€” see the Caching Strategy table above.
  • Terms of service: Data is subject to the ToS of opentransportdata.swiss. OJP, SIRI-SX, and the CKAN catalogue are published under open licences (ODbL / CC BY 4.0) for non-commercial and research use.
  • No guarantees: This server is a community project, not affiliated with the Federal Office of Transport (BAV/OFT) or SBB. Availability depends on upstream APIs.

Before you install (consent)

Adding this server to your MCP client lets the connected AI model issue Swiss public-transport queries on your behalf, using your opentransportdata.swiss API key, and make outbound HTTPS requests to opentransportdata.swiss. Nothing is written upstream and no PII is stored, but you should review the tool list above and confirm you are comfortable granting that access before configuring the server.

Running the HTTP transport safely (no built-in auth)

The server has no authentication of its own. When you run the Streamable HTTP transport (MCP_TRANSPORT=streamable-http), the MCP SDK issues a cryptographically random Mcp-Session-Id per session, but there is no user identity bound to it. Therefore:

  • Do not expose a no-auth instance directly to the public internet. Put it behind an authenticating reverse proxy (OAuth2 proxy, mTLS, or your platform's access control), or restrict it to a trusted network.
  • Keep the default MCP_HOST=127.0.0.1 for local use; only bind 0.0.0.0 inside a controlled container/cloud environment (see Deployment).
  • Scope MCP_CORS_ORIGINS to the origins you actually trust.

See SECURITY.md for the full security posture and the accepted-risk decisions (gateway-level controls).


Known Limitations

  • OJP Fare: Discounts (Halbtax, GA, regional passes) are not always reflected
  • Formation: Stop-based data is only available for TODAY (real-time dependency)
  • Occupancy: SBB, BLS, Thurbo and SOB only โ€“ no private railways
  • SIRI-SX: Returns ALL Swiss disruptions โ†’ use the filter_text parameter
  • CKAN: Requires a separate subscription in the API Manager

Testing

# Unit tests (no API key required)
PYTHONPATH=src pytest tests/ -m "not live"

# Integration tests (API key required)
TRANSPORT_API_KEY=xxx pytest tests/ -m "live"

Changelog

See CHANGELOG.md


Contributing

See CONTRIBUTING.md


License

MIT License โ€” see LICENSE


Author

Hayal Oezkan ยท github.com/malkreide


Credits & Related Projects

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swiss_transport_mcp-0.3.0.tar.gz (113.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swiss_transport_mcp-0.3.0-py3-none-any.whl (62.1 kB view details)

Uploaded Python 3

File details

Details for the file swiss_transport_mcp-0.3.0.tar.gz.

File metadata

  • Download URL: swiss_transport_mcp-0.3.0.tar.gz
  • Upload date:
  • Size: 113.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for swiss_transport_mcp-0.3.0.tar.gz
Algorithm Hash digest
SHA256 76edfde7b5f40d872e096d56d14b92ed9bc17400a0f861751811f06a624123f6
MD5 44a6c03e9930b3146ad939566e6cdaac
BLAKE2b-256 02bff1efb06d6e7864036dd5b1f8f60754d2f8848950632adeeef08a3bf0eace

See more details on using hashes here.

Provenance

The following attestation bundles were made for swiss_transport_mcp-0.3.0.tar.gz:

Publisher: publish.yml on malkreide/swiss-transport-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file swiss_transport_mcp-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for swiss_transport_mcp-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6d11741f9aa8f5daa946f32470787c8a3d42181cb9992a298e9ea501899b0de0
MD5 df78e16eaaccbcd454e5556790bda206
BLAKE2b-256 3c3c86fb46171e23fdc380eb8e7208ea76d8efbe9a0b85149d69b50d0fba2ca2

See more details on using hashes here.

Provenance

The following attestation bundles were made for swiss_transport_mcp-0.3.0-py3-none-any.whl:

Publisher: publish.yml on malkreide/swiss-transport-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page