Skip to content

fix(client): use one tool definition for mirroring and output validation - #3030

Open
Poke-nushi wants to merge 1 commit into
modelcontextprotocol:mainfrom
Poke-nushi:fix/client-tool-definition-snapshot
Open

Poke-nushi wants to merge 1 commit into
modelcontextprotocol:mainfrom
Poke-nushi:fix/client-tool-definition-snapshot

Conversation

@Poke-nushi

@Poke-nushi Poke-nushi commented Oct 15, 2026 •

Copy link
Copy Markdown

When an asynchronous response-cache read overlaps a tools/list refresh, Client.callTool() can build Mcp-Param-* headers from the new tool definition while validating its result against the old definition. A successful handler result then rejects with ProtocolError -32602.

This change memoizes the source tool alongside its compiled output validator. Each attempt derives headers and output validation from that same snapshot; the existing HEADER_MISMATCH recovery resolves a fresh snapshot for the retry.

The regression cases pause a read through the public asynchronous ResponseCacheStore interface, complete a catalog refresh, and resume the call. Both cases check that the stale-header attempt is rejected before handler execution, the retry uses the new definition, and the handler executes exactly once. The positive case accepts output matching the new schema; the negative case rejects output valid only under the old schema. The tests use an in-memory transport and no sleeps.

Validation

All 14 PR checks have passed on GitHub, including Node.js 20/22/24 unit tests and E2E, Bun/Deno, build, examples, and client/server conformance.

Base: modelcontextprotocol/typescript-sdk main at 9bc40e903fde15534ec06ff4c1347d47000dbe16. Local Node.js 22.16.0, pnpm 10.26.1.

  • New regression against the unmodified source: fails with -32602.
  • Client package: 41 test files, 1,000 tests pass, including the new regression.
  • Client typecheck, lint, and build pass.
  • Repository-wide pnpm run build:all and pnpm run lint:all pass.
pnpm --filter @modelcontextprotocol/client test
pnpm --filter @modelcontextprotocol/client typecheck
pnpm --filter @modelcontextprotocol/client lint
pnpm --filter @modelcontextprotocol/client build

AI disclosure: Codex investigated the race, wrote the patch and regression test, drafted this description, and ran the local checks.

@changeset-bot

changeset-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f363369

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core-internal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@3030

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@3030

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@3030

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@3030

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@3030

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@3030

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@3030

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@3030

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@3030

commit: f363369

@Poke-nushi
Poke-nushi marked this pull request as ready for review October 15, 2026 11:20
@Poke-nushi
Poke-nushi requested a review from a team as a code owner October 15, 2026 11:20

@karaaslanz karaaslanz left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent source-backed review of exact head f3633697b426a45354e85549b764bd2f11287fe9. I compared the actual Client.callTool() method at base 9bc40e903fde15534ec06ff4c1347d47000dbe16 (client.ts blob 8118b60cea757e3bdfdcdfa35cd2a95bb558ba99) and this PR (client.ts blob 75b0cb1c7346072c035ace30b09701097de12e4c) using Node 24's TypeScript stripping and a deterministic isolated method harness with a deferred cache read.

The harness pauses outputValidator() after capturing catalog generation Old, refreshes the catalog to generation New before releasing the read, and simulates a server that accepts only Mcp-Param-New-Region and replies with either a New or Old structured output. The harness executes the extracted source method from each commit, not a handwritten paraphrase of the client's callTool flow.

Results, 4/4 expected comparisons PASS:

  • Unmodified base, New response: the headers come from New but the output validator is from Old; it rejects a valid New output with InvalidParams (-32602).
  • Unmodified base, Old response: the same mixed-generation call incorrectly accepts Old output against the new-header call.
  • PR head, New response: the initial Old header attempt is rejected with HEADER_MISMATCH; after one refresh, both header and validator use New, and the call succeeds.
  • PR head, Old response: the same retry uses New metadata and rejects the obsolete Old output (-32602).

This independently reproduces the pre-fix race and confirms the intended generation consistency in the patched code. The fix's one-per-attempt { tool, compiled } snapshot and metadata refresh on HEADER_MISMATCH align with the observed behavior. GitHub Actions workflows for the pinned head report success. Scope: isolated source-extracted method simulation, with mocked cache/transport/schema callbacks, not a full repository/E2E test. No repository files changed. I found no issue in the two tested race paths.

@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Oct 16, 2026

This branch has not been deployed

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

Labels

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants