{
  "markdown": "> 🇨🇭 **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**\n\n# 📊 swiss-statistics-mcp\n\n![Version](https://img.shields.io/badge/version-0.7.2-blue)\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[![No Auth Required](https://img.shields.io/badge/auth-none%20required-brightgreen)](https://github.com/malkreide/swiss-statistics-mcp)\n![CI](https://github.com/malkreide/swiss-statistics-mcp/actions/workflows/ci.yml/badge.svg)\n\n> MCP Server for Swiss Federal Statistical Office (BFS) data via STAT-TAB PxWeb API — 682 datasets across 21 themes, no authentication required\n\n[🇩🇪 Deutsche Version](README.de.md)\n\n---\n\n### Demo\n\n![Demo: Claude using bfs_education_stats](docs/assets/demo.svg)\n\n---\n\n## Maturity\n\nThis server is **Alpha (0.x)** as per the [PyPI classifier](https://pypi.org/classifiers/). Until 1.0:\n\n- Tool names, input schemas, and output JSON keys MAY change between minor versions\n- Pin cloud deployments to a specific git tag, not `main`\n- Production use is acceptable for read-only Open Data scenarios; consider it experimental for anything user-facing\n\nSee [CHANGELOG.md](./CHANGELOG.md) for breaking changes.\n\n---\n\n## Overview\n\n`swiss-statistics-mcp` provides AI-native access to the Swiss Federal Statistical Office (BFS) via the STAT-TAB PxWeb API, without authentication:\n\n| Property | Details |\n|----------|---------|\n| **API** | STAT-TAB PxWeb API v1 |\n| **Endpoint** | `https://www.pxweb.bfs.admin.ch/api/v1/` |\n| **Provider** | Swiss Federal Statistical Office (BFS) |\n| **Datasets** | 682 tables across 21 thematic areas |\n| **Languages** | German (`de`), French (`fr`), Italian (`it`), English (`en`) |\n| **Licence** | Open Government Data (OGD) — [BFS Terms of Use](https://www.bfs.admin.ch/bfs/en/home/grundlagen/nutzungsbedingungen.html) |\n| **Authentication** | None — fully public |\n\n**Anchor demo query:** *\"How many students attended lower secondary schools in the canton of Zurich in 2024?\"* — real BFS figures, no hallucination.\n\n---\n\n## Features\n\n- 📊 **15 tools**: 8 across 21 statistical themes (682 datasets) + a 4-tool commune/historical **reference layer** + 2 construction/real-estate tools + a price-index tool\n- 🔍 **Full-text search** across the entire BFS data catalogue\n- 🎓 **Convenience tools** for education statistics and population data\n- 🏗️ **Construction statistics** — new buildings/dwellings and building investment incl. the Arbeitsvorrat leading indicator\n- 🏠 **Price indices** — construction price index (Baupreisindex, parsed series) and residential property price index (IMPI) via the BFS DAM/CKAN sources\n- 🏔️ **Cross-cantonal comparison** for any table and variable\n- 🔓 **No API key required** — all data under open licences\n- ☁️ **Dual transport** — stdio (Claude Desktop) + Streamable HTTP (cloud)\n\n---\n\n## Prerequisites\n\n- Python 3.11+\n- [uv](https://github.com/astral-sh/uv) (recommended) or pip\n\n---\n\n## Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/malkreide/swiss-statistics-mcp.git\ncd swiss-statistics-mcp\n\n# Install\npip install -e .\n# or with uv:\nuv pip install -e .\n```\n\nOr with `uvx` (no permanent installation):\n\n```bash\nuvx swiss-statistics-mcp\n```\n\n---\n\n## Quickstart\n\n```bash\n# stdio (for Claude Desktop)\npython -m swiss_statistics_mcp.server\n\n# Streamable HTTP, loopback only (default: host=127.0.0.1, port=8000)\npython -m swiss_statistics_mcp.server --http --port 8000\n\n# Streamable HTTP, all interfaces (only behind a reverse proxy with access control)\nMCP_HOST=0.0.0.0 python -m swiss_statistics_mcp.server --http --port 8000\n# or\npython -m swiss_statistics_mcp.server --http --host 0.0.0.0 --port 8000\n```\n\nTry it immediately in Claude Desktop:\n\n> *\"How many teachers worked in the canton of Zurich in 2023?\"*\n> *\"What is the population of canton Bern broken down by age?\"*\n> *\"Compare the social assistance rate across all cantons for 2022.\"*\n\n---\n\n## Configuration\n\n### Claude Desktop\n\nEdit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"swiss-statistics\": {\n      \"command\": \"python\",\n      \"args\": [\"-m\", \"swiss_statistics_mcp.server\"]\n    }\n  }\n}\n```\n\nOr with `uvx`:\n\n```json\n{\n  \"mcpServers\": {\n    \"swiss-statistics\": {\n      \"command\": \"uvx\",\n      \"args\": [\"swiss-statistics-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### Cursor / Windsurf / VS Code + Continue\n\nThe configuration syntax is identical to Claude Desktop. The file name depends on the client:\n\n- **Cursor:** `.cursor/mcp.json` in the project folder, or `~/.cursor/mcp.json` globally\n- **Windsurf:** `~/.codeium/windsurf/mcp_config.json`\n- **VS Code + Continue:** `.continue/config.json`\n\n### Cloud Deployment (SSE for browser access)\n\nFor use via **claude.ai in the browser** (e.g. on managed workstations without local software).\n\n> ⚠️ **Security note — this server has no authentication.** A public URL\n> turns it into an open proxy to the BFS API on your deployment's IP. Any\n> client with the URL can drive the tools, consume your platform quota, and\n> attribute traffic to your IP. Two mitigations, in order of preference:\n>\n> 1. **Put it behind access control** — Render's «Private Service», Cloudflare\n>    Access, or a reverse proxy with Basic-Auth / IP allowlist in front of the\n>    container.\n> 2. **Accept it as a public open-data proxy** — only acceptable because all\n>    data is BFS OGD (Public Open Data) and tools are read-only.\n>\n> The server binds to `127.0.0.1` by default. To expose it on a container\n> port you must explicitly set `MCP_HOST=0.0.0.0` (e.g. as a Render env var)\n> or pass `--host 0.0.0.0`. Do not do this without one of the mitigations above.\n\n**Render.com:**\n1. Push/fork the repository to GitHub\n2. On [render.com](https://render.com): New Web Service → connect GitHub repo\n3. Set environment variable: `MCP_HOST=0.0.0.0`\n4. Set start command: `python -m swiss_statistics_mcp.server --http --port 8000`\n5. In claude.ai under Settings → MCP Servers, add: `https://your-app.onrender.com/sse`\n\n> 💡 *\"stdio for the developer laptop, SSE for the browser.\"*\n\n---\n\n## Output Schema\n\nSince `v0.2.0`, every tool returns a typed Pydantic model rather than a JSON\nstring. FastMCP serializes these as structured content so MCP clients can\nread fields directly.\n\n```python\n# Old (pre-0.2.0)\nresult = await bfs_get_data(...)        # str\ndata = json.loads(result)               # dict\nprint(data[\"rows_total\"])\n\n# New (>= 0.2.0)\nresult = await bfs_get_data(...)        # DataTableResult\nprint(result.rows_total)                # 1000\nprint(result.truncated)                 # True\n```\n\nEvery result carries `error: str | None` and `hint: str | None` at the top\nlevel — `result.error is None` means success. Data-returning tools\n(`bfs_get_data`, `bfs_education_stats`, `bfs_population`,\n`bfs_compare_cantons`) additionally expose `truncated: bool`,\n`rows_total: int`, and `rows_returned: int` for machine-readable cap\ndetection.\n\n| Tool | Result type |\n|------|-------------|\n| `bfs_browse_catalog` | `BrowseCatalogResult` |\n| `bfs_search_tables` | `SearchTablesResult` |\n| `bfs_get_table_metadata` | `TableMetadataResult` |\n| `bfs_get_data` | `DataTableResult` |\n| `bfs_education_stats` | `DataTableResult` |\n| `bfs_population` | `DataTableResult` |\n| `bfs_compare_cantons` | `DataTableResult` |\n| `bfs_featured_datasets` | `FeaturedDatasetsResult` |\n| `lookup_commune` | `LookupCommuneResult` |\n| `resolve_historical_commune` | `ResolveHistoricalCommuneResult` |\n| `list_communes` | `ListCommunesResult` |\n| `search_historical_series` | `SearchHistoricalSeriesResult` |\n| `bfs_construction_activity` | `ConstructionActivityResult` |\n| `bfs_construction_investment` | `ConstructionInvestmentResult` |\n| `bfs_price_index` | `PriceIndexResult` |\n\nReference-layer results additionally carry `source` (attribution) and `provenance` (`live_api` \\| `cached`); `SearchHistoricalSeriesResult` also carries `licence_note` with the mandatory HSSO NonCommercial notice. The construction and price-index results carry `source` + `provenance` on the same envelope pattern.\n\n---\n\n## Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `bfs_featured_datasets` | Curated list of highly relevant datasets (focus on education and demographics) |\n| `bfs_browse_catalog` | Browse the catalogue: all 21 themes (no `theme_code`), or all tables in a theme (e.g. `theme_code=\"15\"` = Education and Science) |\n| `bfs_search_tables` | Full-text search across the entire data catalogue (682 datasets) |\n| `bfs_get_table_metadata` | Variables, values and metadata for a specific table |\n| `bfs_get_data` | Data retrieval with optional filters by dimensions and values |\n| `bfs_education_stats` | Convenience tool: teachers, pupils, demographic scenarios, scholarships |\n| `bfs_population` | Resident population by canton, year, age structure or sex |\n| `bfs_compare_cantons` | Cross-cantonal comparison for any table and any variable |\n| `lookup_commune` | Resolve a commune by name or BFS number as of a given date (canton, validity, LINDAS URI) |\n| `resolve_historical_commune` | Map a historical BFS number onto today's number(s) — re-key old statistics across fusions |\n| `list_communes` | List all communes of a canton as of a given date |\n| `search_historical_series` | Search long-run time series in Historical Statistics of Switzerland (HSSO) |\n| `bfs_construction_activity` | New buildings & dwellings per commune (yearly), incl. dwelling room-size mix |\n| `bfs_construction_investment` | Building investment & Arbeitsvorrat (leading indicator) by region/canton/commune |\n| `bfs_price_index` | Construction price index (Baupreisindex, parsed series) / residential property price index (IMPI, source links) |\n\nFour of these tools form the **reference layer** of the portfolio (see [Join Keys](#join-keys)): they turn official BFS commune numbers into a reliable join key and let you re-key statistics that predate a municipal merger. The two `bfs_construction_*` tools cover STAT-TAB theme 09 (Bau- und Wohnungswesen) — see [Construction sources](#construction-sources). `bfs_price_index` covers price indices that are **not** in STAT-TAB — see [Price-index sources](#price-index-sources).\n\n### Construction sources\n\n| Cube ID | Title | Coverage | Used by |\n|---------|-------|----------|---------|\n| `px-x-0904030000_106` | Neu erstellte Gebäude mit Wohnungen nach Gemeinde, Gebäudetyp | 2013– | `bfs_construction_activity` |\n| `px-x-0904030000_105` | Neu erstellte Wohnungen nach Gemeinde, Anzahl Zimmer | 2013– | `bfs_construction_activity` |\n| `px-x-0904010000_205` | Bauinvestitionen und Arbeitsvorrat nach Grossregion/Kanton/Gemeinde | 1994– | `bfs_construction_investment` |\n\n> The pre-2013 Gemeinde-level building series lives in the discontinued cubes `px-x-0904030000_101`/`_104` (1995–2012), which use a different geo coding and are not queried by these tools. Building/dwelling figures are the **consolidated official yearly** statistics — for up-to-date register states and the construction pipeline, cross-validate against `swiss-housing-mcp` (deliberate redundancy).\n\n### Price-index sources\n\n`bfs_price_index` covers two indices that are **not** published via STAT-TAB. Their datasets live on [opendata.swiss](https://opendata.swiss) (CKAN); the data files themselves are [BFS DAM assets](https://dam-api.bfs.admin.ch).\n\n| Index | Source | Returns |\n|-------|--------|---------|\n| `baupreisindex` | opendata.swiss dataset *Schweizerischer Baupreisindex (Multibasen)* → DAM **XLSX** asset | Parsed national semi-annual index series (Schweiz, Baugewerbe Total), with the base period |\n| `impi` | opendata.swiss dataset *Schweizerischer Wohnimmobilienpreisindex (IMPI)* → DAM **PDF/HTML** assets | Official source links only — BFS does not publish a machine-readable IMPI series |\n\n> Two quirks are handled for you: `ckan.opendata.swiss` returns **HTTP 403** to default User-Agents, so every call sends a custom `swiss-statistics-mcp/<version>` User-Agent; and DAM assets **mix formats**, so the XLSX is selected by verifying the response `content-type` (PDFs are skipped). Results are cached for 24 h.\n\n### Example Use Cases\n\n| Query | Tool |\n|-------|------|\n| *\"How many teachers worked in Zurich in 2023?\"* | `bfs_education_stats` |\n| *\"How will upper secondary enrolment develop until 2031?\"* | `bfs_education_stats` |\n| *\"What is the population of canton Zurich by age?\"* | `bfs_population` |\n| *\"Compare the social assistance rate across all cantons\"* | `bfs_compare_cantons` |\n| *\"Is there data on school buildings?\"* | `bfs_search_tables` |\n| *\"Which Zurich communes have merged since 2000, and onto which of today's BFS numbers must I re-key old statistics?\"* | `resolve_historical_commune` |\n| *\"List all communes of canton Glarus today\"* | `list_communes` |\n| *\"Find long-run series on population in HSSO\"* | `search_historical_series` |\n| *\"How many new dwellings were built in Winterthur since 2018, by room size?\"* | `bfs_construction_activity` |\n| *\"What is the building investment and Arbeitsvorrat for canton Zurich?\"* | `bfs_construction_investment` |\n| *\"How has the construction price index moved since 2015?\"* | `bfs_price_index` |\n\n[→ More use cases by audience →](EXAMPLES.md)\n\n---\n\n## Themes\n\n| Code | Theme | Code | Theme |\n|------|-------|------|-------|\n| 01 | Population | 12 | Money, banks, insurance |\n| 02 | Territory and environment | 13 | Social security |\n| 03 | Work and income | 14 | Health |\n| 04 | National economy | **15** | **Education and science** |\n| 05 | Prices | 16 | Culture, media, information society |\n| 06 | Industry and services | 17 | Politics |\n| 07 | Agriculture and forestry | 18 | General government |\n| 08 | Energy | 19 | Crime and criminal justice |\n| 09 | Construction and housing | 20 | Economic and social situation |\n| 10 | Tourism | 21 | Sustainable development |\n| 11 | Mobility and transport | | |\n\n---\n\n## Architecture\n\n```\n┌─────────────────┐     ┌──────────────────────────────┐     ┌──────────────────────────┐\n│   Claude / AI   │────▶│  Swiss Statistics MCP          │────▶│  BFS STAT-TAB            │\n│   (MCP Host)    │◀────│  (MCP Server)                │◀────│  PxWeb API v1            │\n└─────────────────┘     │                              │     └──────────────────────────┘\n                        │  15 Tools                    │\n                        │  + commune/historical ref    │\n                        │  + construction (theme 09)   │\n                        │  + price indices (DAM/CKAN)  │\n                        │  Stdio | Streamable HTTP     │\n                        │                              │\n                        │  No authentication required  │\n                        └──────────────────────────────┘\n```\n\n### Data Source Characteristics\n\n| Source | Protocol | Coverage | Auth | Licence |\n|--------|----------|----------|------|---------|\n| BFS STAT-TAB | PxWeb REST API | 682 tables, 21 themes | None | OGD |\n| BFS AGVCH (commune register) | REST (CSV/XLSX) | Snapshots, mutations, correspondances | None | OGD |\n| HSSO (historical statistics) | Static XLSX dumps | ~750 long-run tables | None | CC BY-NC-SA 3.0 |\n| BFS DAM + opendata.swiss (CKAN) | CKAN metadata + DAM XLSX/PDF | Baupreisindex, IMPI | None (custom UA required) | OGD |\n\n### Architecture decision\n\n- **AGVCH commune register → Architecture A (live-API-only).** The [official REST service](https://www.agvchapp.bfs.admin.ch/de/home) (`snapshot` / `correspondances` / `mutations` / `levels`) is a clean, versioned, no-auth API — verified live on 2026-07-19 — so the commune tools query it directly with a 24 h in-memory cache and the shared retry policy. No dump fallback is needed. **Finding:** the live snapshot CSV header uses `Inscription,Radiation,Rec_Type_fr` (not the `Einschreibung,Streichung` names printed in the API PDF), and `HistoricalCode` is **not** globally unique across levels — the `Parent` link is disambiguated by tier when deriving a commune's canton.\n- **HSSO → Architecture C (dump-only).** HSSO offers no API, only static per-table XLSX at stable URLs (`/get/{CHAPTER}.{NN}{suffix}.xlsx`). `search_historical_series` builds a cached title index from the chapter pages and returns the stable download URL. HSSO is licensed **CC BY-NC-SA 3.0 (NonCommercial)** — different from this server's OGD baseline — so every HSSO response carries an explicit NonCommercial notice in `licence_note`.\n\n---\n\n## Join Keys\n\nThe reference layer exists so that data from different servers in the [Swiss Public Data MCP Portfolio](https://github.com/malkreide) can be joined reliably. Three identifiers are the portfolio-wide keys:\n\n| Key | What it identifies | Canonical form | Notes |\n|-----|--------------------|----------------|-------|\n| **BFS commune number** (`BfsCode`) | A political commune | integer, e.g. `261` (Zürich) | The primary join key across statistics, geo, education and health data. Stable LINDAS/Linked-Data URI: `https://ld.admin.ch/municipality/{BfsCode}`. **Not stable over time** — a merger issues a new number, so historical data must be re-keyed via `resolve_historical_commune`. |\n| **EGID** | A single building (Eidg. Gebäudeidentifikator) | 9-digit integer | The join key for building/dwelling-level data (GWR, energy, addresses). A commune contains many EGIDs; `BfsCode` is the commune each EGID sits in. |\n| **Canton abbreviation** | A canton | two letters, e.g. `ZH` | The coarsest geographic key. Derivable from any commune via its `Parent` chain (exposed as `canton_abbr`). |\n\n**Why re-keying matters.** BFS commune numbers change whenever communes merge, split, or move canton. Statistics published before a merger use the old number; joining them to today's data without re-keying silently drops or misattributes rows. `resolve_historical_commune(bfs_number, from_date, to_date)` returns the `resolves_to` set — the current number(s) old figures must be aggregated onto — plus the `mutation_path` (the fusions/renamings, with dates). Other portfolio servers are meant to mirror this contract conceptually so the same key resolves the same way everywhere.\n\n**Example (anchor query).** *\"Which Zurich communes have merged since 2000?\"* — e.g. old `132 Hirzel` and `133 Horgen` both re-key onto today's `295 Horgen`; `134/140/142` onto `293 Wädenswil`.\n\n---\n\n## Project Structure\n\n```\nswiss-statistics-mcp/\n├── src/swiss_statistics_mcp/\n│   ├── __init__.py              # Package\n│   └── server.py                # 15 tools\n├── tests/\n│   └── test_server.py           # Unit + integration tests (mocked HTTP)\n├── .github/workflows/ci.yml     # GitHub Actions (Python 3.11/3.12/3.13)\n├── pyproject.toml\n├── CHANGELOG.md\n├── CONTRIBUTING.md              # English\n├── CONTRIBUTING.de.md           # German version\n├── SECURITY.md                  # English\n├── SECURITY.de.md               # German version\n├── LICENSE\n├── README.md                    # This file (English)\n└── README.de.md                 # German version\n```\n\n---\n\n## Observability\n\nThe server emits one **JSON log line per tool call** on stderr:\n\n```jsonc\n{\"ts\": \"2026-05-20T04:02:28\", \"level\": \"INFO\", \"logger\": \"swiss_statistics_mcp\",\n \"event\": \"tool_start\", \"tool\": \"bfs_browse_catalog\", \"rid\": \"1091cb73\", \"params_keys\": [\"theme_code\", \"lang\", \"limit\"]}\n{\"ts\": \"2026-05-20T04:02:28\", \"level\": \"INFO\", \"logger\": \"swiss_statistics_mcp\",\n \"event\": \"tool_end\", \"tool\": \"bfs_browse_catalog\", \"rid\": \"1091cb73\", \"status\": \"ok\", \"duration_ms\": 303}\n```\n\n- `rid` — 8-char correlation id linking `tool_start` and `tool_end` for the same call\n- `params_keys` — sorted list of input field names (no values, no PII)\n- `duration_ms` — per-call latency on the `tool_end` event\n- `status` — `\"ok\"` or `\"error\"`; `error_type` is added when a tool raises\n\nRender and other cloud platforms can index these directly for per-tool latency\ndashboards and error-rate alerts. Set `MCP_LOG_LEVEL=DEBUG` for verbose output\nor `WARNING` to suppress per-call events.\n\n> ℹ️ Logs go to **stderr** so they never collide with the MCP protocol on\n> stdio transport (which uses stdout).\n\n---\n\n## Resilience\n\nThe server absorbs transient BFS-API hiccups before they reach the LLM:\n\n- **Retries** — `5xx`, `429`, and network errors are retried up to 3 times with\n  exponential backoff (0.5s → 4s). `4xx` errors surface immediately so client\n  bugs aren't masked. Tunable via `MCP_RETRY_MAX_ATTEMPTS`,\n  `MCP_RETRY_WAIT_INITIAL`, `MCP_RETRY_WAIT_MAX` env vars.\n- **Metadata cache** — Table metadata (variables, value domains, last_updated)\n  is cached in-memory per `(table_id, lang)` for 1h. Cold list/detail flows\n  warm the cache; subsequent calls return instantly.\n- **Concurrency cap** — Fan-out metadata fetches in `bfs_browse_catalog`\n  (theme mode) run in parallel bounded by `FANOUT_CONCURRENCY = 5`. For `limit=20` this\n  cuts wall-clock from ~20s sequential to ~4s, without overwhelming the\n  upstream API.\n\n---\n\n## Known Limitations\n\n- **PxWeb API:** Rate limiting may apply for rapid successive queries; the server uses a 1-hour cache for the catalogue index and a 1-hour cache for table metadata\n- **Language:** Dataset titles and dimension values are in German by default; French, Italian and English coverage varies by table\n- **JSON-STAT2:** Some complex cross-tabulations may return large result sets; use dimension filters to narrow queries\n- **Commune register (AGVCH):** Live snapshot CSV headers use `Inscription/Radiation/Rec_Type_fr` (not the `Einschreibung/Streichung` names in the API PDF); `HistoricalCode` is not globally unique across levels, so the canton is derived by walking the `Parent` chain one tier at a time. Snapshots/mutations are cached for 24 h.\n- **HSSO:** Licensed **CC BY-NC-SA 3.0 (NonCommercial)** — attribution required, no commercial use; every response carries this in `licence_note`. HSSO exposes no per-table period filter, so `search_historical_series`'s `period` argument is an informational hint only — verify the actual span in the XLSX. `search_historical_series` returns the stable XLSX download URL, not the parsed series values.\n- **PxWeb commune codes are not consistent across cubes.** In `px-x-0904030000_106`/`_107` the value code IS the zero-padded BFS number (`0261`); in `px-x-0904030000_105` it is an opaque sequential id (`160`) and the BFS number appears only in the label (`......0261 Zürich`). `bfs_construction_activity` resolves each cube against its own live dimension values by matching the label-embedded BFS number, never by guessing the code.\n- **Construction coverage:** the current Gemeinde-level building series starts in **2013**; `bfs_construction_activity` therefore accepts `since_year >= 2013`. Values are the consolidated official yearly statistics. Building investment values (`bfs_construction_investment`) are in **1000 CHF**; the `Arbeitsvorrat` is the following year's building volume (a monetary leading indicator).\n- **Price indices (`bfs_price_index`):** the **IMPI** (residential property price index) is published by BFS only as **PDF/HTML** — there is no machine-readable series — so `index=\"impi\"` returns the official source links plus an explicit limitation, not values. The **Baupreisindex** XLSX is parsed to the national semi-annual series (Schweiz, Baugewerbe Total); regional/object-type breakdowns exist in the source XLSX but are not returned. The DAM asset ids are resolved live from CKAN metadata (never hard-coded), because they change on republish; if the upstream XLSX structure changes, the tool degrades to a clear error rather than returning wrong values.\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. This server builds no ASGI app to send an `initialize` through, so\nthe gate asserts the SDK constants rather than a measured response — the\nweaker form, named rather than left unsaid.\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 API key required)\nPYTHONPATH=src pytest tests/ -m \"not live\"\n\n# Integration tests (live API calls)\npytest tests/ -m \"live\"\n```\n\n---\n\n## Safety & Limits\n\n- **Read-only:** All tools perform HTTP GET requests only — no data is written, modified, or deleted.\n- **No personal data:** STAT-TAB returns aggregated statistical datasets. No personally identifiable information (PII) is processed or stored by this server.\n- **Rate limits:** The PxWeb API is a public endpoint without documented rate limits; avoid tight loops over the full 682-table catalogue. The server enforces a 30s timeout per request and caches the catalogue index for 1 hour.\n- **Data freshness:** BFS publishes updated figures periodically (not real-time). Figures reflect the state of the upstream database at query time.\n- **Terms of service:** Data is subject to the [BFS Terms of Use (OGD)](https://www.bfs.admin.ch/bfs/en/home/grundlagen/nutzungsbedingungen.html). All STAT-TAB data is published as Open Government Data and may be freely used with attribution.\n- **No guarantees:** This server is a community project, not affiliated with the Swiss Federal Statistical Office. Availability depends on the upstream BFS API.\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n---\n\n## Security\n\nRead-only, no PII, no authentication, single fixed BFS endpoint. See\n[SECURITY.md](SECURITY.md) for the full security posture and accepted-risk\ndecisions.\n\n---\n\n## License\n\nMIT License — see [LICENSE](LICENSE)\n\n---\n\n## Author\n\nHayal Oezkan · [malkreide](https://github.com/malkreide)\n\n---\n\n## Credits & Related Projects\n\n- **BFS:** [www.bfs.admin.ch](https://www.bfs.admin.ch/) — Swiss Federal Statistical Office\n- **STAT-TAB:** [www.pxweb.bfs.admin.ch](https://www.pxweb.bfs.admin.ch/) — PxWeb database interface\n- **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) — Anthropic / Linux Foundation\n- **Related:** [swiss-cultural-heritage-mcp](https://github.com/malkreide/swiss-cultural-heritage-mcp) — SIK-ISEA, Nationalmuseum, Nationalbibliothek\n- **Related:** [fedlex-mcp](https://github.com/malkreide/fedlex-mcp) — Swiss federal law via Fedlex SPARQL\n- **Related:** [zurich-opendata-mcp](https://github.com/malkreide/zurich-opendata-mcp) — CKAN, weather, air quality, City of Zurich\n- **Related:** [swiss-transport-mcp](https://github.com/malkreide/swiss-transport-mcp) — OJP journey planning, SIRI-SX disruptions\n- **Related:** [global-education-mcp](https://github.com/malkreide/global-education-mcp) — UNESCO UIS and OECD Education at a Glance\n- **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide)\n\n<!-- mcp-name: io.github.malkreide/swiss-statistics-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    \"swiss-statistics-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"swiss-statistics-mcp\"\n      ]\n    }\n  }\n}\n```\n<!-- END GENERATED: install -->\n",
  "bytes": 28723,
  "sha": "32522acea057e95b67146186f2c1f6910d1b6a2ef7c53f851494601ac925d3ca",
  "repo_slug": "malkreide/swiss-statistics-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_malkreide_swiss_statistics_mcp_a482e5a1/readme"
}