smhi-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@smhi-mcpget the weather forecast for Stockholm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SMHI MCP Server
A Model Context Protocol (MCP) server that provides Swedish weather data through SMHI's open APIs, deployed as a Cloudflare Worker.
π€οΈ Features
Real-time Weather Data: Access current weather conditions from SMHI weather stations
Weather Forecasts: Get detailed forecasts for any location in Sweden
Station Search: Find weather stations by name using fuzzy matching
Multi-Resolution Data: Access hourly, daily, and monthly weather data
Historical Data: Query past weather records with pagination and date filtering
MCP Protocol: Full compatibility with Claude and other MCP clients
Serverless: Runs on Cloudflare Workers with global edge deployment
No API Keys Required: Uses SMHI's free open data APIs
Smart Caching: Multi-level caching with Cloudflare Cache API and R2 storage for optimal performance
Rate Limiting: Built-in request rate limiting to respect SMHI API limits
Forecasts use SMHI's SNOW1gv1 endpoint. The legacy PMP3gv2 forecast API was retired by SMHI on 2026-03-31.
Related MCP server: mcp-norwegian-weather
π Live Demo
Deployed Server: https://smhi-mcp.hakan-3a6.workers.dev
π Available Tools (16 Total)
Snowmobile Conditions Tools
Tool | Description | Parameters |
| List weather stations for snowmobile conditions by region with capability info | None |
| Get latest temperature reading from a specific station |
|
| Get latest snow depth reading from a specific station |
|
| Get weather forecast for coordinates |
|
Legacy Tools (Deprecated)
Tool | Description | Parameters |
| [DEPRECATED] Use | None |
| [DEPRECATED] Use | None |
Multi-Resolution Data Tools
Tool | Description | Parameters |
| Get precipitation data with multiple resolutions |
|
| Get temperature data with multiple resolutions |
|
| Get detailed station metadata and available periods |
|
Historical Data & Pagination
Tool | Description | Parameters |
| Get historical data with pagination and date filtering. Reads the CSV archive for |
|
| Get all temperature stations with pagination |
|
| Get all snow depth stations with pagination |
|
| Get all precipitation stations with pagination |
|
Station Search Tools
Tool | Description | Parameters |
| Search stations by name within a specific parameter type |
|
| Search stations by name across all parameter types |
|
| Find nearest stations to given coordinates |
|
Search Parameters Details
query(required): Station name to search for (fuzzy matching)parameter(search_stations_by_name only): Parameter type filter1= Temperature stations5= Daily precipitation stations7= Hourly precipitation stations8= Snow depth stations
limit(optional, default: 10): Maximum number of results to returnthreshold(optional, default: 0.3): Minimum similarity score (0.0-1.0) for fuzzy matchingactive_only(optional, default: true): Only return stations with recent data activity
Coordinate Search Parameters
latitude(required): Latitude in WGS84 decimal degrees (e.g., 65.353 for Adak)longitude(required): Longitude in WGS84 decimal degrees (e.g., 18.5837 for Adak)parameter(optional, default: "1"): Parameter type (1=temp, 5=precip, 8=snow)radius_km(optional, default: 50): Maximum search radius in kilometerslimit(optional, default: 10): Maximum number of stations to returnactive_only(optional, default: true): Only return active stations
π οΈ Quick Start
Option 1: Use the Deployed Server
Connect your MCP client to: https://smhi-mcp.hakan-3a6.workers.dev
Option 2: Deploy Your Own
# Clone the repository
git clone https://github.com/yourusername/smhi-mcp.git
cd smhi-mcp
# Install dependencies
npm install
# Create R2 bucket for caching (required)
wrangler r2 bucket create smhi-historical-data
# Deploy to Cloudflare Workers
npm run deployOption 3: Local Development
# Start local development server
npm run dev
# The server will be available at http://localhost:8787π‘ MCP Protocol Examples
Initialize Connection
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {}
}
}List Available Tools
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}Get Weather Forecast (Stockholm)
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_weather_forecast",
"arguments": {
"lat": 59.3293,
"lon": 18.0686
}
}
}List Snowmobile Conditions Stations
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "list_snowmobile_conditions",
"arguments": {}
}
}Get Station Temperature
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "get_station_temperature",
"arguments": {
"station_id": "159880"
}
}
}Search for Weather Stations
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "search_stations_by_name_multi_param",
"arguments": {
"query": "Stockholm",
"limit": 5,
"threshold": 0.3,
"active_only": true
}
}
}Find Nearest Stations by Coordinates
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "get_stations_near_location",
"arguments": {
"latitude": 65.353,
"longitude": 18.5837,
"parameter": "1",
"radius_km": 50,
"limit": 5
}
}
}Get Historical Data with Pagination
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "get_historical_data",
"arguments": {
"station_id": "159880",
"parameter": "1",
"period": "corrected-archive",
"limit": 20,
"fromDate": "2024-01-01",
"toDate": "2024-01-31"
}
}
}π§ͺ Testing
Using cURL
# Test MCP initialization
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}}}' \
https://smhi-mcp.hakan-3a6.workers.dev
# List snowmobile conditions stations
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "list_snowmobile_conditions", "arguments": {}}}' \
https://smhi-mcp.hakan-3a6.workers.dev
# Test weather forecast for Stockholm
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_weather_forecast", "arguments": {"lat": 59.3293, "lon": 18.0686}}}' \
https://smhi-mcp.hakan-3a6.workers.dev
# Search for weather stations
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "search_stations_by_name_multi_param", "arguments": {"query": "Stockholm", "limit": 3}}}' \
https://smhi-mcp.hakan-3a6.workers.dev
# Find nearest stations to coordinates (Adak area)
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": {"name": "get_stations_near_location", "arguments": {"latitude": 65.353, "longitude": 18.5837, "radius_km": 50}}}' \
https://smhi-mcp.hakan-3a6.workers.devUsing Make Commands
# Run built-in tests
make test-mcp
make test-forecast
make test-stationsUnit Tests
npm test # vitest runCovers transport behaviour (test/mcp-transport.test.js) and request
accounting (test/request-limits.test.js). Both exist because of a real
incident β see below.
π‘οΈ Protecting the request quota
The 2026-08 runaway incident
GET /mcp used to answer with a finite text/event-stream body: one
data: line, then the stream closed. EventSource treats a closed stream as a
dropped connection and reconnects after ~1s, forever. Three concurrent clients
doing that generated ~420,000 Worker requests in four days against a
100,000/day account limit, and tripped Cloudflare's daily limit email.
The fix: a server with no server-initiated messages must answer 405 on GET,
per the MCP Streamable HTTP spec. Conforming clients then stop retrying.
test/mcp-transport.test.js locks this in.
What the in-Worker limiter can and cannot do
checkRequestLimits() now runs for every method, not just POST (it
previously sat in the POST branch only, which is why the GET flood went
unnoticed). But be clear about its scope:
An in-Worker limiter cannot protect the Cloudflare request quota. By the time it runs, the request is already billed. A 429 costs exactly as much quota as a 200.
Its real value is shedding load off the upstream SMHI APIs and failing loudly
rather than silently. It is also backed by caches.default, which is
per-colo, so the count is per-edge-location, not global.
Actually protecting the quota
Requests must be rejected before they reach the Worker:
Put the service on a real zone (not
*.workers.dev) and add a WAF rate-limiting rule. Traffic blocked at the WAF is not billed as a Worker invocation.Disable the
workers.devroute once a custom domain is in place, so the unprotected hostname stops being reachable.Note that the endpoint currently has no authentication β anyone with the URL can invoke it.
ποΈ Architecture
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β MCP Client β β Cloudflare β β SMHI APIs β
β (Claude) βββββΊβ Worker βββββΊβ (opendata) β
β β β (smhi-mcp) β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββThe server acts as a bridge between MCP clients and SMHI's weather APIs:
MCP Client sends JSON-RPC requests
Cloudflare Worker processes requests and calls SMHI APIs
SMHI APIs provide real-time weather data
Worker formats response according to MCP protocol
β‘ Performance & Caching
The server implements a multi-level caching strategy for optimal performance:
Cloudflare Cache API
Current weather data: 15 minutes TTL
Weather forecasts: 30 minutes TTL
Station metadata: 1 week TTL
Historical data: 24 hours TTL
R2 Storage (CSV Data)
Standard CSV files: 24 hours TTL
Recent year data: 1 hour TTL (more volatile)
Historical data (>2 years): 1 week TTL (stable)
Rate Limiting
30 requests per minute maximum to respect SMHI API limits
Automatic request throttling with user-friendly error messages
Configuration Requirements
# wrangler.toml - R2 bucket for CSV caching
[[r2_buckets]]
binding = "HISTORICAL_DATA"
bucket_name = "smhi-historical-data"π Data Sources
All weather data comes from SMHI (Swedish Meteorological and Hydrological Institute):
Observations API: Real-time weather station data
Forecast API: Weather predictions up to 10 days
Station Metadata: Information about monitoring stations
Sample Weather Stations
Station ID | Name | Location | Note |
159880 | Arvidsjaur | Northern Sweden | Temperature & precipitation |
155960 | TΓ€rnaby/Hemavan | Mountain region | Temperature data |
155940 | MosekΓ€lla/Hemavan | Mountain region | Same location, different parameter names |
132170 | Storlien-Storvallen | Norwegian border | Alpine weather station |
188850 | KatterjΓ₯kk/RiksgrΓ€nsen | Arctic region | Northernmost station |
Note: Some stations have different names depending on the parameter type. Use search_stations_by_name_multi_param to find stations across all data types.
Active Filtering: By default, search tools only return stations with recent data activity. Set active_only: false to include all stations, including inactive ones.
π§ Configuration
Required Configuration
The worker requires an R2 bucket for CSV caching:
# wrangler.toml - Required R2 configuration
[[r2_buckets]]
binding = "HISTORICAL_DATA"
bucket_name = "smhi-historical-data"Optional Environment Variables
# wrangler.toml - Optional configuration
[env.production.vars]
# Add public configuration here
# Optional secrets for authentication
[[env.production.secrets]]
API_KEY = "your-api-key-for-auth"Authentication (Optional)
To enable API key authentication, set the API_KEY secret:
wrangler secret put API_KEYπ Development Commands
# Install dependencies
make install
# Start development server
make dev
# Deploy to Cloudflare
make deploy
# View logs
make logs
# Show deployment status
make status
# Run tests
make test-mcpModular Architecture
The codebase has been refactored into a modular structure:
src/
βββ api/ # SMHI API integration
βββ config/ # Constants and configuration
βββ data/ # Station data and presets
βββ handlers/ # WebSocket and SSE handlers
βββ mcp/ # MCP protocol server
βββ middleware/ # Rate limiting and middleware
βββ services/ # Weather data services
βββ tools/ # MCP tool definitions
βββ utils/ # Caching and utility functionsπ Claude Integration
To use this server with Claude:
Desktop App: Add the server URL in Claude's MCP settings
API: Include the server in your MCP client configuration
Custom Integration: Use the JSON-RPC protocol directly
Example Claude conversations:
You: "Show me weather stations for snowmobile conditions"
Claude: "Let me get the snowmobile conditions monitoring stations for you."
[Uses list_snowmobile_conditions]
Claude: "Here are 18 weather stations organized by region for snowmobile conditions. The Arctic/Mountain region has KatterjΓ₯kk/RiksgrΓ€nsen with both temperature and snow depth data..."
You: "What's the current temperature in Arvidsjaur?"
Claude: "Let me check the current temperature in Arvidsjaur for you."
[Uses get_station_temperature with station_id "159880"]
Claude: "The current temperature in Arvidsjaur is 15.8Β°C."
You: "Find weather stations near Stockholm"
Claude: "Let me search for weather stations near Stockholm."
[Uses search_stations_by_name_multi_param with query "Stockholm"]
Claude: "I found several weather stations near Stockholm, including Stockholm A (98230) and Stockholm-Observatoriekullen (98210)."
You: "Show me temperature data for MosekΓ€lla from January 2024"
Claude: "Let me search for MosekΓ€lla and get the temperature data."
[Uses search_stations_by_name_multi_param with query "MosekΓ€lla"]
[Uses get_historical_data with station_id "155940", fromDate "2024-01-01", toDate "2024-01-31"]
Claude: "Found MosekΓ€lla (station 155940) - here's the temperature data for January 2024..."
You: "Find weather stations near Adak (65.353Β°N, 18.5837Β°E)"
Claude: "Let me find the nearest weather stations to those coordinates."
[Uses get_stations_near_location with latitude 65.353, longitude 18.5837, radius_km 50]
Claude: "I found 4 temperature stations within 50km: MalΓ₯ (20.5km), MalΓ₯-BrΓ€nnan A (22.4km), BuresjΓΆn A (40.6km), and Arvidsjaur A (41.3km)."π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
Fork the repository
Create a feature branch:
git checkout -b feature/amazing-featureMake your changes
Test locally:
npm run devDeploy and test:
npm run deploySubmit a pull request
π License
This project is open source and available under the MIT License.
π Related Projects
Model Context Protocol - The protocol specification
SMHI Open Data - Weather data source
Cloudflare Workers - Serverless platform
π Support
Issues: GitHub Issues
Discussions: GitHub Discussions
SMHI API Docs: SMHI Open Data Portal
Built with β€οΈ for the MCP ecosystem | Powered by SMHI Open Data | Deployed on Cloudflare Workers
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server for Xweather weather data: conditions, forecasts, alerts, and more.
MCP server for weather with reasoning β umbrella advice, outdoor checks, city comparisons.
An MCP server for weather information by @kulybaba
An MCP server for weather information by @kulybaba
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that aggregates data from six weather sources to provide current conditions, forecasts, air quality, and aviation METAR information, specifically optimized for European and Nordic locations. It supports multi-source data merging and functions without API keys for several integrated providers.7MIT
- AlicenseAqualityDmaintenanceAn MCP server that provides current weather conditions and hourly forecasts for locations across Norway using the MET Norway Locationforecast 2.0 API. It features coordinate support, geocoding for any Norwegian location, and built-in configurations for major cities.211MIT
- AlicenseNot gradedqualityDmaintenanceMCP Server for global weather, forecasts, air quality, and climate data using Open-Meteo, no API key required.MIT
- AlicenseAqualityBmaintenanceMCP server for weather forecasts via Open-Meteo (no API key needed), providing current weather, hourly, and daily forecasts with geocoding support.3MIT