Skip to content

fix: mirror params.taskId into Mcp-Name for tasks requests (SEP-2663) - #2613

Open
jwcarman wants to merge 3 commits into
modelcontextprotocol:mainfrom
jwcarman:fix/tasks-mcp-name-header
Open

fix: mirror params.taskId into Mcp-Name for tasks requests (SEP-2663)#2613
jwcarman wants to merge 3 commits into
modelcontextprotocol:mainfrom
jwcarman:fix/tasks-mcp-name-header

Conversation

@jwcarman

@jwcarman jwcarman commented Aug 4, 2026

Copy link
Copy Markdown

Summary

The Streamable HTTP client transport omits the Mcp-Name header on tasks/get / tasks/update / tasks/cancel. SEP-2663's Streamable HTTP binding makes that header a client MUST for these methods:

When tasks/get, tasks/update, or tasks/cancel is sent over the Streamable HTTP transport, the client MUST set the Mcp-Name header to the value of params.taskId. This allows transport intermediaries and load balancers to route subsequent requests for the same task to the server instance holding its state, which is typically required for correctness.

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-internalMCP_NAME_HEADER_SOURCE gains the three tasks/*taskId rows (type widened to 'name' | 'uri' | 'taskId'). validateStandardRequestHeaders needs no code change — it is already table-driven — so SDK servers now also require/cross-check Mcp-Name on tasks requests, symmetric with the client.
  • @modelcontextprotocol/client_applyBodyDerivedHeaders now derives Mcp-Name from the shared MCP_NAME_HEADER_SOURCE table instead of a hardcoded resources/read-vs-params.name ternary, so client emission and server validation cannot drift apart. The lookup is Object.hasOwn-guarded like the server side. Sentinel encoding is unchanged and applies to the taskId value as well.

Behavior notes

  • Client: previously-omitted header is now sent for the three tasks methods; all other methods emit byte-identical headers (tools/call/prompts/getparams.name, resources/readparams.uri, off-table methods → none — pinned by a new tasks/list negative test).
  • Server: a tasks request with a matching or absent-because-legacy envelope is unaffected; a modern-enveloped tasks request now gets the same presence/cross-check treatment the core three methods get. Requests that previously slipped through with a missing/mismatched header on tasks methods are now rejected per the SEP — stricter, but spec-conforming.

Tests

  • standardHeaderValidation.test.ts: tasks missing-header rejection (names params.taskId), matching-header pass for all three methods, mismatch rejection, tasks/list off-table pass, and the exact-table test updated for the SEP-2663 rows.
  • mcpParamMirroring.test.ts: client emits Mcp-Name: <taskId> for all three tasks methods and no header for tasks/list.
  • Full runs: core-internal 1437, client 798, server 468 — all passing; typecheck:all + lint:all clean.

Fixes modelcontextprotocol/inspector#1917 (the Inspector bug is this SDK behavior — Inspector needs only the dependency bump once released).

🤖 Generated with Claude Code

@jwcarman
jwcarman requested a review from a team as a code owner August 4, 2026 12:40
@changeset-bot

changeset-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e562cd4

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

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/core-internal Patch
@modelcontextprotocol/client Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/codemod 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 Aug 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

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

@modelcontextprotocol/codemod

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

@modelcontextprotocol/core

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

@modelcontextprotocol/server

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

@modelcontextprotocol/server-legacy

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

@modelcontextprotocol/express

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

@modelcontextprotocol/fastify

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

@modelcontextprotocol/hono

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

@modelcontextprotocol/node

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

commit: e562cd4

@cliffhall

cliffhall commented Aug 5, 2026

Copy link
Copy Markdown
Member

@KKonstantinov Verified, Inspector not sending Mcp-Name header on tasks/get, tasks/update, tasks/cancel.

Screenshot 2026-08-05 at 12 57 03 PM Screenshot 2026-08-05 at 12 57 53 PM Screenshot 2026-08-05 at 12 59 03 PM

@jwcarman

jwcarman commented Aug 5, 2026

Copy link
Copy Markdown
Author

Verified end-to-end with this branch's @modelcontextprotocol/client packed into a local Inspector 2.0.0 (the reported repro environment), against a conforming 2026-07-28 server (mocapi's tasks example) that rejects tasks requests lacking Mcp-Name with -32020 HeaderMismatch.

With the fix in place, all three methods now carry Mcp-Name mirroring params.taskId, and the full task lifecycle works through the Inspector UI — task creation → tasks/get polling → mid-task elicitation answered via tasks/update → completion, plus tasks/cancel on a second task. Every request in the Network log succeeded (the same flows 400 on the pre-fix SDK at the first poll).

tasks/getmcp-name mirrors the polled taskId:

tasks/get with Mcp-Name present

tasks/update — same header derivation on the elicitation answer:

tasks/update with Mcp-Name present

tasks/cancel — and on cancellation:

tasks/cancel with Mcp-Name present

@KKonstantinov

Copy link
Copy Markdown
Contributor

@claude review

@claude claude Bot 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.

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-Name header is absent on a tools/call / prompts/get / resources/read request ... disagrees with the body's params.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-Name header is absent on a tools/call / prompts/get / resources/read request ... disagrees with the body's params.name / params.uri", while the same file's exact-table test now asserts the SEP-2663 rows ('tasks/get':

Comment thread packages/core-internal/src/shared/inboundClassification.ts
Comment thread packages/client/src/client/streamableHttp.ts Outdated
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`.
@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Aug 18, 2026
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.

Tasks requests over Streamable HTTP omit the required Mcp-Name header (SEP-2663)

4 participants