Skip to content

Draft: Task Event Sources#2

Open
LucaButBoring wants to merge 4 commits into
modelcontextprotocol:mainfrom
LucaButBoring:feat/tasks-events
Open

Draft: Task Event Sources#2
LucaButBoring wants to merge 4 commits into
modelcontextprotocol:mainfrom
LucaButBoring:feat/tasks-events

Conversation

@LucaButBoring

Copy link
Copy Markdown

Motivation and Context

Introduces a draft proposal explaining how Tasks and Events can integrate with each other to support intermediate result delivery. Intended to spur discussion for now.

This SEP extends CreateTaskResult (defined by SEP-2663) with an optional eventSource field that associates a task with an event stream from the MCP Events primitive. A client that receives a CreateTaskResult containing eventSource subscribes to the named event using the provided input parameters, receiving intermediate results routed to the model while the task executes in the background. The server terminates the event stream when the task reaches a terminal status.


🤖 Generated by cajoling Claude Code into writing something sensible.

@LucaButBoring LucaButBoring requested a review from a team as a code owner May 27, 2026 22:49

## Motivation

[SEP-2663](https://modelcontextprotocol.io/seps/2663-tasks-extension) provides no mechanism for delivering intermediate output to the model during task execution. The model's only visibility into an in-progress task is the `statusMessage` field — a single optional string observable at poll time. For tasks executing over minutes to hours, the model cannot present partial findings, adjust its reasoning based on what has been discovered, or decide to cancel a task producing irrelevant results.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's two orthogonal things here:

  1. We want tasks to support intermediate results in some form (progress, intermediate artifacts like a single build step completed, etc.)
  2. We want tasks to support server push to reduce latency of information updates (compared to today's only option, which is polling)

I feel integrating with events is a solution to (2), and should be separate from (1). Why? We should support intermediate results without needing events: you should be able to get intermediate results via polling and then events are purely a latency optimisation if the client+server can support the push/webhook events delivery mode.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we might need to take a step back and spec out how we want intermediate results to work for tasks more generally? Is it actually a stream of events, or is it more like a state machine? Events can be lossy whereas a state machine always has a "current state" that you can always retrieve via polling. I can see cases for both, which is why it may be good to explore the problem space a bit more to make sure we have the right abstraction.

@LucaButBoring LucaButBoring May 28, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main property driving this proposal is that events are expected to be delivered to the model, and therefore if those events are not useful context, events should not be used to send them. A task is a state machine that the application needs to poll and only really cares about its current state, while events are a stream that the model needs to observe, as it needs to understand how the underlying work evolves.

Not to mention that the model is generally not supposed to be able to poll a task on-demand directly, at least not as the primary mode of interacting with it. If someone wants that, they can expose a tool that accepts the task ID or similar to return the current state in some form that is meaningful to the model itself (or, if an application developer really wants the agent in the loop at all times, it can be an application-level tool instead).

Comment thread proposals/XXXX-task-event-sources.md Outdated
Comment thread proposals/XXXX-task-event-sources.md Outdated
Comment on lines +78 to +80
"input": {
"taskId": "786512e2-9e0d-44bd-8f29-789f320fe840"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also provide the initial cursor since cursor: null would mean "start from latest" and the client might miss something if it were published between task creation and initial subscribe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding to this - on stateless deployments where Events push mode isn't viable (no persistent push channel) and clients fall back to poll, the initial-cursor requirement is more significant no?

In the time between tools/call --> CreateTaskResult and the client's first events/poll, the round-trip is sadly unavoidable iiuc. So without an initial cursor the client has to either replay-from-zero (if the source is cursored and retains history) or put up with the fact that events emitted in that window are lost (if cursorless). So for tasks that emit a single early event that is of high value (e.g. CI's first stage finishing very fast), the "lost window" case is observable. So may be we could make this concrete and say "TaskEventSource SHOULD carry the cursor the client should resume from. Same shape as the Events primitive's events/subscribe response."

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added: 74b5c76

@panyam

panyam commented May 28, 2026

Copy link
Copy Markdown

One q I wanted to clarify wrt SEP-2663's in-task input flow (TaskElicit / TaskSample) - Both mechanisms trigger (pardon the pun) server-side activity/intent to the client mid-task, but they have opposite semantics. TaskElicit is request-and-await, while eventSource would be fire-and-forget (emit, no response expected, task keeps running). Though these compose orthogonally in principle i think it would worthwhile Worth pinning down in the spec text either as a "see also" or a one-line clarification Something liek "eventSource MUST NOT be used to gather input that the task's continuation depends on - that's inputRequests / tasks/update's contract. Event payloads are observational and they don't pause the task." Otherwise it's easy for an implementer to misuse events as a substitute for TaskElicit and end up with a stuck task whose 'response' never arrives because nothing is listening for it.

@LucaButBoring

LucaButBoring commented May 28, 2026

Copy link
Copy Markdown
Author

@panyam Elicitation is aimed at the end-user rather than the model - is it possible to use eventSource for that unintentionally? However, technically, it is actually be valid for a task to do something stateful like that with the model, albeit somewhat unreliably.

Consider a tool that returns a task with an event source, and that event source's payloads carry the task ID - an additional tool could accept that task ID as a regular parameter (this would be an explicit state handle a la SEP-2567) and do something along the lines of SEP-2669's proposed tasks/steer to asynchronously change/redirect the task in some way.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants