| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Moving discussion from #2557 over here @CaitieM20 @markdroth @Randgalt @kurtisvg @localden @pja-ant @dsp-ant @maxisbey @maciej-kisiel @ylxlpl (Tagged everyone who commented on #2557) |
Sorry, something went wrong.
|
Thanks for putting this together, @LucaButBoring - I'll post the comment that I was typing earlier in #2557 and let you validate how much of this is still relevant. Some notes beyond the other bits I called out in the review. There's a few places where I think the SEP is a little underspecified:
A couple of schema regressions I noticed too:
|
Sorry, something went wrong.
|
@localden Thanks for the feedback, going through this:
This revision limits resultType: "task" to CreateTaskResult to avoid any ambiguity, noticed that issue while rewriting this. GetTaskResult was always flat, the distinction was that we made CreateTaskResult nested at the last minute in 2025-11-25 to allow switching on it. That nesting is a holdover from before we had resultType, so we can actually flatten CreateTaskResult, too. edit: updated
This revision does require keys to be unique over the lifetime of a task, and not reused between distinct requests.
Yup, that's how tasks/update works in this revision.
I struck out that phrasing in this revision, now it can actually be either, as tasks/update is eventually-consistent - but the new key uniqueness constraint means that this is fine from the client's perspective, now.
To deal with that, in this revision, tasks/cancel no longer has any result (and is also eventually-consistent, like tasks/update).
A TTL in integer seconds makes sense, but I'm not sure if a polling interval in integer seconds does - 500ms would be a reasonable polling interval for a relatively quick, but high-variance (1s-20s) task. A duration is probably better-expressed with units included in the value (e.g. "500ms"), but that would be nonstandard for us - I suppose I could name it pollIntervalMilliseconds, but that feels awkward and inconsistent in its own right, since nothing else includes units in the field name so far. edit: updated to include units in the field names
Noted, I'll update the phrasing here - it actually doesn't really mean the MCP server fell over either, the literal intent is just that if the inner request returns a JSON-RPC error, that's failed, and in every other case (including a tool call with isError: true), that's completed. edit: updated
I don't think there's an inconsistency here? requestState is already on the request shape for tasks/get - the requirement is that the client echoes whatever the server gives it. So, in the case where the full task record is in requestState, the server would return the initial value in CreateTaskResult, the client would pick that up, and then it would echo it in tasks/get, maintaining the full record through that flow. edit: updated, I misinterpreted this - noted here
Ah, I missed that on #2557 - I'll make sure this is handled correctly when I write the schema changes here. |
Sorry, something went wrong.
|
This is great, allows integration of various organizational extensions. |
Sorry, something went wrong.
The option space is:
IMO:
I agree that (4) is non-standard, but IMO we just make it the standard starting now and make sure that TTL lists also adopts this standard. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This SEP defines an extension that allows a server respond to a tools/call request with an asynchronous task handle instead of a final result, allowing the client to retrieve the eventual result by polling. The extension introduces three methods: tasks/get, tasks/update, and tasks/cancel; a polymorphic-result discriminator (resultType: "task"); and a Task shape that carries a task status, in-progress server-to-client requests, and a final result or error. Task creation is server-directed: the client signals support by including the extension in its per-request capabilities, and the server decides on a per-request basis whether to materialize a task.
Tasks will become a foundational building block of MCP and are expected to be supported in future protocol versions. The experimental tasks feature in the 2025-11-25 specification served as a stopgap until the protocol's extension mechanism was available. Now that extensions have been formalized, moving tasks to an official extension gives the feature time to incubate and evolve based on additional real-world implementation feedback, without being constrained by the core specification's release cadence. Once the extension has stabilized and achieved broad adoption, it is intended to be promoted into the core protocol.
This proposal removes the version of tasks specified in the 2025-11-25 release. It is shaped by implementation feedback since that release and by several changes to the base protocol expected to arrive in the 2026-06-30 specification:
Motivation and Context
The experimental tasks feature served as an alternate execution mode for tool calls, elicitation, and sampling, allowing receivers to return a poll handle instead of blocking until a final result was ready. Implementation experience surfaced several challenges:
The handshake is fragile. Tasks today expose method-level capabilities (tasks.requests.tools.call declares that tools/call MAY be task-augmented) alongside a tool-level execution.taskSupport field that declares whether a particular tool will accept the augmentation. Clients express their own support for tasks by passing a task parameter on their requests, but MUST NOT include it if the method/tool does not support tasks. A client that wants to opt into tasks must therefore prime its state with a tools/list call before issuing any task-augmented request, and cannot blindly attach a task parameter to every request to handle tools isomorphically. This is confusing, implicit, and easy to get wrong.
tasks/result is a blocking trap. In the current flow, a client that observes input_required is required to call tasks/result prematurely so that the server has an SSE stream on which to side-channel elicitation or sampling requests. tasks/result then blocks until the entire operation completes. This forces long-lived persistent connections that many clients and servers do not want to implement, and it conflicts with SEP-2260, which disallows unsolicited server-to-client requests outright. Under SEP-2260, the SSE semantics that justified the blocking behavior no longer apply.
tasks/list scoping cannot be defined. To avoid clients cancelling or retrieving results for tasks they shouldn't have access to, all tasks should be bound to some sort of "authorization context," the implementation of which is left to individual servers according to their existing bespoke permission models. However, in many cases, it is not possible to perform this binding, in which case the task ID becomes the only line of defense against contamination. In this scenario, it is unsafe for a server to support tasks/list at all. While it was possible for tasks to instead be bound to a session, SEP-2567 removes sessions from the protocol. There is no other natural scope a server can define unilaterally — task IDs can be unguessable handles that a server can recognize one at a time, but servers cannot reliably correlate two unrelated handles to the same caller without additional state.
Beyond implementation challenges, tasks face another structural issue: Client-hosted tasks are no longer expressible. SEP-1686 permitted clients to host tasks for elicitation and sampling, in part to avoid coupling tasks to tool calls. SEP-2260 makes any unsolicited server-to-client request invalid; every server-to-client polling request under client-hosted tasks would be unsolicited by definition.
This proposal intends to solve the above issues by redesigning certain aspects of the feature and moving tasks out to an official extension. Redefining tasks as an official extension gives the feature more time to incubate and evolve independently of the core specification, promoting adoption. As part of the redesign, this proposal consolidates the polling lifecycle into tasks/get and a new tasks/update to remove the blocking tasks/result method. The redesign allows servers to return tasks unsolicited (in response to ordinary, non-task-flagged requests) to eliminate the per-request opt-in and the tools/list warmup, relying instead on the extension capability as the single handshake point. Finally, this proposal removes client-hosted elicitation and sampling tasks in compliance with SEP-2260.
How Has This Been Tested?
Conformance test suite: modelcontextprotocol/conformance#262
Breaking Changes
Described in proposal.
Types of changes
Checklist
Additional context
Supersedes #2557.
AI Use Disclosure: The extension SEP document in this PR was initially drafted using claude.ai with the previous iteration as a reference. I rewrote/rephrased many sections myself and verified its correctness, using claude.ai as a reviewer to iteratively scrub out issues.