Skip to content

Statistik.bs MCP ​

Gives models the indicators, time series and web tables of the Statistisches Amt Basel-Stadt on statistik.bs.ch.

Endpointhttps://mcp.statistik.bs.ch/mcp
API referencemcp.statistik.bs.ch/docs
CodeDCC-BS/mcp-statistikportal
Imageghcr.io/dcc-bs/mcp-statistikportal
Data sourcePublic JSON endpoints of the statistikportal Nuxt app (see llms.txt) and its Meilisearch index

The portal has two kinds of content that the server covers:

  • About 1,080 indicators in the Indikatorenportal. Each has metadata (reading aid, explanations, sources) and a time series.
  • About 220 web tables (Webtabellen), the Excel tables of the statistical yearbook, often with several sheets.

The data.bs.ch portal holds many of the same numbers as raw datasets. Use the Data.bs.ch MCP for those. statistik.bs.ch adds the explanations, the official selection of indicators and the yearbook tables.

Tools ​

ToolWhat it does
search_indicatorsRanked search over the indicator index: title, then subtitle, subtopic, topic or set, then reading aid. Filters thema, unterthema, kennzahlenset, raeumliche_gliederung
get_indicatorMetadata of one indicator: unit, reading aid (lesehilfe), explanations, sources (quellenangabe), links, spatial units, last update. HTML becomes plain text
get_indicator_dataThe time series as headers and rows with numbers parsed, plus unit, source, notes on definitions and series breaks, and latest, the newest period with values
get_facetsValid values with counts for thema, unterthema, kennzahlenset, raeumliche_gliederung and darstellungsart
get_tableOne web table by file name, table code or download URL. Returns the sheet list, one sheet as columns and rows, footnotes, the Steckbrief (contact, reference period, source), the Excel download_url and a csv_url
search_portalFull-text search over all portal content: articles, web tables, maps, dashboards, OGD datasets and indicators. Only registered when MEILISEARCH_URL and MEILISEARCH_KEY are set. It is on in production

Examples:

text
search_indicators(search="Arbeitslosenquote")
search_indicators(search="Leerwohnungen", raeumliche_gliederung="Wohnviertel")
get_indicator_data(indicator_id=5813, max_rows=24)
get_table(table="t03.4.04", max_rows=12, from_end=True)
get_table(table="t10.1.04", sheet="Jul")

Every result that can be cited has title, portal_url and aktualisierungsdatum (the Stand). For web tables, source and aktualisierungsdatum come from the Steckbrief.

How the search works ​

The server downloads the indicator index, keeps it in memory for an hour (INDEX_CACHE_TTL) and ranks it itself.

  • Use German terms. Umlauts may be written ä, ae or a.
  • Plural and case endings and compounds are tolerated: Leerwohnungen finds Leerwohnungsquote.
  • Ties go to an exact title, then the shorter title, then the newer update.
  • match tells the model how good the hits are: all_terms, partial (with matched_terms and a hint) or none.

The index holds only the indicators shown in the Indikatorenportal. About 2,500 indicator files exist. The others can be read by id with get_indicator and get_indicator_data, but nothing lists them. search_portal finds content outside the index.

Shaping data for a model ​

The portal delivers every value as a string, and web tables keep the layout of the yearbook with multi-row headers and group rows. The server reshapes both before the model sees them.

  • get_indicator_data parses numbers and derives unit from the subtitle ("in %" becomes %, "Anzahl Personen" becomes Personen, null if unclear). Up to 600 characters of explanations and reading aid go along as notes, so definitions and series breaks reach the model.
  • get_table joins multi-row headers into one column name ("Schweizer Bevölkerung – Männlich") and turns group rows such as Total into a Gruppe column. Numbers are rounded to six significant digits. Placeholders like … stay text.
  • Long series keep the newest rows. Output stops at max_rows (default 200) and at about 12,000 characters, with truncated: true and a hint. get_table pages with offset or from_end=True.
  • Invalid input fails with a ToolError that names the fix: an unknown sheet lists the valid sheets, a bad indicator id points to search_indicators.

Configuration ​

All settings are optional.

VariableDefaultMeaning
STATISTIK_PORTAL_DOMAINstatistik.bs.chPortal that serves /api/*, for example a test host
STATISTIK_PORTAL_API_URLhttps://<domain>Where the server fetches from. Links in results stay https://<domain>
INDEX_CACHE_TTL3600Seconds the indicator index stays in memory
MEILISEARCH_URLnoneEnables search_portal, together with the key
MEILISEARCH_KEYnoneA search-only key for the product index
MCP_ALLOWED_HOSTSunset, protection offAllowed Host headers, see Hosting

In the facade deployment, STATISTIK_PORTAL_API_URL is http://statistik.bs.ch, because from inside that network the portal only answers on port 80. Links in tool results still use https.

Connecting a client ​

Hosted: add https://mcp.statistik.bs.ch/mcp as an MCP tool server. No key is needed.

Locally over stdio, in claude_desktop_config.json or ~/.cursor/mcp.json:

json
{
  "mcpServers": {
    "statistik-bs": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/DCC-BS/mcp-statistikportal", "statistik-bs-mcp"]
    }
  }
}

The repository's docs/api-analyse.md lists gaps in the portal API that the server works around.

Developed with ❤️ by the DCC. Documentation released under the MIT License.