{
  "markdown": "> 🇨🇭 **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**\n\n# 📡 bakom-mcp\n\n[![Version](https://img.shields.io/badge/version-3.0.0-blue)](https://github.com/malkreide/bakom-mcp/releases)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-purple)](https://modelcontextprotocol.io/)\n[![Auth](https://img.shields.io/badge/auth-none%20required-brightgreen)](https://www.bakom.admin.ch/bakom/en/home/digital-switzerland-and-internet/open-data.html)\n![CI](https://github.com/malkreide/bakom-mcp/actions/workflows/ci.yml/badge.svg)\n\n> MCP server for BAKOM open data – broadband, mobile coverage, media and Swiss telecom statistics.\n\n[🇩🇪 Deutsche Version](README.de.md)\n\n<p align=\"center\">\n  <img src=\"assets/demo.svg\" alt=\"Demo: Claude queries fibre and 5G coverage via MCP tool call\" width=\"720\">\n</p>\n\n---\n\n## Overview\n\n**bakom-mcp** connects AI assistants like Claude to the Swiss Federal Office of Communications (BAKOM) open data infrastructure. It enables natural-language queries about broadband availability, 5G/4G coverage, mobile antenna locations, BAKOM datasets on radio and television, and telecommunications statistics — all without API keys.\n\nAll data is published as Open Government Data (OGD) on opendata.swiss / geo.admin.ch under **CC BY 4.0** — see the [Data Licence](#data-licence) section below for attribution requirements.\n\n**Anchor demo query:** *\"Which school buildings in district 7 do not yet have fibre optic connectivity?\"*\n\n> `bakom_multi_standort_konnektivitaet` delivers the comparison table automatically.\n\n[→ More use cases by audience →](EXAMPLES.md)\n\n---\n\n## Scope\n\n### What this server does\n\n✓ Read-only queries against three public BAKOM/Confederation APIs:\n  - `api3.geo.admin.ch` / `wms.geo.admin.ch` (broadband, mobile coverage, antennas)\n  - `ckan.opendata.swiss` (telecom statistics, dataset metadata)\n\n✓ Returns aggregated, anonymous data — no personal data, no household-level identification.\n\n✓ Bound to Swiss WGS84 coordinates (lat 45.8–47.9, lon 5.9–10.6) via Pydantic input validation.\n\n✓ Egress is locked to a [code-layer allow-list](src/bakom_mcp/server.py) of the six known data-source hosts.\n\n### What this server does not\n\n✗ Send data anywhere (read-only, no write tools).\n\n✗ Access the local filesystem (no path-traversal surface).\n\n✗ Use authentication tokens (none required — all sources are public OGD).\n\n✗ Cache or persist user inputs across calls.\n\n✗ Execute shell commands or arbitrary code (no `subprocess`/`os.system`/`eval`).\n\n---\n\n## Features\n\n- 📶 **Broadband availability** – Fixed-line coverage at 30/100/300/500/1000 Mbit/s (250×250m grid)\n- 🔌 **Fibre status** – FTTB/FTTH availability per location\n- 📍 **Multi-location comparison** – Connectivity check for up to 20 locations simultaneously\n- 📱 **Mobile coverage** – 5G/4G/3G outdoor coverage (100×100m grid)\n- 📡 **Antenna search** – Mobile and broadcast transmitters within a configurable radius\n- 📺 **Radio & TV datasets** – Full-text search across BAKOM's radio and television datasets on opendata.swiss\n- 🗞️ **Media landscape** – BAKOM media structure reports and datasets\n- 📊 **Telecom statistics** – Fixed-line, mobile, broadband market data via opendata.swiss\n- 🗂️ **Broadband Atlas catalogue** – All BAKOM dataset layers with direct API links\n- 🔓 **No authentication required** – All data is Open Government Data (OGD)\n\n---\n\n## Prerequisites\n\n- Python 3.11+\n- `uv` or `pip` for installation\n- Internet connection (live APIs: geo.admin.ch, opendata.swiss)\n\n---\n\n## Installation\n\n```bash\n# Recommended: uvx (no permanent installation required)\nuvx bakom-mcp\n\n# Or install with pip\npip install bakom-mcp\n\n# Development install\ngit clone https://github.com/malkreide/bakom-mcp\ncd bakom-mcp\npip install -e \".[dev]\"\n```\n\n---\n\n## Quickstart\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"bakom\": {\n      \"command\": \"uvx\",\n      \"args\": [\"bakom-mcp\"]\n    }\n  }\n}\n```\n\n**Config file locations:**\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n### Cloud / HTTP Transport\n\n```bash\npython -m bakom_mcp.server --http\n# Server running at http://localhost:8050/mcp\n```\n\nConfiguration via environment variables (see [`.env.example`](.env.example)):\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `BAKOM_MCP_HOST` | `127.0.0.1` | Bind address. Set to `0.0.0.0` only on trusted networks (warning logged). |\n| `BAKOM_MCP_PORT` | `8050` | TCP port. |\n| `BAKOM_MCP_CORS_ORIGINS` | _(empty)_ | Comma-separated allowed origins for browser clients. Empty = CORS disabled. |\n\n### Docker\n\nA hardened container image is provided. Suitable for cloud deployments behind a reverse proxy (Caddy, Traefik, nginx).\n\n```bash\n# Build & run via compose (recommended)\ndocker compose up --build\n\n# Or via docker run\ndocker build -t bakom-mcp:latest .\ndocker run --rm \\\n  --read-only \\\n  --cap-drop=ALL \\\n  --security-opt=no-new-privileges \\\n  --tmpfs /tmp:rw,size=16M \\\n  -p 127.0.0.1:8050:8050 \\\n  bakom-mcp:latest\n```\n\nThe image runs as **non-root** (UID 10001), uses a **read-only filesystem**, drops **all Linux capabilities** and refuses **privilege escalation**. Resource limits are configured in [`docker-compose.yml`](docker-compose.yml) (256 MB memory, 0.5 CPU, 64 PIDs). The default port mapping binds to `127.0.0.1` only — for public exposure, terminate TLS and CORS at a reverse proxy.\n\n### Cursor / VS Code / LibreChat\n\n```json\n{\n  \"bakom\": {\n    \"command\": \"uvx\",\n    \"args\": [\"bakom-mcp\"],\n    \"transport\": \"stdio\"\n  }\n}\n```\n\n> 💡 *\"stdio for the developer laptop, HTTP/SSE for the browser.\"*\n\n---\n\n## Available Tools (12)\n\n### Broadband & Connectivity\n\n| Tool | Description |\n|------|-------------|\n| `bakom_broadband_coverage` | Fixed-line coverage at a coordinate (30–1000 Mbit/s) |\n| `bakom_glasfaser_verfuegbarkeit` | FTTB/FTTH fibre availability |\n| `bakom_multi_standort_konnektivitaet` | Connectivity comparison for up to 20 locations |\n\n### Mobile & Transmitters\n\n| Tool | Description |\n|------|-------------|\n| `bakom_mobilfunk_abdeckung` | 5G/4G/3G outdoor coverage |\n| `bakom_sendeanlagen_suche` | Mobile antennas within a configurable radius |\n| `bakom_frequenzdaten` | Radio/TV transmitter sites near a location |\n\n### Media & RTV\n\n| Tool | Description |\n|------|-------------|\n| `bakom_rtv_suche` | Search BAKOM radio/TV datasets on opendata.swiss |\n| `bakom_medienstruktur_info` | Swiss media landscape datasets |\n| `bakom_aktuell` | Recently updated BAKOM datasets on a topic (catalogue, not press releases) |\n| `bakom_medien_statistik` | Market shares, reach and programme structure from the BAKOM cubes on LINDAS |\n\n### Statistics & Catalogue\n\n| Tool | Description |\n|------|-------------|\n| `bakom_telekomstatistik_uebersicht` | Telecom statistics from opendata.swiss |\n| `bakom_breitbandatlas_datensaetze` | Full catalogue of Broadband Atlas layers |\n| `bakom_check_api_status` | 🔍 Health check for all configured data sources |\n\n---\n\n## Example Prompts\n\n```\nWhat is the broadband situation at Schulhaus Leutschenbach (47.4148, 8.5654)?\n\nCompare 5G and fibre coverage for these three school buildings: [coordinates]\n\nWhich radio stations are licensed in canton Zurich?\n\nShow me the current BAKOM position on 5G frequency allocation.\n\nList all Broadband Atlas datasets available via geo.admin.ch.\n```\n\n---\n\n## Safety & Limits\n\n| Aspect | Details |\n|--------|---------|\n| **Access** | Read-only (`readOnlyHint: true`) — the server cannot modify or delete any data |\n| **Personal data** | No personal data — all sources are aggregated, public open data |\n| **Rate limits** | Built-in per-query caps (max 50 antennas, max 20 locations, max 50 RTV results) |\n| **Timeout** | 20 seconds per API call |\n| **Authentication** | No API keys required — all 3 APIs are publicly accessible |\n| **Licences** | All data under CC0 / open licences (Open Government Data) |\n| **Terms of Service** | Subject to ToS of the respective data sources: [geo.admin.ch](https://www.geo.admin.ch/en/general-terms-and-conditions-fsdi), [opendata.swiss](https://opendata.swiss/en/terms-of-use) |\n\n---\n\n## Data Sources\n\n| Source | Data | Authentication |\n|--------|------|----------------|\n| [geo.admin.ch](https://api3.geo.admin.ch) | Broadband Atlas, mobile coverage, antenna locations | None |\n| [opendata.swiss](https://opendata.swiss) | BAKOM datasets, telecom statistics | None |\n| [lindas.admin.ch](https://lindas.admin.ch/query) | BAKOM media statistics cubes (SPARQL) | None |\n\nAll data is published under open licences (CC0 / OGD).\n\n### Architecture decision — media statistics\n\n`bakom_medien_statistik` uses **Architecture A (live API only)**, verified live on 2026-08-13:\n\n- The SPARQL endpoint is `https://lindas.admin.ch/query`; the `/sparql` path documented elsewhere returns 404.\n- The OFCOM cubes live in the named graph `https://lindas.admin.ch/ofcom/cube`. Without an explicit `FROM`, a query hits the default graph and sees 2010 cubes from every federal office instead of 540.\n- A bulk download exists for only 137 of 540 cubes, so a dump-first architecture would cover a quarter of the data.\n- An unknown graph answers HTTP 200 with zero rows — a silent empty, not an error. Requests carry retry with exponential backoff; egress-policy violations are not retried.\n\n**Scope, stated in the tool description because the README does not reach the model:** the cubes cover the programmes that were *surveyed*, not the full inventory. LINDAS's own count cube reports 199+39+17 radio programmes for 2020, while the `Programm` dimension across all cubes exposes 128 labels — some of which are the same station spelled differently (`Energy BE` / `Energy Bern`), plus `Durchschnitt`, which is an aggregate rather than a station. This tool is a statistics source, not a broadcaster register; licensed broadcasters are listed in the [RTV database](https://rtvdb.ofcomnet.ch/de), which offers no machine-readable interface.\n\n---\n\n## Synergies with the MCP Portfolio\n\n**bakom-mcp** can be combined with other servers in the portfolio for multi-dimensional queries:\n\n```\nzurich-opendata-mcp  →  school building addresses\n         +\n    bakom-mcp         →  fibre and 5G status\n         =\n\"Digital equity\" dashboard for all school districts\n```\n\nFurther combinations:\n- `srgssr-mcp` + `bakom-mcp` → Media coverage and broadcast network\n- `swiss-statistics-mcp` + `bakom-mcp` → Telecom market development\n- `fedlex-mcp` + `bakom-mcp` → Regulatory context (RTVG, FMG)\n\n---\n\n## Project Structure\n\n```\nbakom-mcp/\n├── src/bakom_mcp/\n│   ├── __init__.py          # Package\n│   ├── server.py            # MCP server (12 tools, 2 resources)\n│   └── py.typed             # PEP 561 type marker\n├── tests/\n│   └── test_integration.py  # Integration tests (live APIs)\n├── assets/\n│   └── demo.svg             # Demo flow diagram\n├── .github/workflows/\n│   ├── ci.yml               # CI: lint, syntax, import, tests\n│   └── publish.yml          # PyPI publish on release\n├── .gitignore\n├── pyproject.toml           # Build config (hatchling)\n├── CHANGELOG.md\n├── CONTRIBUTING.md          # Contribution guide (English)\n├── CONTRIBUTING.de.md       # Contribution guide (German)\n├── SECURITY.md              # Security policy (English)\n├── SECURITY.de.md           # Security policy (German)\n├── LICENSE                  # MIT License\n├── README.md                # This file (English)\n└── README.de.md             # German version\n```\n\n---\n\n## MCP Protocol Version\n\nThis server speaks **two protocol eras** over the same endpoint. The client's\nfirst request on a connection decides which one applies; a later claim from the\nother era is refused.\n\n| Era | Revision | Who reaches it |\n|---|---|---|\n| `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. |\n| Per-request envelope | **`2026-07-28`** | A request carrying the `2026-07-28` `_meta` envelope opens a modern connection. |\n\nBoth revisions are pinned in\n[`tests/test_protocol_version.py`](tests/test_protocol_version.py) and asserted\nagainst the installed SDK, so a Dependabot bump of `mcp` cannot move either one\nsilently. The handshake ceiling is measured against a live `initialize` through\nthe assembled ASGI stack, not read off a constant name.\n\nNote that the SDK's `LATEST_PROTOCOL_VERSION` is an alias for the **modern**\nera, not for the handshake era — pinning against it alone would leave the era\nthat current clients actually negotiate free to drift.\n\n**Update policy.** When the gate fails, do not edit the constant blindly: read\nthe spec changelog between the two revisions, verify the server still behaves,\nthen move the constant, this section, `README.de.md` and\n[`CHANGELOG.md`](CHANGELOG.md) together.\n\n---\n\n## Testing\n\n```bash\n# Unit tests (no network required)\nPYTHONPATH=src pytest tests/ -m \"not live\"\n\n# Integration tests (live APIs, internet required)\nPYTHONPATH=src pytest tests/ -m \"live\"\n```\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) ([🇩🇪 Deutsch](CONTRIBUTING.de.md)) for guidelines on reporting bugs, suggesting features, and submitting pull requests.\n\n---\n\n## Security\n\nTo report a vulnerability, see the [Security Policy](SECURITY.md) ([🇩🇪 Deutsch](SECURITY.de.md)). Please report security issues privately via GitHub Security Advisories rather than public issues.\n\n---\n\n## Software Licence\n\nMIT License — see [LICENSE](LICENSE).\n\n## Data Licence\n\nThe BAKOM open data delivered through this server is published under **[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.de)**. When using or redistributing tool outputs, attribute the source as:\n\n> *Source: Federal Office of Communications (BAKOM) via opendata.swiss / geo.admin.ch · Licence: CC BY 4.0*\n\nTool outputs already include this footer automatically. The Markdown response format ends with the attribution line; downstream applications that consume the JSON format should propagate the source/licence metadata to their end users.\n\n---\n\n## Author\n\nHayal Oezkan · [github.com/malkreide](https://github.com/malkreide)\n\n---\n\n## Credits & Related Projects\n\n- **Data:** [BAKOM Open Data](https://www.bakom.admin.ch/bakom/en/home/digital-switzerland-and-internet/open-data.html) – Federal Office of Communications (OFCOM/BAKOM)\n- **Geodata:** [geo.admin.ch](https://api3.geo.admin.ch) – swisstopo / Federal Geodata Infrastructure\n- **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) – Anthropic / Linux Foundation\n- **Related:** [zurich-opendata-mcp](https://github.com/malkreide/zurich-opendata-mcp) – MCP server for Zurich city open data\n- **Related:** [swiss-transport-mcp](https://github.com/malkreide/swiss-transport-mcp) – Swiss public transport MCP server\n- **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide)\n\n---\n\n*Part of the Swiss Open Data MCP portfolio — public data deserves public interfaces.*\n\n<!-- mcp-name: io.github.malkreide/bakom-mcp -->\n\n<!-- BEGIN GENERATED: install -->\n## Installation\n\nRun 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`):\n\n```json\n{\n  \"mcpServers\": {\n    \"bakom-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"bakom-mcp\"\n      ]\n    }\n  }\n}\n```\n<!-- END GENERATED: install -->\n",
  "bytes": 15858,
  "sha": "4972249863fb9919b4e07ecb4fdc82947661f7e65128b34ba8d51d7c227a34ab",
  "repo_slug": "malkreide/bakom-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_malkreide_bakom_mcp_93454408/readme"
}