@cyanheads/nasa-power-mcp-server 0.2.0 → 0.4.0

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.
Files changed (66) hide show
  1. package/AGENTS.md +26 -11
  2. package/CLAUDE.md +26 -11
  3. package/Dockerfile +9 -3
  4. package/README.md +50 -79
  5. package/changelog/0.3.x/0.3.0.md +35 -0
  6. package/changelog/0.4.x/0.4.0.md +36 -0
  7. package/dist/config/server-config.d.ts +1 -0
  8. package/dist/config/server-config.d.ts.map +1 -1
  9. package/dist/config/server-config.js +11 -1
  10. package/dist/config/server-config.js.map +1 -1
  11. package/dist/index.js +3 -8
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/comparison-contracts.d.ts +7 -2
  14. package/dist/mcp-server/tools/definitions/comparison-contracts.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/comparison-contracts.js +3 -1
  16. package/dist/mcp-server/tools/definitions/comparison-contracts.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.d.ts +5 -0
  18. package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.d.ts.map +1 -1
  19. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts +1 -0
  20. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts +15 -0
  22. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js +21 -3
  24. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts +24 -4
  26. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js +9 -3
  28. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts +2 -0
  30. package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js +5 -5
  33. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js.map +1 -1
  34. package/dist/mcp-server/tools/definitions/point-contracts.d.ts +2 -0
  35. package/dist/mcp-server/tools/definitions/point-contracts.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/point-contracts.js +1 -5
  37. package/dist/mcp-server/tools/definitions/point-contracts.js.map +1 -1
  38. package/dist/services/canvas/canvas-service.d.ts +9 -3
  39. package/dist/services/canvas/canvas-service.d.ts.map +1 -1
  40. package/dist/services/canvas/canvas-service.js +58 -32
  41. package/dist/services/canvas/canvas-service.js.map +1 -1
  42. package/dist/services/canvas/format.d.ts +0 -1
  43. package/dist/services/canvas/format.d.ts.map +1 -1
  44. package/dist/services/canvas/format.js +3 -3
  45. package/dist/services/canvas/format.js.map +1 -1
  46. package/dist/services/canvas/types.d.ts +11 -1
  47. package/dist/services/canvas/types.d.ts.map +1 -1
  48. package/dist/services/canvas/types.js +2 -3
  49. package/dist/services/canvas/types.js.map +1 -1
  50. package/dist/services/power/normalize.d.ts.map +1 -1
  51. package/dist/services/power/normalize.js +24 -8
  52. package/dist/services/power/normalize.js.map +1 -1
  53. package/dist/services/power/power-service.d.ts.map +1 -1
  54. package/dist/services/power/power-service.js +9 -3
  55. package/dist/services/power/power-service.js.map +1 -1
  56. package/dist/services/power/presets.d.ts +2 -0
  57. package/dist/services/power/presets.d.ts.map +1 -1
  58. package/dist/services/power/presets.js +2 -0
  59. package/dist/services/power/presets.js.map +1 -1
  60. package/dist/services/power/types.d.ts +5 -3
  61. package/dist/services/power/types.d.ts.map +1 -1
  62. package/dist/services/power/types.js +12 -4
  63. package/dist/services/power/types.js.map +1 -1
  64. package/package.json +11 -8
  65. package/server.json +37 -19
  66. package/skills/nasa-power-guide/SKILL.md +73 -0
package/AGENTS.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** nasa-power-mcp-server
4
- **Version:** 0.2.0
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.4.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0
8
8
  **Zod:** ^4.6.7
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.
@@ -52,16 +52,18 @@ Read the real contracts before extending them:
52
52
  - `src/services/power/power-service.ts` owns fixed-origin requests, bounded pacing, cache and deadlines.
53
53
  - `src/services/canvas/canvas-service.ts` owns explicit-token admission, provenance, staging and cleanup.
54
54
 
55
- Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registered; eight are enabled by default. `NASA_POWER_DATAFRAME_DROP_ENABLED=true` exposes explicit table removal. No resources or prompts are registered.
55
+ Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registered; eight are enabled by default. `NASA_POWER_DATAFRAME_DROP_ENABLED=true` exposes explicit table removal. No resources or prompts are registered. `createApp({ skills: 'skills' })` serves the published `skills/nasa-power-guide/SKILL.md` as `skill://nasa-power-guide/SKILL.md`; when a tool's gates, units or identifiers change, update that skill and re-verify its claims against live behavior.
56
56
 
57
57
  ### Configuration and distribution
58
58
 
59
- `src/config/server-config.ts` lazily parses the drop flag with `z.stringbool().default(false)` and exports fixed `POWER_LIMITS`. Never use `z.coerce.boolean()` for environment flags.
59
+ `src/config/server-config.ts` lazily parses the drop flag with `z.stringbool().default(false)`, the positive `NASA_POWER_MAX_CANVASES_PER_PROCESS` (default eight), and exports fixed `POWER_LIMITS`. Never use `z.coerce.boolean()` for environment flags.
60
60
 
61
- `src/index.ts` fixes DuckDB to 256 MiB engine memory, four canvases per tenant, a one-hour sliding TTL and a six-hour absolute cap. The service further enforces eight tables per canvas, eight canvases per process and a 30-minute table TTL. Engine memory is not total RSS or temporary-disk capacity.
61
+ `src/index.ts` defaults DuckDB to 256 MiB engine memory, four canvases per tenant, a one-hour sliding TTL and a six-hour absolute cap while honoring validated operator overrides. Pass resolved core canvas limits to service admission and provenance retention. The service enforces the configured process cap, eight tables per canvas and a 30-minute table TTL. Engine memory is not total RSS or temporary-disk capacity.
62
62
 
63
63
  `src/index.ts` also defaults `CANVAS_PROVIDER_TYPE` to `duckdb` once `parseConfig()` has loaded `.env`, so npm and Docker installs, which install the native dependency normally on supported platforms, stage by default; `none` disables staging. The portable MCPB fixes `CANVAS_PROVIDER_TYPE=none` because its bundle strips DuckDB's native bindings. Keep those option sets distinct.
64
64
 
65
+ `@duckdb/node-api` stays on 1.5.6: the framework's optional peer also admits 2.x, but its canvas SQL gate refuses `SHOW <table>` on DuckDB 2 with `plan_operator_not_allowed` (cyanheads/mcp-ts-core#800), a query 1.5.x answers with the column listing.
66
+
65
67
  ### Identity and lifecycle
66
68
 
67
69
  `createApp()` sets both `name` and `title` to `nasa-power-mcp-server`. npm install surfaces use `@cyanheads/nasa-power-mcp-server`; registry identity is `io.github.cyanheads/nasa-power-mcp-server`. Derive descriptions from `package.json`; do not duplicate one into the entrypoint.
@@ -159,9 +161,18 @@ scripts/ # Build, validation and packaging
159
161
 
160
162
  ## Skills
161
163
 
162
- 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.
164
+ Two skill trees live at the project root, written for different readers:
165
+
166
+ | Tree | For | Written by |
167
+ |:-----|:----|:-----------|
168
+ | `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
169
+ | `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |
170
+
171
+ Development skills are modular instructions. 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. Their 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 them to every agent that installs it.
172
+
173
+ A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.
163
174
 
164
- **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.
175
+ **Agent skill directory:** Copy `framework-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.
165
176
 
166
177
  Available skills:
167
178
 
@@ -173,6 +184,7 @@ Available skills:
173
184
  | `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
174
185
  | `add-resource` | Scaffold a new resource definition |
175
186
  | `add-prompt` | Scaffold a new prompt definition |
187
+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
176
188
  | `add-service` | Scaffold a new service integration |
177
189
  | `add-test` | Scaffold test file for a tool, resource, or service |
178
190
  | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
@@ -220,7 +232,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
220
232
  | `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` |
221
233
  | `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` |
222
234
  | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
223
- | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
235
+ | `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) |
224
236
  | `bun run list-skills` | Print the skill registry |
225
237
  | `bun run tree` | Generate directory structure doc |
226
238
  | `bun run format` | Auto-fix formatting (safe fixes only) |
@@ -242,7 +254,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
242
254
 
243
255
  ## Bundling
244
256
 
245
- `bun run bundle` builds the stdio-only portable MCPB for Claude Desktop. `scripts/clean-mcpb.ts` prunes development dependencies, dependency-shipped agent docs and platform-specific native bindings. The manifest fixes `CANVAS_PROVIDER_TYPE=none`: inline observations work, oversized source requests fail with narrowing guidance, and dataframe tools report that the provider is unavailable. `@duckdb/node-api` is a direct dependency for npm and Docker installs on supported platforms; bundle pruning does not change that package contract.
257
+ `bun run bundle` builds the stdio-only portable MCPB for Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship 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. `scripts/clean-mcpb.ts` prunes development dependencies, a dependency package root's agent docs, platform-specific native bindings, node-gyp build output and the framework's build-only files. Its agent-doc selector is anchored to a package root so the MCP SDK's runtime `dist/ext/skills/` modules survive (cyanheads/mcp-ts-core#794); `tests/scripts/mcpb-agent-docs.test.ts` holds the cleaner and `lint:packaging` to the same selection. The manifest fixes `CANVAS_PROVIDER_TYPE=none`: inline observations work, oversized source requests fail with narrowing guidance, and dataframe tools report that the provider is unavailable. `@duckdb/node-api` is a direct dependency for npm and Docker installs on supported platforms; bundle pruning does not change that package contract.
246
258
 
247
259
  **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": ""`.
248
260
 
@@ -305,14 +317,17 @@ import { getMyService } from '@/services/my-domain/my-service.js';
305
317
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
306
318
  - [ ] New codes and presets are checked against the selected community/resolution dictionary; source units, fill-as-`null`, `omittedParameters` and annual-period distinctions survive normalization
307
319
  - [ ] Producing tools present through `CanvasService.present()`: oversized data stages completely or fails with `canvas_required` / `dataset_limit`, never a truncated source prefix
308
- - [ ] Workload bounds live in `POWER_LIMITS`; the four fixed canvas settings match across `src/index.ts`, `.env.example`, `server.json`, `manifest.json` and both plugin manifests
320
+ - [ ] Workload bounds live in `POWER_LIMITS`; configurable canvas defaults and process cap match across `src/index.ts`, `.env.example`, `server.json`, `manifest.json` and both plugin manifests
309
321
  - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
310
322
  - [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
311
323
  - [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
312
324
  - [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
313
325
  - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
314
326
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
327
+ - [ ] `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 `.codex-plugin/mcp.json`. Neither file carries a user-supplied variable (no `""`, no `${…}` besides `${PLUGIN_ROOT}`/`${PLUGIN_DATA}`): keys reach Codex through `.codex-plugin/mcp.json` `env_vars`, which works only while `.codex-plugin/plugin.json` keeps `"mcpServers": "./.codex-plugin/mcp.json"`
315
328
  - [ ] `.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
316
329
  - [ ] `.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
317
330
  - [ ] `.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`
331
+ - [ ] Any new root dev path has a root-anchored line in `.mcpbignore`; `bun run bundle` and `lint:packaging` pass
332
+ - [ ] 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
318
333
  - [ ] `npm run devcheck` passes
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** nasa-power-mcp-server
4
- **Version:** 0.2.0
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.4.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0
8
8
  **Zod:** ^4.6.7
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.
@@ -52,16 +52,18 @@ Read the real contracts before extending them:
52
52
  - `src/services/power/power-service.ts` owns fixed-origin requests, bounded pacing, cache and deadlines.
53
53
  - `src/services/canvas/canvas-service.ts` owns explicit-token admission, provenance, staging and cleanup.
54
54
 
55
- Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registered; eight are enabled by default. `NASA_POWER_DATAFRAME_DROP_ENABLED=true` exposes explicit table removal. No resources or prompts are registered.
55
+ Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registered; eight are enabled by default. `NASA_POWER_DATAFRAME_DROP_ENABLED=true` exposes explicit table removal. No resources or prompts are registered. `createApp({ skills: 'skills' })` serves the published `skills/nasa-power-guide/SKILL.md` as `skill://nasa-power-guide/SKILL.md`; when a tool's gates, units or identifiers change, update that skill and re-verify its claims against live behavior.
56
56
 
57
57
  ### Configuration and distribution
58
58
 
59
- `src/config/server-config.ts` lazily parses the drop flag with `z.stringbool().default(false)` and exports fixed `POWER_LIMITS`. Never use `z.coerce.boolean()` for environment flags.
59
+ `src/config/server-config.ts` lazily parses the drop flag with `z.stringbool().default(false)`, the positive `NASA_POWER_MAX_CANVASES_PER_PROCESS` (default eight), and exports fixed `POWER_LIMITS`. Never use `z.coerce.boolean()` for environment flags.
60
60
 
61
- `src/index.ts` fixes DuckDB to 256 MiB engine memory, four canvases per tenant, a one-hour sliding TTL and a six-hour absolute cap. The service further enforces eight tables per canvas, eight canvases per process and a 30-minute table TTL. Engine memory is not total RSS or temporary-disk capacity.
61
+ `src/index.ts` defaults DuckDB to 256 MiB engine memory, four canvases per tenant, a one-hour sliding TTL and a six-hour absolute cap while honoring validated operator overrides. Pass resolved core canvas limits to service admission and provenance retention. The service enforces the configured process cap, eight tables per canvas and a 30-minute table TTL. Engine memory is not total RSS or temporary-disk capacity.
62
62
 
63
63
  `src/index.ts` also defaults `CANVAS_PROVIDER_TYPE` to `duckdb` once `parseConfig()` has loaded `.env`, so npm and Docker installs, which install the native dependency normally on supported platforms, stage by default; `none` disables staging. The portable MCPB fixes `CANVAS_PROVIDER_TYPE=none` because its bundle strips DuckDB's native bindings. Keep those option sets distinct.
64
64
 
65
+ `@duckdb/node-api` stays on 1.5.6: the framework's optional peer also admits 2.x, but its canvas SQL gate refuses `SHOW <table>` on DuckDB 2 with `plan_operator_not_allowed` (cyanheads/mcp-ts-core#800), a query 1.5.x answers with the column listing.
66
+
65
67
  ### Identity and lifecycle
66
68
 
67
69
  `createApp()` sets both `name` and `title` to `nasa-power-mcp-server`. npm install surfaces use `@cyanheads/nasa-power-mcp-server`; registry identity is `io.github.cyanheads/nasa-power-mcp-server`. Derive descriptions from `package.json`; do not duplicate one into the entrypoint.
@@ -159,9 +161,18 @@ scripts/ # Build, validation and packaging
159
161
 
160
162
  ## Skills
161
163
 
162
- 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.
164
+ Two skill trees live at the project root, written for different readers:
165
+
166
+ | Tree | For | Written by |
167
+ |:-----|:----|:-----------|
168
+ | `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
169
+ | `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |
170
+
171
+ Development skills are modular instructions. 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. Their 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 them to every agent that installs it.
172
+
173
+ A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.
163
174
 
164
- **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.
175
+ **Agent skill directory:** Copy `framework-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.
165
176
 
166
177
  Available skills:
167
178
 
@@ -173,6 +184,7 @@ Available skills:
173
184
  | `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
174
185
  | `add-resource` | Scaffold a new resource definition |
175
186
  | `add-prompt` | Scaffold a new prompt definition |
187
+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
176
188
  | `add-service` | Scaffold a new service integration |
177
189
  | `add-test` | Scaffold test file for a tool, resource, or service |
178
190
  | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
@@ -220,7 +232,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
220
232
  | `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` |
221
233
  | `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` |
222
234
  | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
223
- | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
235
+ | `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) |
224
236
  | `bun run list-skills` | Print the skill registry |
225
237
  | `bun run tree` | Generate directory structure doc |
226
238
  | `bun run format` | Auto-fix formatting (safe fixes only) |
@@ -242,7 +254,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
242
254
 
243
255
  ## Bundling
244
256
 
245
- `bun run bundle` builds the stdio-only portable MCPB for Claude Desktop. `scripts/clean-mcpb.ts` prunes development dependencies, dependency-shipped agent docs and platform-specific native bindings. The manifest fixes `CANVAS_PROVIDER_TYPE=none`: inline observations work, oversized source requests fail with narrowing guidance, and dataframe tools report that the provider is unavailable. `@duckdb/node-api` is a direct dependency for npm and Docker installs on supported platforms; bundle pruning does not change that package contract.
257
+ `bun run bundle` builds the stdio-only portable MCPB for Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship 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. `scripts/clean-mcpb.ts` prunes development dependencies, a dependency package root's agent docs, platform-specific native bindings, node-gyp build output and the framework's build-only files. Its agent-doc selector is anchored to a package root so the MCP SDK's runtime `dist/ext/skills/` modules survive (cyanheads/mcp-ts-core#794); `tests/scripts/mcpb-agent-docs.test.ts` holds the cleaner and `lint:packaging` to the same selection. The manifest fixes `CANVAS_PROVIDER_TYPE=none`: inline observations work, oversized source requests fail with narrowing guidance, and dataframe tools report that the provider is unavailable. `@duckdb/node-api` is a direct dependency for npm and Docker installs on supported platforms; bundle pruning does not change that package contract.
246
258
 
247
259
  **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": ""`.
248
260
 
@@ -305,14 +317,17 @@ import { getMyService } from '@/services/my-domain/my-service.js';
305
317
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
306
318
  - [ ] New codes and presets are checked against the selected community/resolution dictionary; source units, fill-as-`null`, `omittedParameters` and annual-period distinctions survive normalization
307
319
  - [ ] Producing tools present through `CanvasService.present()`: oversized data stages completely or fails with `canvas_required` / `dataset_limit`, never a truncated source prefix
308
- - [ ] Workload bounds live in `POWER_LIMITS`; the four fixed canvas settings match across `src/index.ts`, `.env.example`, `server.json`, `manifest.json` and both plugin manifests
320
+ - [ ] Workload bounds live in `POWER_LIMITS`; configurable canvas defaults and process cap match across `src/index.ts`, `.env.example`, `server.json`, `manifest.json` and both plugin manifests
309
321
  - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
310
322
  - [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
311
323
  - [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
312
324
  - [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
313
325
  - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
314
326
  - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
327
+ - [ ] `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 `.codex-plugin/mcp.json`. Neither file carries a user-supplied variable (no `""`, no `${…}` besides `${PLUGIN_ROOT}`/`${PLUGIN_DATA}`): keys reach Codex through `.codex-plugin/mcp.json` `env_vars`, which works only while `.codex-plugin/plugin.json` keeps `"mcpServers": "./.codex-plugin/mcp.json"`
315
328
  - [ ] `.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
316
329
  - [ ] `.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
317
330
  - [ ] `.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`
331
+ - [ ] Any new root dev path has a root-anchored line in `.mcpbignore`; `bun run bundle` and `lint:packaging` pass
332
+ - [ ] 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
318
333
  - [ ] `npm run devcheck` passes
package/Dockerfile CHANGED
@@ -14,7 +14,7 @@
14
14
  # output. A stage that compiles a native addon needs the target-arch toolchain
15
15
  # and cannot cross-compile this way — drop the flag there.
16
16
  # ==============================================================================
17
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS build
17
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS build
18
18
 
19
19
  WORKDIR /usr/src/app
20
20
 
@@ -29,6 +29,11 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
29
29
  # Copy the rest of the source code
30
30
  COPY . .
31
31
 
32
+ # The skills a server publishes (`createApp({ skills: 'skills' })`) are read at
33
+ # runtime from the application root. Created when the project has none, so the
34
+ # production stage's copy below holds with or without one.
35
+ RUN mkdir -p skills
36
+
32
37
  # Build the application
33
38
  RUN bun run build
34
39
 
@@ -46,7 +51,7 @@ RUN bun run build
46
51
  # A clean image rather than `FROM build`: the build stage's node_modules holds
47
52
  # devDependencies.
48
53
  # ==============================================================================
49
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS deps
54
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS deps
50
55
 
51
56
  WORKDIR /usr/src/app
52
57
 
@@ -114,7 +119,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
114
119
  # dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
115
120
  # and CMD, which run on the real target at container start.
116
121
  # ==============================================================================
117
- FROM oven/bun:1.4.3-slim AS production
122
+ FROM oven/bun:1.4.4-slim AS production
118
123
 
119
124
  WORKDIR /usr/src/app
120
125
 
@@ -136,6 +141,7 @@ COPY --from=deps /usr/src/app/node_modules ./node_modules
136
141
 
137
142
  # Copy the compiled application code from the build stage
138
143
  COPY --from=build /usr/src/app/dist ./dist
144
+ COPY --from=build /usr/src/app/skills ./skills
139
145
 
140
146
  # Mirror CLI (MirrorService adopters only — Tier 3, opt-in):
141
147
  # Copy your mirror lifecycle scripts and emit a runtime tsconfig so Bun resolves
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.2.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/nasa-power-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.3.1-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/nasa-power-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/nasa-power-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.3-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.4.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/nasa-power-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.4.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/nasa-power-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/nasa-power-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.4-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -19,6 +19,12 @@
19
19
 
20
20
  </div>
21
21
 
22
+ <div align="center">
23
+
24
+ **Public Hosted Server:** [https://nasa-power.caseyjhand.com/mcp](https://nasa-power.caseyjhand.com/mcp)
25
+
26
+ </div>
27
+
22
28
  ---
23
29
 
24
30
  ## Overview
@@ -41,6 +47,14 @@
41
47
 
42
48
  Eight tools are enabled by default; `NASA_POWER_DATAFRAME_DROP_ENABLED=true` enables the ninth.
43
49
 
50
+ ### Skills
51
+
52
+ | Skill | Description |
53
+ |:---|:---|
54
+ | `nasa-power-guide` | Which tool answers a typical-month versus a dated question, grid-cell size, data freshness, rate versus total units, degree days, the one-data-call-at-a-time gate, and SQL over staged tables |
55
+
56
+ The skill ships in `skills/nasa-power-guide/SKILL.md`. Plugin hosts load it from the package, and MCP clients that support the Skills extension read it from the server as `skill://nasa-power-guide/SKILL.md`.
57
+
44
58
  ## Capability reference
45
59
 
46
60
  ### `nasa_power_get_timeseries` <sub>tool</sub>
@@ -54,14 +68,16 @@ Eight tools are enabled by default; `NASA_POWER_DATAFRAME_DROP_ENABLED=true` ena
54
68
  ### `nasa_power_get_climatology` <sub>tool</sub>
55
69
 
56
70
  - Point coordinates with the same selection and corrections; optional `startYear` and `endYear` together, otherwise the source product range. No `temporalResolution` input.
57
- - Always inline, with no `canvas_id`; a profile over the 40,000-character budget fails `dataset_limit`. The `SI_EF_TILTED_SURFACE` alias lists its returned members, numeric angles and N/S orientation text included, in `expansions`.
71
+ - Small profiles remain inline; larger profiles return a bounded preview and complete `dataset` handle. Pass optional `canvas_id` to reuse an existing workspace. Without DuckDB, narrow oversized selections for inline output. `SI_EF_TILTED_SURFACE` and the distinct `SI_TILTED_AVG`, `SI_TILTED_MIN`, `SI_TILTED_MAX` groups list only their actually returned members, numeric angles and N/S orientation text included, in `expansions`. A group is omitted only when none of its members return.
58
72
 
59
73
  ---
60
74
 
61
75
  ### `nasa_power_get_region` <sub>tool</sub>
62
76
 
63
77
  - One `parameter` over ordered `latitudeMin` / `latitudeMax` and `longitudeMin` / `longitudeMax`, each axis spanning 2–10 degrees (`invalid_region` otherwise); split antimeridian regions into separate calls. `temporalResolution` is `daily`, `monthly` or `climatology`; point corrections are unsupported.
78
+ - Daily regions require date bounds and monthly regions require year bounds. Regional climatology accepts no year fields and reports the source's precomputed period; use `nasa_power_get_climatology` for a custom point year window.
64
79
  - Returns cell-tagged `rows`, `cellCount` and per-period `summaries` with equal-weight cell `min` / `mean` / `max`, not area-weighted. Optional `canvas_id` reuses a canvas for staging.
80
+ - Oversized summary envelopes return `summaryDataset` with all cross-cell summaries, exact `summaryCount` and a whole-record inline preview. Query the table in `summaryIndex` order; SQL `units: null` restores an omitted source unit. Observations and summaries register together and retain linked provenance.
65
81
 
66
82
  ---
67
83
 
@@ -117,6 +133,7 @@ POWER-specific:
117
133
  - Historical gridded observations, rather than site measurements or forecasts. Meteorological coverage starts in 1981, solar in 1984 and hourly in 2001; source configuration and publication latency further constrain availability.
118
134
  - One active upstream request, starts at least two seconds apart, bounded retries and a 55-second overall call deadline. NASA publishes no numeric request allowance. References cache for 24 hours and observations for 10 minutes.
119
135
  - Source fill values become `null`; zero remains a measurement. Monthly annual keys and climatology `ANN` remain distinct from monthly observations. LST labels have no UTC offset.
136
+ - `T_ZONES` and `TM_ZONES` retain numeric climate-zone codes with `valueKind: categorical`, including in SQL column metadata. They have no arithmetic summaries or comparison sort metrics; explicit numeric sorting by either is rejected.
120
137
 
121
138
  Agent-friendly output:
122
139
 
@@ -124,93 +141,36 @@ Agent-friendly output:
124
141
  - Explicit gaps — `omittedParameters` distinguishes an absent series from fill values; arithmetic observation means and calendar-season buckets are not climate normals, annual yields or water balances.
125
142
  - Complete datasets — inline outputs each fit 40,000 characters. Larger datasets stage all rows with a preview of at most 20 rows and a `dataset` handle; unsupported or oversized requests fail with narrowing guidance instead of returning a capped source prefix.
126
143
 
127
- Staging requires DuckDB and supports at most 250,000 rows, 64 parameter columns and 32 MiB of serialized rows per dataset. Canvases are process-local capabilities: pass the returned token explicitly and route later calls to the same process. Tables expire after 30 minutes, canvases after one idle hour with a six-hour absolute cap; limits are eight tables per canvas, four canvases per tenant and eight per process. Engine memory is 256 MiB per canvas, not a total RSS or temporary-disk quota.
144
+ Staging requires DuckDB and supports at most 250,000 rows, 64 parameter columns and 32 MiB of serialized rows per dataset. Canvases are process-local capabilities: pass the returned token explicitly and route later calls to the same process. Tables expire after 30 minutes with eight tables per canvas. Defaults are one idle hour and six absolute hours per canvas, four canvases per tenant, eight per process, and 256 MiB engine memory per canvas. Operators can size canvas capacity, memory and lifetime below; engine memory does not bound total RSS or temporary disk.
128
145
 
129
146
  ## Getting started
130
147
 
131
- Add the following to your MCP client configuration file.
148
+ Connect to the hosted instance, no install:
132
149
 
133
- ```json
134
- {
135
- "mcpServers": {
136
- "nasa-power-mcp-server": {
137
- "type": "stdio",
138
- "command": "bunx",
139
- "args": ["@cyanheads/nasa-power-mcp-server@latest"],
140
- "env": { "MCP_TRANSPORT_TYPE": "stdio" }
141
- }
142
- }
143
- }
150
+ ```sh
151
+ claude mcp add --transport http nasa-power-mcp-server https://nasa-power.caseyjhand.com/mcp
144
152
  ```
145
153
 
146
- Or with npx (no Bun required):
154
+ Or run it locally:
147
155
 
148
- ```json
149
- {
150
- "mcpServers": {
151
- "nasa-power-mcp-server": {
152
- "type": "stdio",
153
- "command": "npx",
154
- "args": ["-y", "@cyanheads/nasa-power-mcp-server@latest"],
155
- "env": { "MCP_TRANSPORT_TYPE": "stdio" }
156
- }
157
- }
158
- }
156
+ ```sh
157
+ claude mcp add nasa-power-mcp-server -- npx -y @cyanheads/nasa-power-mcp-server
159
158
  ```
160
159
 
161
- Or with Docker:
160
+ For other clients, add this to the MCP config file:
162
161
 
163
162
  ```json
164
163
  {
165
164
  "mcpServers": {
166
165
  "nasa-power-mcp-server": {
167
- "type": "stdio",
168
- "command": "docker",
169
- "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/nasa-power-mcp-server:latest"]
166
+ "command": "npx",
167
+ "args": ["-y", "@cyanheads/nasa-power-mcp-server"]
170
168
  }
171
169
  }
172
170
  }
173
171
  ```
174
172
 
175
- For Streamable HTTP, set the transport and start the server:
176
-
177
- ```sh
178
- MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
179
- # Server listens at http://127.0.0.1:3010/mcp
180
- ```
181
-
182
- ### Prerequisites
183
-
184
- - [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
185
- - npm and Docker install native DuckDB dependencies normally on supported platforms, and staging and SQL are on by default. Set `CANVAS_PROVIDER_TYPE=none` to keep every result inline.
186
- - The portable Claude Desktop MCPB bundle fixes `CANVAS_PROVIDER_TYPE=none` and supports inline results. Narrow an oversized request or use npm/Docker for analytics.
187
-
188
- ### Installation
189
-
190
- 1. **Clone the repository:**
191
-
192
- ```sh
193
- git clone https://github.com/cyanheads/nasa-power-mcp-server.git
194
- ```
195
-
196
- 2. **Navigate into the directory:**
197
-
198
- ```sh
199
- cd nasa-power-mcp-server
200
- ```
201
-
202
- 3. **Install dependencies:**
203
-
204
- ```sh
205
- bun install
206
- ```
207
-
208
- 4. **Configure environment:**
209
-
210
- ```sh
211
- cp .env.example .env
212
- # edit .env to change transport, logging or canvas settings
213
- ```
173
+ npm and Docker installs stage large results in DuckDB for SQL by default; set `CANVAS_PROVIDER_TYPE=none` to keep every result inline. The Claude Desktop bundle is inline-only: narrow an oversized request, or use npm or Docker for analytics. No NASA account or API key is required. For Docker or a source build, see [Running the server](#running-the-server).
214
174
 
215
175
  ## Configuration
216
176
 
@@ -218,22 +178,33 @@ cp .env.example .env
218
178
  |:---|:---|:---|
219
179
  | `NASA_POWER_DATAFRAME_DROP_ENABLED` | Expose explicit staged-table removal. | `false` |
220
180
  | `CANVAS_PROVIDER_TYPE` | `duckdb` stages large results for SQL on npm/Docker; `none` keeps results inline. MCPB fixes `none`. | `duckdb` |
221
- | `CANVAS_DEFAULT_MEMORY_LIMIT_MB` | Fixed engine memory limit; not total RSS or temporary disk. | `256` |
222
- | `CANVAS_MAX_CANVASES_PER_TENANT` | Fixed tenant canvas cap. | `4` |
223
- | `CANVAS_TTL_MS` | Fixed sliding canvas lifetime. | `3600000` |
224
- | `CANVAS_ABSOLUTE_CAP_MS` | Fixed absolute canvas lifetime. | `21600000` |
181
+ | `CANVAS_DEFAULT_MEMORY_LIMIT_MB` | Per-canvas engine memory, integer ≥64 MiB; not total RSS or temporary disk. | `256` |
182
+ | `CANVAS_MAX_CANVASES_PER_TENANT` | Positive tenant canvas cap; auth-none callers share one tenant. | `4` |
183
+ | `NASA_POWER_MAX_CANVASES_PER_PROCESS` | Positive process canvas cap, including pending and failed cleanup reservations. | `8` |
184
+ | `CANVAS_TTL_MS` | Positive sliding canvas lifetime in milliseconds. | `3600000` |
185
+ | `CANVAS_ABSOLUTE_CAP_MS` | Absolute canvas lifetime in milliseconds, at least the sliding TTL. | `21600000` |
225
186
  | `MCP_TRANSPORT_TYPE` | `stdio` or `http`. | `stdio` |
226
187
  | `MCP_HTTP_HOST` / `MCP_HTTP_PORT` | HTTP bind address and port. | `127.0.0.1` / `3010` |
227
188
  | `MCP_AUTH_MODE` | `none`, `jwt` or `oauth`. | `none` |
228
189
  | `MCP_LOG_LEVEL` | Minimum log level. | `info` |
229
190
  | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
230
191
 
231
- DuckDB startup rejects changes to the four fixed canvas settings. See [`.env.example`](./.env.example) for optional transport, authentication, storage, logging and telemetry settings.
192
+ Unset, blank and whole-value placeholder settings retain these defaults. Invalid sizing fails startup. Byte budgets, table limits and producer concurrency remain fixed independently of operator canvas sizing. See [`.env.example`](./.env.example) for optional transport, authentication, storage, logging and telemetry settings.
232
193
 
233
194
  ## Running the server
234
195
 
235
196
  ### Local development
236
197
 
198
+ Requires [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
199
+
200
+ - **Clone and install:**
201
+
202
+ ```sh
203
+ git clone https://github.com/cyanheads/nasa-power-mcp-server.git
204
+ cd nasa-power-mcp-server && bun install
205
+ cp .env.example .env # optional: transport, logging or canvas settings
206
+ ```
207
+
237
208
  - **Build and run the production version**:
238
209
 
239
210
  ```sh
@@ -255,17 +226,17 @@ DuckDB startup rejects changes to the four fixed canvas settings. See [`.env.exa
255
226
  ### Docker
256
227
 
257
228
  ```sh
258
- docker build -t nasa-power-mcp-server .
259
- docker run --rm -p 127.0.0.1:3010:3010 nasa-power-mcp-server
229
+ docker run --rm -p 127.0.0.1:3010:3010 ghcr.io/cyanheads/nasa-power-mcp-server:latest
260
230
  ```
261
231
 
262
- The image defaults to HTTP on port 3010 in stateless session mode and logs to `/var/log/nasa-power-mcp-server`. OpenTelemetry peer dependencies are installed by default; build with `--build-arg OTEL_ENABLED=false` to omit them. For a client outside loopback, configure authentication and TLS at the deployment boundary.
232
+ The image defaults to HTTP on port 3010 in stateless session mode and logs to `/var/log/nasa-power-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/nasa-power-mcp-server:latest"]`. To build locally, `docker build -t nasa-power-mcp-server .`; OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them. For a client outside loopback, configure authentication and TLS at the deployment boundary.
263
233
 
264
234
  ## Project structure
265
235
 
266
236
  | Path | Purpose |
267
237
  |:---|:---|
268
- | `src/index.ts` | Nine tool registrations, bounded canvas setup and service shutdown |
238
+ | `src/index.ts` | Nine tool registrations, the published skill tree, bounded canvas setup and service shutdown |
239
+ | `skills` | The `nasa-power-guide` agent skill, published to plugin hosts and served over MCP |
269
240
  | `src/config` | Workload limits and server-specific environment parsing |
270
241
  | `src/mcp-server/tools/definitions` | Tool contracts and comparison orchestration |
271
242
  | `src/services/power` | Paced NASA client, normalization, presets and scientific summaries |
@@ -0,0 +1,35 @@
1
+ ---
2
+ summary: "Complete regional analysis and configurable canvases"
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.0 — 2026-10-24
8
+
9
+ ## Added
10
+
11
+ - **Canvas sizing** honors validated memory, tenant capacity and lifetime overrides; `NASA_POWER_MAX_CANVASES_PER_PROCESS` controls process capacity with the existing default of eight ([#19](https://github.com/cyanheads/nasa-power-mcp-server/issues/19)).
12
+ - **Regional summary staging** returns complete observation and summary tables with `summaryDataset`, exact `summaryCount`, ordered `summaryIndex` retrieval and linked provenance; registration failures roll back the complete new batch ([#24](https://github.com/cyanheads/nasa-power-mcp-server/issues/24)).
13
+ - **Point climatology** accepts optional `canvas_id` for workspace reuse and stages oversized profiles completely with bounded previews ([#23](https://github.com/cyanheads/nasa-power-mcp-server/issues/23)).
14
+
15
+ ## Changed
16
+
17
+ - Regional climatology no longer accepts startYear or endYear; omit both even when they match the source period (#20). Use point climatology for custom year windows.
18
+ - **SQL columns named `current_schema`** now require table qualification: use `c.current_schema` rather than `current_schema` when selecting that CTE column.
19
+ - **Development tooling** follows the adopted framework guidance and audit classification; Bun runtime and Docker images move from 1.4.3 to 1.4.4.
20
+ - **Hosted access** is documented at `https://nasa-power.caseyjhand.com/mcp` and advertised in registry metadata.
21
+
22
+ ## Fixed
23
+
24
+ - **Climate-zone codes** `T_ZONES` and `TM_ZONES` retain numeric values with categorical metadata and no arithmetic summaries or numeric comparison sorting; shared source units remain present independently of numeric comparability ([#21](https://github.com/cyanheads/nasa-power-mcp-server/issues/21)).
25
+ - **Tilted-solar aliases** `SI_TILTED_AVG`, `SI_TILTED_MIN` and `SI_TILTED_MAX` report only their returned members in `expansions` and are omitted only when no member returns ([#22](https://github.com/cyanheads/nasa-power-mcp-server/issues/22)).
26
+ - **Environment initialization** loads `.env` before server-specific parsing; framework error text and link rendering preserve literal source values on both response surfaces.
27
+
28
+ ## Dependencies
29
+
30
+ - `@cyanheads/mcp-ts-core` ^0.13.15 → ^0.13.16.
31
+ - `@duckdb/node-api` ^1.5.6-r.1 → ^1.5.6-r.2.
32
+ - `@biomejs/biome` 2.5.15 → 2.6.2.
33
+ - `ignore` ^7.0.12 → ^7.0.13.
34
+ - `tsc-alias` ^1.9.7 → ^1.9.8.
35
+ - Added test dependencies `markdown-it` ^15.0.3 and `@types/markdown-it` ^14.2.0.
@@ -0,0 +1,36 @@
1
+ ---
2
+ summary: "Publishes the nasa-power-guide agent skill, served over MCP as skill://nasa-power-guide/SKILL.md, and moves to mcp-ts-core 0.13.18, which stops HTTP memory growth under Bun and keeps the framework build-only files out of the .mcpb bundle."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.4.0 — 2026-10-28
8
+
9
+ ## Added
10
+
11
+ - **`nasa-power-guide` agent skill** — `skills/nasa-power-guide/SKILL.md`, served over MCP as `skill://nasa-power-guide/SKILL.md` (plus the `skill://{+path}` template) through `createApp({ skills })` (mcp-ts-core 0.13.18). It covers which tool answers a typical-month versus a dated question, grid-cell size, data freshness, rate versus total units, degree days, the one-data-call-at-a-time gate, and SQL over staged tables. It ships in the npm package, the Docker image, and the `.mcpb` bundle.
12
+ - **Agent Plugins `plugin.json` and `mcp.json`** at the repo root (mcp-ts-core 0.13.18 template, [cyanheads/mcp-ts-core#426](https://github.com/cyanheads/mcp-ts-core/issues/426)) — the 1.0.0 manifest read by Codex, Cursor, GitHub Copilot, VS Code and Kiro, and a stdio `npx -y @cyanheads/nasa-power-mcp-server` entry with `MCP_TRANSPORT_TYPE=stdio`.
13
+ - **`add-skill` framework skill** for authoring published skills in root `skills/`.
14
+
15
+ ## Changed
16
+
17
+ - **The `.mcpb` bundle** no longer carries the framework's build-only files (its `changelog/`, `config/`, `scripts/`, `templates/`, `CLAUDE.md`, `AGENTS.md`) (mcp-ts-core 0.13.18, [cyanheads/mcp-ts-core#791](https://github.com/cyanheads/mcp-ts-core/issues/791)). The bundle cleaner and `lint:packaging` select agent docs only at a dependency package's root, so the MCP SDK's runtime `dist/ext/skills/` modules stay in the bundle ([cyanheads/mcp-ts-core#794](https://github.com/cyanheads/mcp-ts-core/issues/794), ported locally with `tests/scripts/mcpb-agent-docs.test.ts`).
18
+ - **`.mcpbignore`** uses root-anchored entries and now also excludes `/changelog/`, `/.claude-plugin/`, `/.codex-plugin/`, `/plugin.json`, `/mcp.json`, `/CLAUDE.md`, `/AGENTS.md`, `/.dockerignore`, `/.gitattributes` and `/devcheck.config.json`; root `skills/` ships.
19
+ - **`package.json` `files`** gains `skills/`, and the **Dockerfile** copies `skills/` into the production image.
20
+ - **`lint:packaging`** also validates the root `plugin.json`/`mcp.json` pair and the published `skills/` tree; `check-skills-sync` identifies framework skills by frontmatter only.
21
+ - **README getting started** is restructured: a hosted-endpoint `claude mcp add` line, an `npx` install, one generic JSON config, and the Docker run command against the published image; the clone-and-install steps move under Local development. A Skills section lists `nasa-power-guide`.
22
+ - **Bug report form** replaces five separate fields with one Environment table.
23
+ - **`bunfig.toml`** `minimumReleaseAge` drops from 3 days to 1 day (`@cyanheads/mcp-ts-core` stays excluded).
24
+ - **Framework skills**: 17 updated to the mcp-ts-core 0.13.18 versions.
25
+ - **`tests/config/startup.test.ts`** links `skills/` into its scratch directory so the bundled entry finds it; `CLAUDE.md`, `AGENTS.md` and `docs/design.md` describe the published skill and the DuckDB 1.5.6 hold.
26
+
27
+ ## Fixed
28
+
29
+ - **HTTP memory growth under Bun** — the HTTP transport no longer retains about 71 KiB per request through its shutdown signal, so sustained HTTP traffic no longer grows memory until the process runs out. 0.3.0 shipped on mcp-ts-core 0.13.16, and the Docker image runs under Bun (mcp-ts-core 0.13.17, [cyanheads/mcp-ts-core#786](https://github.com/cyanheads/mcp-ts-core/issues/786)).
30
+
31
+ ## Dependencies
32
+
33
+ - `@cyanheads/mcp-ts-core` ^0.13.16 → ^0.13.18.
34
+ - `@biomejs/biome` 2.6.2 → 2.6.3.
35
+ - `ignore` ^7.0.13 → ^7.0.14.
36
+ - `tsc-alias` ^1.9.8 → ^1.9.9.
@@ -37,6 +37,7 @@ export declare const POWER_LIMITS: {
37
37
  };
38
38
  /** Server-specific environment configuration. */
39
39
  export declare const ServerConfigSchema: z.ZodObject<{
40
+ maxCanvasesPerProcess: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
40
41
  dataframeDropEnabled: z.ZodDefault<z.ZodCodec<z.ZodString, z.ZodBoolean>>;
41
42
  }, z.core.$strip>;
42
43
  /** Parsed configuration consumed by services. */
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,4EAA4E;AAC5E,eAAO,MAAM,YAAY;aACvB,UAAU,EAAE,IAAK;aACjB,UAAU,EAAE,EAAE;aACd,WAAW,EAAE,KAAM;aACnB,UAAU,EAAE,KAAM;aAClB,SAAS,EAAE,KAAM;aACjB,QAAQ,EAAE,CAAC;aACX,kBAAkB,EAAE,EAAE;aACtB,SAAS;aACT,mBAAmB;aACnB,IAAI,EAAE,MAAO;aACb,OAAO,EAAE,EAAE;aACX,QAAQ;aACR,gBAAgB,EAAE,KAAM;aACxB,UAAU;aACV,YAAY,EAAE,GAAG;aACjB,cAAc,EAAE,QAAU;aAC1B,SAAS,EAAE,MAAO;aAClB,mBAAmB;aACnB,oBAAoB;aACpB,mBAAmB;aACnB,oBAAoB;aACpB,SAAS,EAAE,CAAC;aACZ,eAAe,EAAE,CAAC;aAClB,iBAAiB,EAAE,CAAC;aACpB,kBAAkB,EAAE,CAAC;aACrB,UAAU,EAAE,OAAS;aACrB,WAAW,EAAE,OAAS;aACtB,mBAAmB,EAAE,QAAU;aAC/B,OAAO,EAAE,KAAM;aACf,aAAa,EAAE,KAAM;aACrB,SAAS,EAAE,IAAK;aAChB,OAAO,EAAE,CAAC;CACF,CAAC;AACX,iDAAiD;AACjD,eAAO,MAAM,kBAAkB;;iBAK7B,CAAC;AACH,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,wEAAwE;AACxE,wBAAgB,eAAe,IAAI,YAAY,CAK9C"}
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,4EAA4E;AAC5E,eAAO,MAAM,YAAY;aACvB,UAAU,EAAE,IAAK;aACjB,UAAU,EAAE,EAAE;aACd,WAAW,EAAE,KAAM;aACnB,UAAU,EAAE,KAAM;aAClB,SAAS,EAAE,KAAM;aACjB,QAAQ,EAAE,CAAC;aACX,kBAAkB,EAAE,EAAE;aACtB,SAAS;aACT,mBAAmB;aACnB,IAAI,EAAE,MAAO;aACb,OAAO,EAAE,EAAE;aACX,QAAQ;aACR,gBAAgB,EAAE,KAAM;aACxB,UAAU;aACV,YAAY,EAAE,GAAG;aACjB,cAAc,EAAE,QAAU;aAC1B,SAAS,EAAE,MAAO;aAClB,mBAAmB;aACnB,oBAAoB;aACpB,mBAAmB;aACnB,oBAAoB;aACpB,SAAS,EAAE,CAAC;aACZ,eAAe,EAAE,CAAC;aAClB,iBAAiB,EAAE,CAAC;aACpB,kBAAkB,EAAE,CAAC;aACrB,UAAU,EAAE,OAAS;aACrB,WAAW,EAAE,OAAS;aACtB,mBAAmB,EAAE,QAAU;aAC/B,OAAO,EAAE,KAAM;aACf,aAAa,EAAE,KAAM;aACrB,SAAS,EAAE,IAAK;aAChB,OAAO,EAAE,CAAC;CACF,CAAC;AACX,iDAAiD;AACjD,eAAO,MAAM,kBAAkB;;;iBAY7B,CAAC;AACH,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,wEAAwE;AACxE,wBAAgB,eAAe,IAAI,YAAY,CAO9C"}