Skip to content

SEP-1686 (experimental): Implement Tasks #626

Open

Description

@findleyr

Add support for SEP-1686, when it lands.

Activity

  1. jba commented on Nov 13, 2025

    @jba
    Contributor

    Tasks pose a challenge for backwards compatibility. A tool call can now return its usual result, or a CreateTaskResult.

    One way to deal with this would be to have separate functions and types on the server for registering a tool that might be a task. The handler's return type would be some union of CallToolResult and CreateTaskResult. We'd also need a separate function on the client.

    A better idea, although it's a bit ugly, is to add a CreateTaskResult field to CallToolResult. When a tool is called as a task, it sets this field and leaves all the others empty. Callers of ClientSession.CallTool would have to recognize that state. Maybe we could have ClientSession.CallToolAsTask that returns a CreateTaskResult – no union type is needed on the client side. But that method would not be able to mark the call as being a task call: that bit is set in the call's params (arguments), not its metadata (see https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1732/files#r2524912400 for my failed pushback). In our SDK, CallToolParams.Arguments is an any, so we can't easily look at it. (One hideous possibility: we require that the arguments marshal to JSON, so we could marshal, unmarshal into a map[string]any, and set the task info.) We may be able to do better on the server side, recognizing that a call is a task call, because we already unmarshal into a map[string]any.

  2. LucaButBoring commented on Nov 21, 2025

    @LucaButBoring

    Relaying here from #general-sdk-dev:

    There doesn't seem to be a central ticket for all SDK implementations of a particular change, so sharing this here for SEP-1686 -- the following revisions have been merged on top of the base specification PR for tasks:

    • Clarify request lifetimes in task diagrams: PR
    • Fix capability phrasing in spec: PR
    • Remove an incorrectly-added field from notifications/cancelled: PR
    • Adjust schema to better-communicate where tasks are valid: PR
    • Rename/move annotations.taskHint to execution.taskSupport, tweak its enum values, and change a SHOULD to a MUST for the behavior when this is declared:
    • GetTask polling clarification: PR
    • SSE stream management clarification: PR

    The actual functional changes should be limited to taskHint, but the rest should help clarify some details around polling.

    To save the trouble of going through each of the above PRs one by one, you can also go to the file on the "Files changed" tab here, searching for docs/specification/draft/basic/utilities/tasks.mdx, and then using the "Display the rich diff" button, as shown:
    Image

  3. LucaButBoring commented on Nov 24, 2025

    @LucaButBoring

    One very last change (we're doing a hard cutoff on additional changes here): adding a lastUpdatedAt to the Task object: modelcontextprotocol/modelcontextprotocol#1875

  4. findleyr commented on Nov 25, 2025

    @findleyr
    ContributorAuthor

    Given the number of changes here, and the fact that this is still experimental, we probably won't get to this until next week. My hope is that it still makes it into the next release.

  5. added this to the v1.2.0 milestone on Nov 25, 2025
  6. He-Pin commented on Nov 29, 2025

    @He-Pin
  7. findleyr commented on Dec 10, 2025

    @findleyr
    ContributorAuthor

    Unfortunately, I don't think this is going to make v1.2.0, for which we want a prerelease this week.

    Since this is an experimental feature, that may be acceptable. However, we should land this in master shortly after cutting the release.

  8. modified the milestones: v1.2.0, v1.3.0 on Dec 10, 2025
  9. removed this from the v1.3.0 milestone on Jan 26, 2026
  10. changed the title [-]SEP-1686: implement Tasks[/-] [+]SEP-1686 (experimental): Implement Tasks[/+] on Feb 12, 2026
  11. IgorKasianenko commented on Aug 11, 2026

    @IgorKasianenko

    #755 has had no activity since Feb 18 (~6 months) — per the project's own timeout policy, treating this as stalled rather than in-progress.

    Following @jba's suggestion in #755 to split this into smaller PRs instead of one big one: proposing to start with capability negotiation only (declaring task support, no execution yet), as a first mergeable step. Before writing code — the open design question from #755 still needs an answer: how does a tool call transparently become a task, and who owns lifecycle management once it does? Happy to draft options for that here if useful.

    @JoannaaKL — if you're still planning to continue #755, happy to collaborate instead of duplicating. Otherwise I'll open a fresh PR scoped to capabilities only.

  12. JoannaaKL commented on Aug 14, 2026

    @JoannaaKL

    Hey folks sorry for dropping the work on this one. I am happy to continue working on #755 and descope it to only capability negotiation as suggested 😄

  13. blakebauman commented on Aug 15, 2026

    @blakebauman

    A concrete, proxy-visible consequence of this being unimplemented, in case it is useful for scoping.

    mcp.Tool has no field for the tool-level execution block, so a tool's declared task support does not survive a decode/encode round trip through the SDK. Observed with a TypeScript server (@modelcontextprotocol/sdk 1.29) behind a Go SDK-based gateway:

    // direct from the upstream
    {"name":"get-time", "execution":{"taskSupport":"forbidden"}, ...}
    
    // the same tool, proxied through a Go SDK client + server
    {"name":"get-time", ...}   // execution dropped

    For an application this is invisible. For anything that re-serializes tools — gateways, proxies, aggregators, caches — it is silent data loss on a field a client may reasonably act on, and the loss is not detectable from the Go side since the field never appears in the struct.

    Not asking for the task lifecycle itself to be prioritized: just noting that modeling the tool-level execution field (or preserving unknown members on Tool) fixes the pass-through case independently of implementing tasks, if the two can be separated.

  14. chaholl commented on Oct 2, 2026

    @chaholl

    A client-side consequence of the missing execution field, in addition to the proxy case above.

    MCP 2025-11-25 says a client that doesn't do task-augmented calls must not offer a taskSupport: "required" tool as a plain tool, because the server answers every plain call with an error; server-everything's simulate-research-query returns -32601 ... requires task augmentation. Because mcp.Tool drops execution when the tools/list result is decoded, and the result arrives already typed, no client middleware can recover it. So a go-sdk client can't filter these tools, and they reach the model as callable tools that always fail.

    Adding Execution *ToolExecution (with TaskSupport string) to Tool would fix this independently of implementing the task lifecycle.

    Context: found while moving PromptKit's MCP client onto go-sdk (AltairaLabs/PromptKit#2141).

  15. MehdiMamas commented on Oct 3, 2026

    @MehdiMamas

    I'd like to help move this forward on top of #755, following the split @jba suggested there (capabilities first, then the task data structure and ID generation). Two small pieces that leave the open lifetime question alone:

    Task type and task ID generation, from the tasks extension: taskId, status, statusMessage, createdAt, lastUpdatedAt, ttlMs, pollIntervalMs, plus an unexported generator using crypto/rand.Text (the spec requires unguessable IDs). Stacked on #755 as a draft PR until it lands. No CreateTaskResult, no tasks/* methods, no change to how a tool call becomes a task.
    Tool.Execution with TaskSupport, as a separate PR on main, so execution.taskSupport from 2025-11-25 servers survives decoding (the proxy and client cases @blakebauman and @chaholl described above). It only preserves the field; nothing acts on it.
    Both add exported API, so I'm flagging them here first. Happy to open a separate proposal issue if you'd prefer. @JoannaaKL, I don't want to step on your next steps, so tell me if either overlaps with what you're planning.

    My use case is github/github-mcp-server#3406 (watch_workflow_run), which would return a task once the SDK can serve one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready for workHas enough information to start

    Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions