bag-health-mcp
Part of the Swiss Public Data MCP Portfolio β connecting AI models to Swiss public data sources.
MCP server for Swiss public health data. Its core is the Swiss Federal Office of Public Health (BAG) Infectious Disease Dashboard (IDD) β epidemiological surveillance for 51 pathogens (influenza, COVID-19, measles, wastewater surveillance, and more) β extended with a multi-source health-indicator layer over the Swiss Health Observatory (Obsan), the Versorgungsatlas (health-care supply atlas, with cantonal series) and Sucht Schweiz (HBSC youth survey). All read-only, public Open Government Data.
What You Can Do
"Wie ist die aktuelle Grippesituation im Kanton ZΓΌrich verglichen mit den letzten Wochen?"
β bag_health_mcp__get_canton_situation(canton="ZH")
"Gibt es aktuell einen Masernausbruch in der Schweiz?"
β bag_health_mcp__get_disease_data(series_id="measles/cases/incValue/year", canton="all")
"Wie entwickelt sich das SARS-CoV-2-Signal im Abwasser?"
β bag_health_mcp__list_series(topic="wastewater_viral_load")
β bag_health_mcp__get_disease_data(series_id="wastewater_viral_load/NA/value/date", ...)
"Welche Krankheitsdaten stellt das BAG aktuell bereit?"
β bag_health_mcp__list_diseases()
"Wie hat sich der Alkoholkonsum bei 15-JΓ€hrigen seit 2010 entwickelt?" # π― anchor query
β bag_health_mcp__search_health_indicators(source="suchtschweiz", topic="alkohol")
β bag_health_mcp__get_indicator_series(source="suchtschweiz",
indicator_id="monam/alkoholkonsum-alter-11-15", region="ZH", year_from=2010)
β More use cases by audience β
π― Anchor demo query β Β«Wie hat sich der Alkoholkonsum bei 15-JΓ€hrigen im Kanton ZΓΌrich seit 2010 entwickelt, und wie steht der Kanton im Schweizer Vergleich da?Β» The HBSC youth series (via Obsan) answers the Switzerland-wide trend since 2010 with 95% confidence intervals. This particular indicator is national only, so the response includes a
region_notesaying so (HBSC is not cantonally representative). Most other Obsan indicators are published by canton β see the note on cuts below. These are aggregated population statistics β not individual advice. Seedocs/tool-design-health-indicators.md.
Tools
Infectious-disease surveillance (BAG IDD):
| Tool | Description |
|---|---|
bag_health_mcp__list_diseases | List all 51 disease topics, grouped by category |
bag_health_mcp__list_series | List data series for a specific disease |
bag_health_mcp__get_series_details | Get available filter dimensions (canton, age, sex) |
bag_health_mcp__get_disease_data | Fetch time-series surveillance data |
bag_health_mcp__get_canton_situation | Situational overview for a canton (Schulamt use case) |
bag_health_mcp__list_export_files | List available complete export datasets |
bag_health_mcp__download_export | Download raw CSV/JSON export |
bag_health_mcp__get_data_version | Current data version (updated every Wednesday) |
Health indicators β Obsan, Versorgungsatlas & Sucht Schweiz (multi-source):
| Tool | Description |
|---|---|
bag_health_mcp__search_health_indicators | Search indicators by source (obsan / versorgungsatlas / suchtschweiz), topic, region, year range |
bag_health_mcp__get_indicator_series | Fetch one indicator's time series, naming which cut it is (variant: national / by canton / by age class / by social position / distribution) and which others exist. Pass region='ZH' for the cantonal cut; 95% CIs throughout |
β οΈ Aggregated population statistics only. The indicator tools serve population-level aggregates (prevalences/metrics by age/sex/region) β not individual advice, diagnosis or case assessment, and no personal data. This is stated in both tool descriptions and every response (
aggregate_statistics_notice), and matters especially forsuchtschweiz(HBSC), which touches prevention topics in a school context. Sources: Obsanind.obsan.admin.ch(clean JSON API); Sucht Schweiz HBSC via the Obsan mirror (national); Versorgungsatlas returns a cantonal year/value series (26 cantons + aCHnational total, with 95% CIs and a canton-vs-CH ratio) from the Tarifpool. See the per-source probe notes.
Obsan publishes an indicator in several cuts, not one series. Measured over 60 catalogue entries on 2026-08-08: 50 have a cantonal cut (
kg), 49 one by age class (ag), 24 one by social position (sd) β and only 3 the plain national one (g). They are different measurements with different units, soget_indicator_seriesnames the cut it returned invariantand lists the rest invariants_available, rather than presenting one as a stand-in for another. Eight of the 60 publish no series at all; that case fails with its reason instead of returning an empty result. The census is recorded and dated intests/fixtures/obsan_variant_census.json.
Tool annotations
All tools carry MCP tool annotations so a host can reason about them without calling. Every tool is identical here β it only ever reads from the public, allow-listed data sources (BAG IDD, Obsan, Versorgungsatlas):
| Annotation | Value | Meaning |
|---|---|---|
readOnlyHint | true | No tool mutates any state. |
destructiveHint | false | No destructive side effects. |
idempotentHint | true | Repeating a call has no additional effect. |
openWorldHint | true | Tools reach an external system (the upstream data APIs). |
A host may therefore treat all calls as safe, cacheable reads. The values are
declared once as READ_ONLY in server.py and applied to all 10 tools.
MCP Primitives
This server uses all three MCP primitives, each for what it is best at:
Tools (10) β live, parameterised actions that call the IDD API (above).
Resources β static, read-only reference data a host can fetch and cache, no arguments or upstream call needed:
| Resource URI | Description |
|---|---|
bag://reference/cantons | Canton codes accepted by the tools (incl. FL, all) |
bag://reference/disease-categories | Disease-topic taxonomy by category |
bag://reference/data-licence | Source, attribution and licence terms |
Prompts β reusable, parameterised workflows a host can surface (e.g. as slash-commands):
| Prompt | Arguments | Purpose |
|---|---|---|
canton_situation_brief | canton | Draft a Schulamt public-health situation brief |
outbreak_check | disease, canton | Check whether a disease is currently elevated |
Live surveillance data stays behind Tools (it is parameterised and changes weekly); fixed reference data is exposed as Resources; recommended multi-tool workflows are packaged as Prompts.
Relevance for Schools & City Administration
Schulamt / KreisschulbehΓΆrden:
- Monitor influenza and ARI incidence in your canton
- Single measles case β alert for schools with low vaccination coverage
- Pertussis tracking β protect unvaccinated infants (siblings of school children)
Stadtverwaltung / KI-Fachgruppe:
- Public Health Reporting with structured weekly data
- Wastewater surveillance as 1-week lead indicator before clinical cases
Synergy with portfolio:
bag-epl-mcpβ "What treatments are listed?" (EPL medication database)bag-health-mcpβ "What is currently spreading?" (surveillance data)
Data Source
- IDD API:
https://api.idd.bag.admin.chβ No authentication required - Update cycle: Every Wednesday
- Coverage: Switzerland + Liechtenstein (FL), 26 cantons
- Topics: 51 pathogens, 1386 data series
Datenquellen & Lizenzen / Data sources & licences
| Source | Provider | Licence | Attribution required |
|---|---|---|---|
| Infectious Disease Dashboard (IDD) | Federal Office of Public Health (FOPH / BAG) | opendata.swiss Open Government Data β free use, source attribution required (Swiss OGD terms, CC BY-equivalent) | Yes |
| Health indicators | Obsan β Swiss Health Observatory (ind.obsan.admin.ch) | No explicit machine-readable licence; treat as Swiss OGD practice β free use, cite the per-indicator source | Yes |
| Health-care supply atlas | Versorgungsatlas (BAG/Obsan, versorgungsatlas.ch) | Same (Swiss OGD practice, cite source) | Yes |
| HBSC youth survey | Sucht Schweiz β HBSC, obtained via the Obsan mirror | Same (Swiss OGD practice, cite Β«Sucht Schweiz β HBSCΒ») | Yes |
Required citation: Federal Office of Public Health FOPH β Infectious Disease
Dashboard (IDD), open data via opendata.swiss. For the indicator tools, each
response's provenance.source names the concrete upstream (e.g. Β«Sucht Schweiz β
HBSCΒ» via Obsan). Every tool response carries attribution in a provenance block
(attribution + license fields) so downstream consumers can surface it
automatically.
Architecture:
βββββββββββββββββββ api.idd.bag.admin.ch (IDD API, no auth)
MCP Host β bag-health-mcp ββββΆ ind.obsan.admin.ch (Obsan JSON API)
(Claude, etc.) βββΆβ MCP SDK ββββΆ versorgungsatlas.ch (indicator catalogue)
β 10 Tools β all HTTPS, egress allow-listed, no auth
βββββββββββββββββββ
Installation
Claude Desktop (stdio)
{
"mcpServers": {
"bag-health": {
"command": "uvx",
"args": ["bag-health-mcp"]
}
}
}
Cloud / HTTP
pip install bag-health-mcp
python -m bag_health_mcp.server --http --port 8000
Transport, host and port are set via environment variables β MCP_TRANSPORT
(http/stdio), MCP_HOST, MCP_PORT β which is the recommended way for
deployments (the --http flag still works for local use). The server binds to
127.0.0.1 by default so a local HTTP server is not exposed to the network.
Container/cloud deployments bind all interfaces by setting MCP_HOST=0.0.0.0
explicitly β the provided Dockerfile does this.
β οΈ Security: HTTP transport exposes the server on the network. Only bind beyond
127.0.0.1in a network-isolated environment β never directly on a public/shared network. Binding to a non-localhost host logs a warning at startup. The default stdio transport has no network surface. Seedocs/security-posture.md.
HTTP auth (optional): set MCP_AUTH_TOKEN to require
Authorization: Bearer <token> on every HTTP request (401 otherwise). Unset =
no auth (fine for stdio/local). This gates who may invoke the server; for real
user identity, front it with a gateway.
CORS (browser clients): set MCP_CORS_ORIGINS to a comma-separated origin
allow-list to enable cross-origin browser access; the Mcp-Session-Id header is
exposed so stateful sessions work. Empty = no cross-origin (never a wildcard).
Host allow-list (DNS rebinding): set MCP_ALLOWED_HOSTS to a comma-separated
list of the names this server is reachable under, including the port, e.g.
bag.example.ch:8000. Requests arriving under any other Host are rejected
with 421; loopback stays allowed so container health checks keep working.
Unset on a non-localhost bind, the check is left off and a warning is logged β
that is the gateway-fronted deployment, where the gateway validates Host. It
is not guessed: on 0.0.0.0 the reachable name is unknowable here, and a wrong
guess would reject the very deployment it is meant to protect.
This is independent of MCP_AUTH_TOKEN. The token says who is asking; this
says under which name the server is addressed. A rebinding attack runs in a
browser that already holds the token.
For running at scale (session affinity, resource limits, MCP gateway), see the
deployment & scaling guide and the reference
manifests in deploy/.
Logging: the server emits structured JSON logs (one object per line, with an
RFC 5424 severity) to stderr β stdout is reserved for the stdio JSON-RPC
transport. Set the level with MCP_LOG_LEVEL (default INFO).
Tracing (optional): install the telemetry extra and point the server at an OTLP collector to get OpenTelemetry spans per tool-call plus instrumented outbound HTTP:
pip install "bag-health-mcp[telemetry]"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4318"
# optional: OTEL_SERVICE_NAME=bag-health-mcp
Tracing is a no-op unless both the extra is installed and an OTEL_*
endpoint is set. Spans carry only the tool name and (on error) the exception
class β never tool arguments, cantons or surveillance data.
Available Disease Topics
| Category | Topics |
|---|---|
| Respiratory | influenza, covid19, acute_respiratory_infection, respiratory_pathogens |
| Enteric | campylobacteriosis, salmonellosis, ehec, listeriosis, hepatitis_a/e |
| STI & Bloodborne | hiv, aids, syphilis, gonorrhea, hepatitis_b/c, chlamydiosis |
| Vaccine-preventable | measles, pertussis, rubella, tetanus, diphtheria, ipd, meningo |
| Vector-borne | lyme_borreliosis, tick-borne_encephalitis, dengue, malaria, zika |
| Wastewater | wastewater_viral_load, wastewater_sequencing |
Demo
Claude asking about the influenza situation in canton Zurich β single tool call, structured result, actionable German-language summary.
MCP Protocol Version
This server speaks two protocol eras over the same endpoint. The client's first request on a connection decides which one applies; a later claim from the other era is refused.
| 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. |
Both revisions are pinned in
tests/test_protocol_version.py and asserted
against the installed SDK, so a Dependabot bump of mcp cannot move either one
silently. The handshake ceiling is measured against a live initialize through
the assembled ASGI stack, not read off a constant name.
Note that the SDK's LATEST_PROTOCOL_VERSION is an alias for the modern
era, not for the handshake era β pinning against it alone would leave the era
that current clients actually negotiate free to drift.
Update policy. When the gate fails, do not edit the constant blindly: read
the spec changelog between the two revisions, verify the server still behaves,
then move the constant, this section, README.de.md and
CHANGELOG.md together.
Safety & Limits
| Aspect | Details |
|---|---|
| Access | Read-only β no write operations possible |
| Egress | Code-layer allow-list: the server only contacts three public data hosts (api.idd.bag.admin.ch, ind.obsan.admin.ch, www.versorgungsatlas.ch), HTTPS-only, enforced on every request incl. redirect hops (SSRF/SEC-004 + SEC-021). Network-layer companion policy in deploy/networkpolicy.yaml |
| Personal data | None β all sources are aggregated/anonymised (BAG IDD at canton level by law; indicators are population aggregates by age/sex/region) |
| Rate limits | No published IDD API rate limit; server caps responses at 104 data points per call by default (limit_weeks param) |
| Timeout | 30 s per API call |
| Authentication | No API keys required β all data publicly accessible |
| Data licence | opendata.swiss OGD β free use, source attribution required (CC BY-equivalent). FOPH IDD must be cited; see Data sources & licences |
| Terms of Service | Subject to BAG IDD API ToS |
Known Limitations
- Beta API: IDD API is labelled
v0.1 betaβ schema may change without notice - Weekly cadence: Data is not real-time; updated Wednesdays only
- Canton granularity: Some rare diseases have insufficient cases for canton-level data (suppressed for privacy)
- Age groups: Available dimensions vary by disease series; use
bag_health_mcp__get_series_detailsto check
Compliance
- ISDS (Stadt ZΓΌrich): a draft information-security protection-needs
classification (Schutzbedarfsanalyse per Grundwert + measures mapping) is in
docs/isds-klassifikation.md. It is a technically-grounded draft pending ISBO/OIZ sign-off β not a binding classification. - Data classification (Schulamt): the data is classified ΓFFENTLICH / BUI
(public OGD, no personal data, aggregated at canton level with small cells
suppressed at source). Draft scheme + aggregation-risk note in
docs/datenklassifikation-schulamt.md; the aggregatingbag_health_mcp__get_canton_situationtool surfaces this in its response. - Security posture: lethal-trifecta assessment (the server is strictly
read-only β not affected), secret-management decision (no secrets β public
data), and network-exposure notes are in
docs/security-posture.md. - Phase architecture: this is a Phase 1 (read-only) server; write/send
capabilities are deferred behind documented prerequisites. See
docs/roadmap.md. - Reporting vulnerabilities: see the security policy for how to report security issues privately.
Contributing
See CONTRIBUTING.md (Deutsch).
Security
See SECURITY.md (Deutsch) for the security posture and how to report a vulnerability confidentially.
License
Code: MIT (see LICENSE).
Data: BAG IDD is Open Government Data on opendata.swiss under free use with mandatory source attribution (Swiss OGD terms, CC BY-equivalent) β not public domain. Cite the Federal Office of Public Health FOPH (IDD) when reusing the data; see Data sources & licences.
Author
Hayal Oezkan Β· github.com/malkreide
Related Portfolio Servers
swiss-statistics-mcpβ BFS demographic databag-epl-mcpβ BAG medication reimbursement listzurich-opendata-mcpβ City of Zurich open 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": {
"bag-health-mcp": {
"command": "uvx",
"args": [
"bag-health-mcp"
]
}
}
}