Summary
On SDK v2 (@modelcontextprotocol/server@2.0.0 + @modelcontextprotocol/node@2.0.0), a server implementing the Tasks extension (io.modelcontextprotocol/tasks, SEP-2663) cannot serve tasks/get or tasks/cancel: requests to these methods are rejected with -32601 before custom request handlers are consulted, even when a handler is registered and the client declares the tasks extension capability.
Root cause
packages/core-internal/src/shared/protocol.ts gates incoming requests with:
if (isSpecRequestMethod(request.method) && !codec.hasRequestMethod(request.method)) {
// → -32601 before custom handler lookup
}
isSpecRequestMethod() (packages/core-internal/src/wire/codec.ts) answers against the union of the 2025 and 2026 method registries. tasks/get and tasks/cancel are in that union — they existed in the 2025-11-25 revision as experimental core methods — but the 2026-07-28 codec does not contain them, because tasks moved out of core into the extension. Result:
tasks/get / tasks/cancel → "spec method" (per the legacy registry) + "not in modern codec" → rejected with -32601 before the handler table is checked.
tasks/update (a name new in the redesigned extension, absent from the legacy registry) → passes the gate, and a custom handler registered via server.server.setRequestHandler(...) works fine.
So any extension method that happens to collide with a historical core method name is unservable in a modern-era session, while brand-new names work. The asymmetry between tasks/update (works) and tasks/get/tasks/cancel (unreachable) makes this easy to verify.
Repro sketch
import { McpServer } from '@modelcontextprotocol/server';
// server advertises { extensions: { 'io.modelcontextprotocol/tasks': {} } } in server/discover
server.server.setRequestHandler('tasks/update', updateSchema, handler); // ✅ reachable
server.server.setRequestHandler('tasks/get', getSchema, handler); // registered, but…
// client (2026-07-28 _meta envelope, tasks extension declared in clientCapabilities):
// → tasks/update: handler runs
// → tasks/get: -32601 (handler never consulted)
Impact
The officially specified Tasks extension polling flow (tasks/get) and cooperative cancellation (tasks/cancel) cannot be implemented on SDK v2 without bypassing the SDK. We currently work around it by intercepting these two methods at the HTTP layer (routing on the Mcp-Method request header before the request reaches the SDK), which we would very much like to delete.
Possible directions
- Scope the gate to the negotiated era's registry only — don't reject a method merely because an older revision once defined it (the modern codec's ignorance of a legacy name shouldn't outrank an explicitly registered handler).
- Or: let explicitly registered custom handlers take precedence over the spec-method gate.
- Or: when a server advertises an extension, admit that extension's method names into
hasRequestMethod for the session.
Option 1 or 2 also future-proofs other extensions whose method names were ever part of a historical core revision.
Related: #2189 (Tasks extension tracking), #2188 (extensions framework), #2528 / #2569 (extension notifications in subscriptions/listen — the notification-side sibling of this request-side gap).
Summary
On SDK v2 (
@modelcontextprotocol/server@2.0.0+@modelcontextprotocol/node@2.0.0), a server implementing the Tasks extension (io.modelcontextprotocol/tasks, SEP-2663) cannot servetasks/getortasks/cancel: requests to these methods are rejected with-32601before custom request handlers are consulted, even when a handler is registered and the client declares the tasks extension capability.Root cause
packages/core-internal/src/shared/protocol.tsgates incoming requests with:isSpecRequestMethod()(packages/core-internal/src/wire/codec.ts) answers against the union of the 2025 and 2026 method registries.tasks/getandtasks/cancelare in that union — they existed in the 2025-11-25 revision as experimental core methods — but the 2026-07-28 codec does not contain them, because tasks moved out of core into the extension. Result:tasks/get/tasks/cancel→ "spec method" (per the legacy registry) + "not in modern codec" → rejected with-32601before the handler table is checked.tasks/update(a name new in the redesigned extension, absent from the legacy registry) → passes the gate, and a custom handler registered viaserver.server.setRequestHandler(...)works fine.So any extension method that happens to collide with a historical core method name is unservable in a modern-era session, while brand-new names work. The asymmetry between
tasks/update(works) andtasks/get/tasks/cancel(unreachable) makes this easy to verify.Repro sketch
Impact
The officially specified Tasks extension polling flow (
tasks/get) and cooperative cancellation (tasks/cancel) cannot be implemented on SDK v2 without bypassing the SDK. We currently work around it by intercepting these two methods at the HTTP layer (routing on theMcp-Methodrequest header before the request reaches the SDK), which we would very much like to delete.Possible directions
hasRequestMethodfor the session.Option 1 or 2 also future-proofs other extensions whose method names were ever part of a historical core revision.
Related: #2189 (Tasks extension tracking), #2188 (extensions framework), #2528 / #2569 (extension notifications in
subscriptions/listen— the notification-side sibling of this request-side gap).