feat(server): Tasks extension at @modelcontextprotocol/server/ext/tasks - #2782
Draft
mattzcarey wants to merge 5 commits into
Draft
mattzcarey wants to merge 5 commits into
mattzcarey wants to merge 5 commits into
Conversation
…-universe gate The inbound and outbound era gates rejected any method name that ever appeared in a past protocol revision's registry but is absent from the current era's registry, even when the consumer explicitly registered a handler (or supplied a schema on send) for it. This made extension methods that reuse a historical core method name unreachable: the Tasks extension (SEP-2663) defines `tasks/get` and `tasks/cancel`, both of which the 2025-11-25 revision used for now-removed core methods, so a 2026-era server could never serve them and a 2026-era client could never send them — every attempt answered -32601 or threw MethodNotSupportedByProtocolVersion before the handler or the transport were ever consulted. Both gates now only apply to TYPED dispatch (setRequestHandler(method, handler) inbound, request(method, options) outbound) — exactly the path the SDK's own built-ins (initialize, ping, logging/setLevel) use, which correctly stays era-gated. A method registered or sent with an EXPLICIT schema (setRequestHandler(method, schemas, handler) / request(request, resultSchema, options)) is the extension-authoring path: the consumer supplied their own validation, so a historical registry collision no longer blocks it. Fixes #2598
🦋 Changeset detectedLatest commit: b04649e 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: |
2 tasks
9 tasks
# Conflicts: # packages/core-internal/src/shared/protocol.ts
TasksExtension is the server side of io.modelcontextprotocol/tasks as a ServerExtension: it advertises the capability, serves tasks/get, tasks/update and tasks/cancel, gates task handles on the client capability (-32021, including through a tools/call override for handles minted outside the extension), and offers tasks.create(ctx) for a tool handler to answer with a task handle. TaskStore (create / get / update / cancel over JSON) is the seam a server implements over its own state and execution; how the work runs is the server's own. InMemoryTaskStore is the in-process reference with a writer handle (status, requireInput, complete, fail, cancel signal). Wire types and zod schemas for the extension's 2026-07-28 schema are exported. Stacked on the server extensions seam and on #2599 (explicit-schema handlers escape the era gate), which tasks/get and tasks/cancel need.
mattzcarey
force-pushed
the
feat/pluggable-task-workflow
branch
from
September 17, 2026 07:24
774c3f9 to
b04649e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
@modelcontextprotocol/server/ext/tasks: the server side of the Tasks extension (io.modelcontextprotocol/tasks, SEP-2663) as aServerExtension.The SDK owns the wire, nothing else.
TasksExtensionadvertises the capability, servestasks/get/tasks/update/tasks/cancelas explicit-schema custom methods, refuses requests that did not declare the extension (-32021), binds tasks to the request principal, and gives a tool handlertasks.create(ctx)to answer with a flatCreateTaskResult(resultType: "task", forwarded verbatim by the 2026 encode seam). Atools/calloverride gates task handles minted some other way (an external engine's own create) on the same capability.The server owns the task.
TaskStoreis four methods over JSON —create,get,update,cancel— that a server implements over whatever holds its task state. How the work behind a task runs (a queue, a workflow engine, a durable-execution runtime) is invisible to the SDK and deliberately unspecified.InMemoryTaskStoreis the in-process reference; it adds a writerhandle(taskId)(status,requireInput,complete,fail, an abortsignal) so a plain async function can drive a task, and records an opaquecontextper task for an execution to pick up.Wire types and zod schemas are authored against ext-tasks
2026-07-28and exported.Tests
packages/server/test/ext/tasks, 10 tests; full server suite 497 green.tasks.e2e.test.ts— a realClientagainst the statelesscreateMcpHandler(freshMcpServerper request, the store as the only shared state): capability advertised, task handle ontools/call, status and completion,input_required→tasks/update→ result, failure, cooperative cancel via the handle signal,-32602unknown task,-32021as a JSON-RPC error fromtasks/*, fromtasks.create, and from thetools/calloverride.inMemoryStore.test.ts— durable create with context, partial answers accumulate and the first answer wins, cancel rejects a pending input wait and later writes are ignored, TTL purge, principal fail-closed, argument validation.SDK interactions worth knowing about
inputResponsesis a reserved multi-round-trip name. The protocol layer lifts it out of every client request's params on the 2026 era and surfaces it atctx.mcpReq.inputResponses.tasks/updateuses the same name, so the handler reads it back from the context (handler-side schema optional, wire schema required). Possibly worth a note in the extension spec.ClientrejectsresultType: "task"ontools/call([v2] Tasks extension: tools/call rejects CreateTaskResult but accepts an omitted discriminator as complete #2637). The e2e test poststools/callraw; every other method goes through the realClient. The requester half is ext-tasks#21 (@modelcontextprotocol/ext-tasks), which has no server side — this is the complementary half.notifications/tasksoversubscriptions/listenis not implemented (subscriptions/listen cannot carry extension notifications (blocks notifications/tasks) #2569); polling only.Design notes
registerTaskwith a replayablestep.do/sleep/elicitAPI and an in-memory execution engine). That is out: execution is the server's concern, and the spec-level story is only the API shape. The workflow layer lives on indurable-mcp-server, which becomes oneTaskStoreimplementation among many.@modelcontextprotocol/serverso extensions have a home next to the server they extend.Refs #2189, #2598, #2637, #2569. Requires #2820 and #2599.