@cyanheads/nasa-power-mcp-server 0.1.1 → 0.3.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 (81) hide show
  1. package/AGENTS.md +20 -16
  2. package/CLAUDE.md +20 -16
  3. package/Dockerfile +3 -3
  4. package/README.md +103 -47
  5. package/changelog/0.2.x/0.2.0.md +23 -0
  6. package/changelog/0.3.x/0.3.0.md +35 -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 +7 -8
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/comparison-contracts.d.ts +132 -368
  14. package/dist/mcp-server/tools/definitions/comparison-contracts.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/comparison-contracts.js +17 -5
  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 +180 -300
  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-compare-locations.tool.js +1 -1
  20. package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts +1 -0
  22. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.js +1 -1
  24. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.d.ts +3 -1
  26. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.js +7 -2
  28. package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts +88 -162
  30. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js +30 -6
  32. package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts +34 -51
  34. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js +9 -3
  36. package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js.map +1 -1
  37. package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts +86 -136
  38. package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts.map +1 -1
  39. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js +30 -28
  41. package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js.map +1 -1
  42. package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.d.ts.map +1 -1
  43. package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.js +17 -8
  44. package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.js.map +1 -1
  45. package/dist/mcp-server/tools/definitions/point-contracts.d.ts +40 -280
  46. package/dist/mcp-server/tools/definitions/point-contracts.d.ts.map +1 -1
  47. package/dist/mcp-server/tools/definitions/point-contracts.js +1 -5
  48. package/dist/mcp-server/tools/definitions/point-contracts.js.map +1 -1
  49. package/dist/services/canvas/canvas-service.d.ts +20 -4
  50. package/dist/services/canvas/canvas-service.d.ts.map +1 -1
  51. package/dist/services/canvas/canvas-service.js +225 -64
  52. package/dist/services/canvas/canvas-service.js.map +1 -1
  53. package/dist/services/canvas/errors.d.ts.map +1 -1
  54. package/dist/services/canvas/errors.js +20 -8
  55. package/dist/services/canvas/errors.js.map +1 -1
  56. package/dist/services/canvas/format.d.ts +0 -1
  57. package/dist/services/canvas/format.d.ts.map +1 -1
  58. package/dist/services/canvas/format.js +3 -3
  59. package/dist/services/canvas/format.js.map +1 -1
  60. package/dist/services/canvas/types.d.ts +64 -1
  61. package/dist/services/canvas/types.d.ts.map +1 -1
  62. package/dist/services/canvas/types.js +7 -3
  63. package/dist/services/canvas/types.js.map +1 -1
  64. package/dist/services/power/errors.js +1 -1
  65. package/dist/services/power/errors.js.map +1 -1
  66. package/dist/services/power/normalize.d.ts.map +1 -1
  67. package/dist/services/power/normalize.js +25 -9
  68. package/dist/services/power/normalize.js.map +1 -1
  69. package/dist/services/power/power-service.d.ts.map +1 -1
  70. package/dist/services/power/power-service.js +17 -10
  71. package/dist/services/power/power-service.js.map +1 -1
  72. package/dist/services/power/presets.d.ts +25 -0
  73. package/dist/services/power/presets.d.ts.map +1 -1
  74. package/dist/services/power/presets.js +29 -1
  75. package/dist/services/power/presets.js.map +1 -1
  76. package/dist/services/power/types.d.ts +415 -194
  77. package/dist/services/power/types.d.ts.map +1 -1
  78. package/dist/services/power/types.js +71 -9
  79. package/dist/services/power/types.js.map +1 -1
  80. package/package.json +9 -7
  81. package/server.json +42 -24
package/AGENTS.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** nasa-power-mcp-server
4
- **Version:** 0.1.1
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.3.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.16`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
8
8
  **Zod:** ^4.6.7
@@ -34,7 +34,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
34
34
 
35
35
  - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
36
36
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
37
- - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
37
+ - **Keep persistence in `CanvasService`** through its injected `core.storage`; tool handlers do not access storage 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
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.
@@ -56,11 +56,13 @@ Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registere
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
- The portable MCPB fixes `CANVAS_PROVIDER_TYPE=none`. npm and Docker install the native dependency normally on supported platforms and permit `CANVAS_PROVIDER_TYPE=duckdb`. Keep those option sets distinct.
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
+
65
+ `@duckdb/node-api` stays on 1.5.6: framework 0.13.16's optional peer accepts 1.x, so 2.x makes npm installs fail with `ERESOLVE`.
64
66
 
65
67
  ### Identity and lifecycle
66
68
 
@@ -77,15 +79,11 @@ Handlers receive a unified `ctx` object. Key properties:
77
79
  | Property | Description |
78
80
  |:---------|:------------|
79
81
  | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
80
- | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |
81
- | `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
82
- | `ctx.inputs` | The request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped` — limited to what the client declared (`elicitation` and its form/url modes, `sampling`, `roots`). Client-supplied: a consent gate trusts only a `ctx.state` record it stored when it asked, bound to the operation, caller, and target (see the `api-context` skill). |
83
- | `ctx.clientCapabilities` | What the client declared for this request, `undefined` when no view exists. Decides whether to ask for optional context (e.g. roots); never a reason to skip a consent prompt. |
84
- | `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
85
- | `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
86
- | `ctx.signal` | `AbortSignal` for cancellation. |
87
- | `ctx.requestId` | Request ID — the one every log record of the call carries and its error envelope returns as `data.requestId`. |
88
- | `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
82
+ | `ctx.enrich` | Success-path agent context — `nasa_power_search_parameters` calls `.notice()` on an empty match. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
83
+ | `ctx.signal` | `AbortSignal` for cancellation. Producing tools replace it with the call budget's signal, so the 55-second source deadline also aborts staging. |
84
+ | `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. Canvas records are owned per tenant, and a token presented by another tenant is `canvas_not_found`. |
85
+
86
+ Staged-table provenance is written through `core.storage` in `CanvasService`, keyed by tenant, canvas and table; no handler uses `ctx.state`.
89
87
 
90
88
  ---
91
89
 
@@ -230,11 +228,14 @@ When you complete a skill's checklist, check the boxes and add a completion time
230
228
  | `bun run format` | Auto-fix formatting (safe fixes only) |
231
229
  | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
232
230
  | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
231
+ | `bun run test:coverage` | Run tests with coverage |
232
+ | `bun run start` | Run the built server with the configured transport |
233
233
  | `bun run start:stdio` | Production mode (stdio) |
234
234
  | `bun run start:http` | Production mode (HTTP) |
235
235
  | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
236
236
  | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
237
237
  | `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
238
+ | `bun run release:github` | Create or repair the GitHub Release on the version tag and attach the `.mcpb` (run by `release-and-publish`) |
238
239
  | `bun run publish-mcp` | Authenticate with the macOS Keychain token and publish registry metadata |
239
240
 
240
241
  **CI is one file.** `.github/workflows/codeql.yml` (scaffolded) is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.
@@ -302,8 +303,11 @@ import { getMyService } from '@/services/my-domain/my-service.js';
302
303
  - [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`)
303
304
  - [ ] Optional nested objects: handler guards for empty inner values from form-based clients (`if (input.obj?.field && ...)`, not just `if (input.obj)`). When regex/length constraints matter, use `z.union([z.literal(''), z.string().regex(...).describe(...)])` — literal variants are exempt from `describe-on-fields`.
304
305
  - [ ] JSDoc `@fileoverview` + `@module` on every file
305
- - [ ] `ctx.log` for logging, `ctx.state` for storage
306
+ - [ ] `ctx.log` for logging; persistence only through `CanvasService`
306
307
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
308
+ - [ ] New codes and presets are checked against the selected community/resolution dictionary; source units, fill-as-`null`, `omittedParameters` and annual-period distinctions survive normalization
309
+ - [ ] Producing tools present through `CanvasService.present()`: oversized data stages completely or fails with `canvas_required` / `dataset_limit`, never a truncated source prefix
310
+ - [ ] 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
307
311
  - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
308
312
  - [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
309
313
  - [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
package/CLAUDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** nasa-power-mcp-server
4
- **Version:** 0.1.1
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.3.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.16`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
8
8
  **Zod:** ^4.6.7
@@ -34,7 +34,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
34
34
 
35
35
  - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
36
36
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
37
- - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
37
+ - **Keep persistence in `CanvasService`** through its injected `core.storage`; tool handlers do not access storage 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
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.
@@ -56,11 +56,13 @@ Tool files live in `src/mcp-server/tools/definitions/`. Nine tools are registere
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
- The portable MCPB fixes `CANVAS_PROVIDER_TYPE=none`. npm and Docker install the native dependency normally on supported platforms and permit `CANVAS_PROVIDER_TYPE=duckdb`. Keep those option sets distinct.
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
+
65
+ `@duckdb/node-api` stays on 1.5.6: framework 0.13.16's optional peer accepts 1.x, so 2.x makes npm installs fail with `ERESOLVE`.
64
66
 
65
67
  ### Identity and lifecycle
66
68
 
@@ -77,15 +79,11 @@ Handlers receive a unified `ctx` object. Key properties:
77
79
  | Property | Description |
78
80
  |:---------|:------------|
79
81
  | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
80
- | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |
81
- | `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
82
- | `ctx.inputs` | The request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped` — limited to what the client declared (`elicitation` and its form/url modes, `sampling`, `roots`). Client-supplied: a consent gate trusts only a `ctx.state` record it stored when it asked, bound to the operation, caller, and target (see the `api-context` skill). |
83
- | `ctx.clientCapabilities` | What the client declared for this request, `undefined` when no view exists. Decides whether to ask for optional context (e.g. roots); never a reason to skip a consent prompt. |
84
- | `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
85
- | `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
86
- | `ctx.signal` | `AbortSignal` for cancellation. |
87
- | `ctx.requestId` | Request ID — the one every log record of the call carries and its error envelope returns as `data.requestId`. |
88
- | `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
82
+ | `ctx.enrich` | Success-path agent context — `nasa_power_search_parameters` calls `.notice()` on an empty match. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
83
+ | `ctx.signal` | `AbortSignal` for cancellation. Producing tools replace it with the call budget's signal, so the 55-second source deadline also aborts staging. |
84
+ | `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. Canvas records are owned per tenant, and a token presented by another tenant is `canvas_not_found`. |
85
+
86
+ Staged-table provenance is written through `core.storage` in `CanvasService`, keyed by tenant, canvas and table; no handler uses `ctx.state`.
89
87
 
90
88
  ---
91
89
 
@@ -230,11 +228,14 @@ When you complete a skill's checklist, check the boxes and add a completion time
230
228
  | `bun run format` | Auto-fix formatting (safe fixes only) |
231
229
  | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
232
230
  | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
231
+ | `bun run test:coverage` | Run tests with coverage |
232
+ | `bun run start` | Run the built server with the configured transport |
233
233
  | `bun run start:stdio` | Production mode (stdio) |
234
234
  | `bun run start:http` | Production mode (HTTP) |
235
235
  | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
236
236
  | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
237
237
  | `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
238
+ | `bun run release:github` | Create or repair the GitHub Release on the version tag and attach the `.mcpb` (run by `release-and-publish`) |
238
239
  | `bun run publish-mcp` | Authenticate with the macOS Keychain token and publish registry metadata |
239
240
 
240
241
  **CI is one file.** `.github/workflows/codeql.yml` (scaffolded) is the only GitHub Actions workflow: CodeQL is GitHub-owned end to end, and the file runs only while the repo's CodeQL *default setup* is turned off. Verification — `devcheck`, tests, the release gates — runs locally; don't add a workflow that re-runs it.
@@ -302,8 +303,11 @@ import { getMyService } from '@/services/my-domain/my-service.js';
302
303
  - [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`)
303
304
  - [ ] Optional nested objects: handler guards for empty inner values from form-based clients (`if (input.obj?.field && ...)`, not just `if (input.obj)`). When regex/length constraints matter, use `z.union([z.literal(''), z.string().regex(...).describe(...)])` — literal variants are exempt from `describe-on-fields`.
304
305
  - [ ] JSDoc `@fileoverview` + `@module` on every file
305
- - [ ] `ctx.log` for logging, `ctx.state` for storage
306
+ - [ ] `ctx.log` for logging; persistence only through `CanvasService`
306
307
  - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
308
+ - [ ] New codes and presets are checked against the selected community/resolution dictionary; source units, fill-as-`null`, `omittedParameters` and annual-period distinctions survive normalization
309
+ - [ ] Producing tools present through `CanvasService.present()`: oversized data stages completely or fails with `canvas_required` / `dataset_limit`, never a truncated source prefix
310
+ - [ ] 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
307
311
  - [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
308
312
  - [ ] If wrapping external API: raw/domain/output schemas reviewed against real upstream sparsity/nullability before finalizing required vs optional fields
309
313
  - [ ] If wrapping external API: normalization and `format()` preserve uncertainty; do not fabricate facts from missing upstream data
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
 
@@ -46,7 +46,7 @@ RUN bun run build
46
46
  # A clean image rather than `FROM build`: the build stage's node_modules holds
47
47
  # devDependencies.
48
48
  # ==============================================================================
49
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS deps
49
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS deps
50
50
 
51
51
  WORKDIR /usr/src/app
52
52
 
@@ -114,7 +114,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
114
114
  # dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
115
115
  # and CMD, which run on the real target at container start.
116
116
  # ==============================================================================
117
- FROM oven/bun:1.4.3-slim AS production
117
+ FROM oven/bun:1.4.4-slim AS production
118
118
 
119
119
  WORKDIR /usr/src/app
120
120
 
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  <div align="center">
2
2
  <h1>@cyanheads/nasa-power-mcp-server</h1>
3
3
  <p><b>Query NASA POWER solar, wind, building, and agroclimatology data via MCP. STDIO or Streamable HTTP.</b>
4
- <div>9 Tools • 0 Resources • 0 Prompts</div>
4
+ <div>9 Tools</div>
5
5
  </p>
6
6
  </div>
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.1.1-blue.svg?style=flat-square)](https://github.com/cyanheads/nasa-power-mcp-server/releases) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![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.3.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.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
@@ -45,66 +51,70 @@ Eight tools are enabled by default; `NASA_POWER_DATAFRAME_DROP_ENABLED=true` ena
45
51
 
46
52
  ### `nasa_power_get_timeseries` <sub>tool</sub>
47
53
 
48
- - `latitude` / `longitude`, `temporalResolution` (`hourly`, `daily`, `monthly`), and an inclusive `startDate` / `endDate` or `startYear` / `endYear` window. `selection` chooses a named `preset` or explicit `parameters`: at most 15 effective hourly codes or 20 otherwise, including corrections.
49
- - `community` (`RE`, `SB`, `AG`; default `RE`), `units` (`metric`, `imperial`) and `timeStandard` (`LST`, `UTC`) retain the returned source units and time labels. Optional `siteElevation` adds `PSC`; paired `windElevation` (10–300 meters) and `windSurface` (`grass`, `airportice`) add `WSC`.
50
- - Returns `request`, `metadata`, `parameters`, `expansions`, `omittedParameters`, `summaries`, `rows`, exact `rowCount` and nullable `dataset`. Optional `canvas_id` reuses a workspace when staging is needed.
54
+ - `latitude` / `longitude` and `temporalResolution`: `hourly` and `daily` take an inclusive `startDate` / `endDate`, `monthly` takes `startYear` / `endYear`. `selection` is a named `preset` (default `solar_pv`) or explicit `parameters`, at most 15 effective codes hourly and 20 otherwise, corrections included.
55
+ - `community` (`RE`, `SB`, `AG`; default `RE`), `units` (`metric`, `imperial`) and `timeStandard` (`LST`, `UTC`). Optional `siteElevation` adds `PSC`; paired `windElevation` (10–300 meters) and `windSurface` add `WSC`. Use `nasa_power_list_reference` topic `wind_surfaces` for all 17 canonical NASA aliases, including `airportgrass`, `airportice` and `vegtype_1`; vegetation underscores are retained.
56
+ - Returns `metadata`, `parameters`, `expansions`, `omittedParameters`, `summaries`, `rows`, an exact `rowCount` and `dataset` (null unless staged). Optional `canvas_id` reuses a canvas when staging is needed.
51
57
 
52
58
  ---
53
59
 
54
60
  ### `nasa_power_get_climatology` <sub>tool</sub>
55
61
 
56
- - Point coordinates and the same parameter selection and corrections; optionally supply both `startYear` and `endYear`, otherwise use the source product range. No `temporalResolution` input is needed.
57
- - Complete monthly and annual records remain inline; the climatology alias `SI_EF_TILTED_SURFACE` preserves returned numeric angles and orientation text through `expansions` and parameter metadata.
62
+ - Point coordinates with the same selection and corrections; optional `startYear` and `endYear` together, otherwise the source product range. No `temporalResolution` input.
63
+ - 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
64
 
59
65
  ---
60
66
 
61
67
  ### `nasa_power_get_region` <sub>tool</sub>
62
68
 
63
- - One `parameter`, ordered `latitudeMin` / `latitudeMax` and `longitudeMin` / `longitudeMax`, each spanning 2–10 degrees; split antimeridian regions. Supports daily, monthly and climatology windows, with common community, units and time settings.
64
- - Returns cell-tagged observations, `cellCount` and per-period `cellSummaries`. Means weight each returned grid cell equally; point correction fields and hourly regions are unsupported. Optional `canvas_id` reuses a staging workspace.
69
+ - 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.
70
+ - 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.
71
+ - 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.
72
+ - 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
73
 
66
74
  ---
67
75
 
68
76
  ### `nasa_power_compare_locations` <sub>tool</sub>
69
77
 
70
- - 2–10 `locations`, each with coordinates and a `label` of 1–120 characters, plus shared selection, corrections and window. Defaults to climatology; supports hourly, daily and monthly comparisons too.
71
- - Location-tagged rows, per-site metadata and summaries, seasonal buckets, `partial` and `failed` preserve operational failures alongside successes. `possibleSharedGridCell` flags nearby points without asserting exact grid identity.
72
- - Optional `sort` takes a numeric selected `parameter` and `direction` (`asc`, `desc`); sorting requires known identical units. Optional `canvas_id` reuses a workspace. All-site failure returns `all_locations_failed`.
78
+ - 2–10 `locations`, each with coordinates and a `label` of 1–120 characters, sharing one selection, corrections and window. `temporalResolution` defaults to `climatology`; `hourly`, `daily` and `monthly` also work.
79
+ - Returns successful `locations` with per-site provenance, location-tagged `rows`, annual and calendar-season `summaries`, and `failed` entries with `partial: true` when some sites fail; all-site failure returns `all_locations_failed`. `possibleSharedGridCell` flags sites that may share a grid cell.
80
+ - Optional `sort` (`parameter`, `direction` `asc` / `desc`) orders by a selected numeric parameter with known identical units. Optional `canvas_id` reuses a canvas for staging.
81
+ - When summaries exceed the response budget, `summaryDatasets.buckets` and `summaryDatasets.locations` point to complete SQL tables; inline summaries become whole-record previews and each location includes `summaryCount`. Query in `summaryIndex` order; the location-summary table includes `locationId`. SQL `units: null` represents an omitted source unit, not a known unit.
73
82
 
74
83
  ---
75
84
 
76
85
  ### `nasa_power_search_parameters` <sub>tool</sub>
77
86
 
78
- - `community` (default `RE`), `temporalResolution` (default `daily`), optional `filter` (up to 256 characters), `offset` and `limit` 1–100 (default 20).
79
- - Case-insensitive tokens filter the complete dictionary before sorted pagination; blank filter browses it. `entries`, `total`, `hasMore` and `truncated` describe the filtered page.
87
+ - `community` (default `RE`), `temporalResolution` (default `daily`), optional `filter` (up to 256 characters), `offset` and positive integer `limit` (default 20, effective cap 100 with a notice when capped).
88
+ - Every case-insensitive `filter` token must match a code, name or definition across the complete dictionary before sorted pagination; a blank filter browses it. `entries`, `total`, `hasMore` and `truncated` describe the page.
80
89
 
81
90
  ---
82
91
 
83
92
  ### `nasa_power_list_reference` <sub>tool</sub>
84
93
 
85
94
  - `topic`: `presets` (default), `communities`, `coverage`, `missing_data`, `wind_surfaces` or `dataframes`.
86
- - Returns typed `entries`, `guidance`, `documentationUrls` and `verifiedOn`. Presets are `solar_pv`, `wind_turbine`, `building_hvac`, `agroclimatology` and `water_balance`; `building_hvac` is unavailable hourly.
95
+ - Returns typed `entries`, `guidance`, `documentationUrls` and `verifiedOn`. Presets are `solar_pv`, `wind_turbine`, `building_hvac`, `agroclimatology` and `water_balance`; `building_hvac` is unavailable hourly. `agroclimatology` adds `T2M_MAX` and `T2M_MIN` outside hourly resolution; monthly/climatology extremes remain source aggregates, without reconstructed daily records or calculated growing degree days.
87
96
 
88
97
  ---
89
98
 
90
99
  ### `nasa_power_dataframe_describe` <sub>tool</sub>
91
100
 
92
101
  - Required `canvas_id` from a producing response; optional `table` limits inspection to one table.
93
- - Returns current canvas/table expiry, actual SQL columns with source units and original scientific `provenance`. Only tables in the supplied canvas are visible.
102
+ - Returns canvas and table expiry, SQL columns with source units, and original scientific `provenance`. Only tables in the supplied canvas are visible.
94
103
 
95
104
  ---
96
105
 
97
106
  ### `nasa_power_dataframe_query` <sub>tool</sub>
98
107
 
99
- - Required `canvas_id` and one read-only SELECT in `sql` (up to 16,384 characters); `rowLimit` 1–1,000 (default 100). Joins and aggregates operate on tables shown by describe; files, writes and system catalogs are denied.
100
- - Returns `columns`, `rows`, `rowCount`, `shownCount`, `truncated` and `countSemantics` (`exact` or `materialized_prefix`). SQL has a 10-second deadline; the complete structured and rendered outputs each must fit 40,000 characters.
108
+ - Required `canvas_id` and one read-only SELECT in `sql` (up to 16,384 characters); positive integer `rowLimit` defaults to 100 and is capped at 1,000, with an effective-cap notice for larger requests. Files, writes and system catalogs are denied.
109
+ - Returns `columns`, `rows`, `rowCount`, `shownCount`, `truncated` and `countSemantics` (`exact` or `materialized_prefix`). SQL has a 10-second deadline; the structured and rendered outputs each must fit 40,000 characters.
110
+ - SQL mistakes retain bounded identifiers or conversion values with recovery guidance. Operational failures use generic messages; engine paths and native causes are excluded.
101
111
 
102
112
  ---
103
113
 
104
114
  ### `nasa_power_dataframe_drop` <sub>tool</sub>
105
115
 
106
116
  - Required `canvas_id` and exact `table`; enabled only by `NASA_POWER_DATAFRAME_DROP_ENABLED=true`.
107
- - Returns `dropped: false` for an absent table in a live canvas. Expired or unknown canvases return `canvas_not_found`.
117
+ - Returns `dropped: false` for an absent table while other tables keep the canvas alive. Removing the final table also retires its canvas and frees its slot; subsequent describe, query or drop calls return `canvas_not_found`.
108
118
 
109
119
  ## Features
110
120
 
@@ -115,6 +125,7 @@ POWER-specific:
115
125
  - 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.
116
126
  - 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.
117
127
  - 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.
128
+ - `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.
118
129
 
119
130
  Agent-friendly output:
120
131
 
@@ -122,13 +133,28 @@ Agent-friendly output:
122
133
  - 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.
123
134
  - 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.
124
135
 
125
- 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.
136
+ 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.
126
137
 
127
138
  ## Getting started
128
139
 
140
+ ### Public Hosted Instance
141
+
142
+ A public instance is available at `https://nasa-power.caseyjhand.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP:
143
+
144
+ ```json
145
+ {
146
+ "mcpServers": {
147
+ "nasa-power-mcp-server": {
148
+ "type": "streamable-http",
149
+ "url": "https://nasa-power.caseyjhand.com/mcp"
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
129
155
  ### Self-Hosted / Local
130
156
 
131
- Add this to your MCP client configuration:
157
+ Add the following to your MCP client configuration file.
132
158
 
133
159
  ```json
134
160
  {
@@ -172,27 +198,44 @@ Or with Docker:
172
198
  }
173
199
  ```
174
200
 
175
- For Streamable HTTP from a local checkout:
201
+ For Streamable HTTP, set the transport and start the server:
176
202
 
177
203
  ```sh
178
204
  MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
179
- # http://127.0.0.1:3010/mcp
205
+ # Server listens at http://127.0.0.1:3010/mcp
180
206
  ```
181
207
 
182
208
  ### Prerequisites
183
209
 
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. Set `CANVAS_PROVIDER_TYPE=duckdb` to enable staging and SQL; the default is `none`.
210
+ - [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
211
+ - 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
212
  - 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
213
 
188
214
  ### Installation
189
215
 
216
+ 1. **Clone the repository:**
217
+
190
218
  ```sh
191
219
  git clone https://github.com/cyanheads/nasa-power-mcp-server.git
220
+ ```
221
+
222
+ 2. **Navigate into the directory:**
223
+
224
+ ```sh
192
225
  cd nasa-power-mcp-server
226
+ ```
227
+
228
+ 3. **Install dependencies:**
229
+
230
+ ```sh
193
231
  bun install
232
+ ```
233
+
234
+ 4. **Configure environment:**
235
+
236
+ ```sh
194
237
  cp .env.example .env
195
- bun run rebuild
238
+ # edit .env to change transport, logging or canvas settings
196
239
  ```
197
240
 
198
241
  ## Configuration
@@ -200,38 +243,50 @@ bun run rebuild
200
243
  | Variable | Description | Default |
201
244
  |:---|:---|:---|
202
245
  | `NASA_POWER_DATAFRAME_DROP_ENABLED` | Expose explicit staged-table removal. | `false` |
203
- | `CANVAS_PROVIDER_TYPE` | `none` for inline results; `duckdb` for staging and SQL on npm/Docker. MCPB fixes `none`. | `none` |
204
- | `CANVAS_DEFAULT_MEMORY_LIMIT_MB` | Fixed engine memory limit; not total RSS or temporary disk. | `256` |
205
- | `CANVAS_MAX_CANVASES_PER_TENANT` | Fixed tenant canvas cap. | `4` |
206
- | `CANVAS_TTL_MS` | Fixed sliding canvas lifetime. | `3600000` |
207
- | `CANVAS_ABSOLUTE_CAP_MS` | Fixed absolute canvas lifetime. | `21600000` |
246
+ | `CANVAS_PROVIDER_TYPE` | `duckdb` stages large results for SQL on npm/Docker; `none` keeps results inline. MCPB fixes `none`. | `duckdb` |
247
+ | `CANVAS_DEFAULT_MEMORY_LIMIT_MB` | Per-canvas engine memory, integer ≥64 MiB; not total RSS or temporary disk. | `256` |
248
+ | `CANVAS_MAX_CANVASES_PER_TENANT` | Positive tenant canvas cap; auth-none callers share one tenant. | `4` |
249
+ | `NASA_POWER_MAX_CANVASES_PER_PROCESS` | Positive process canvas cap, including pending and failed cleanup reservations. | `8` |
250
+ | `CANVAS_TTL_MS` | Positive sliding canvas lifetime in milliseconds. | `3600000` |
251
+ | `CANVAS_ABSOLUTE_CAP_MS` | Absolute canvas lifetime in milliseconds, at least the sliding TTL. | `21600000` |
208
252
  | `MCP_TRANSPORT_TYPE` | `stdio` or `http`. | `stdio` |
209
253
  | `MCP_HTTP_HOST` / `MCP_HTTP_PORT` | HTTP bind address and port. | `127.0.0.1` / `3010` |
210
254
  | `MCP_AUTH_MODE` | `none`, `jwt` or `oauth`. | `none` |
211
255
  | `MCP_LOG_LEVEL` | Minimum log level. | `info` |
212
- | `OTEL_ENABLED` | Enable OpenTelemetry instrumentation. | `false` |
256
+ | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
213
257
 
214
- DuckDB startup rejects changes to the four fixed canvas settings. See [`.env.example`](./.env.example) for optional transport, authentication, storage, logging and telemetry settings.
258
+ 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.
215
259
 
216
260
  ## Running the server
217
261
 
218
262
  ### Local development
219
263
 
220
- ```sh
221
- bun run rebuild
222
- bun run start:stdio # or start:http
223
- bun run devcheck
224
- bun run test
225
- ```
264
+ - **Build and run the production version**:
265
+
266
+ ```sh
267
+ # One-time build
268
+ bun run rebuild
269
+
270
+ # Run the built server
271
+ bun run start:http
272
+ # or
273
+ bun run start:stdio
274
+ ```
275
+
276
+ - **Run checks and tests**:
277
+ ```sh
278
+ bun run devcheck # Lints, formats, type-checks, and more
279
+ bun run test # Runs the test suite
280
+ ```
226
281
 
227
282
  ### Docker
228
283
 
229
284
  ```sh
230
285
  docker build -t nasa-power-mcp-server .
231
- docker run --rm -p 127.0.0.1:3010:3010 -e CANVAS_PROVIDER_TYPE=duckdb nasa-power-mcp-server
286
+ docker run --rm -p 127.0.0.1:3010:3010 nasa-power-mcp-server
232
287
  ```
233
288
 
234
- The image defaults to HTTP on port 3010. For a client outside loopback, configure authentication and TLS at the deployment boundary.
289
+ 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.
235
290
 
236
291
  ## Project structure
237
292
 
@@ -247,12 +302,13 @@ The image defaults to HTTP on port 3010. For a client outside loopback, configur
247
302
 
248
303
  ## Development guide
249
304
 
250
- See [`CLAUDE.md`](./CLAUDE.md) and [`AGENTS.md`](./AGENTS.md) for architectural rules.
305
+ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
251
306
 
252
- - Handlers throw, framework catches; use `ctx.log` and `ctx.state`.
253
- - Register tools in the `createApp()` array in `src/index.ts`.
254
- - Validate NASA responses before normalization; preserve sparse fields, source units and annual-period distinctions.
255
- - `content[]` and `structuredContent` carry the same complete data.
307
+ - Handlers throw, framework catches — no `try/catch` in tool logic
308
+ - Use `ctx.log` for logging and `ctx.signal` for cancellation
309
+ - Register new tools in the `createApp()` array in `src/index.ts`
310
+ - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields, and preserve source units and annual-period distinctions
311
+ - `content[]` and `structuredContent` carry the same complete data
256
312
 
257
313
  ## Contributing
258
314
 
@@ -265,4 +321,4 @@ bun run test
265
321
 
266
322
  ## License
267
323
 
268
- Apache 2.0. See [LICENSE](./LICENSE).
324
+ This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.
@@ -0,0 +1,23 @@
1
+ ---
2
+ summary: "Complete NASA POWER comparisons with bounded analytical outputs."
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.2.0 — 2026-10-20
8
+
9
+ ## Changed
10
+
11
+ - **Canvas lifecycle:** Dropping the last table now retires its canvas; subsequent describe, query, and drop calls with that token return `canvas_not_found`. ([#10](https://github.com/cyanheads/nasa-power-mcp-server/issues/10))
12
+ - **DuckDB** is enabled by default for npm and Docker; set `CANVAS_PROVIDER_TYPE=none` to disable staging. The portable MCPB remains inline-only.
13
+ - **Normalized request output schemas** share typed fields while preserving every request echo, including per-location provenance. ([#13](https://github.com/cyanheads/nasa-power-mcp-server/issues/13))
14
+ - **Output limits** above 100 dictionary entries or 1,000 SQL rows use the effective cap with a notice; explicit parameter codes deduplicate after normalization and date schemas advertise their Gregorian shape. ([#14](https://github.com/cyanheads/nasa-power-mcp-server/issues/14))
15
+ - **`agroclimatology`** adds `T2M_MAX` and `T2M_MIN` outside hourly resolution; reference entries show exact expansions and retain source-period semantics. ([#16](https://github.com/cyanheads/nasa-power-mcp-server/issues/16))
16
+
17
+ ## Fixed
18
+
19
+ - **Location comparisons** stage complete observations, bucket summaries and per-location summaries together, returning bounded previews and ordered SQL handles with exact counts. Failed registrations roll back the entire batch. ([#9](https://github.com/cyanheads/nasa-power-mcp-server/issues/9))
20
+ - **Wind corrections** use all 17 canonical NASA surface aliases, preserving vegetation underscores and documented airport-ice aliases. ([#8](https://github.com/cyanheads/nasa-power-mcp-server/issues/8))
21
+ - **SQL diagnostics** retain bounded caller-correctable identifiers and conversion values with recovery guidance, excluding engine paths, stacks and native causes. Operational errors retain safe classifications. ([#12](https://github.com/cyanheads/nasa-power-mcp-server/issues/12))
22
+ - **Upstream 429 errors** report `retryable: true` to callers while preserving fail-fast handling, cooldown and `Retry-After`. ([#15](https://github.com/cyanheads/nasa-power-mcp-server/issues/15))
23
+ - **Documentation** aligns tool usage, native distribution defaults and canvas persistence guidance.
@@ -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.
@@ -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"}