met-mcp
# met-mcp
An MCP server for weather forecasts from MET Norway (Yr). Built as a learning project.
Available three ways:
- **npm**: [`met-mcp`](https://www.npmjs.com/package/met-mcp) — run locally over stdio
- **Remote**: `https://met-mcp.met-mcp-worker.workers.dev/mcp` — Streamable HTTP on Cloudflare Workers, add it as a claude.ai connector
- **MCP registry**: [`io.github.trondbjoroy/met-mcp`](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.trondbjoroy/met-mcp)
## Tools
- `geocode_location(name, count)` — find coordinates for a place name. Uses the free Open-Meteo geocoder, because MET has no geocoding API.
- `get_forecast(latitude, longitude, hours)` — hourly forecast from MET Locationforecast 2.0. Works worldwide, best in the Nordics.
- `get_nowcast(latitude, longitude)` — radar-based precipitation for the next 2 hours. Nordics only.
- `get_weather_alerts(latitude?, longitude?)` — official Norwegian danger warnings (MetAlerts). Omit coordinates for all of Norway.
- `get_sun_events(latitude, longitude, date, utc_offset)` — sunrise, sunset, and solar noon. Worldwide.
- `get_ocean_forecast(latitude, longitude, hours)` — wave height and sea temperature along the Norwegian coast.
- `find_weather_stations(latitude, longitude, count)` — nearest official weather stations (Frost).
- `get_historical_weather(station_id, from, to, elements)` — daily historical observations from the Frost archive; some stations go back over 100 years.
The two Frost tools need a free client ID from https://frost.met.no/auth/requestCredentials.html, set as the `FROST_CLIENT_ID` env var (or a Cloudflare secret for the worker). The other tools need nothing.
Tool definitions live in `src/tools.ts`, shared between the stdio server and the worker.
## Build and test
```bash
npm install
npm run build
npm test
```
The test spawns the server over stdio and calls both tools against the live APIs.
## Use with Claude Code
```bash
claude mcp add met-weather -- node C:\Users\trond\met-mcp\dist\index.js
```
Then ask Claude: "What is the weather in Bergen tomorrow?"
## MET terms of service
- Send an identifying User-Agent with contact info. Set the `MET_USER_AGENT` env var, e.g. `met-mcp/0.1.0 you@example.com`.
- Send at most 4 decimals in coordinates. The server rounds for you.
- Data license: NLOD / CC BY 4.0. Credit MET Norway when you show the data.
## Remote version
`worker/` holds the Cloudflare Workers deployment: the same two tools served over MCP Streamable HTTP with `createMcpHandler` (MCP SDK v2). Develop with `npm run dev`, ship with `npm run deploy` (both from `worker/`).
## How it works
- `src/index.ts` is the whole server. It speaks MCP over stdio: JSON-RPC on stdin/stdout.
- Each tool declares an input schema with Zod. The MCP client (Claude) reads the schema and validates arguments.
- Tool results are `content` blocks. This server returns JSON as text, trimmed to what an LLM needs — the raw MET response has ~90 timesteps with far more fields.
- Never write to stdout with `console.log`. It corrupts the protocol stream. Log to stderr.
TDQS
Scored across 8 tools
Each tool targets a distinct weather data need: geocoding, hourly forecasts, nowcasting, alerts, solar events, ocean conditions, station lookup, and historical observations. The only potentially overlapping pair (get_forecast vs get_nowcast) is clearly separated by forecast horizon and description.
All tool names follow a consistent verb_noun snake_case pattern, with get_ as the dominant prefix and find_/geocode_ as clear exceptions that still follow the same grammatical structure. This makes the tool surface highly predictable.
Eight tools is well-scoped for a weather data server: each tool handles a specific data type or lookup step without redundancy. The count is large enough to cover the domain but small enough to avoid overwhelming an agent.
The tool set covers the full observation-to-forecast lifecycle: geocoding to obtain coordinates, station discovery for historical data, current conditions, short-term nowcast, forecast, alerts, ocean conditions, and solar events. No critical gaps are apparent for the stated weather domain.