Skip to main content
Glama

πŸ‡¨πŸ‡­ Part of the Swiss Public Data MCP Portfolio

πŸ“Ί srgssr-mcp

Version License: MIT Python 3.11+ MCP CI Data Source

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


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 (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 directly.


Installation

# 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):

uvx srgssr-mcp

Or via pip:

pip install srgssr-mcp

Quickstart

# 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):

{
  "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):

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_<domain>_<action> with the namespace prefix srgssr_ and a semantically meaningful <domain>_<action> 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 β†’


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

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.


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 for the threat-model implications.

Phase 1 Completion Criteria

  • 14 read-only tools across five thematic clusters (Weather, Video, Audio, EPG, Polis)

  • OAuth2 Client Credentials authentication with token caching

  • Bilingual documentation (EN/DE)

  • Test suite (unit + live) β€” see OPS-001

  • Structured logging β€” see OBS-003 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 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 holds both eras against the SDK and is the check that catches drift. Bumps are tracked in CHANGELOG.md under the matching release.

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 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.


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; 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

Terms of Service

Subject to the SRG SSR Developer Terms of Use β€” users remain responsible for attribution and compliance


Known Limits

  • Rate Limits: SRG SSR APIs enforce rate limits β€” see 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

  • Non-commercial use: SRG SSR API terms restrict commercial use without explicit permission from api@srgssr.ch

  • Weather coverage: SRF Meteo covers Switzerland only


Contributing

See CONTRIBUTING.md (English) Β· CONTRIBUTING.de.md (German)


Security

For the full security posture, vulnerability reporting process and accepted-risk register, see SECURITY.md (English) Β· 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 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.

  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. 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 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:

{"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):

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

# 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


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 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.

This server's MIT license covers the source code only; it does not relicense the upstream data.


License

MIT License β€” see LICENSE

The SRG SSR APIs used in this project are subject to the SRG SSR Terms of Use.


Author

Hayal Oezkan Β· github.com/malkreide


Server

Description

zurich-opendata-mcp

City of Zurich open data (OSTLUFT air quality, weather, parking, geodata)

swiss-transport-mcp

Swiss public transport – OJP 2.0 journey planning, SIRI-SX disruptions

swiss-environment-mcp

BAFU environmental data – air quality, hydrology, natural hazards

swiss-statistics-mcp

BFS STAT-TAB – 682 statistical datasets

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)

Installation

Run via 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):

{
  "mcpServers": {
    "srgssr-mcp": {
      "command": "uvx",
      "args": [
        "srgssr-mcp"
      ]
    }
  }
}

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/malkreide/srgssr-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server