> πŸ‡¨πŸ‡­ **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)** # πŸ“Ί srgssr-mcp ![Version](https://img.shields.io/badge/version-2.0.1-blue) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-purple)](https://modelcontextprotocol.io/) [![CI](https://github.com/malkreide/srgssr-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/malkreide/srgssr-mcp/actions) [![Data Source](https://img.shields.io/badge/Data-SRG%20SSR%20Public%20API-red)](https://developer.srgssr.ch) > MCP server connecting AI models to SRG SSR public APIs – weather, TV/radio metadata, program guide and Swiss votations/elections since 1900 (SRF, RTS, RSI, RTR, SWI). [πŸ‡©πŸ‡ͺ Deutsche Version](README.de.md)

Demo: Claude asks a question β†’ srgssr-mcp tool call β†’ grounded answer from SRG SSR Polis API

--- ## Overview **srgssr-mcp** gives AI assistants like Claude direct access to the public APIs of SRG SSR – Switzerland's national public broadcaster. Weather forecasts, TV and radio metadata, electronic program guides, and historical democratic data (votations and elections since 1900) are all accessible through a single standardised MCP interface. The server covers five thematic clusters: SRF Weather, Video, Audio, EPG and Polis (Swiss Democracy). Each cluster maps to a group of purpose-built tools that translate raw SRG SSR API data into clean JSON responses. **Anchor demo query:** *"What were the cantonal results of the popular vote on initiative X in Zurich?"* – answered with historical real-time data from the Polis system, not a hallucination. --- ## Features - 🌦️ **Weather** – location search, current conditions, 24h hourly forecast, 7-day forecast (SRF Meteo) - πŸ“Ί **Video** – TV show listings, latest episodes, live TV channels across all business units - πŸŽ™οΈ **Audio** – radio show listings, audio episodes, live radio stations - πŸ“… **EPG** – daily program schedule for any TV or radio channel - πŸ—³οΈ **Polis** – popular votes and elections since 1900, national and cantonal results - 🏒 **Multi-unit** – SRF (DE), RTS (FR), RSI (IT), RTR (RM), SWI (multilingual) - πŸ” **OAuth2** – automatic token management with Client Credentials flow - ☁️ **Dual transport** – stdio for Claude Desktop, Streamable HTTP/SSE for cloud deployment --- ## Prerequisites - Python 3.11+ - **API keys** from [developer.srgssr.ch](https://developer.srgssr.ch) (free registration): 1. Create an account and log in 2. Under "My Apps", create a new application 3. Add the product **SRG SSR PUBLIC API V2** 4. Note your **Consumer Key** and **Consumer Secret** > ⚠️ **Terms of use:** SRG SSR APIs are available for non-commercial use. For commercial use, contact [api@srgssr.ch](mailto:api@srgssr.ch) directly. --- ## Installation ```bash # Clone the repository git clone https://github.com/malkreide/srgssr-mcp.git cd srgssr-mcp # Install pip install -e . ``` Or with `uvx` (no permanent installation): ```bash uvx srgssr-mcp ``` Or via pip: ```bash pip install srgssr-mcp ``` --- ## Quickstart ```bash # Set credentials export SRGSSR_CONSUMER_KEY="your-consumer-key" export SRGSSR_CONSUMER_SECRET="your-consumer-secret" # Start the server (stdio mode for Claude Desktop) srgssr-mcp ``` Try it immediately in Claude Desktop: > *"What will the weather be like in Zurich tomorrow?"* > *"What's on SRF 1 tonight?"* > *"Which popular votes took place in the canton of Bern between 2010 and 2020?"* --- ## Configuration ### Claude Desktop **Minimal (recommended):** ```json { "mcpServers": { "srgssr": { "command": "uvx", "args": ["srgssr-mcp"], "env": { "SRGSSR_CONSUMER_KEY": "your-consumer-key", "SRGSSR_CONSUMER_SECRET": "your-consumer-secret" } } } } ``` **Config file locations:** - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` After saving, restart Claude Desktop completely. ### Other MCP Clients Compatible with Cursor, Windsurf, VS Code + Continue, LibreChat, Cline, and self-hosted models via `mcp-proxy`. Set the same environment variables. ### Cloud Deployment (SSE for browser access) For use via **claude.ai in the browser** (e.g. on managed workstations without local software): ```bash SRGSSR_CONSUMER_KEY=... \ SRGSSR_CONSUMER_SECRET=... \ SRGSSR_MCP_TRANSPORT=streamable-http \ SRGSSR_MCP_HOST=0.0.0.0 \ SRGSSR_MCP_PORT=8000 \ python -m srgssr_mcp.server ``` Transport, host, port and mount path are all driven by environment variables (see `srgssr_mcp.server.Settings`). Valid values for `SRGSSR_MCP_TRANSPORT` are `stdio` (default), `sse`, and `streamable-http`. > πŸ’‘ *"stdio for the developer laptop, SSE for the browser."* --- ## MCP Primitives This server exposes all three orthogonal MCP primitives: | Primitive | Mental model | What's here | |---|---|---| | **Tools** (verbs) | Executable functions / parametrized queries | 15 tools β€” search, list, fetch, aggregate | | **Resources** (nouns) | Cache-friendly passive data behind URIs | EPG entries and immutable votation results | | **Prompts** (recipes) | Reusable workflow templates | Voting analysis & daily briefing | Tools cover parametrized searches (year ranges, free-text, paginated listings) where every call may yield different results. Resources expose stable data points that are safe to cache: a published EPG for a given channel/date, or the final result of a closed Swiss votation. Prompts standardise recurring multi-step analyses so users don't have to phrase them from scratch. ### Resources | URI template | Description | |---|---| | `epg://{bu}/{channel_id}/{date}` | Daily TV/radio program guide for SRF, RTS, RSI (e.g. `epg://srf/srf-1/2026-04-30`) | | `votation://{votation_id}` | Detailed result of a closed Swiss popular vote (e.g. `votation://v1`) | **EPG station ids** β€” hyphenated, and not the same as the livestream ids: | Business unit | TV | Radio | |---|---|---| | SRF | `srf-1`, `srf-2`, `srf-info` | `srf-1`, `srf-2`, `srf-2-kultur`, `srf-3`, `srf-4`, `srf-musikwelle`, `srf-virus` | | RTS | `rts-1`, `rts-2`, `rts-info` | `LA1ERE`, `ESPACE2`, `COULEUR3`, `OPTION_MUSIQUE` | | RSI | `la-1`, `la-2` | `rete-uno`, `rete-due`, `rete-tre` | ### Prompts | Name | Arguments | Purpose | |---|---|---| | `analyse_abstimmungsverhalten` | `votation_id`, `focus` (`stadt_land` / `sprachregionen` / `kantone`) | Structured analysis of a Swiss popular vote | | `tagesbriefing_kanton` | `location`, `channel_id`, `business_unit`, `date` | Daily briefing combining weather and EPG | --- ## Available Tools ### Tool Naming Convention This server uses **`snake_case`** for tool names, following Python ecosystem idioms. While MCP best practice favors `camelCase` for optimal LLM tokenization, `snake_case` remains acceptable and keeps tool names aligned with the underlying Python function identifiers. All tools follow the pattern `srgssr__` with the namespace prefix `srgssr_` and a semantically meaningful `_` suffix (e.g. `srgssr_weather_current`, `srgssr_polis_get_votations`). ### 🌦️ SRF Weather (4 tools) | Tool | Description | Data Source | |---|---|---| | `srgssr_weather_search_location` | Search for a location by name or postal code to obtain a `geolocationId` | SRF Meteo | | `srgssr_weather_current` | Current weather conditions for a Swiss location | SRF Meteo | | `srgssr_weather_forecast_24h` | Hourly 24-hour forecast | SRF Meteo | | `srgssr_weather_forecast_7day` | Daily 7-day forecast | SRF Meteo | ### πŸ“Ί Video (3 tools) | Tool | Description | Data Source | |---|---|---| | `srgssr_video_get_shows` | List TV shows for a business unit (`character_filter` a–z/# selects one initial; omit it to fan out over all 27) | SRG SSR IL | | `srgssr_video_get_episodes` | Retrieve latest episodes of a show | SRG SSR IL | | `srgssr_video_get_livestreams` | List live TV channels | SRG SSR IL | ### πŸŽ™οΈ Audio (3 tools) | Tool | Description | Data Source | |---|---|---| | `srgssr_audio_get_shows` | List radio shows for one radio channel (`channel_id` required β€” the v2 API has no per-unit listing) | SRG SSR IL | | `srgssr_audio_get_episodes` | Retrieve audio episodes of a show | SRG SSR IL | | `srgssr_audio_get_livestreams` | List live radio stations | SRG SSR IL | ### πŸ“… EPG – Electronic Program Guide (1 tool) | Tool | Description | Data Source | |---|---|---| | `srgssr_epg_get_programs` | Daily program schedule for a TV or radio channel | SRG SSR IL | ### πŸ—³οΈ Polis – Swiss Democracy (3 tools) | Tool | Description | Data Source | |---|---|---| | `srgssr_polis_get_votations` | Popular votes since 1900 (national or cantonal) | Polis API | | `srgssr_polis_get_votation_results` | Detailed results of a specific vote | Polis API | | `srgssr_polis_get_elections` | Election results since 1900 | Polis API | ### Supported Business Units | Code | Unit | Language | |---|---|---| | `srf` | SRF (Schweizer Radio und Fernsehen) | German | | `rts` | RTS (Radio TΓ©lΓ©vision Suisse) | French | | `rsi` | RSI (Radiotelevisione svizzera) | Italian | | `rtr` | RTR (Radiotelevisiun Svizra Rumantscha) | Romansh | | `swi` | SWI swissinfo.ch | Multilingual | ### Example Use Cases | Query | Tool | |---|---| | *"Weather in Zurich tomorrow?"* | `srgssr_weather_forecast_24h` | | *"What's on SRF 1 tonight?"* | `srgssr_epg_get_programs` | | *"Latest Tagesschau episodes?"* | `srgssr_video_get_episodes` | | *"Popular votes in Canton Bern 2010–2020?"* | `srgssr_polis_get_votations` | | *"Cantonal results of the mask initiative vote?"* | `srgssr_polis_get_votation_results` | | *"All current RTS radio shows?"* | `srgssr_audio_get_livestreams` β†’ `srgssr_audio_get_shows` | β†’ [More use cases by audience](EXAMPLES.md) β†’ --- ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Claude / LLMβ”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ MCP (stdio) β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ srgssr-mcp Server β”‚ β”‚ β”œβ”€ Weather Tools (4) β”‚ β”‚ β”œβ”€ EPG Tools (1) β”‚ β”‚ β”œβ”€ Polis Tools (3) β”‚ β”‚ β”œβ”€ Video Tools (3) β”‚ β”‚ └─ Audio Tools (3) β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ HTTPS (OAuth2) β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SRG SSR Public APIs β”‚ β”‚ developer.srgssr.chβ”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Data Sources | Source | Data | Access | |---|---|---| | [developer.srgssr.ch](https://developer.srgssr.ch) | SRG SSR PUBLIC API V2 (weather, A/V, EPG, Polis) | OAuth2 (free registration) | **Attribution:** SRG SSR APIs are subject to the [SRG SSR Terms of Use](https://developer.srgssr.ch). --- ## Development Phase This server is in **Phase 1: Read-only Wrapper**. The server exposes only `GET`-style operations against public SRG SSR APIs. There are no write, mutate or delete capabilities by design β€” see [Safety & Limits](#-safety--limits) for the threat-model implications. ### Phase 1 Completion Criteria - [x] 14 read-only tools across five thematic clusters (Weather, Video, Audio, EPG, Polis) - [x] OAuth2 Client Credentials authentication with token caching - [x] Bilingual documentation (EN/DE) - [x] Test suite (unit + live) β€” see [OPS-001](audits/2026-04-30-srgssr-mcp/findings/OPS-001-test-strategy.md) - [x] Structured logging β€” see [OBS-003](#logging) and CHANGELOG - [ ] Production-ready error handling (uniform retry/backoff, typed error envelopes) ### Future Phases - **Phase 2 (Write):** **Not planned.** The SRG SSR Public APIs are read-only by contract; there is no upstream surface to write to. - **Phase 3 (Multi-Agent):** **Evaluation deferred.** Will be reconsidered once user feedback indicates concrete multi-agent workflows that this server should orchestrate (e.g. cross-server aggregation with `swiss-statistics-mcp` or `swiss-transport-mcp`). --- ## MCP Protocol Version `mcp` 2.x serves **two protocol eras** over the same server, and the client's first request on a connection decides which one applies: | Era | Revision | Who reaches it | |---|---|---| | `initialize` handshake | `2024-11-05` … **`2025-11-25`** | What today's clients speak. The server answers with the revision asked for, or with the `2025-11-25` ceiling when the request asks for something newer. | | Per-request envelope | **`2026-07-28`** | A request carrying the `2026-07-28` `_meta` envelope opens a modern connection. | `PROTOCOL_VERSION` in [`src/srgssr_mcp/_app.py`](src/srgssr_mcp/_app.py) names the **modern** era. It is validated at import time against the installed SDK's `SUPPORTED_PROTOCOL_VERSIONS` β€” but that list is backwards-compatible and still contains `2024-11-05`, so the membership check catches a revision being dropped, never a drift. [`tests/test_protocol_version.py`](tests/test_protocol_version.py) holds both eras against the SDK and is the check that catches drift. Bumps are tracked in [CHANGELOG.md](CHANGELOG.md) under the matching release. ### What the server actually puts on the wire The table above used to be an assertion held against SDK constants. [`tests/test_spec_2026_07_28.py`](tests/test_spec_2026_07_28.py) now sends real requests through `MCPServer.streamable_http_app()` over `httpx.ASGITransport` and asserts the answers, in both eras. What that measurement pins: | Property | On the wire | |---|---| | Modern entry | `server/discover`, no `initialize`, no `Mcp-Session-Id`; `supportedVersions: ["2026-07-28"]` | | Handshake ceiling | A client asking for `2026-07-28` via `initialize` gets `2025-11-25` back β€” measured, not inferred | | Server identity | `io.modelcontextprotocol/serverInfo` rides the `_meta` of **every** modern result and carries the installed package version | | Freshness (SEP-2549) | `ttlMs: 300000`, `cacheScope: public` on `server/discover` and the four listing methods | | Tool display names | `title` per [`BaseMetadata`](https://modelcontextprotocol.io/specification), mirrored into `annotations.title` for handshake-era clients, which read only there | | Withdrawn at `2026-07-28` | `ping`, `logging/setLevel`, `resources/subscribe`, `resources/unsubscribe` and `tasks/*` answer `-32601` | | Logging capability | Not declared, and not used: SEP-2577 deprecates it as of `2026-07-28`, and delivery there is a per-request opt-in the server must not presume. Tool activity goes to structlog on stderr β€” see [`src/srgssr_mcp/tools/__init__.py`](src/srgssr_mcp/tools/__init__.py) | The measurement paid for itself on its first run, against 425 green tests and 96% coverage: `serverInfo.version` was the empty string, every `tools/call` came back `isError` (a FastMCP 1.x `ctx.info(msg, **kwargs)` call shape that `mcp` 2.x rejects), and eight tools refused the enum string their own published `inputSchema` advertises (`ConfigDict(strict=True)` demands an enum *instance*, which no JSON payload can carry). None of that is visible from inside the process β€” which is the whole argument for testing the wire. ### Update Policy - SDK dependency updates land via Dependabot (`.github/dependabot.yml`, monthly cadence, grouped under the `mcp-sdk` label) and run the full test suite before merge. - Spec bumps are evaluated on a feature branch against the relevant MCP SDK release; the [official MCP changelog](https://modelcontextprotocol.io/specification/draft/changelog) is the source of truth for breaking changes. - A spec-version bump is always documented in `CHANGELOG.md` and, if it changes the externally observable wire contract, triggers a minor or major release per [Semantic Versioning](https://semver.org/). --- ## Project Structure ``` srgssr-mcp/ β”œβ”€β”€ src/srgssr_mcp/ β”‚ β”œβ”€β”€ __init__.py # Package β”‚ └── server.py # MCPServer: 15 tools, OAuth2 client β”œβ”€β”€ .github/ β”‚ └── workflows/ β”‚ └── ci.yml # GitHub Actions CI (Python 3.11–3.13) β”œβ”€β”€ pyproject.toml # Build configuration (hatchling) β”œβ”€β”€ CHANGELOG.md β”œβ”€β”€ CONTRIBUTING.md # English β”œβ”€β”€ CONTRIBUTING.de.md # German β”œβ”€β”€ SECURITY.md # Security policy (English) β”œβ”€β”€ SECURITY.de.md # Security policy (German) β”œβ”€β”€ LICENSE # MIT β”œβ”€β”€ README.md # This file (English) └── README.de.md # German version ``` --- ## πŸ›‘οΈ Safety & Limits | Aspect | Details | |--------|---------| | **Access** | Read-only β€” the server only reads from SRG SSR APIs and cannot post, modify or delete any content | | **Personal data** | No personal data β€” all endpoints serve public broadcast metadata, weather observations and historical votation/election results | | **Rate limits** | Subject to the tier of your OAuth2 application on [developer.srgssr.ch](https://developer.srgssr.ch); the server adds sensible per-query caps (e.g. max 100 episodes, 50 shows per list call) | | **Timeout** | 30 seconds per upstream API call | | **Authentication** | OAuth2 Client Credentials (free registration); secrets stay local, never logged | | **Licensing & use** | SRG SSR APIs are for **non-commercial use**; commercial use requires written permission from [api@srgssr.ch](mailto:api@srgssr.ch) | | **Terms of Service** | Subject to the [SRG SSR Developer Terms of Use](https://developer.srgssr.ch) β€” users remain responsible for attribution and compliance | --- ## Known Limits - **Rate Limits:** SRG SSR APIs enforce rate limits β€” see [developer.srgssr.ch](https://developer.srgssr.ch) for details on the tier of your OAuth2 application - **Data Freshness:** EPG data may be delayed by up to 6 hours - **Historical Data:** Polis data goes back to 1900 β€” older data is not available - **Geo-Restriction:** Some streaming APIs are only available within Switzerland - **API keys required:** SRG SSR APIs require free OAuth2 credentials from [developer.srgssr.ch](https://developer.srgssr.ch) - **Non-commercial use:** SRG SSR API terms restrict commercial use without explicit permission from [api@srgssr.ch](mailto:api@srgssr.ch) - **Weather coverage:** SRF Meteo covers Switzerland only --- ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) (English) Β· [CONTRIBUTING.de.md](CONTRIBUTING.de.md) (German) --- ## Security For the full security posture, vulnerability reporting process and accepted-risk register, see [SECURITY.md](SECURITY.md) (English) Β· [SECURITY.de.md](SECURITY.de.md) (German). The key egress control is summarised below. ### Egress Allowlist The server implements a **code-layer egress allowlist** (SEC-021, combined with SEC-004 SSRF defense) to prevent unintended external requests. Every outbound HTTP request is validated by `_validate_url_safe()` in [`src/srgssr_mcp/_http.py`](src/srgssr_mcp/_http.py) before it is issued. **Three controls per request:** 1. **HTTPS-only** β€” `http://`, `file://`, `ftp://` and other non-HTTPS schemes are rejected. 2. **Host allowlist** β€” the URL hostname must equal `ALLOWED_HOSTS = {"api.srgssr.ch"}` (exact match β€” subdomain tricks like `api.srgssr.ch.attacker.example` are blocked). One host covers everything: the OAuth2 token endpoint and every data endpoint. 3. **IP blocklist** β€” every resolved IP for the hostname is checked against private, loopback, link-local (incl. `169.254.169.254` cloud-metadata), CGNAT, multicast and reserved ranges (IPv4 + IPv6). Any single match aborts the request β€” defense-in-depth against DNS rebinding. Violations surface as `ValueError` and are mapped to a localized `Konfigurationsfehler: …` message by `_handle_error`, so internal network details never leak to the MCP client. **Adding a new SRG SSR domain:** 1. Update `ALLOWED_HOSTS` in [`src/srgssr_mcp/_http.py`](src/srgssr_mcp/_http.py). 2. Document the reason in the PR and `CHANGELOG.md`. 3. Add a positive test case in `tests/test_unit.py` (mirror `test_validate_url_safe_accepts_public_srgssr_host`). **Network-Layer Egress (for future SSE/HTTP deployments):** see [`docs/network-egress.md`](docs/network-egress.md). For the current `stdio` transport, network-layer controls do not apply β€” the process runs in the MCP client's user context. --- ## Logging The server uses **structured logging** (OBS-003) via [`structlog`](https://www.structlog.org/) with JSON output to **stderr** β€” keeping `stdout` clean for the stdio transport's JSON-RPC traffic. **Format:** - JSON-encoded events, one per line - ISO 8601 UTC `timestamp` on every record - RFC 5424 severity levels: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency` - Per-call bound context: `tool`, `business_unit`, `channel_id`, `query`, etc. **Example output:** ```json {"event": "tool_invoked", "tool": "srgssr_weather_search_location", "query": "Bern", "level": "info", "logger": "mcp.srgssr.weather", "timestamp": "2026-04-30T14:23:45.123Z"} {"event": "tool_succeeded", "tool": "srgssr_weather_search_location", "query": "Bern", "result_count": 3, "matched_variant": "Bern", "level": "info", "logger": "mcp.srgssr.weather", "timestamp": "2026-04-30T14:23:45.456Z"} ``` **Log levels (RFC 5424):** | Level | Used for | |-------|----------| | `debug` | OAuth token cache hits, internal state | | `info` | Tool invocations, successful responses, server lifecycle | | `warning` | Recoverable conditions (rate-limit approaching, unsupported business unit) | | `error` | API failures, timeouts (recoverable) | | `critical` | Credential issues, service degradation | **Configuration:** The default level is `info`. Override via the `SRGSSR_LOG_LEVEL` environment variable (`debug`, `info`, `warning`, `error`, `critical`): ```bash SRGSSR_LOG_LEVEL=debug srgssr-mcp ``` JSON output is aggregator-friendly β€” pipe stderr to Datadog, Splunk, Loki, etc., and filter by structured fields (`tool`, `business_unit`, `level`) without regex parsing. --- ## Testing ```bash # Unit tests (no network required) PYTHONPATH=src pytest tests/ -m "not live" # Integration tests (requires SRG SSR API keys) PYTHONPATH=src pytest tests/ -m "live" # Linting ruff check src/ ``` --- ## Changelog See [CHANGELOG.md](CHANGELOG.md) --- ## Data Sources & Licenses All data exposed by this server is fetched live from a single upstream provider, **SRG SSR Public API V2** (`https://api.srgssr.ch`). Every tool return is a typed [Pydantic `BaseModel`](src/srgssr_mcp/_models.py) that embeds `source` / `license` / `provenance_url` / `fetched_at` at the top level β€” so downstream consumers can record the data origin without round-tripping through this README. The SDK exposes the corresponding `outputSchema` in the `tools/list` manifest so MCP clients can plan follow-up calls precisely. | Cluster | Provider | License | Notes | |---|---|---|---| | Weather | SRF Meteo (api.srgssr.ch) | SRG SSR Terms of Use | Geo-restricted to Switzerland | | Video / Audio / EPG | SRF Β· RTS Β· RSI Β· RTR Β· SWI | SRG SSR Terms of Use | Metadata only β€” stream URLs are not redistributed | | Polis (Votations / Elections) | SRG SSR Polis | SRG SSR Terms of Use | Historical data since 1900 | **Use of the SRG SSR APIs** - Non-commercial use: free, no application required. - Commercial use: written permission required via [api@srgssr.ch](mailto:api@srgssr.ch). This server's MIT license covers the source code only; it does **not** relicense the upstream data. --- ## License MIT License β€” see [LICENSE](LICENSE) The SRG SSR APIs used in this project are subject to the [SRG SSR Terms of Use](https://developer.srgssr.ch). --- ## Author Hayal Oezkan Β· [github.com/malkreide](https://github.com/malkreide) --- ## Credits & Related Projects - **Data:** [SRG SSR Developer Portal](https://developer.srgssr.ch) Β· SRF Meteo Β· Polis API - **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) – Anthropic / Linux Foundation - **Related:** | Server | Description | |---|---| | [zurich-opendata-mcp](https://github.com/malkreide/zurich-opendata-mcp) | City of Zurich open data (OSTLUFT air quality, weather, parking, geodata) | | [swiss-transport-mcp](https://github.com/malkreide/swiss-transport-mcp) | Swiss public transport – OJP 2.0 journey planning, SIRI-SX disruptions | | [swiss-environment-mcp](https://github.com/malkreide/swiss-environment-mcp) | BAFU environmental data – air quality, hydrology, natural hazards | | [swiss-statistics-mcp](https://github.com/malkreide/swiss-statistics-mcp) | BFS STAT-TAB – 682 statistical datasets | | [fedlex-mcp](https://github.com/malkreide/fedlex-mcp) | Swiss federal law via Fedlex SPARQL | **Synergy example:** *"What were the results of the 2020 popular votes in Canton Zurich – and how did turnout compare to the national average?"* β†’ `srgssr-mcp` (Polis, cantonal results) + `swiss-statistics-mcp` (BFS, turnout data) - **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide) ## Installation Run via [`uv`](https://docs.astral.sh/uv/)'s `uvx` β€” no clone or manual install needed. Add to your MCP client config (`mcpServers` for Claude Desktop, Cursor and Windsurf; use a top-level `servers` key for VS Code in `.vscode/mcp.json`): ```json { "mcpServers": { "srgssr-mcp": { "command": "uvx", "args": [ "srgssr-mcp" ] } } } ```