.codex-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited .codex-plugin (MCP Server) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center"> <h1>@cyanheads/nws-weather-mcp-server</h1> <p><b>Get US weather forecasts, active alerts, and current observations via the National Weather Service API. STDIO or Streamable HTTP.</b> <div>10 Tools • 2 Resources</div> </p> </div>
<div align="center">
</div>
<div align="center">
</div>
<div align="center">
Public Hosted Server: https://nws.caseyjhand.com/mcp
</div>
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. |
skill://nws-weather-guide/SKILL.md | Decision gates for combining forecasts, alerts, station trends, and forecaster discussions. |
Alert types are also reachable via nws_list_alert_types. The weather guide is available through the MCP Skills extension and in the plugin's skills/ directory.
nws_get_forecast <sub>tool</sub>latitude + 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 <sub>tool</sub>area, 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 <sub>tool</sub>alert_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 <sub>tool</sub>totalAlerts (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 <sub>tool</sub>station_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 <sub>tool</sub>station_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 <sub>tool</sub>latitude + 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 <sub>tool</sub>event filter in nws_search_alertsnws_get_office_discussion <sub>tool</sub>office: 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 <sub>tool</sub>zone_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 <sub>resource</sub>application/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 parsingConnect to the hosted instance, no install:
claude mcp add --transport http nws-weather-mcp-server https://nws.caseyjhand.com/mcpOr run it locally:
claude mcp add nws-weather-mcp-server -- npx -y @cyanheads/nws-weather-mcp-serverFor other clients, add this to the MCP config file:
{
"mcpServers": {
"nws-weather-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/nws-weather-mcp-server"]
}
}
}NWS_USER_AGENT optionally sets the contact identifier sent to NWS. For Docker or a source build, see Running the server.
| 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.
Source development requires Bun v1.4+. The published executable also runs on Node.js v24+.
git clone https://github.com/cyanheads/nws-weather-mcp-server.git
cd nws-weather-mcp-server && bun install
cp .env.example .env # One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio bun run devcheck # Lints, formats, type-checks
bun run test # Runs test suitedocker run --rm -p 3010:3010 ghcr.io/cyanheads/nws-weather-mcp-server:latestThe image defaults to HTTP transport, stateless session mode, and logs to /var/log/nws-weather-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/nws-weather-mcp-server:latest"]. To build locally, docker build -t nws-weather-mcp-server .; OpenTelemetry peers are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools, the alert-types resource, and published skills. |
skills/ | Published agent skills, also served over MCP. |
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 testApache-2.0 — see LICENSE for details.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.