@cyanheads/noaa-marine-mcp-server 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +37 -22
- package/CLAUDE.md +37 -22
- package/README.md +73 -81
- package/changelog/0.3.x/0.3.2.md +30 -0
- package/changelog/template.md +7 -24
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** noaa-marine-mcp-server
|
|
4
|
-
**Version:** 0.3.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.3.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.2`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
8
|
-
**Zod:** ^4.4
|
|
8
|
+
**Zod:** ^4.6.4
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -37,6 +37,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
37
37
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
38
38
|
- **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
|
|
39
39
|
- **Secrets in env vars only** — never hardcoded.
|
|
40
|
+
- **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.
|
|
40
41
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
41
42
|
|
|
42
43
|
---
|
|
@@ -145,7 +146,13 @@ export function getServerConfig() {
|
|
|
145
146
|
|
|
146
147
|
### Server identity and instructions
|
|
147
148
|
|
|
148
|
-
This server's `createApp()` identity is the bare repository name in both `name` and `title`: `noaa-marine-mcp-server`. `instructions` is optional server-level orientation sent to clients on every `initialize`; use it for deployment guidance and cross-tool workflows instead of repeating the same context across tool descriptions.
|
|
149
|
+
This server's `createApp()` identity is the bare repository name in both `name` and `title`: `noaa-marine-mcp-server`. Never add `description` or `websiteUrl` — the framework derives canonical metadata from `package.json`, and a copy in `createApp()` is drift. `instructions` is optional server-level orientation sent to clients on every `initialize`; use it for deployment guidance and cross-tool workflows instead of repeating the same context across tool descriptions.
|
|
150
|
+
|
|
151
|
+
### Session posture and shutdown
|
|
152
|
+
|
|
153
|
+
`createApp({ sessionMode: 'stateless' })` declares the HTTP session posture in `src/` rather than leaving it to a deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value (an empty string and an unsubstituted `${…}` placeholder read as unset and fall through to the option). Stateless is correct here: no tool gates on `ctx.requestInput`. A server that does gain one declares `{ default: 'stateful', require: 'stateful' }` so startup fails with a `ConfigurationError` instead of serving a mode a 2025-era HTTP client can never answer in. Stdio is never refused.
|
|
154
|
+
|
|
155
|
+
`teardown(core)` is the `setup()` counterpart — it releases a watcher, socket, or non-`unref()`'d timer after the transport stops and before the logger closes. Neither service here allocates one (both hold an in-memory station cache and nothing else), so the hook is unused; add it the moment a service acquires a handle it must close. `SIGTERM`/`SIGINT` exit the process explicitly once shutdown settles.
|
|
149
156
|
|
|
150
157
|
---
|
|
151
158
|
|
|
@@ -171,7 +178,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
171
178
|
|
|
172
179
|
Handlers throw — the framework catches, classifies, and formats.
|
|
173
180
|
|
|
174
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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, and the 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit hint when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
181
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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, and the 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit hint when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
175
182
|
|
|
176
183
|
```ts
|
|
177
184
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -253,9 +260,9 @@ src/
|
|
|
253
260
|
|
|
254
261
|
## Skills
|
|
255
262
|
|
|
256
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
263
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant for those agents.
|
|
257
264
|
|
|
258
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
265
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
259
266
|
|
|
260
267
|
Available skills:
|
|
261
268
|
|
|
@@ -274,8 +281,9 @@ Available skills:
|
|
|
274
281
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
275
282
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
276
283
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
277
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
278
|
-
| `release-
|
|
284
|
+
| `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 |
|
|
285
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixup commits autosquashed into the stack, PR body kept in sync. Release PR mode only |
|
|
286
|
+
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
279
287
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
280
288
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
281
289
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -294,7 +302,7 @@ Available skills:
|
|
|
294
302
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
295
303
|
| `api-workers` | Cloudflare Workers runtime |
|
|
296
304
|
|
|
297
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
305
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
298
306
|
|
|
299
307
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
300
308
|
|
|
@@ -310,9 +318,10 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
310
318
|
| `bun run rebuild` | Clean + build |
|
|
311
319
|
| `bun run clean` | Remove build artifacts |
|
|
312
320
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
313
|
-
| `bun run audit:
|
|
314
|
-
| `bun run
|
|
315
|
-
| `bun run lint:
|
|
321
|
+
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
322
|
+
| `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` |
|
|
323
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
324
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, README version-badge parity (run by devcheck) |
|
|
316
325
|
| `bun run list-skills` | Print the skill registry |
|
|
317
326
|
| `bun run tree` | Generate directory structure doc |
|
|
318
327
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
@@ -328,11 +337,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
328
337
|
|
|
329
338
|
## Bundling
|
|
330
339
|
|
|
331
|
-
`bun 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 dependency-shipped agent docs
|
|
340
|
+
`bun 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 packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
332
341
|
|
|
333
|
-
**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.
|
|
342
|
+
**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": ""`.
|
|
334
343
|
|
|
335
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
344
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
336
345
|
|
|
337
346
|
---
|
|
338
347
|
|
|
@@ -353,16 +362,22 @@ security: false # optional — true ONLY for a source
|
|
|
353
362
|
...
|
|
354
363
|
```
|
|
355
364
|
|
|
356
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's own source code
|
|
365
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
357
366
|
|
|
358
367
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
359
368
|
|
|
360
|
-
**Section order
|
|
369
|
+
**Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
|
|
361
370
|
|
|
362
371
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
363
372
|
|
|
364
373
|
---
|
|
365
374
|
|
|
375
|
+
## Publishing
|
|
376
|
+
|
|
377
|
+
**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 brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
366
381
|
## Imports
|
|
367
382
|
|
|
368
383
|
```ts
|
|
@@ -389,7 +404,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
389
404
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
390
405
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
391
406
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
392
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
393
|
-
- [ ] `.codex-plugin/mcp.json` updated — server name key
|
|
394
|
-
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry
|
|
407
|
+
- [ ] `.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
|
|
408
|
+
- [ ] `.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
|
|
409
|
+
- [ ] `.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 write `"KEY": ""` into `env`
|
|
395
410
|
- [ ] `bun run devcheck` passes
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** noaa-marine-mcp-server
|
|
4
|
-
**Version:** 0.3.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.3.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.2`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
8
|
-
**Zod:** ^4.4
|
|
8
|
+
**Zod:** ^4.6.4
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -37,6 +37,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
37
37
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
38
38
|
- **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
|
|
39
39
|
- **Secrets in env vars only** — never hardcoded.
|
|
40
|
+
- **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.
|
|
40
41
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
41
42
|
|
|
42
43
|
---
|
|
@@ -145,7 +146,13 @@ export function getServerConfig() {
|
|
|
145
146
|
|
|
146
147
|
### Server identity and instructions
|
|
147
148
|
|
|
148
|
-
This server's `createApp()` identity is the bare repository name in both `name` and `title`: `noaa-marine-mcp-server`. `instructions` is optional server-level orientation sent to clients on every `initialize`; use it for deployment guidance and cross-tool workflows instead of repeating the same context across tool descriptions.
|
|
149
|
+
This server's `createApp()` identity is the bare repository name in both `name` and `title`: `noaa-marine-mcp-server`. Never add `description` or `websiteUrl` — the framework derives canonical metadata from `package.json`, and a copy in `createApp()` is drift. `instructions` is optional server-level orientation sent to clients on every `initialize`; use it for deployment guidance and cross-tool workflows instead of repeating the same context across tool descriptions.
|
|
150
|
+
|
|
151
|
+
### Session posture and shutdown
|
|
152
|
+
|
|
153
|
+
`createApp({ sessionMode: 'stateless' })` declares the HTTP session posture in `src/` rather than leaving it to a deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value (an empty string and an unsubstituted `${…}` placeholder read as unset and fall through to the option). Stateless is correct here: no tool gates on `ctx.requestInput`. A server that does gain one declares `{ default: 'stateful', require: 'stateful' }` so startup fails with a `ConfigurationError` instead of serving a mode a 2025-era HTTP client can never answer in. Stdio is never refused.
|
|
154
|
+
|
|
155
|
+
`teardown(core)` is the `setup()` counterpart — it releases a watcher, socket, or non-`unref()`'d timer after the transport stops and before the logger closes. Neither service here allocates one (both hold an in-memory station cache and nothing else), so the hook is unused; add it the moment a service acquires a handle it must close. `SIGTERM`/`SIGINT` exit the process explicitly once shutdown settles.
|
|
149
156
|
|
|
150
157
|
---
|
|
151
158
|
|
|
@@ -171,7 +178,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
171
178
|
|
|
172
179
|
Handlers throw — the framework catches, classifies, and formats.
|
|
173
180
|
|
|
174
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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, and the 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit hint when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
181
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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, and the 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit hint when runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
175
182
|
|
|
176
183
|
```ts
|
|
177
184
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -253,9 +260,9 @@ src/
|
|
|
253
260
|
|
|
254
261
|
## Skills
|
|
255
262
|
|
|
256
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
263
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant for those agents.
|
|
257
264
|
|
|
258
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
265
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
259
266
|
|
|
260
267
|
Available skills:
|
|
261
268
|
|
|
@@ -274,8 +281,9 @@ Available skills:
|
|
|
274
281
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
275
282
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
276
283
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
277
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
278
|
-
| `release-
|
|
284
|
+
| `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 |
|
|
285
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixup commits autosquashed into the stack, PR body kept in sync. Release PR mode only |
|
|
286
|
+
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
279
287
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
280
288
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
281
289
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -294,7 +302,7 @@ Available skills:
|
|
|
294
302
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
295
303
|
| `api-workers` | Cloudflare Workers runtime |
|
|
296
304
|
|
|
297
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
305
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
298
306
|
|
|
299
307
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
300
308
|
|
|
@@ -310,9 +318,10 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
310
318
|
| `bun run rebuild` | Clean + build |
|
|
311
319
|
| `bun run clean` | Remove build artifacts |
|
|
312
320
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
313
|
-
| `bun run audit:
|
|
314
|
-
| `bun run
|
|
315
|
-
| `bun run lint:
|
|
321
|
+
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response when `devcheck` flags a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
322
|
+
| `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` |
|
|
323
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
324
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, README version-badge parity (run by devcheck) |
|
|
316
325
|
| `bun run list-skills` | Print the skill registry |
|
|
317
326
|
| `bun run tree` | Generate directory structure doc |
|
|
318
327
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
@@ -328,11 +337,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
328
337
|
|
|
329
338
|
## Bundling
|
|
330
339
|
|
|
331
|
-
`bun 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 dependency-shipped agent docs
|
|
340
|
+
`bun 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 packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
332
341
|
|
|
333
|
-
**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.
|
|
342
|
+
**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": ""`.
|
|
334
343
|
|
|
335
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
344
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
336
345
|
|
|
337
346
|
---
|
|
338
347
|
|
|
@@ -353,16 +362,22 @@ security: false # optional — true ONLY for a source
|
|
|
353
362
|
...
|
|
354
363
|
```
|
|
355
364
|
|
|
356
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's own source code
|
|
365
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
357
366
|
|
|
358
367
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
359
368
|
|
|
360
|
-
**Section order
|
|
369
|
+
**Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
|
|
361
370
|
|
|
362
371
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
363
372
|
|
|
364
373
|
---
|
|
365
374
|
|
|
375
|
+
## Publishing
|
|
376
|
+
|
|
377
|
+
**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 brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
366
381
|
## Imports
|
|
367
382
|
|
|
368
383
|
```ts
|
|
@@ -389,7 +404,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
389
404
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
390
405
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
391
406
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
392
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
393
|
-
- [ ] `.codex-plugin/mcp.json` updated — server name key
|
|
394
|
-
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry
|
|
407
|
+
- [ ] `.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
|
|
408
|
+
- [ ] `.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
|
|
409
|
+
- [ ] `.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 write `"KEY": ""` into `env`
|
|
395
410
|
- [ ] `bun run devcheck` passes
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/noaa-marine-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/noaa-marine-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -27,128 +27,120 @@
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Overview
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
US tide, current, and buoy data from NOAA CO-OPS and NDBC. Find tide, water-level, and current stations plus NDBC buoys, then fetch tide predictions, observed water levels, tidal currents, and live buoy conditions from any MCP client. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
|
|
33
|
+
|
|
34
|
+
### Tools
|
|
33
35
|
|
|
34
36
|
| Tool | Description |
|
|
35
37
|
|:-----|:------------|
|
|
36
|
-
| `noaa_marine_find_stations` | Find CO-OPS tide/water-level/current stations and NDBC buoys
|
|
37
|
-
| `noaa_marine_get_tide_predictions` | High/low tide predictions
|
|
38
|
-
| `noaa_marine_get_water_level` | Observed water level
|
|
39
|
-
| `noaa_marine_get_currents` |
|
|
40
|
-
| `noaa_marine_get_conditions` | Live
|
|
41
|
-
| `noaa_marine_get_current_profile` | Observed ocean-current depth profile from an NDBC ADCP buoy
|
|
42
|
-
| `noaa_marine_get_ocean_observations` | Sub-surface water-column observations
|
|
38
|
+
| `noaa_marine_find_stations` | Find CO-OPS tide/water-level/current stations and NDBC buoys by location, name, state, or data capability. |
|
|
39
|
+
| `noaa_marine_get_tide_predictions` | High/low tide predictions or a 6-minute curve for a CO-OPS tide station. |
|
|
40
|
+
| `noaa_marine_get_water_level` | Observed water level paired with predictions for a CO-OPS station, with a storm-surge residual summary. |
|
|
41
|
+
| `noaa_marine_get_currents` | CO-OPS tidal current predictions — max flood/ebb/slack events or a 6-minute curve. |
|
|
42
|
+
| `noaa_marine_get_conditions` | Live NDBC buoy conditions: waves, wind, sea-surface and air temperature, pressure. |
|
|
43
|
+
| `noaa_marine_get_current_profile` | Observed ocean-current depth profile from an NDBC ADCP buoy. |
|
|
44
|
+
| `noaa_marine_get_ocean_observations` | Sub-surface water-column observations (temperature, salinity, oxygen, and more) from an NDBC station. |
|
|
43
45
|
|
|
44
|
-
###
|
|
46
|
+
### Resources
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
| Resource | Description |
|
|
49
|
+
|:---|:---|
|
|
50
|
+
| `noaa-marine://station/{station_id}` | Metadata for a CO-OPS or NDBC station by ID: name, coordinates, source, data capabilities, and — for NDBC — physical platform class. |
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
- Returns a unified station list with source, coordinates, distance, data capabilities, and — for NDBC — the physical platform class (buoy, fixed, oilrig, dart, tao, usv, other)
|
|
50
|
-
- Station lists are cached in-memory (6-hour TTL) — first call after startup may be slightly slower
|
|
51
|
-
- Required first step: CO-OPS and NDBC use non-overlapping ID systems; guessing a station ID reliably fails
|
|
52
|
+
All resource data is also reachable via tools — use `noaa_marine_find_stations` to discover station IDs before accessing the resource.
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
## Capability reference
|
|
55
|
+
|
|
56
|
+
### `noaa_marine_find_stations` <sub>tool</sub>
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
- Filter by proximity (`latitude`/`longitude` + `radius_km`, default 100 km, max 1000 km), name/ID substring, US state/territory (CO-OPS only), source (`coops`/`ndbc`/`all`), or `types`: data capabilities (`tide`, `current`, `water_level`, `met`, `current_profile`) or NDBC platform class (`buoy`)
|
|
59
|
+
- Returns up to `limit` (default 20, max 200) unified stations with source, coordinates, distance, data capabilities, and — for NDBC — physical platform class (buoy, fixed, oilrig, dart, tao, usv, other)
|
|
60
|
+
- `total_found` and `truncated` report the full match count before the limit is applied
|
|
61
|
+
- Station lists are cached in-memory with a 6-hour TTL — first call after startup may be slightly slower
|
|
62
|
+
- Typed `incomplete_coordinates` error when only one of latitude/longitude is supplied; `no_results` when nothing matches
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### `noaa_marine_get_tide_predictions` <sub>tool</sub>
|
|
58
67
|
|
|
59
|
-
-
|
|
68
|
+
- `hilo` (default, high/low events) or `6min` continuous curve; up to 1 year per request
|
|
60
69
|
- Eight datums: MLLW (default, US nautical chart), MHHW, MSL, MTL, MHW, MLW, CD, STND
|
|
61
|
-
- Time zone
|
|
62
|
-
-
|
|
63
|
-
- Maximum date range: 1 year per request (typed error `date_range_exceeded` for longer ranges)
|
|
70
|
+
- Time zone (`lst_ldt` default, `gmt`, `lst`) and units (`english` default feet, `metric` meters)
|
|
71
|
+
- Typed `date_range_exceeded`, `invalid_date_range`, `station_not_found`, and `no_predictions` errors
|
|
64
72
|
|
|
65
73
|
---
|
|
66
74
|
|
|
67
|
-
### `noaa_marine_get_water_level`
|
|
68
|
-
|
|
69
|
-
Observed water level vs. predicted — the storm surge view.
|
|
75
|
+
### `noaa_marine_get_water_level` <sub>tool</sub>
|
|
70
76
|
|
|
71
|
-
- 6-minute observed water level
|
|
72
|
-
- Paired tide predictions fetched in parallel
|
|
73
|
-
-
|
|
74
|
-
-
|
|
77
|
+
- 6-minute observed water level with quality flags (`p` preliminary, `v` verified) and optional sensor `sigma`
|
|
78
|
+
- Paired 6-minute tide predictions fetched in parallel — failure degrades gracefully, observed levels still return
|
|
79
|
+
- `residual_summary` (max surge, max drawdown) only when both series are present
|
|
80
|
+
- Up to 31 days per request; typed `date_range_exceeded`, `station_not_found`, and `no_data` errors
|
|
75
81
|
|
|
76
82
|
---
|
|
77
83
|
|
|
78
|
-
### `noaa_marine_get_currents`
|
|
84
|
+
### `noaa_marine_get_currents` <sub>tool</sub>
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
- Current station IDs use alphanumeric format (e.g., `ACT4176`), distinct from numeric tide station IDs — use `find_stations` with `types: ["current"]` to discover them
|
|
86
|
+
- `MAX_SLACK` (default): max flood, max ebb, and slack events only — the actionable view for passage planning
|
|
87
|
+
- `6min`: continuous current curve; units `english` (knots, default) or `metric` (m/s)
|
|
88
|
+
- Current station IDs are alphanumeric (e.g. `ACT4176`), distinct from numeric tide/water-level IDs
|
|
89
|
+
- Up to 1 year per request; typed `date_range_exceeded`, `invalid_date_range`, `station_not_found`, and `no_predictions` errors
|
|
85
90
|
|
|
86
91
|
---
|
|
87
92
|
|
|
88
|
-
### `noaa_marine_get_conditions`
|
|
89
|
-
|
|
90
|
-
Live NDBC buoy observations (most recent ~45 days, updated every 10 minutes).
|
|
93
|
+
### `noaa_marine_get_conditions` <sub>tool</sub>
|
|
91
94
|
|
|
92
|
-
- Wave height
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
- All sensor fields nullable (`null` when buoy sensor did not report — normal for offshore buoys)
|
|
97
|
-
- All values in SI units except `TIDE` (feet) and `VIS` (nautical miles), which are rarely populated at offshore buoys
|
|
95
|
+
- Wave height/period/direction, wind speed/gust/direction, sea-surface and air temperature, dew point, barometric pressure
|
|
96
|
+
- All values SI except `tide_ft` (feet) and `visibility_nmi` (nautical miles), both rarely populated at offshore buoys
|
|
97
|
+
- Every sensor field is nullable — `null` when the buoy did not report, never a fabricated value
|
|
98
|
+
- Updated roughly every 10 minutes; typed `buoy_not_found` and `no_sensor_data` errors
|
|
98
99
|
|
|
99
100
|
---
|
|
100
101
|
|
|
101
|
-
### `noaa_marine_get_current_profile`
|
|
102
|
-
|
|
103
|
-
Observed ocean-current depth profile from an NDBC ADCP buoy — the most recent measurement at each depth bin.
|
|
102
|
+
### `noaa_marine_get_current_profile` <sub>tool</sub>
|
|
104
103
|
|
|
105
|
-
- Depth (m), direction (degrees true,
|
|
106
|
-
-
|
|
107
|
-
- Most NDBC stations serve no ADCP profile
|
|
108
|
-
- Direction or speed is `null`
|
|
104
|
+
- Depth (m), direction (degrees true, flow-toward), and speed (cm/s) per bin, shallowest first
|
|
105
|
+
- Observed NDBC ADCP measurement — distinct from `noaa_marine_get_currents`, a CO-OPS tidal-current *prediction*
|
|
106
|
+
- Most NDBC stations serve no ADCP profile; use `find_stations` with `types: ["current_profile"]` to discover ones that do
|
|
107
|
+
- Direction or speed is `null` per bin when the sensor did not report that component; typed `profile_not_found` and `no_current_data` errors
|
|
109
108
|
|
|
110
|
-
|
|
109
|
+
---
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
### `noaa_marine_get_ocean_observations` <sub>tool</sub>
|
|
113
112
|
|
|
114
|
-
- Water temperature
|
|
115
|
-
-
|
|
116
|
-
- Sensor coverage is sparse — most stations report only temperature and salinity;
|
|
117
|
-
-
|
|
113
|
+
- Water temperature, conductivity, salinity, dissolved oxygen (% and ppm), chlorophyll, turbidity, pH, and redox potential per depth
|
|
114
|
+
- Water-column counterpart to `noaa_marine_get_conditions` (surface weather and sea state)
|
|
115
|
+
- Sensor coverage is sparse — most stations report only temperature and salinity; unreported values are `null`, never a fabricated zero
|
|
116
|
+
- No capability filter identifies ocean-sensor coverage — call on candidate `source="ndbc"` IDs and expect `observations_not_found` on stations with no `.ocean` file
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
---
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
|:-----|:-----|:------------|
|
|
123
|
-
| Resource | `noaa-marine://station/{station_id}` | Metadata for a CO-OPS or NDBC station by ID: name, coordinates, source, data capabilities, state, and — for NDBC — physical platform class. |
|
|
120
|
+
### `noaa-marine://station/{station_id}` <sub>resource</sub>
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
- Station record as `application/json` — name, coordinates, source, capabilities, state, and (NDBC) platform class
|
|
123
|
+
- `station_id` comes from `noaa_marine_find_stations`
|
|
124
|
+
- Cached with a 6-hour TTL (`cacheHint`)
|
|
126
125
|
|
|
127
126
|
## Features
|
|
128
127
|
|
|
129
|
-
Built on [`@cyanheads/mcp-ts-core`](https://
|
|
130
|
-
|
|
131
|
-
- Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
|
|
132
|
-
- Unified error handling — handlers throw, framework catches, classifies, and formats with typed error contracts
|
|
133
|
-
- Structured logging with optional OpenTelemetry tracing
|
|
134
|
-
- STDIO and Streamable HTTP transports
|
|
135
|
-
- MCP 2026 cache hints for static definition lists and station metadata
|
|
136
|
-
- Pluggable auth: `none`, `jwt`, `oauth`
|
|
128
|
+
Built on [`@cyanheads/mcp-ts-core`](https://github.com/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.
|
|
137
129
|
|
|
138
|
-
|
|
130
|
+
CO-OPS / NDBC-specific:
|
|
139
131
|
|
|
140
132
|
- In-memory station cache (6-hour TTL) for CO-OPS and NDBC station lists — discovery is fast after first startup
|
|
141
133
|
- CO-OPS and NDBC integrated in a unified station model — `find_stations` fans out across both sources in parallel
|
|
142
|
-
- NDBC fixed-width text parser
|
|
143
|
-
- Paired water-level
|
|
134
|
+
- NDBC fixed-width text parser normalizes `MM` (missing sensor data) to `null`, never passes it through as a string
|
|
135
|
+
- Paired water-level and prediction fetches for storm-surge residual computation
|
|
144
136
|
- CO-OPS `application=` courtesy parameter sent on every request (configurable via `NOAA_APPLICATION_ID`)
|
|
145
137
|
|
|
146
138
|
Agent-friendly output:
|
|
147
139
|
|
|
148
|
-
- Datum echoed on every tide/water-level response
|
|
149
|
-
- `total_found` on `find_stations` shows count before `limit` slice so agents know whether to re-query
|
|
150
|
-
- All NDBC sensor fields explicitly nullable
|
|
151
|
-
- Typed station source (`coops` | `ndbc`)
|
|
140
|
+
- Datum echoed on every tide/water-level response so agents state units and reference correctly without assumptions
|
|
141
|
+
- `total_found` on `find_stations` shows the count before the `limit` slice, so agents know whether to re-query
|
|
142
|
+
- All NDBC sensor fields explicitly nullable — agents don't fabricate missing readings
|
|
143
|
+
- Typed station `source` (`coops` | `ndbc`) plus a data-capability `type` and (NDBC only) a `platform` class — agents branch on data, not string parsing
|
|
152
144
|
|
|
153
145
|
## Getting started
|
|
154
146
|
|
|
@@ -169,7 +161,7 @@ Connect directly via Streamable HTTP — no install, no API key:
|
|
|
169
161
|
}
|
|
170
162
|
```
|
|
171
163
|
|
|
172
|
-
### Self-
|
|
164
|
+
### Self-Hosted / Local
|
|
173
165
|
|
|
174
166
|
Add the following to your MCP client configuration file:
|
|
175
167
|
|
|
@@ -272,7 +264,7 @@ cp .env.example .env
|
|
|
272
264
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
273
265
|
| `MCP_HTTP_PORT` | Port for HTTP server. | `3010` |
|
|
274
266
|
| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
275
|
-
| `MCP_SESSION_MODE` | HTTP session mode: `auto`, `stateful`, or `stateless`.
|
|
267
|
+
| `MCP_SESSION_MODE` | HTTP session mode: `auto`, `stateful`, or `stateless`. The server declares `stateless` in `src/index.ts`; set this to override. | `stateless` |
|
|
276
268
|
| `MCP_LOG_LEVEL` | Log level (RFC 5424). | `info` |
|
|
277
269
|
| `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
|
|
278
270
|
| `OTEL_ENABLED` | Enable [OpenTelemetry instrumentation](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
|
|
@@ -335,7 +327,7 @@ See [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and archit
|
|
|
335
327
|
|
|
336
328
|
## Contributing
|
|
337
329
|
|
|
338
|
-
Issues
|
|
330
|
+
Issues are welcome. Run checks and tests before submitting:
|
|
339
331
|
|
|
340
332
|
```sh
|
|
341
333
|
bun run devcheck
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Adopts mcp-ts-core 0.13.2: sessionMode is declared in src/index.ts rather than left to MCP_SESSION_MODE, argument rejections carry the structured error envelope, a CO-OPS/NDBC 500 now retries, and the framework skill tree moves to framework-skills/."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.3.2 — 2026-09-16
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`createApp({ sessionMode: 'stateless' })`** declares the HTTP session posture in `src/index.ts` instead of relying on the `MCP_SESSION_MODE` env default; the `/.well-known/mcp.json` server card now publishes the resolved mode under `_meta` key `io.github.cyanheads.mcp-ts-core/sessionMode`.
|
|
12
|
+
- **`bun run audit:fix`** (`bun audit fix`) — first response to a transitive advisory, ahead of `bun update <name>`, `bun dedupe`, and `audit:refresh`.
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **Framework** `@cyanheads/mcp-ts-core` `0.12.3` → `^0.13.2`.
|
|
17
|
+
- **Argument rejections carry `structuredContent.error`** — an invalid tool call now returns `isError: true` with `error.code` `-32602` alongside the existing readable text.
|
|
18
|
+
- **A CO-OPS/NDBC upstream 500 retries** instead of failing on the first attempt; a 501 carries `data.retryable: false` (cyanheads/mcp-ts-core#323).
|
|
19
|
+
- **`NOAA_APPLICATION_ID`** reads an empty string or an unsubstituted `${…}` placeholder as unset, falling back to the `noaa-marine-mcp-server` default instead of sending the literal text (cyanheads/mcp-ts-core#427).
|
|
20
|
+
- **`SIGTERM`/`SIGINT` end the process explicitly** — exit `0` once shutdown settles, `1` on the 10s ceiling.
|
|
21
|
+
- **Bun engines floor raised to `>=1.4.0`.**
|
|
22
|
+
- **Framework skill tree moved `skills/` → `framework-skills/`** so the `.mcpb` bundle and plugin installs no longer ship development skills (cyanheads/mcp-ts-core#428).
|
|
23
|
+
- **Repo docs refreshed** — README restructured around an Overview and a per-primitive capability reference; issue forms, `CONTRIBUTING.md`, and `.claude-plugin/plugin.json` (`$schema`, keywords) updated for the current conventions.
|
|
24
|
+
|
|
25
|
+
## Dependencies
|
|
26
|
+
|
|
27
|
+
- `zod` `^4.4.3` → `^4.6.4`
|
|
28
|
+
- `@biomejs/biome` `^2.5.10` → `^2.5.13`
|
|
29
|
+
- `ignore` `^7.0.6` → `^7.0.9`
|
|
30
|
+
- `tsc-alias` `^1.9.2` → `^1.9.5`
|
package/changelog/template.md
CHANGED
|
@@ -117,30 +117,13 @@ security: false
|
|
|
117
117
|
in that unrelated item's metadata.
|
|
118
118
|
|
|
119
119
|
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
120
|
-
via `gh release create --notes-from-tag`.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Dependency bumps: ← section header
|
|
128
|
-
← blank line
|
|
129
|
-
- `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
|
|
130
|
-
← blank line
|
|
131
|
-
Changed: ← only sections with entries
|
|
132
|
-
← blank line
|
|
133
|
-
- `format()` output includes `query` in text mode
|
|
134
|
-
← blank line
|
|
135
|
-
Added:
|
|
136
|
-
← blank line
|
|
137
|
-
- `manifest.json` scaffolded for MCPB bundle support
|
|
138
|
-
- Install badges (Claude Desktop, Cursor, VS Code)
|
|
139
|
-
← blank line
|
|
140
|
-
<N> tests pass; `bun run devcheck` clean. ← footer
|
|
141
|
-
|
|
142
|
-
Never a flat comma-separated string. Always structured markdown with
|
|
143
|
-
sections. The tag must scan well as a rendered GitHub Release page.
|
|
120
|
+
via `gh release create --notes-from-tag`. It is a condensed digest of this
|
|
121
|
+
entry, never a copy, and its format is owned by the `release-and-publish`
|
|
122
|
+
skill (step 4, "Create the annotated tag"): the entry's `summary:` as the
|
|
123
|
+
theme line without the version, flat headline bullets — no Keep-a-Changelog
|
|
124
|
+
section headers, no gates line — at most one deps line, issue backlinks,
|
|
125
|
+
and the changelog link last. In release-PR mode the `git-wrapup` skill
|
|
126
|
+
authors that digest as the PR body's `## Changes` and the tag copies it.
|
|
144
127
|
-->
|
|
145
128
|
|
|
146
129
|
## Added
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mEAAmE,CAAC;AAC5G,OAAO,EAAE,2BAA2B,EAAE,MAAM,wEAAwE,CAAC;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iEAAiE,CAAC;AACxG,OAAO,EAAE,8BAA8B,EAAE,MAAM,2EAA2E,CAAC;AAC3H,OAAO,EAAE,4BAA4B,EAAE,MAAM,yEAAyE,CAAC;AACvH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,KAAK,EAAE;QACL,sBAAsB;QACtB,4BAA4B;QAC5B,uBAAuB;QACvB,qBAAqB;QACrB,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;KAC/B;IACD,SAAS,EAAE,CAAC,yBAAyB,CAAC;IACtC,OAAO,EAAE,EAAE;IACX,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,gBAAgB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,0BAA0B,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KACxE;IACD,YAAY,EACV,iDAAiD;QACjD,qFAAqF;QACrF,uCAAuC;QACvC,uEAAuE;QACvE,uEAAuE;QACvE,4DAA4D;QAC5D,mGAAmG;QACnG,2EAA2E;QAC3E,qIAAqI;QACrI,mGAAmG;QACnG,gFAAgF;QAChF,mFAAmF;IAErF,KAAK,CAAC,IAAI;QACR,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC1D,eAAe,EAAE,CAAC;IACpB,CAAC;CACF,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mEAAmE,CAAC;AAC5G,OAAO,EAAE,2BAA2B,EAAE,MAAM,wEAAwE,CAAC;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iEAAiE,CAAC;AACxG,OAAO,EAAE,8BAA8B,EAAE,MAAM,2EAA2E,CAAC;AAC3H,OAAO,EAAE,4BAA4B,EAAE,MAAM,yEAAyE,CAAC;AACvH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE;QACL,sBAAsB;QACtB,4BAA4B;QAC5B,uBAAuB;QACvB,qBAAqB;QACrB,uBAAuB;QACvB,2BAA2B;QAC3B,8BAA8B;KAC/B;IACD,SAAS,EAAE,CAAC,yBAAyB,CAAC;IACtC,OAAO,EAAE,EAAE;IACX,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,gBAAgB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,0BAA0B,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KACxE;IACD,YAAY,EACV,iDAAiD;QACjD,qFAAqF;QACrF,uCAAuC;QACvC,uEAAuE;QACvE,uEAAuE;QACvE,4DAA4D;QAC5D,mGAAmG;QACnG,2EAA2E;QAC3E,qIAAqI;QACrI,mGAAmG;QACnG,gFAAgF;QAChF,mFAAmF;IAErF,KAAK,CAAC,IAAI;QACR,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC1D,eAAe,EAAE,CAAC;IACpB,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/noaa-marine-mcp-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"mcpName": "io.github.cyanheads/noaa-marine-mcp-server",
|
|
5
5
|
"description": "Find NOAA tide stations and NDBC buoys, fetch tide predictions, water levels, tidal currents, and live buoy conditions via MCP. STDIO or Streamable HTTP.",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
|
|
25
25
|
"clean": "bun run scripts/clean.ts",
|
|
26
26
|
"devcheck": "bun run scripts/devcheck.ts",
|
|
27
|
+
"audit:fix": "bun audit fix",
|
|
27
28
|
"audit:refresh": "rm -f bun.lock && bun install && bun audit",
|
|
28
29
|
"tree": "bun run scripts/tree.ts",
|
|
29
30
|
"list-skills": "bun run scripts/list-skills.ts",
|
|
@@ -82,24 +83,24 @@
|
|
|
82
83
|
"license": "Apache-2.0",
|
|
83
84
|
"packageManager": "bun@1.4.0",
|
|
84
85
|
"engines": {
|
|
85
|
-
"bun": ">=1.
|
|
86
|
+
"bun": ">=1.4.0",
|
|
86
87
|
"node": ">=24.0.0"
|
|
87
88
|
},
|
|
88
89
|
"publishConfig": {
|
|
89
90
|
"access": "public"
|
|
90
91
|
},
|
|
91
92
|
"dependencies": {
|
|
92
|
-
"@cyanheads/mcp-ts-core": "0.
|
|
93
|
+
"@cyanheads/mcp-ts-core": "^0.13.2",
|
|
93
94
|
"pino-pretty": "^13.1.3",
|
|
94
|
-
"zod": "^4.4
|
|
95
|
+
"zod": "^4.6.4"
|
|
95
96
|
},
|
|
96
97
|
"devDependencies": {
|
|
97
|
-
"@biomejs/biome": "^2.5.
|
|
98
|
+
"@biomejs/biome": "^2.5.13",
|
|
98
99
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
99
100
|
"@types/node": "^26.2.0",
|
|
100
101
|
"depcheck": "^1.4.7",
|
|
101
|
-
"ignore": "^7.0.
|
|
102
|
-
"tsc-alias": "^1.9.
|
|
102
|
+
"ignore": "^7.0.9",
|
|
103
|
+
"tsc-alias": "^1.9.5",
|
|
103
104
|
"typescript": "^7.0.2",
|
|
104
105
|
"vitest": "^4.1.11"
|
|
105
106
|
}
|
package/server.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/noaa-marine-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.2",
|
|
10
10
|
"remotes": [
|
|
11
11
|
{
|
|
12
12
|
"type": "streamable-http",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
20
20
|
"identifier": "@cyanheads/noaa-marine-mcp-server",
|
|
21
21
|
"runtimeHint": "bun",
|
|
22
|
-
"version": "0.3.
|
|
22
|
+
"version": "0.3.2",
|
|
23
23
|
"packageArguments": [
|
|
24
24
|
{
|
|
25
25
|
"type": "positional",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
56
56
|
"identifier": "@cyanheads/noaa-marine-mcp-server",
|
|
57
57
|
"runtimeHint": "bun",
|
|
58
|
-
"version": "0.3.
|
|
58
|
+
"version": "0.3.2",
|
|
59
59
|
"packageArguments": [
|
|
60
60
|
{
|
|
61
61
|
"type": "positional",
|