pypi meteoswiss-mcpstdioMITupdated 8d ago
MCP server for Swiss weather and climate data from MeteoSwiss.
O que dá para fazer com meteoswiss mcp?
🌦️ meteoswiss-mcp
MCP server for Swiss weather and climate data from MeteoSwiss.
Connects AI models to the SwissMetNet measurement network (160+ stations, 10-minute interval), MeteoSwiss ICON-CH1/CH2-EPS forecasts and climate normals 1991–2020. Part of the swiss-public-data-mcp portfolio.
Demo query (anchor example)
How suitable is next Wednesday for the sports day at Leutschenbach school?
→ meteo_school_check(location="Zürich Oerlikon", activity="Sporttag") returns a 🟢/🟡/🔴 traffic light for each day of the coming week — straight from the MeteoSwiss ICON model.
Combined with swiss-environment-mcp:
How were air quality and weather at Leutschenbach school yesterday?
→ meteo_current(station='REH') + env_nabel_current(station='ZUE') = a complete environmental picture.
→ More use cases by audience →
Tools (6)
| Tool | Description | Data source |
|---|---|---|
meteo_stations |
List SwissMetNet stations (filterable by canton) | Embedded |
meteo_current |
Current 10-min observations for a station | BGDI STAC API |
meteo_forecast |
1–16 day forecast for a place or coordinates | Open-Meteo / MeteoSwiss ICON |
meteo_school_check |
🟢/🟡/🔴 traffic light for outdoor school events | Open-Meteo / MeteoSwiss ICON |
meteo_climate_normals |
Monthly climate normals 1991–2020 | Embedded (KLO, SMA, BER, LUG, GVE) |
meteo_warnings |
Active official weather warnings (storm, thunderstorm, heat, forest fire, …) — nationwide, by canton, or by PLZ | MeteoSwiss App-API + opendata.swiss |
Tool annotations (MCP hints)
All tools carry explicit MCP annotations — relevant for the client approval UI and for the LLM's safety decisions.
| Tool | readOnlyHint |
destructiveHint |
idempotentHint |
openWorldHint |
|---|---|---|---|---|
meteo_stations |
✅ | ✗ | ✅ | ✗ (curated list) |
meteo_current |
✅ | ✗ | ✗ (live data) | ✅ (upstream STAC) |
meteo_forecast |
✅ | ✗ | ✗ (live data) | ✅ (upstream Open-Meteo) |
meteo_school_check |
✅ | ✗ | ✗ (live data) | ✅ (geocoding + forecast) |
meteo_climate_normals |
✅ | ✗ | ✅ | ✗ (embedded normals) |
meteo_warnings |
✅ | ✗ | ✗ (live data) | ✅ (MeteoSwiss App-API) |
Read rules: all 6 tools are readOnly + non-destructive — the server fundamentally cannot write or delete anything. idempotentHint=False marks tools that return different values depending on when they are called.
MCP protocol version
| Aspect | Value |
|---|---|
| Tested spec versions | 2024-11-05, 2025-03-26, 2025-06-18 (via the mcp[cli] SDK) |
| MCP SDK version | see pyproject.toml → mcp[cli]>=2.0.0,<3 (the MCPServer API from mcp.server.mcpserver) |
| Update policy | Dependabot watches mcp[cli]; spec bumps are documented in the CHANGELOG with a "Tool Definition Changes" marker |
→ Full roadmap & update strategy: docs/roadmap.md
Quick start
Claude Desktop
{
"mcpServers": {
"meteoswiss": {
"command": "uvx",
"args": ["meteoswiss-mcp"]
}
}
}
Claude Desktop (local development)
{
"mcpServers": {
"meteoswiss": {
"command": "uv",
"args": ["run", "--directory", "/path/to/meteoswiss-mcp", "meteoswiss-mcp"]
}
}
}
Cloud / Render.com (Streamable HTTP)
Configuration via ENV variables (the CLI flags --http / --port N still work as an override):
| Variable | Default | Meaning |
|---|---|---|
MCP_TRANSPORT |
stdio |
stdio or streamable-http |
MCP_HOST |
127.0.0.1 |
Bind address — never change locally |
MCP_PORT |
8000 |
Port |
MCP_ALLOW_ANY_HOST |
unset | Must be set to 1 to allow the server to bind to 0.0.0.0 (containers/cloud only) |
MCP_LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR — structured JSON logs on stderr |
MCP_ALLOWED_ORIGINS |
unset | Comma-separated list of allowed origins for CORS. Empty = CORS disabled (same-origin only). Mcp-Session-Id is exposed automatically. |
MCP_API_KEY |
unset | If set: every request except /health requires X-API-Key: <key> or Authorization: Bearer <key>. Constant-time comparison. |
MCP_STATELESS_HTTP |
0 |
1 enables the SDK's stateless mode → each HTTP request opens a new session. Prerequisite for multi-replica deploys without sticky sessions (SCALE-002/003). |
OTEL_EXPORTER_OTLP_ENDPOINT |
unset | If set + pip install meteoswiss-mcp[otel]: OpenTelemetry spans per tool call + automatic httpx instrumentation are sent as OTLP-HTTP to the collector. |
OTEL_SERVICE_NAME |
meteoswiss_mcp |
Service name in the OTel resources |
MCP_CACHE_ENABLED |
1 |
0 disables the TTL cache entirely (e.g. for end-to-end tests) |
MCP_CACHE_TTL_STAC |
300 |
TTL in seconds for STAC SMN observations (default 5 min) |
MCP_CACHE_TTL_OPEN_METEO |
600 |
TTL for ICON forecasts (default 10 min) |
MCP_CACHE_TTL_GEOCODING |
3600 |
TTL for geocoding lookups (default 1 h) |
MCP_CACHE_TTL_OPENDATA |
3600 |
TTL for the opendata.swiss catalogue (default 1 h) |
MCP_CACHE_TTL_WARNINGS |
300 |
TTL for warnings (MeteoSwiss App-API / structured override; default 5 min) |
MCP_CLIMATE_NORMALS_PATH |
unset | Path to a JSON file with additional climate normals — see data/climate-normals.example.json |
MCP_WARNINGS_API_URL |
unset | Override for the default MeteoSwiss App-API source: URL of a structured MeteoSwiss warnings API (e.g. the future OGD warnings REST endpoint). The host must be on the egress allow-list. Schema-tolerant (GeoJSON features, a warnings array or items). Unset → live App-API. |
MCP_CLIMATE_NORMALS_URL_TEMPLATE |
unset | URL template for runtime lookup of climate normals (for stations without embedded or JSON values). Tokens: {station} (lowercase), {STATION} (uppercase), {param} (MeteoSwiss code tre200m0/rre150m0/sre000m0). Example: https://data.geo.admin.ch/.../{station}/{param}.txt. The host must be on the egress allow-list. |
# Local test (safe, loopback only)
MCP_TRANSPORT=streamable-http meteoswiss-mcp
# Container / Render
MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 MCP_ALLOW_ANY_HOST=1 meteoswiss-mcp
Docker / Render
The repo includes a production-ready multi-stage Dockerfile (non-root user, HEALTHCHECK) and a render.yaml blueprint:
# Build + test locally
docker build -t meteoswiss-mcp .
docker run --rm -p 8000:8000 meteoswiss-mcp
curl http://127.0.0.1:8000/health # → {"status":"ok","service":"meteoswiss-mcp"}
On Render: "New → Blueprint" → select the repo. Defaults (plan starter, Frankfurt, single instance) are set in render.yaml.
Important: numInstances: 1 is set deliberately — sticky-session routing for multi-replica (audit SCALE-002/003) is not yet implemented.
Structured logging
All tool invocations, upstream failures and egress blocks are emitted as JSON events on stderr (stdio-transport safe). Example:
{"tool": "meteo_forecast", "days": 7, "has_coords": false, "event": "tool_invoked", "level": "info", "timestamp": "2026-05-20T07:00:00Z"}
{"tool": "meteo_forecast", "endpoint": "geocoding", "error_type": "HTTPStatusError", "event": "upstream_failed", "level": "warning", "timestamp": "..."}
{"url": "https://evil.example.com/", "method": "GET", "reason": "host not in allow-list", "event": "egress_blocked", "level": "warning", "timestamp": "..."}
HTTP-mode security
MCP_HOSTdeliberately defaults to127.0.0.1so that--httpon a dev laptop is not accidentally exposed to the local subnet (audit finding SEC-016).- All outgoing HTTP calls (including redirect follows) are validated against an allow-list:
data.geo.admin.ch,api.open-meteo.com,geocoding-api.open-meteo.com,opendata.swiss. Other hosts and IP literals (in particular169.254.169.254, RFC1918) are rejected withEgressBlocked(SEC-004 / SEC-021). - CORS: disabled by default (same-origin only). Browser clients (e.g. claude.ai web) need
MCP_ALLOWED_ORIGINS=<csv>— theMcp-Session-Idheader is then automatically inAccess-Control-Expose-Headers(SDK-004). - API-key auth: disabled by default. In a production HTTP setup, always set
MCP_API_KEY=<random>— requests without a validX-API-KeyorAuthorization: Bearer …are rejected with 401 (SEC-009 / SEC-013)./healthstays open for container health probes.
Example: production HTTP stack
# 32 bytes of randomness as the auth key
export MCP_API_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
MCP_TRANSPORT=streamable-http \
MCP_HOST=0.0.0.0 \
MCP_ALLOW_ANY_HOST=1 \
MCP_ALLOWED_ORIGINS=https://app.example.com \
MCP_API_KEY="$MCP_API_KEY" \
meteoswiss-mcp
Example queries
School planning
Which days next week are suitable for a sports day in Zürich?
→ meteo_school_check(location="Zürich", activity="Sporttag")
What will the weather be at Leutschenbach school on Friday?
→ meteo_forecast(location="Zürich Oerlikon", days=5)
Show me current readings from the nearest MeteoSwiss station to Zürich-Schwamendingen.
→ meteo_current(station="REH")
Climate comparison
How much rain normally falls in June in Zürich?
→ meteo_climate_normals(station="KLO")
Is Lugano really much sunnier than Zürich? Show me the annual values.
→ meteo_climate_normals(station="LUG") + meteo_climate_normals(station="SMA")
Infrastructure & environment
Are there currently any weather warnings for the canton of Zürich?
→ meteo_warnings(canton="ZH")
Show me a 10-day forecast for the Heerenschürli sports facility with hourly values.
→ meteo_forecast(location="Sportanlage Heerenschürli Zürich", days=10, hourly=True)
Architecture
Claude Desktop / AI agent
│
│ MCP (stdio / Streamable HTTP)
▼
meteoswiss-mcp (MCPServer)
│
├── meteo_stations ──────────────── [embedded: ~20 SMN stations]
│
├── meteo_current ───────────────── BGDI STAC API
│ data.geo.admin.ch/api/stac/v1
│ Collection: ch.meteoschweiz.ogd-smn
│
├── meteo_forecast ──────────────── Open-Meteo
├── meteo_school_check ──────────── api.open-meteo.com/v1/meteoswiss
│ (MeteoSwiss ICON-CH1/CH2-EPS, 1–2 km)
│
├── meteo_climate_normals ───────── [embedded: normals 1991–2020]
│
└── meteo_warnings ──────────────── app-prod-ws.meteoswiss-app.ch
(MeteoSwiss App-API) + opendata.swiss
Data sources
| Source | URL | License |
|---|---|---|
| BGDI STAC API (MeteoSwiss OGD) | data.geo.admin.ch/api/stac/v1 |
CC BY 4.0 |
| Open-Meteo (MeteoSwiss ICON) | api.open-meteo.com/v1/meteoswiss |
CC BY 4.0 |
| Open-Meteo Geocoding | geocoding-api.open-meteo.com |
CC BY 4.0 |
| opendata.swiss CKAN | opendata.swiss/api/3/action |
CC BY 4.0 |
| MeteoSwiss App-API (warnings) | app-prod-ws.meteoswiss-app.ch/v1/plzDetail |
CC BY 4.0 |
Safety & limits
| Aspect | Details |
|---|---|
| Access | Read-only (readOnlyHint: true on all tools) — the server cannot modify or delete any data |
| Personal data | No personal data — all sources are aggregated, publicly available open data |
| Rate limits | Built-in per-query caps: max 50 results per API call, 30 s timeout |
| Authentication | No API keys required — all data sources are publicly accessible |
| Licenses | All data under CC BY 4.0 (MeteoSwiss Open Government Data) |
| Terms of Service | Subject to the ToS of the respective data sources: MeteoSwiss OGD, Open-Meteo, opendata.swiss |
Known limitations
| ID | Tool | Description |
|---|---|---|
| BUG-01 | meteo_current |
STAC asset structure can vary per station; fallback to a direct link is implemented |
| LIM-01 | meteo_climate_normals |
Only 5 stations embedded (KLO, SMA, BER, LUG, GVE); the rest via an opendata.swiss link |
| LIM-02 | meteo_warnings |
Live warnings come from the MeteoSwiss App-API (plzDetail) — public and unauthenticated, but undocumented (mobile-app backend, not the OGD REST API). There is no nationwide endpoint, so the countrywide view aggregates one representative capital PLZ per canton (sub-regional warnings outside that PLZ may be missed — narrow with plz/canton). MCP_WARNINGS_API_URL overrides it once the official OGD warnings REST API ships. |
| LIM-03 | meteo_current |
Shows 10-min values in UTC; no automatic conversion to local time |
Responsibility matrix — snow & precipitation (delineation vs. swiss-environment-mcp)
To avoid duplicating snow and precipitation data across the portfolio,
responsibilities are split as follows. meteoswiss-mcp owns atmospheric
precipitation and weather; swiss-environment-mcp (SLF domain) owns snow on the
ground and avalanche danger.
| Data | meteoswiss-mcp (MeteoSwiss) | swiss-environment-mcp (BAFU / SLF) |
|---|---|---|
| Precipitation amount (mm): measurement network, forecast, climate normals | ✅ meteo_current / meteo_forecast / meteo_climate_normals |
❌ |
| Snowfall as a current weather condition | ✅ meteo_current / meteo_forecast (weather code) |
❌ |
| Weather warnings (storm, thunderstorm, heat) | ✅ meteo_warnings |
❌ |
Snow depth on the ground (HS) |
❌ | ✅ SLF IMIS / study-plot ¹ |
Fresh snow 24 h (HN_1D) |
❌ | ✅ SLF ¹ |
| Avalanche danger level | ❌ | ✅ SLF avalanche bulletin ¹ |
| Natural-hazard warnings (flood, avalanche, wildfire) | ❌ | ✅ env_flood_warnings, env_hazard_*, env_wildfire_danger |
Rule: atmospheric precipitation (rain/snowfall as mm) plus weather,
forecast, warnings and climate normals belong to meteoswiss-mcp; snow on the
ground and avalanche danger belong to swiss-environment-mcp (SLF). The SLF
IMIS precipitation sensor is used there only as context for the snowpack and is
never exposed as a precipitation tool, so it does not duplicate MeteoSwiss.
¹ SLF/snow tools in swiss-environment-mcp are in preparation (Phase-1 live-probe
completed 2026-07-19, see that repo's docs/probe-slf.md); the demarcation is
fixed now so the two servers do not collide once implemented.
Portfolio synergies
meteoswiss-mcp
│
├── swiss-environment-mcp Combine weather + air quality (NABEL)
│ "How were weather AND air at Leutschenbach school?"
│
└── zurich-opendata-mcp School locations → weather forecast
"Which schools in Zürich have sports-day weather?"
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. This server builds no ASGI app to send an initialize through, so
the gate asserts the SDK constants rather than a measured response — the
weaker form, named rather than left unsaid.
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.
Testing
# Unit tests (no network)
PYTHONPATH=src pytest tests/ -m "not live" -v
# Live tests (real APIs) — also run daily at 05:17 UTC via
# .github/workflows/live-tests.yml, so a format change upstream
# surfaces even though the unit tests stay green.
PYTHONPATH=src pytest tests/ -m live -v
# Linting — install the local gates once with `pre-commit install`
# to run these (and the CI guards) before every commit.
ruff check src/ tests/ scripts/
ruff format --check src/ tests/ scripts/
Development
git clone https://github.com/malkreide/meteoswiss-mcp
cd meteoswiss-mcp
pip install -e ".[dev]"
MCP Inspector (local test)
PYTHONPATH=src npx @modelcontextprotocol/inspector python -m meteoswiss_mcp.server
Contributing
See the contributing guidelines (Deutsch).
Security
See the security policy (Deutsch) for the security posture and how to report a vulnerability.
License
MIT License – see LICENSE.
Source data: MeteoSwiss Open Government Data (CC BY 4.0). When using the data, cite: Source: MeteoSwiss.
Author
Hayal Oezkan · github.com/malkreide
Related servers
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": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}
Instalação
Adicione meteoswiss mcp ao seu cliente. Escolha o que você usa.
claude mcp add meteoswiss-mcp -- uvx meteoswiss-mcpcodex mcp add meteoswiss-mcp -- uvx meteoswiss-mcpamp mcp add meteoswiss-mcp -- uvx meteoswiss-mcp{
"mcpServers": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
code --add-mcp '{"name":"meteoswiss-mcp","command":"uvx","args":["meteoswiss-mcp"]}'Or add the block manually to `.vscode/mcp.json` under `servers`.
{
"mcpServers": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"meteoswiss-mcp": {
"command": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"meteoswiss-mcp": {
"type": "local",
"command": "uvx",
"args": [
"meteoswiss-mcp"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"meteoswiss-mcp": {
"command": {
"path": "uvx",
"args": [
"meteoswiss-mcp"
]
}
}
}
}Add to your Zed `settings.json`.
uvx meteoswiss-mcpRun `goose configure`, choose **Add Extension → Command-line Extension**, and paste this command.
6 ferramentas
meteoswiss mcp expõe 6 ferramentas a um agente conectado.
- meteo_stations
- List SwissMetNet stations (filterable by canton)
- meteo_current
- Current 10-min observations for a station
- meteo_forecast
- 1–16 day forecast for a place or coordinates
- meteo_school_check
- 🟢/🟡/🔴 traffic light for outdoor school events
- meteo_climate_normals
- Monthly climate normals 1991–2020
- meteo_warnings
- Active official weather warnings (storm, thunderstorm, heat, forest fire, …) — nationwide, by canton, or by PLZ
Pontuação
81 / 100
Excelente
- Documentação25/25
- Manutenção25/25
- Confiança13/20
- Capacidade6/15
- Instalação12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 1 days ago
- Has a release history
- Repository is not archived
- Licensed MIT
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 6 tool(s) documented
- Provides prompt templates
- Provides resources
- 12 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint — no local install
Histórico de versões
| Versões | Publicada |
|---|---|
| 0.6.1Mais recente | 2 de ago. de 2026 |
| 0.6.0 | 30 de jul. de 2026 |
| 0.5.0 | 30 de jul. de 2026 |
| 0.4.0 | 26 de jul. de 2026 |
| 0.3.3 | 7 de jun. de 2026 |