Skip to content

1.1.0: validated tool registry, CMR compact output and cursors, FIRMS fix, tested release pipeline - #10

Merged
ProgramComputer merged 2 commits into
mainfrom
release/reliability-cmr
Sep 29, 2026
Merged

ProgramComputer merged 2 commits into
mainfrom
release/reliability-cmr

Conversation

@ProgramComputer

Copy link
Copy Markdown
Owner

Reliability fixes and CMR improvements for 1.1.0. Baseline: main at 8d66b2b, npm latest = 1.0.14. CI on main had been red since 1.0.13 because there were no tests.

What changed

Reliability

  • One typed, allowlisted registry now drives tools/list, validation and dispatch.
    • Defaults are applied before handlers run, on every entry point: tools/call, the legacy nasa/* methods, prompts/execute and resource templates.
    • Tool names are never turned into import paths. Unknown tools return JSON-RPC -32602; invalid arguments return isError results.
    • Documented aliases (nasa/apod, nasa/mars-rover, jpl/jd_cal, …) are explicit. nasa/mars-rover was broken before.
  • No fabricated data. The startup sample records and fake template generators are gone, and templates now fetch real data. Images are embedded only when the download really is an image from a nasa.gov host.
  • Resources:
    • Each server instance has its own bounded store (50 entries, 16 MB).
    • Entries are keyed by the full canonical arguments and carry source and retrieved_at.
    • The stateless HTTP transport creates a store per request, so nothing is shared between clients.
  • FIRMS:
    • Uses FIRMS_MAP_KEY and the documented path /api/area/csv/KEY/SOURCE/w,s,e,n/DAYS[/DATE], with bbox as the canonical area.
    • latitude/longitude now need radius_km and are converted to a bbox; polar and antimeridian cases are rejected.
    • Uses an RFC 4180 CSV parser, and detects error text or HTML even when FIRMS returns HTTP 200.
    • The key is redacted everywhere.
  • Upstream errors: every request has a deadline and a size limit. 429s, timeouts, HTML and non-JSON errors become MCP errors; they are never reported as successful empty results.
  • Packaging:
    • Exact dependency ranges and a regenerated lockfile. Removed axios, express, cors, @anthropic-ai/sdk and form-data in favour of built-in fetch/FormData.
    • npm audit --omit=dev reports 0 vulnerabilities (8 before).
    • build no longer runs npm install.
    • The package is an explicit file allowlist with a prepack guard, so tests, .env and fixtures can't ship.
    • The CLI starts only when run directly, not on import. The version comes from package.json, and stdout carries only MCP messages.

CMR (nasa_cmr)

  • Filters:
    • All verified collection and granule filters are exposed and validated, including collection_concept_id, geometry, platform/instrument/project, processing level, data format, download/browse/online flags, facets and verified sort keys.
    • bbox is translated to bounding_box (CMR rejected bbox with HTTP 400).
    • Filters that don't apply to the chosen search type are rejected instead of dropped.
  • Output:
    • Compact typed envelope by default: status, search_type, results, returned_count, total_hits from CMR-Hits, next_cursor, source, retrieved_at, warnings.
    • response_mode: "raw" returns the upstream metadata; fields selects fields and shapes both the structured and text output.
    • Separate JSON and UMM-JSON adapters.
  • Search After cursor:
    • Stateless (cmr1. + base64url JSON), versioned, capped at 8 KB and re-validated on decode.
    • Continue with {"cursor": …} alone. Conflicting filters, sort or search type are rejected, never silently restarted.
    • page/offset remain as a deprecated, mutually exclusive compatibility path.

JPL / other tools. Every advertised parameter was checked against the live APIs. Ones that never worked now return a migration message: Sentry date_min/date_max; Scout orbit_id/summary; Fireball req_energy/req_impact_e. Scout plot/file values were fixed, and Sentry/Scout limit is now applied locally. Other fixes:

Tool Fix
GIBS Sent WMS 1.3.0 bounding boxes in the wrong axis order, so it requested a different region.
POWER time_standard was ignored upstream; it is now sent as time-standard.
Exoplanet nstedAPI rejects ps and ignores top; now uses TAP.
EONET Silently broadened queries that returned nothing; now reports no results.
Mars Rover Photos Upstream is retired (HTTP 404 "No such app"); the tool reports that accurately.

Compatibility decisions

  • Version 1.1.0, chosen by the maintainer. The release still contains changes clients can notice, and CHANGELOG.md lists them as breaking with migration notes:
    • CMR returns the compact envelope by default. Adding response_mode: "raw" alone does not restore the old raw-by-default response.
    • Strict schemas reject unknown parameters.
    • FIRMS needs FIRMS_MAP_KEY and an explicit area.
    • Requires Node ≥ 22 (Node 20 is EOL).
    • The unused global.mcp__* functions and the nasa/subscribe no-op were removed.
  • Kept: TypeScript, the package name, the CLI command, stdio, Streamable HTTP, and all 23 tools.
  • The cursor is unsigned by design. It only carries values a caller could pass directly, the server fixes host, path and headers, and decoded contents go through the same schema as normal input. No per-request secret and no in-memory map are needed.

Test evidence

  • npm test: 118/118 pass locally on Windows, Node 22.13.1. That is unit tests on local fixtures plus stdio and Streamable HTTP integration tests driven by the MCP SDK client against the real CLI, including a raw stdout contamination check, clean shutdown and 8 concurrent HTTP clients. The stdio integration file ran 15× in a row without a failure after fixing an ordering flake in the test.
  • Package gate (scripts/package-smoke.mjs), locally on Windows:
    • Tarball with 90 files, sha512 integrity checked.
    • Installed outside the repo with runtime dependencies only; no development packages present, and nothing resolves from the checkout.
    • Bin --version prints 1.1.0, and 5/5 transport tests pass against the installed bin.
  • Live scenario through an MCP client against the installed tarball:
    • CMR collections (total_hits from CMR-Hits), cursor-only page 2 with no repeated records, and granules with fields.
    • Invalid granule+keyword request rejected; APOD with DEMO_KEY, SBDB, EONET, Exoplanet TAP and CAD all worked.
    • FIRMS without a key gives a clear configuration error; Mars Rover reported DEMO_KEY rate limiting accurately.
  • Claude Code connected to the installed tarball through a temporary --mcp-config and registered all 23 tools. A full model-driven run is pending a Claude Code re-login on the maintainer's machine.
  • Not yet run: live FIRMS (no authorized FIRMS_MAP_KEY available). The deterministic FIRMS tests all pass.

Security note

1.0.0–1.0.14 on npm shipped a hard-coded FIRMS MAP_KEY (dist/tests/direct-api-test.js), and 1.0.0–1.0.12 shipped dist/.env containing a NASA API key. Cause: setupEnvironment() copied .env into dist/. Both keys must be rotated by the owner. Git history was not rewritten.

Release plan

  1. CI green on Linux, Windows and macOS × Node 22/24, plus the audit job; then merge.
  2. Tag the merged commit v1.1.0 (annotated) and draft the GitHub Release.
  3. One-time owner setup: configure the npm trusted publisher (ProgramComputer / NASA-MCP-server / release.yml / environment npm-release). The existing local npm tokens return 401, and the 2025 NPM_TOKEN secret is not used.
  4. gh workflow run release.yml -f tag=v1.1.0 -f dist_tag=next. This re-tests the tag, packs once, runs the package gate, publishes that exact tarball under next with provenance, then verifies registry integrity, a fresh install, and the MCP tests on Linux and Windows.
  5. Owner promotes: npm dist-tag add @programcomputer/nasa-mcp-server@1.1.0 latest. OIDC cannot run dist-tag.
  6. Verify latest and a fresh npx run, then publish the GitHub Release.

See RELEASING.md for the full runbook.

@ProgramComputer
ProgramComputer merged commit 08b189f into main Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant