Skip to content

noaa cdo mcp server changelog

io.github.cyanheads/noaa-cdo-mcp-server

4 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • AGENTS.md +12 −9

    https://raw.githubusercontent.com/cyanheads/noaa-cdo-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.8.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`− **Engines:** Bun ≥1.4.0, Node ≥24.0.0− NOAA Climate Data Online (CDO) API v2 — historical weather data — plus two separate NCEI bulk-CSV corpora: the Storm Events Database and Billion-Dollar Weather and Climate Disasters. 10 tools, 2 data resources, and 1 published agent skill:− - `noaa_climate_search_storm_events` — search NCEI Storm Events for one year (severe-weather events, damage, casualties, narratives)− CDO station inputs share `stationIdFilter` / `stationIdArrayFilter` and `normalizeStationId` in `shared/validation.ts`: trim, uppercase, and prefix bare 11-character GHCN aliases; generic IDs remain opaque. Successful tools compose normalization notices; the station resource keeps its existing result shape. `/data` alone checks the actual encoded query against 4000 characters before retry/fetch. Fetch-data keeps raw CDO types distinct from its output: documented PRECIP_HLY/HPCP and PRECIP_15/QPC− - `src/services/storm-events/storm-events-service.ts` — NCEI bulk CSV. No token, no JSON envelope, no `resolveCollectionTotal()`. Resolves each year's `_c<publishDate>`-suffixed filename from the live directory index (it is not constructible), decompresses explicitly (the files carry no `Content-Encoding`), and streams a year through the shared CSV reader so the decompressed ~70 MB is never held. Caches the listing and two years of compressed bytes for six hours. Damage parsing lives in `damage.−       noaa-climate-*.tool.ts            # 10 tool definitions− `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by+ **Version:** 0.9.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.22`+ **Engines:** Bun ≥1.4.4, Node ≥24.0.0+ NOAA Climate Data Online (CDO) API v2 — historical weather data — plus three separate NCEI sources: the 1991–2020 U.S. Climate Normals (Access Data Service) and two bulk-CSV corpora, the Storm Events Database and Billion-Dollar Weather and Climate Disasters. 11 tools, 2 data resources, and 1 published agent skill:+ - `noaa_climate_get_normals` — read one station's 1991–2020 U.S. Climate Normals (monthly, daily, hourly, annual/seasonal) in NCEI's native units, with completeness flags; CDO's `NORMAL_*` datasets are the superseded 1981–2010 normals+ - `noaa_climate_search_storm_events` — search NCEI Storm Events for one year (severe-weather events, damage, casualties, narratives; county rows carry `cdoLocationId`, and `countyFips` selects one county)+ CDO station inputs share `stationIdFilter` / `stationIdArrayFilter` and `normalizeStationId` in `shared/validation.ts`: trim, uppercase, and prefix bare 11-character GHCN aliases; generic IDs remain opaque. `noaa_climate_get_normals` is the exception: `ghcnStationIdFilter` / `normalizeGhcnStationId` accept only a GHCN-Daily ID and send the bare form, uppercasing only an ID written all in lowercase because NCEI matches case-sensitively. Successful tools compose normalization notices; the station + - `src/services/storm-events/storm-events-service.ts` — NCEI bulk CSV. No token, no JSON envelope, no `resolveCollectionTotal()`. Resolves each year's `_c<publishDate>`-suffixed filename from the live directory index (it is not constructible), decompresses explicitly (the files carry no `Content-Encoding`), and streams a year through the shared CSV reader so the decompressed ~70 MB is never held. Caches the listing and two years of compressed bytes for six hours. Damage parsing lives in `damage.+ - `src/services/normals/normals-service.ts` — NCEI Access Data Service (`/access/services/data/v1`) for 1991–2020 normals. No token. One dataset per granularity, every datatype named explicitly (the service's own monthly default omits `MLY-TAVG-NORMAL`), `units` never sent, rows of left-padded strings parsed into one record per datatype, a bounded timeout and a total deadline around `withRetry`. A datatype outside the verified `UNITS` table gets no `unit`.+     normals/                            # 1991–2020 normals: Access Data Service client, unit labels+       noaa-climate-*.tool.ts            # 11 tool definitions+ `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by

  • AGENTS.md +13 −7

    https://raw.githubusercontent.com/cyanheads/noaa-cdo-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.7.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1− NOAA Climate Data Online (CDO) API v2 — historical weather data — plus two separate NCEI bulk-CSV corpora: the Storm Events Database and Billion-Dollar Weather and Climate Disasters. 10 tools, 2 resources:− - `noaa_climate_get_billion_dollar_disasters` — query NCEI Billion-Dollar Weather and Climate Disasters, per disaster or per year− | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |− `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was + **Version:** 0.8.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0+ NOAA Climate Data Online (CDO) API v2 — historical weather data — plus two separate NCEI bulk-CSV corpora: the Storm Events Database and Billion-Dollar Weather and Climate Disasters. 10 tools, 2 data resources, and 1 published agent skill:+ - `noaa_climate_get_billion_dollar_disasters` — query NCEI’s retired 1980–2024 Billion-Dollar Weather and Climate Disasters archive, per disaster or per year. Every successful response carries a retirement notice composed with paging guidance. CPI adjustments stay as archived, without current-price rebasing or an inferred base year; `coveredYears` stays row-derived. State shape is advertised as `^\s*[A-Za-z]{2}\s*$`; well-formed unavailable codes remain `unknown_state`.+ CDO station inputs share `stationIdFilter` / `stationIdArrayFilter` and `normalizeStationId` in `shared/validation.ts`: trim, uppercase, and prefix bare 11-character GHCN aliases; generic IDs remain opaque. Successful tools compose normalization notices; the station resource keeps its existing result shape. `/data` alone checks the actual encoded query against 4000 characters before retry/fetch. Fetch-data keeps raw CDO types distinct from its output: documented PRECIP_HLY/HPCP and PRECIP_15/QPC+ `skills/noaa-climate-guide/SKILL.md` is the published caller guide for climate comparisons, coverage, observation quality, and storm-to-station investigation. `src/index.ts` serves it through the MCP Skills extension and a skill resource alongside the two data resources. Plugin, npm, Docker, and MCPB delivery preserve the root `skills/` tree.+ | `add-skill` | Author and publish agent-facing skills from root `skills/`, served over MCP |+ | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, `.mcpbignore` coverage, plugin manifests, the published `skills/` tree (run by devcheck) |+ `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by+ - [ ] `plugin.json` + `mcp.json` (Agent Plugins 1.0.0 — read by Codex, Cursor, GitHub Copilot, VS Code, Kiro) populated — `plugin.json` `name` = the unscoped repo name (lowercase `a-z`, `0-9`, `-`, `.` only); `version`, `description`, `author`, `homepage`, `repository`, `license`, `keywords` from `package.json`; no other top-level field, `extensions` included. `mcp.json` holds one `"type": "stdio"` entry keyed by the unscoped name, `npx -y <full package name>`, and the same fixed `env` as `.code+ - [ ] Any new root dev path has a root-anchored line in `.mcpbignore`; `bun run bundle` and `lint:packaging` pass+ - [ ] If the server publishes a skill in root `skills/`: `lint:packaging` passes, and `field-test` verified every gate and identifier claim in it against live tool behavior

  • AGENTS.md +5 −5

    https://raw.githubusercontent.com/cyanheads/noaa-cdo-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.6.5− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`− **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0− **Zod:** ^4.6.5− **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the + **Version:** 0.7.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1+ **Zod:** ^4.6.7+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewri

  • AGENTS.md +19 −18

    https://raw.githubusercontent.com/cyanheads/noaa-cdo-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.6.4− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`− | `ctx.fail` / `ctx.recoveryFor` | Emit a declared tool/resource failure and carry its canonical recovery hint. |− | `ctx.requestId` / `ctx.tenantId` / `ctx.auth` | Propagated by passing the complete `ctx` as `parentContext` when a service creates an operation context. |− **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reaso−   if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));− throw new McpError(JsonRpcErrorCode.DatabaseError, 'Connection failed', { pool: 'primary' });− | `git-wrapup` | Land working-tree changes as a versioned commit stack on a `release/<version>` branch — version bump, changelog, verify, PR |− | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync |− | `release-and-publish` | Fast-forward `main`, tag, push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |− | `api-context` | Context interface, dual-sink logger, state, requestInput/inputs, enrichment |− | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — it re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |− **CI is one file.** `.github/workflows/codeql.yml` is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.− **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.− **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's bri− - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = package name; `interface.shortDescription` from `package.json` description− - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys− - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys+ **Version:** 0.6.5+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`+ - **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.+ | `ctx.fail` | Emit a declared tool/resource failure; the framework fills the entry's recovery hint on the wire. `ctx.recoveryFor(reason)` reads a declared hint, for forwarding one reason's hint onto another. |+ | `ctx.requestId` / `ctx.tenantId` / `ctx.auth` | Propagated by passing the complete `ctx` as `parentContext` when a service creates an operation context. `ctx.requestId` is the ID every log record of the call carries and its error envelope returns as `data.requestId`. |+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. The framework puts it on the+   if (!item) throw ctx.fail('no_match', `No item ${input.id}`);+ throw new McpError(JsonRpcErrorCode.InitializationFailed, 'Connection failed', { pool: 'primary' });+ | `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |+ | `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |+ | `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |+ | `api-context` | Context interface, RequestContext, logger, state, multi-round-trip input |+ | `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep (the framework pin included) and rewrites the lockfile as `lockfileVersion: 2` |+ **CI is one file.** `.github/workflows/codeql.yml` (scaffolded) is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.+ **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, that every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"` (the host substitutes nothing else — `"${X}"` reaches the server as that literal string), and that an optional string option carries `"default": ""`.+ **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the + - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name (never the npm scope — `lint:packaging` enforces this); `interface.shortDescription` from `package.json` description+ - [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable (API key, contact email, instance URL) is listed in `env_vars` so Codex forwards it from the user's environment. Never write `"KEY": ""` into `env` — an empty value replaces the user's exported key and is read as unset+ - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `author`, `repository`, `license`, `keywords` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name. Every user-supplied variable is declared under `userConfig` (`type`, `title`, `description`; `sensitive: true` for keys and tokens; `required: true` or `default: ""`) and referenced from `env` as `"KEY": "${user_config.<option>}"` — mirror the `user_config` block in `manifest.json`. Never wri