fix: mirror params.taskId into Mcp-Name for tasks requests (SEP-2663) - #2613
fix: mirror params.taskId into Mcp-Name for tasks requests (SEP-2663)#2613jwcarman wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: e562cd4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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 |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
|
@KKonstantinov Verified, Inspector not sending Mcp-Name header on
|
|
Verified end-to-end with this branch's With the fix in place, all three methods now carry
|
|
@claude review |
There was a problem hiding this comment.
Beyond the inline findings, two other concerns were examined and ruled out: the stricter server-side validation shipping as a patch (tasks requests from previously released SDK clients are not newly rejected, since the header check only runs on modern-enveloped exchanges), and the switch from the old any-method params.name fallback to the table-gated lookup (no spec method off the table carries a top-level string params.name, so no previously emitted header is dropped).
Extended reasoning...
Findings were confirmed and will be posted inline (including the era-reachability question about the new tasks rows and two quality issues), so the review body is limited to recording what else was checked and ruled out. The back-compat concern about patch-level stricter validation and the behavior change from the old resources/read-vs-params.name ternary to the table-gated lookup were both investigated during the hunt and did not hold up as bugs. The hunt exited on a round cap rather than running dry, so approval is not appropriate regardless; the inline findings already signal that a human should look.
Additional findings (outside current diff — PR may have been updated during review):
-
🟡
packages/core-internal/test/shared/standardHeaderValidation.test.ts— [quality] Stale Mcp-Name method-set doc comments left enumerating only tools/call/prompts/get/resources/read after the table gained tasks rows — sweep:Mcp-Name.{1,2} header .{0,20}on a .tools/call— e.g. this file's preamble (lines 5-12) and packages/server/test/server/stdHeaderValidation.test.ts lines 5-12; update the method list (and params.name/params.uri enumeration) to match MCP_NAME_HEADER_SOURCE, as the src JSDoc in inboundClassification.ts already was.Extended reasoning...
Concrete cost: the PR updated the JSDoc on MCP_NAME_HEADER_SOURCE and validateStandardRequestHeaders in inboundClassification.ts to name the new tasks rows, and added tasks tests to this very file, but left this file's module preamble ("rejects ... when the required
Mcp-Nameheader is absent on atools/call/prompts/get/resources/readrequest ... disagrees with the body'sparams.name/params.uri") describing the pre-change three-method table — the comment now contradicts the tasks/get/tasks/update/tasks/cancel tests sitting 200 lines below it in the same file. The sibling server-level suite packages/server/test/server/stdHeaderValidation.test.ts carries the same stale enumeration. A maintainer reading either preamble to learn which methods require Mcp-Name gets the wrong (pre-SEP-2663) answer, the exact prose-vs-implementation drift the repo's Documentation recurring catch flags. Nit severity; fix is a comment sweep, no behavior change.Verification: nit — the claim is factually true. packages/core-internal/test/shared/standardHeaderValidation.test.ts lines 8-11 (modified by this PR) still read "when the required
Mcp-Nameheader is absent on atools/call/prompts/get/resources/readrequest ... disagrees with the body'sparams.name/params.uri", while the same file's exact-table test now asserts the SEP-2663 rows ('tasks/get':
Move the method -> source-field lookup and string narrowing into a single `mcpNameSource` helper next to `MCP_NAME_HEADER_SOURCE`, used by both the client transport (emission) and `validateStandardRequestHeaders` (cross-check), so the extraction cannot drift between the two sides. Update the remaining prose that still scoped `Mcp-Name` to `params.name` / `params.uri` (ladder rung rationale, validator comment, test docblocks, migration guide, e2e helper) and add `@modelcontextprotocol/server` to the changeset, since `createMcpHandler`'s rejection surface changes. Tests: non-string `params.taskId` passes the rung on the server and emits no header on the client; a unit test for `mcpNameSource`; an entry-level `createMcpHandler` case for a `tasks/get` missing `Mcp-Name`.






Summary
The Streamable HTTP client transport omits the
Mcp-Nameheader ontasks/get/tasks/update/tasks/cancel. SEP-2663's Streamable HTTP binding makes that header a client MUST for these methods:A conforming server therefore rejects every task poll from this SDK (and from Inspector, which surfaced it: modelcontextprotocol/inspector#1917) with
-32020 HeaderMismatch/ HTTP 400.Changes
@modelcontextprotocol/core-internal—MCP_NAME_HEADER_SOURCEgains the threetasks/*→taskIdrows (type widened to'name' | 'uri' | 'taskId').validateStandardRequestHeadersneeds no code change — it is already table-driven — so SDK servers now also require/cross-checkMcp-Nameon tasks requests, symmetric with the client.@modelcontextprotocol/client—_applyBodyDerivedHeadersnow derivesMcp-Namefrom the sharedMCP_NAME_HEADER_SOURCEtable instead of a hardcodedresources/read-vs-params.nameternary, so client emission and server validation cannot drift apart. The lookup isObject.hasOwn-guarded like the server side. Sentinel encoding is unchanged and applies to the taskId value as well.Behavior notes
tools/call/prompts/get→params.name,resources/read→params.uri, off-table methods → none — pinned by a newtasks/listnegative test).Tests
standardHeaderValidation.test.ts: tasks missing-header rejection (namesparams.taskId), matching-header pass for all three methods, mismatch rejection,tasks/listoff-table pass, and the exact-table test updated for the SEP-2663 rows.mcpParamMirroring.test.ts: client emitsMcp-Name: <taskId>for all three tasks methods and no header fortasks/list.typecheck:all+lint:allclean.Fixes modelcontextprotocol/inspector#1917 (the Inspector bug is this SDK behavior — Inspector needs only the dependency bump once released).
🤖 Generated with Claude Code