π¨π Part of the Swiss Public Data MCP Portfolio
πΏ swiss-environment-mcp
<p align="center"> <img src="assets/demo.svg" alt="Demo: Claude queries NABEL air quality via a swiss-environment-mcp tool call and gets a WHO 2021 compliance check" width="820"> </p>MCP server connecting AI models to Swiss environmental data from BAFU β air quality, hydrology, natural hazards, wildfire danger and open environmental datasets.
Overview
swiss-environment-mcp gives AI assistants like Claude direct access to real-time environmental data from Swiss federal authorities β no API keys required. Air quality readings from the national NABEL monitoring network, hydrological gauging stations, natural hazard bulletins, and the full BAFU dataset catalogue are all accessible through a single standardised MCP interface.
The server covers four thematic clusters: air quality (NABEL), hydrology, natural hazards, and the BAFU open data catalogue. Each cluster maps to a group of purpose-built tools that translate raw agency data into clean JSON responses.
Anchor demo query: "What is the current air quality at the NABEL station ZΓΌrich-Kaserne β and does it comply with WHO 2021 guidelines?" β More use cases by audience β
Features
- π¬οΈ Air quality monitoring β 16 NABEL stations, NOβ/Oβ/PM10/PM2.5/SOβ/CO, Swiss LRV + WHO 2021 limit checks
- π§ Hydrology β water levels, flow rates, temperatures across Swiss gauging stations
- π¨ Flood warnings β active alerts filtered by danger level and canton
- ποΈ Natural hazard bulletin β SLF/BAFU bulletin in DE/FR/IT/EN, region-specific warnings
- π₯ Wildfire danger β canton- and region-level fire danger index
- π¦ BAFU open data catalogue β search and retrieve environmental datasets via CKAN
- π No authentication required β all data sources are publicly accessible
- βοΈ Dual transport β stdio for Claude Desktop, Streamable HTTP/SSE for cloud deployment
Prerequisites
- Python 3.11+
- No API keys needed β all endpoints are publicly accessible without authentication
Installation
# Clone the repository
git clone https://github.com/malkreide/swiss-environment-mcp.git
cd swiss-environment-mcp
# Install
pip install -e .
Or with uvx (no permanent installation):
uvx swiss-environment-mcp
Or via pip:
pip install swiss-environment-mcp
Quickstart
# Start the server (stdio mode for Claude Desktop)
swiss-environment-mcp
Try it immediately in Claude Desktop:
"What is the current air quality at NABEL station ZΓΌrich-Kaserne?" "Are there any active flood warnings in Switzerland right now?" "What is the wildfire danger level in Canton Valais?"
Configuration
Claude Desktop
Minimal (recommended):
{
"mcpServers": {
"swiss-environment": {
"command": "uvx",
"args": ["swiss-environment-mcp"],
"env": {}
}
}
}
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.
Cloud Deployment (SSE for browser access)
For use via claude.ai in the browser (e.g. on managed workstations without local software):
Render.com (recommended):
- Push/fork the repository to GitHub
- On render.com: New Web Service β connect GitHub repo
- Render detects
render.yamlautomatically - In claude.ai under Settings β MCP Servers, add:
https://your-app.onrender.com/sse
Docker:
docker build -t swiss-environment-mcp .
docker run -p 8000:8000 swiss-environment-mcp
π‘ "stdio for the developer laptop, SSE for the browser."
Available Tools
π¬οΈ Air Quality / NABEL (3 tools)
| Tool | Description | Data Source |
|---|---|---|
env_nabel_stations | List all 16 NABEL monitoring stations with location type and canton | NABEL / BAFU |
env_nabel_current | Current air quality data for a station (NOβ, Oβ, PM10, PM2.5, SOβ, CO) | NABEL / BAFU |
env_air_limits_check | Compare a measurement against Swiss LRV limits and WHO 2021 guidelines | Built-in |
π§ Hydrology (4 tools)
| Tool | Description | Data Source |
|---|---|---|
env_hydro_stations | Filter hydrological gauging stations by canton or water body | hydrodaten.admin.ch |
env_hydro_current | Current water level, flow rate and temperature at a station | hydrodaten.admin.ch |
env_hydro_history | Historical hourly values (up to 30 days) with download links β οΈ | hydrodaten.admin.ch |
env_flood_warnings | Active flood warnings filtered by danger level and canton | hydrodaten.admin.ch |
ποΈ Natural Hazards (3 tools)
| Tool | Description | Data Source |
|---|---|---|
env_hazard_overview | Current natural hazard bulletin (SLF/BAFU) in DE/FR/IT/EN | naturgefahren.ch |
env_hazard_regions | Region-specific warnings (floods, avalanches, rockfall) | naturgefahren.ch |
env_wildfire_danger | Wildfire danger index by canton and region | waldbrandgefahr.ch |
π Environmental Data Catalogue (2 tools)
| Tool | Description | Data Source |
|---|---|---|
env_bafu_datasets | Search BAFU datasets on opendata.swiss (CKAN API) | opendata.swiss |
env_bafu_dataset_detail | Full metadata and download URLs for a specific dataset | opendata.swiss |
Example Use Cases
| Query | Tool |
|---|---|
| "Air quality at ZΓΌrich-Kaserne right now?" | env_nabel_current |
| "Does 45 Β΅g/mΒ³ NOβ exceed the Swiss limit?" | env_air_limits_check |
| "Current water level of the Limmat in Zurich?" | env_hydro_current |
| "Active flood warnings in Switzerland?" | env_flood_warnings |
| "Natural hazard bulletin for GraubΓΌnden?" | env_hazard_overview |
| "Wildfire danger in Canton Valais?" | env_wildfire_danger |
| "BAFU biodiversity datasets on opendata.swiss?" | env_bafu_datasets |
π‘οΈ Safety & Limits
| Aspect | Details |
|---|---|
| Access | Read-only (readOnlyHint: true) β the server cannot modify or delete any data |
| Personal data | No personal data β all sources are aggregated, public environmental measurements |
| Rate limits | Built-in per-query caps (e.g. max 30 days hydrology history, 50 dataset search results) |
| Timeout | 30 seconds per API call |
| Authentication | No API keys required β all BAFU endpoints are publicly accessible |
| Licenses | BAFU Open Government Data (OGD) β free reuse with mandatory attribution |
| Terms of Service | Subject to ToS of the respective data sources: BAFU / opendata.swiss, hydrodaten.admin.ch, naturgefahren.ch, waldbrandgefahr.ch |
Architecture
βββββββββββββββββββ βββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Claude / AI ββββββΆβ Swiss Environment MCP ββββββΆβ BAFU / Swiss Agencies β
β (MCP Host) βββββββ (MCP Server) βββββββ β
βββββββββββββββββββ β β β hydrodaten.admin.ch β
β 12 Tools Β· 3 Resources β β naturgefahren.ch β
β Stdio | SSE β β waldbrandgefahr.ch β
β β β opendata.swiss (CKAN) β
β api_client.py β ββββββββββββββββββββββββββββ
β server.py (FastMCP) β
βββββββββββββββββββββββββββββ
Data Sources
| Source | Data | Licence |
|---|---|---|
| hydrodaten.admin.ch | Water levels, flow rates, temperatures (10-min intervals) | BAFU OGD |
| naturgefahren.ch | Natural hazard bulletin (SLF/BAFU) | BAFU/SLF |
| waldbrandgefahr.ch | Wildfire danger index | BAFU |
| opendata.swiss | BAFU data catalogue (CKAN API) | OGD |
All data: publicly accessible, no authentication required.
Attribution required: BAFU must be cited as the source when using their data.
Project Structure
swiss-environment-mcp/
βββ src/swiss_environment_mcp/
β βββ __init__.py # Package
β βββ server.py # FastMCP server: 12 tools, 3 resources
β βββ api_client.py # HTTP client + egress allow-list (SSRF guard)
β βββ logging_setup.py # structlog -> stderr
βββ tests/
β βββ test_unit.py # Mocked unit tests (no network) β CI default
β βββ test_integration.py # Live API tests (marker: live)
β βββ test_20_scenarios.py # Live scenario coverage
βββ scripts/tool_snapshot.py # Tool-definition hash snapshot (rug-pull guard)
βββ docs/ # security.md, scaling.md, roadmap.md
βββ .github/
β βββ dependabot.yml # Monthly dependency/action updates
β βββ workflows/ # ci.yml, security.yml (gitleaks), live-tests.yml, publish.yml
βββ Dockerfile # Multi-stage, non-root container
βββ render.yaml / Procfile # Cloud deployment
βββ tool-snapshot.json # Committed tool-definition snapshot
βββ .env.example # Non-secret config template
βββ pyproject.toml # Build configuration (hatchling)
Single-module layout (rationale, audit ARCH-011): the 12 tools live in one
server.pyrather than atools/package. They are thin, uniform wrappers overapi_client.pysharing the same input/response patterns, so a single well-sectioned module stays more navigable than 4 near-identical files. This is a deliberate, documented deviation; a split is revisited if tool logic grows non-uniform.
MCP Protocol Version & Maintenance
- MCP protocol: negotiated at
initializetime and handled by the pinned MCP SDK (mcp[cli]). The SDK version is the canonical pin; SDK/protocol updates land via Dependabot PRs (.github/dependabot.yml, monthly). - Tool-definition stability (audit SEC-022): any change to a tool's name,
description or parameters changes
tool-snapshot.json; CI fails until the snapshot is regenerated and aCHANGELOGentry + version bump are added. - Update policy: review Dependabot PRs monthly; bump the version (semver) on any tool-definition or behaviour change.
Lifecycle Phase
This server is in Phase 1 (read-only) β all tools read-only, no auth, no side
effects. The phase model and prerequisites for Phase 2 (write/auth) are in
docs/roadmap.md. Security architecture (SSRF/egress, secret
management, lethal-trifecta assessment): docs/security.md.
Scaling/session strategy: docs/scaling.md.
Known Limitations
env_hydro_history: The historical hourly data endpoint is currently returning 404 errors from hydrodaten.admin.ch (BUG-01 β under investigation). The tool will return download links as a fallback.- NABEL: Near-real-time data only; no historical time series via this server.
- Natural hazards: Bulletin availability depends on SLF/BAFU publication schedule.
- Wildfire danger: Regional granularity varies by season and data availability.
Testing
# Unit tests (no API keys or network required)
PYTHONPATH=src pytest tests/ -m "not live"
# Integration tests (requires live BAFU APIs)
PYTHONPATH=src pytest tests/ -m "live"
# Linting
ruff check src/
Contributing
See CONTRIBUTING.md (English) Β· CONTRIBUTING.de.md (German)
Security
Security policy and posture: SECURITY.md (English) Β· SECURITY.de.md (German).
Full security architecture: docs/security.md.
Changelog
See CHANGELOG.md
License
MIT License β see LICENSE
Source data is subject to BAFU terms of use. Attribution to BAFU is required when using their data.
Author
Hayal Oezkan Β· github.com/malkreide
Credits & Related Projects
- Data: BAFU / Bundesamt fΓΌr Umwelt Β· hydrodaten.admin.ch Β· naturgefahren.ch Β· opendata.swiss
- Protocol: Model Context Protocol β Anthropic / Linux Foundation
- Related:
| 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-road-mobility-mcp | GBFS shared mobility, EV charging, DATEX II traffic |
| swiss-statistics-mcp | BFS STAT-TAB β 682 statistical datasets |
Synergy example: "What was the air quality at Schulhaus Leutschenbach today β and how does it compare to the national NABEL average?"
β zurich-opendata-mcp (OSTLUFT, local) + swiss-environment-mcp (NABEL, national)
- Portfolio: Swiss Public Data MCP Portfolio
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": {
"swiss-environment-mcp": {
"command": "uvx",
"args": [
"swiss-environment-mcp"
]
}
}
}