Skip to content

feat: npm-version self-check + accurate aztec_status version#21

Merged
critesjosh merged 2 commits intomainfrom
feat/version-self-check
May 2, 2026
Merged

feat: npm-version self-check + accurate aztec_status version#21
critesjosh merged 2 commits intomainfrom
feat/version-self-check

Conversation

@critesjosh
Copy link
Copy Markdown
Collaborator

Summary

Closes the version-staleness gap surfaced during the v1.20.0 dogfood test. Three changes together:

1. npm self-check at startup

New src/utils/version-self-check.ts GETs https://registry.npmjs.org/@aztec/mcp-server/latest at boot (2s timeout, fails silently on network/registry error), compares against the live MCP_VERSION (already read from package.json by src/version.ts), caches the result in a module-level singleton.

2. Surface the warning in two places

MCP instructions banner — appended to SEMANTIC_INSTRUCTIONS / LOCAL_ONLY_INSTRUCTIONS when outdated. Written for the LLM consumer, who'll pass it to the user:

⚠️ UPDATE AVAILABLE: this MCP server is running v1.20.0, but v1.21.0 is the current release on npm. Tell the user they're on an outdated version, and that bug reports about behavior may already be fixed in the latest release. To upgrade, ensure their MCP client config uses @aztec/mcp-server@latest so npx fetches the newest:
• Claude Desktop / Cursor / Codex: change the args to ["-y", "@aztec/mcp-server@latest"] in the MCP server config and restart the client.
• Claude Code: claude mcp remove aztec-docs && claude mcp add aztec-docs ... -- npx -y @aztec/mcp-server@latest
• If installed globally: npm uninstall -g @aztec/mcp-server && npm install -g @aztec/mcp-server@latest

aztec_status output — single-line warning so a curious user running diagnostics sees the same signal: ⚠️ UPDATE AVAILABLE: v1.20.0 → v1.21.0 on npm. Switch your MCP config to \@aztec/mcp-server@latest` and restart the client.`

3. Fix the staleness bug in aztec_status

formatStatus previously rendered MCP server version: ${syncMetadata.mcpVersion} — that field is set at last-sync time, not boot. After upgrading the package, aztec_status would still show the old version until you ran aztec_sync_repos. Fixed by reading the live MCP_VERSION from src/version.ts. Sync-metadata version moves to a contextual annotation only rendered when different ("(last sync ran under MCP server v1.5.0 — re-run aztec_sync_repos to refresh metadata)").

README

Every install snippet pinned to @aztec/mcp-server@latest. Added a note explaining why npx caching makes the bare form a foot-gun.

Test plan

  • npm run build (tsc clean)
  • npx vitest run264/264 (was 247; +19 for version-self-check.test.ts, +4 reworked format.test.ts cases for the new live-version display)
  • End-to-end stdio probe confirmed:
    • initializeresult.instructions contains the upgrade banner with both versions and @aztec/mcp-server@latest guidance
    • tools/call aztec_status → live MCP server version: 1.6.0 + ⚠️ UPDATE AVAILABLE: v1.6.0 → v1.20.0 on npm.
  • After release: confirm the warning correctly does NOT appear when the running version equals npm-latest (the "up to date" path is exercised in unit tests; would be nice to confirm in production once published).

Failure modes

  • Registry down / no network / 5xx / malformed body: fetchLatestNpmVersion returns null, no banner injected, aztec_status shows live version without an npm-latest line. Server boots normally.
  • User running a pre-release of the next version (e.g. 1.21.0-rc.1 while 1.20.0 is on npm): compareSemver strips the prerelease suffix, so 1.21.0-rc.1 does NOT flag as outdated against 1.20.0. (Strictly: it flags 1.21.0-rc.1 as equal to 1.21.0 but NEWER than 1.20.0 — correct.)

🤖 Generated with Claude Code

critesjosh and others added 2 commits May 2, 2026 01:14
Closes the staleness gap that drove this PR's parent dogfood test:
the MCP server pulled its displayed version from sync metadata
(populated at last `aztec_sync_repos` run, not at boot), so an
upgraded package would still report the OLD version under
`aztec_status`. Combined with npx's package cache, this made
"running an out-of-date MCP" near-undetectable from inside a session.

Three changes solve it together:

1. New `src/utils/version-self-check.ts`. At startup the server
   GETs `https://registry.npmjs.org/@aztec/mcp-server/latest` (2s
   timeout, fails silently on network/registry error). Compares
   against the live `MCP_VERSION` (read from package.json by
   `src/version.ts`). On `outdated`, the result is cached in a
   module-level singleton so both the InitializeResult `instructions`
   banner AND the `aztec_status` text can surface it without a
   second registry hit.

2. Banner content: `instructions` gets a multi-line "UPDATE
   AVAILABLE" footer that names both versions and lists the
   `@aztec/mcp-server@latest` upgrade paths for Claude Desktop /
   Cursor / Codex / Claude Code / global install. Written to be
   read by the LLM consumer — the model passes it through to the
   user. `aztec_status` gets a single-line warning so a curious
   user running diagnostics sees the same signal.

3. `formatStatus` now reads the live `MCP_VERSION` from the
   `version.ts` import — fixes the staleness bug. Sync-metadata
   version moves to a contextual annotation that only renders
   when it differs from live ("(last sync ran under MCP server
   v1.5.0 — re-run aztec_sync_repos to refresh metadata)").
   Always-show ordering: live version, npm-latest comparison,
   repos dir, sync metadata.

README updated: every install snippet (`npx`, `npm install -g`,
`.mcp.json` examples) pinned to `@aztec/mcp-server@latest`. Added a
prominent note explaining why npx caching makes the bare form a
foot-gun.

Tests: 264/264. New `tests/utils/version-self-check.test.ts` with 19
cases (semver edge cases incl. pre-release, fetch failure modes,
cache lifecycle, banner formatting). Updated
`tests/utils/format.test.ts` for the new live-version display + 4
new cases for the upgrade-line behaviors. End-to-end probe over
stdio confirmed the banner reaches both `initialize` (instructions)
and `tools/call aztec_status` correctly when the running version
trails npm-latest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t-loop pin

Codex review feedback on PR #21. The previous implementation only
cleared the abort timer on the success path — when `fetchImpl`
rejected (network error, malformed body), the catch block returned
null but the timer was left running until it fired. In a long-lived
MCP server that's invisible (the timer just expires harmlessly
seconds later), but in short-lived processes / tests it would
prevent the event loop from exiting cleanly.

Two-line fix:
- `timer.unref()` (Node-only, optional-chained for portability) so
  the timer alone never keeps the loop alive.
- `clearTimeout(timer)` moved into a `finally` block so it runs on
  both success and exception paths.

Belt-and-braces by design: either fix alone is sufficient, but
together they leave no path for a stray timer.

19 version-self-check tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@critesjosh critesjosh merged commit 011fc0d into main May 2, 2026
6 checks passed
@critesjosh critesjosh deleted the feat/version-self-check branch May 2, 2026 01:25
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

🎉 This PR is included in version 1.21.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant