modelcontextprotocol / modelcontextprotocol/typescript-sdk

v2: extension methods shadowed by legacy spec-method registry — custom tasks/get & tasks/cancel handlers unreachable (-32601 before handler lookup)

Open
#2,598 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs design spec-2026-07-28 v2
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

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
  1. 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).
  2. Or: let explicitly registered custom handlers take precedence over the spec-method gate.
  3. 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).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/core-internal/src/shared/protocol.ts and packages/core-internal/src/wire/codec.ts, then reproduce the contrasting tasks/update and tasks/get behavior from the issue. Trace the request gate and handler lookup, and validate the chosen fix with coverage showing registered tasks/get and tasks/cancel handlers are reachable while the extension session remains supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.