Skip to content

Bound the catalog refresh, keep unavailable connections in the file, scope listings before a refresh (0.3.14) - #26

Merged
MainSequenceDev merged 1 commit into
mainfrom
claude/registry-defects
Oct 26, 2026
Merged

MainSequenceDev merged 1 commit into
mainfrom
claude/registry-defects

Conversation

@MainSequenceDev

Copy link
Copy Markdown
Contributor

Summary

Fixes the two registry defects found while accepting the Codex 0.7.9 release (recorded in that release's acceptance notes), as devtools 0.3.14. Design record updated in docs/adr/0004-shared-project-tool-registry.md.

  1. A refresh can no longer write an empty, healthy scope. readProjectToolCatalog runs within one budget (budgetMs, default DEFAULT_PROJECT_REFRESH_BUDGET_MS = 300 s). A connection whose read fails, or is not read before the budget runs out, stays in the catalog marked unavailable: { at, code, message } with its earlier tools. The mark is persisted in project-tool-catalog.json, so state() / view() and the mcp-catalog action report it from any process (the Claude Desktop hook runs one worker per action and previously could never see a refresh's unavailable). A successful read or update() clears it. A refresh aborts its dispatcher's signal when it returns, so a read left behind by the budget cannot hold a socket. Observed before the fix: a 773 s forced refresh that wrote the scope empty and reported nothing.
  2. Listings are scoped before any refresh. createProjectToolCatalog accepts backend and offers ready(), which resolves the host's backend without a network request the way auth-status does (process environment, shared preferences, default). Until the backend is known a listing is empty, never another backend's tools. Observed before the fix: a fresh Codex server's first tools/list carried 144 tools from two backends.

Additive API only: new budgetMs/backend options, ready(), ProjectToolUnavailable, DEFAULT_PROJECT_REFRESH_BUDGET_MS; unavailable on connection records and state().connections[].unavailable. Tool names, the file format (new optional field) and mcp-call are unchanged.

Testing

  • npm test: 184 pass (3 new registry tests; one existing test rewritten for the empty-until-known listing; the mcp-catalog test now checks that the next process sees the mark).
  • node scripts/check-release.cjs, node scripts/check-docs.cjs pass.
  • Live, with the rebuilt Codex plugin (branch codex/registry-defects): the shared catalog on the workstation holds three connections across two backends; a fresh server's first tools/list returned 103 tools = 16 + the 87 of its own backend only.

Release

Tag v0.3.14 on the merge commit runs the release workflow, which publishes to npm. The Codex consumer (mainsequence-codex-plugin branch codex/registry-defects, 0.7.12) pins 0.3.14 by a local pack integrity for its test run and must be re-pinned from the registry after publishing (npm pack is not byte-reproducible across machines). Claude Desktop needs only a pin bump; its hook already passes an explicit backend to mcp-catalog.

🤖 Generated with Claude Code

…scope listings before a refresh (0.3.14)

Two defects found while accepting the 0.7.9 Codex release:

- A forced catalog refresh took 773 s and wrote the scope empty without naming the
  connection unavailable. readProjectToolCatalog now runs within one budget
  (budgetMs, default DEFAULT_PROJECT_REFRESH_BUDGET_MS = 300 s): a connection whose
  read fails, or is not read before the budget runs out, stays in the catalog
  marked `unavailable: { at, code, message }` with its earlier tools, so a scope
  whose connections exist is never written empty and healthy. The mark lives in the
  file, so state() and the mcp-catalog action report it from any process, not only
  the one that ran the refresh; a successful read or update() clears it. A refresh
  aborts its dispatcher's signal when it returns, so a read the budget left behind
  cannot keep a socket.
- A fresh Codex server's first tools/list listed every cached scope because the
  backend was learned only from a refresh. createProjectToolCatalog takes `backend`
  and offers ready(), which resolves it without a network request the way
  auth-status does (environment, shared preferences, default); until the backend is
  known a listing is empty, never another backend's tools.

Tests cover the written mark and its clearing, the budget (hung reads, retained
tools, released signal), the empty-until-known listing and ready(); the mcp-catalog
test checks that the next process sees the mark. ADR 0004 and the README record the
behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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