MCP Server for Swiss public transport – OJP journey planner, real-time departures, disruptions, occupancy, ticket prices, train formations and open data from opentransportdata.swiss
Drift inferred · capture-to-capture
No drift recorded — single capability capture; advisories appear once its surface changes.
transport stdio · streamable-http · http · sse
verified
reported
listed in the official MCP registry counts 8 tools · 0 res
· 0 prompts
permission surface via code analysis
tools
-
check_transport_api_status
-
get_train_composition
-
get_train_occupancy
-
transport_departures
-
transport_get_dataset
-
transport_nearby_stops
-
transport_search_datasets
-
transport_search_stop
prompt-surface
shipped agent-instruction files + hidden-content / dangerous-code findings —
quoted from the analyzed source
analyzed commit 1f9b5e9 · analyzer v33 · 1w ago
skills & prompt files 1
- agent-rules malkreide-swiss-transport-mcp-1f9b5e9/CLAUDE.md
danger signals1
- suspicious endpoint 169.254.169.254 (cloud metadata) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_net_security.py :44
"https://169.254.169.254/latest/meta-data/", # cloud metadata
evidence-backed
findings quoted directly from the published source artifact — not inferred
code files: 38
filesystem 11
- fs malkreide-swiss-transport-mcp-1f9b5e9/audits/2026-06-03T064903-Z-swiss-transport-mcp/findings/_generator.py :5
RUN = pathlib.Path(sys.argv[1]) - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/scripts/check_version_sync.py :35
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/scripts/classify_live_run.py :55
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/scripts/record_fixtures.py :47
from pathlib import Path - fs malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/ojp_client.py :13
from pathlib import Path - fs malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/tool_integrity.py :22
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/fixture_data.py :14
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_classify_live_run.py :21
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_ojp_contract.py :277
source = (ojp_client.__file__ and open(ojp_client.__file__, encoding="utf-8").read()) or "" - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_response_shape.py :207
from pathlib import Path - fs (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_server_config.py :5
from pathlib import Path
network 15
- net malkreide-swiss-transport-mcp-1f9b5e9/audits/2026-06-03T064903-Z-swiss-transport-mcp/findings/_generator.py :49
"Einen `@asynccontextmanager`-Lifespan mit `AsyncExitStack` einrichten, der EINEN gepoolten `httpx.AsyncClient` erstellt und beim Shutdown sauber schliesst. `api_client.py` von Per-Request-Clients auf - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/scripts/record_fixtures.py :50
import httpx - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/api_client.py :16
import httpx - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/api_infrastructure.py :21
import httpx - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/net_security.py :26
from urllib.parse import urlparse - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/occupancy.py :260
import httpx - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/retry.py :25
import httpx - net malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :31
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_api_client.py :8
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_lifespan.py :3
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_observability.py :9
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_place_resolution.py :21
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_response_shape.py :21
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_retry_policy.py :14
import httpx - net (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_tool_outputs.py :15
import httpx
secrets 2
- secrets malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :69
siri_sx_key=os.environ.get("SIRI_SX_API_KEY"), - secrets (weak) malkreide-swiss-transport-mcp-1f9b5e9/tests/test_server.py :23
_HAS_KEY = bool(os.environ.get("TRANSPORT_API_KEY") or os.environ.get("TRANSPORT_OJP_API_KEY"))
tool registrations 8
- transport_search_stop malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :396
- transport_nearby_stops malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :451
- transport_departures malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :578
- transport_search_datasets malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :732
- transport_get_dataset malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :794
- get_train_occupancy malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :908
- get_train_composition malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :1042
- check_transport_api_status malkreide-swiss-transport-mcp-1f9b5e9/src/swiss_transport_mcp/server.py :1105
declared dependencies 11
- mcp@>=2.0.0,<3
- modern@*
- httpx@>=0.27.0,<1.0.0
- pydantic@>=2.0.0,<3.0.0
- pytest@>=8.0.0
- pytest-asyncio@>=0.23.0
- pytest-cov@>=5.0.0
- respx@>=0.21.0
- .@*
- ruff@==0.16.1
- opentelemetry-sdk@>=1.20.0,<2.0.0
transport_posture 2
- network-listening, binds all interfaces, no auth detected malkreide-swiss-transport-mcp-1f9b5e9/tests/test_cors.py :150
host="0.0.0.0", # noqa: S104 - network-listening, binds all interfaces, no auth detected malkreide-swiss-transport-mcp-1f9b5e9/tests/test_transport_security.py :124
app = server._build_http_app("streamable-http", _ORIGINS, host="0.0.0.0", port=8080)