Get US weather forecasts, active alerts, and current observations.
One address for any MCP client. No sign-in needed on this server.
10 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Get US weather forecasts, active alerts, and current observations via the National Weather Service API. STDIO or Streamable HTTP.
Public Hosted Server: https://nws.caseyjhand.com/mcp
Real-time US weather from the National Weather Service: forecasts, active alerts, current conditions, and forecaster discussions for any point in the 50 states and US territories, plus zone forecasts, coastal and marine alerts, and a week of station history. No API key required.
| Tool | Description |
|---|---|
nws_get_forecast | 7-day or hourly forecast for coordinates. Resolves NWS grid internally. |
nws_search_alerts | Active weather alerts filtered by area, point, zone, event, severity, urgency, certainty, and status. |
nws_get_alert | One alert message by ID, current or superseded (~7-day retention). |
nws_get_alert_counts | Active alert counts nationwide, per state/territory or marine area, and per marine region. |
nws_get_observations | Current conditions by coordinates (nearest station) or station ID. |
nws_get_observation_history | A station's recent observations, newest first, paged back through the ~7 days NWS keeps. |
nws_find_stations | Nearby observation stations sorted by distance with bearing. |
nws_list_alert_types | All valid alert event type names for filter discovery. |
nws_get_office_discussion | Latest narrative product (AFD, HWO, ZFP, SPS, FWF) from a Weather Forecast Office; a product too large to inline comes back as a section outline. |
nws_get_zone_forecast | Text forecast periods for an NWS forecast zone. |
| Resource | Description |
|---|---|
nws://alert-types | Static list of all valid NWS alert event type names. |
Also reachable via the nws_list_alert_types tool, for MCP clients that don't support resources.
nws_get_forecast toollatitude + longitude; returns 14 named 12-hour periods (~7 days) by default, or with hourly: true one-hour periods with dewpoint and humidity, 48 per page of the ~156 upstreamnextCursor carries its mode, so an hourly walk continues hourly without resending hourly: trueinclude_amounts: true adds every native quantitativePrecipitation, snowfallAmount, and iceAccumulation interval as precipitationAmounts (layer, uom, values with validTime and value), plus the separate amountsUpdateTime. Default false; resend it on any cursor page that needs amounts. Interval strings, units, zeroes, and nulls stay unchanged. Missing layers and unavailable amounts are explained in notice; present empty layers carry values: []. Absent units or update time are null. An unavailable amount feed leaves the main forecast available.updateTime, when the grid's forecast data was last updated, beside generatedAt; a notice flags a grid not updated in more than 24 hoursforecastZone and county zone codes for chaining into nws_search_alertsmarine_forecast_unsupported, and land points NWS serves no forecast grid for fail with no_forecast_gridnws_search_alerts toolarea, point, zone, region_type, or region), or none for a national search; event matches case-insensitively and partially; status defaults to Actual (also Exercise, System, Test, Draft)limit (default 25) is capped at 25; a larger limit is served as 25 and reported through requestedLimit/effectiveLimitnextCursor carries the query's filters and page size, so pass it back as cursor on its own; filters sent with it are ignoredaffectedZones entry carries its zone type (forecast/county/fire); references lists superseded messages newest first by sent; a point outside NWS coverage fails with invalid_pointoffice (the issuing office code for nws_get_office_discussion, e.g. RIW), raw vtec strings, the nwsHeadline, and hazard tags such as maxHailSize and maxWindGust, verbatimnws_get_alert toolalert_id: an alert id from nws_search_alerts, or a references[].identifier from any alert; returns { alert } in the same shape as nws_search_alerts, including superseded messagesalert_not_foundnws_get_alert_counts tooltotalAlerts (landAlerts + marineAlerts), plus areas counts keyed by state/territory or marine area code and regions counts keyed by marine region (AL, AT, GL, GM, PA, PI), each listing only codes with an active alerttotalAlerts can exceed a national nws_search_alerts totalCount (status: Actual by default); an alert counts once in each area its zones fall in, so areas can sum past totalAlertsnws_get_observations toolstation_id directly0 a trace and null no amount reported; observations older than 2 hours carry a staleness notice, and a separate warning flags a station with most measurements unavailablenws_get_observation_history toolstation_id (from nws_find_stations); optional start (inclusive) and end (exclusive) as ISO 8601 date-times with seconds and an offset; newest first, back through the ~7 days NWS keepslimit (default 24) is capped at 100; a larger limit is served as 100 and reported through requestedLimit/effectiveLimitnws_get_observations measurement fields, null where the station reported nothing; an unknown station fails with station_not_found, and a malformed window or a start not before end with invalid_time_windownextCursor appears only when older observations remain; there is no totalCount, since NWS reports none for a windownws_find_stations toollatitude + longitude; optional limit (default 10) is capped at 50; a larger limit is served as 50 and reported through requestedLimit/effectiveLimitnws_get_observations, distance (km), bearing, zone codes, elevation, and time zonenws_list_alert_types toolevent filter in nws_search_alertsnws_get_office_discussion tooloffice: usually the 3-letter WFO code (e.g., "SEW" for Seattle) returned as the office field by nws_get_forecast; product_type: AFD (default), HWO, ZFP, SPS, or FWF (Fire Weather Planning Forecast)productText plus issuanceTime, issuingOffice, productName, productCode, wmoCollectiveId; content[] carries the product text in a text code block, so fixed-width tables keep their columns; an unknown office, or one with no current product of the requested type, fails with no_productsoutline of its sections instead of productText; pass sections (AFD section names, or zone codes for ZFP/HWO/SPS/FWF) to get the matching sections, and an unknown name fails with unknown_sectionfire gets its forecast from the FWF: office from the alert's office field, product_type: "FWF", sections: [<zone code>]nws_get_zone_forecast toolzone_id: forecast zone code (e.g., "WAZ315") — returned by nws_get_forecast (forecastZone), nws_find_stations (forecastZone column), and nws_search_alerts (the code of an affectedZones entry with type: "forecast")XXC###) and unknown zone codes fail with zone_not_found, and so do most fire zone codes, whose forecast is their segment of the office FWF on nws_get_office_discussion; a fire code that is also a public zone code returns the public zone's forecast; marine zones with no text forecast fail with marine_forecast_unsupported, and land zones with none with zone_forecast_unavailablenws://alert-types resourceapplication/jsonBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
NWS-specific:
User-Agent header automatically (configurable via NWS_USER_AGENT) — NWS returns 403 without one/points, cached for 1h since grid cells rarely changenextCursor back as cursor for the next page (omitted on the last page)Agent-friendly output:
nws_get_office_discussion, nws_get_zone_forecast, and nws_search_alertsnotice naming the cause and the next stepinvalid_area_code, no_products, zone_not_found, …) so callers branch on data, not string parsingA public instance is available at https://nws.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.
For Claude Code:
claude mcp add --transport http nws-weather-mcp-server https://nws.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"nws-weather-mcp-server": {
"type": "streamableHttp",
"url": "https://nws.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"nws-weather-mcp-server": {
"url": "https://nws.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
Add the following to your MCP client configuration file.
{
"mcpServers": {
"nws-weather-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/nws-weather-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"nws-weather-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/nws-weather-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"nws-weather-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/nws-weather-mcp-server:latest"]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_SESSION_MODE=stateless MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/nws-weather-mcp-server.git
cd nws-weather-mcp-server
bun install
| Variable | Description | Default |
|---|---|---|
NWS_USER_AGENT | User-Agent for NWS API requests. The API requires this header. | (nws-weather-mcp-server, ...) |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_HTTP_HOST | Hostname for HTTP server. | 127.0.0.1 |
MCP_SESSION_MODE | HTTP session mode: stateful, stateless, or auto. | stateless |
MCP_LOG_LEVEL | Log level: debug, info, notice, warning, error. | info |
See .env.example for the full list including auth, storage, and OpenTelemetry options.
Build and run the production version:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks
bun run test # Runs test suite
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and the resource. |
src/mcp-server/tools/definitions/ | Tool definitions (*.tool.ts). |
src/mcp-server/resources/definitions/ | Resource definitions (*.resource.ts). |
src/services/nws/ | NWS API client and response types. |
src/config/ | Environment variable parsing and validation with Zod. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for domain-specific logging, ctx.state for storagecreateApp() arrays in src/index.tsIssues are welcome. Run checks before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.