-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Add the SEP-2663 Tasks extension (core) #3005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Kludex
wants to merge
1
commit into
extension-api-sep-2133
Choose a base branch
from
tasks-extension-sep-2663
base: extension-api-sep-2133
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+674
−20
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,24 +1,48 @@ | ||||||
| # tasks | ||||||
|
|
||||||
| Task-augmented execution: a requestor augments a `tools/call` with a `task`, the | ||||||
| receiver returns a `CreateTaskResult` immediately, and the requestor polls | ||||||
| `tasks/get` and retrieves the deferred result. | ||||||
|
|
||||||
| **Status: deferred.** Tasks ship in 2026-07-28 as | ||||||
| [SEP-2663](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/seps/2663-tasks-extension.md), | ||||||
| an `io.modelcontextprotocol/tasks` extension that is wire-incompatible with the | ||||||
| 2025-11-25 in-core design still carried (types-only) in `mcp_types`. The runtime | ||||||
| needs to be built to the SEP — server-decided augmentation (ignoring the legacy | ||||||
| `params.task`), the `{tasks/get, tasks/update, tasks/cancel}` method set, the | ||||||
| `resultType: "task"` envelope, `execution.taskSupport` gating, and `ttlMs` | ||||||
| fields — so it lands in a separate PR with the conformance `tasks-*` scenarios | ||||||
| wired in. | ||||||
| Task-augmented execution (SEP-2663). A client declares the | ||||||
| `io.modelcontextprotocol/tasks` extension; the server may then answer a | ||||||
| `tools/call` with a `CreateTaskResult` (carrying a task id) instead of blocking, | ||||||
| and the client polls `tasks/get` for status and the eventual result. | ||||||
|
|
||||||
| ## Run it | ||||||
|
|
||||||
| ```bash | ||||||
| # stdio (default — the client spawns the server as a subprocess) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: README run instructions imply stdio demonstrates tasks flow, but stdio cannot negotiate the tasks extension. Users running the default command will not see the documented task behavior. Prompt for AI agents
Suggested change
|
||||||
| uv run python -m stories.tasks.client | ||||||
|
|
||||||
| # HTTP — the client self-hosts the server on a free port, runs, then tears it down | ||||||
| uv run python -m stories.tasks.client --http | ||||||
| ``` | ||||||
|
|
||||||
| ## What to look at | ||||||
|
|
||||||
| - `server.py` `MCPServer("tasks-example", extensions=[Tasks(default_ttl_ms=...)])` — | ||||||
| opt in at construction. The extension advertises `io.modelcontextprotocol/tasks` | ||||||
| and serves `tasks/get` and `tasks/cancel`. | ||||||
| - `mcp.server.tasks.Tasks.intercept_tool_call` — the server DECIDES augmentation; | ||||||
| the legacy `params.task` field is ignored. It augments only for a client that | ||||||
| declared the extension on the request, returning a flat `CreateTaskResult` | ||||||
| (`resultType: "task"`). | ||||||
| - `client.py` `Client(target, extensions={EXTENSION_ID: {}})` — declaring the | ||||||
| extension is what lets the server defer; `main` then reads the `CreateTaskResult` | ||||||
| and polls `tasks/get`, whose completed `DetailedTask` inlines the original | ||||||
| `CallToolResult`. | ||||||
|
|
||||||
| ## Scope | ||||||
|
|
||||||
| This is the SEP-2663 conformant *core*. The tool runs to completion inline (so a | ||||||
| task is observed as `completed` immediately), and the store is in-memory. Deferred | ||||||
| to follow-ups, each needing deeper SDK plumbing: `tasks/update` + the MRTR | ||||||
| `input_required` loop, `ToolExecution.taskSupport` gating with the `-32021` | ||||||
| required-task error, `notifications/tasks`, and SEP-2243 task routing headers. | ||||||
|
|
||||||
| ## Spec | ||||||
|
|
||||||
| [SEP-2663 — Tasks extension](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/seps/2663-tasks-extension.md) | ||||||
| [SEP-2663 — Tasks extension](https://modelcontextprotocol.io/seps/2663-tasks-extension.md) | ||||||
| · [SEP-2133 — extensions capability](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2133) | ||||||
|
|
||||||
| ## See also | ||||||
|
|
||||||
| `apps/` (the additive half of the extension API). | ||||||
| `apps/` (the additive half of the extension API), | ||||||
| `custom_methods/` (a non-spec method without an extension). | ||||||
Empty file.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| """Declare the tasks extension, let the server defer a tool call, then poll tasks/get. | ||
|
|
||
| The client declares `io.modelcontextprotocol/tasks` (via `Client(extensions=...)`), | ||
| so the server is free to answer `tools/call` with a `CreateTaskResult`. `Client` | ||
| exposes only spec verbs, so the augmented call and `tasks/get` drop to | ||
| `client.session`; the thin `_send` helper keeps that out of the story below. | ||
| """ | ||
|
|
||
| from typing import Any, Literal, cast | ||
|
|
||
| import mcp_types as types | ||
| from pydantic import TypeAdapter | ||
|
|
||
| from mcp.client import Client, ClientSession | ||
| from mcp.server.tasks import EXTENSION_ID, GetTaskRequestParams | ||
| from stories._harness import Target, run_client | ||
|
|
||
| _RAW: TypeAdapter[dict[str, Any]] = TypeAdapter(dict) | ||
|
|
||
|
|
||
| class _GetTaskRequest(types.Request[GetTaskRequestParams, Literal["tasks/get"]]): | ||
| method: Literal["tasks/get"] = "tasks/get" | ||
| params: GetTaskRequestParams | ||
|
|
||
|
|
||
| async def _send(session: ClientSession, request: types.Request[Any, Any]) -> dict[str, Any]: | ||
| """Send a request whose result has a non-spec (extension) shape; return the raw dict.""" | ||
| return await session.send_request(cast("types.ClientRequest", request), cast("Any", _RAW)) | ||
|
|
||
|
|
||
| async def main(target: Target, *, mode: str = "auto") -> None: | ||
| async with Client(target, mode=mode, extensions={EXTENSION_ID: {}}) as client: | ||
| # The extension is a modern-only capability negotiated over server/discover. | ||
| # A legacy connection (today's stdio) cannot carry it, and the server then | ||
| # must not augment, so the task flow only runs once it is negotiated. | ||
| if client.server_capabilities.extensions is None: | ||
| return | ||
| assert client.server_capabilities.extensions == {EXTENSION_ID: {}} | ||
|
|
||
| # The server augments this tools/call into a task because we declared the extension. | ||
| call = types.CallToolRequest( | ||
| params=types.CallToolRequestParams(name="render_report", arguments={"title": "Q3", "sections": 2}) | ||
| ) | ||
| created = await _send(client.session, call) | ||
| assert created["resultType"] == "task", created | ||
| task_id = created["taskId"] | ||
|
|
||
| task = await _send(client.session, _GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))) | ||
| assert task["status"] == "completed", task | ||
| assert task["result"]["content"][0]["text"].startswith("# Q3"), task | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| run_client(main) |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| """Tasks (SEP-2663): the server defers a tool call as a task the client polls. | ||
|
|
||
| `Tasks` is an opt-in `Extension`. The server decides, per request, to return a | ||
| `CreateTaskResult` instead of a `CallToolResult` for a client that declared the | ||
| `io.modelcontextprotocol/tasks` extension; the client then polls `tasks/get` for | ||
| status and the eventual result. `render_report` is the kind of slower, multi-step | ||
| tool a caller would rather run as a task than block on. | ||
| """ | ||
|
|
||
| from mcp.server.mcpserver import MCPServer | ||
| from mcp.server.tasks import Tasks | ||
| from stories._hosting import run_server_from_args | ||
|
|
||
|
|
||
| def build_server() -> MCPServer: | ||
| mcp = MCPServer("tasks-example", extensions=[Tasks(default_ttl_ms=60_000)]) | ||
|
|
||
| @mcp.tool(description="Render a multi-section report for the given title.", structured_output=False) | ||
| def render_report(title: str, sections: int) -> str: | ||
| body = "\n".join(f"## Section {n}\n(generated)" for n in range(1, sections + 1)) | ||
| return f"# {title}\n\n{body}" | ||
|
|
||
| return mcp | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| run_server_from_args(build_server) |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: Docs are inconsistent: the Tasks story is documented as implemented/runnable here, but the stories index still labels
tasks/as "not yet implemented".Prompt for AI agents