Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions meetings/2026-01-21_weekly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 2026-01-21 Transports WG (Weekly)

## TL;DR
The group reached a consensus to move forward with deprecating unsolicited
server-to-client requests (sampling and elicitations) on the "get stream" to
simplify protocol design and address security concerns. This decision was
supported by data showing negligible usage in existing repositories. Mark Roth
presented a "Multi-Roundtrip Request" proposal designed to replace the current
SSE-based elicitation flow with a stateless, request-response model where
clients echo back state. The group discussed the technical implications of this
change. Finally, the group identified a need to reconcile this new proposal with
the existing "Tasks" mechanism and will invite the Tasks author (Luca) to the
next meeting.

## Detailed Summary

### Deprecation of Unsolicited Requests
* **Proposal to Remove:** Shaun Smith proposed assuming that unsolicited
sampling and elicitations (server-to-client requests) are removed from the
session track. He argued that eliminating these unsolicited communications
resolves complex state transfer issues regarding cookies and session IDs.
* **Usage Data:** Peter Alexander shared analysis results from scanning several
thousand MCP repositories, finding only one instance of unsolicited sampling
usage (which belonged to Shaun Smith).
* **Decision:** The group agreed to draft a Specification Enhancement Proposal
(SEP) to formally deprecate unsolicited requests on the /GET stream.
* **Scope:** The SEP will specifically disallow servers from sending sampling or
elicitation requests without an associated client request ID.

### Technical Decisions: Multi-Roundtrip Requests
* **Problem Statement:** Mark Roth outlined the inefficiency of the current
elicitation flow, where a server opens an SSE stream for elicitation, forcing
the client to make a completely independent request that must be manually
linked back to the original context by the server.
* **Proposed Stateless Flow:** Mark presented a new pattern where the server
immediately terminates the initial request with a response indicating "more

Choose a reason for hiding this comment

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

I need to think through this more, but this execution path could also be an answer to both the ask for supporting immediate result acceptance in Tasks (modelcontextprotocol/modelcontextprotocol#1905) and being able to "surprise" a client with a Task - implementing this flow would potentially require some sort of dynamic response handling in a way that Tasks don't currently require, in that the client needs to not be waiting for a specific result type. Not certain yet though, just throwing the idea out there right now.

information needed." The client then sends a completely new request containing
the original information plus the elicitation response.
Comment on lines +35 to +38
Copy link
Member

Choose a reason for hiding this comment

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

Did anyone discussed about how this would be implemented? This doesn't seem easy to implement gracefully.

As a developer, I want to write my code once, with a certain logical flow, and if there's an elicitation, I expect to resume on that line. But with this flow, how is the code supposed to happen? Halt the execution at the point, and continue on that point on a different process doesn't seem a good idea.

Choose a reason for hiding this comment

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

This would be a pretty fundamental change to the tool authoring model for all developers, and would almost certainly be a big breaking change (I also brought this up last month when it was discussed in SFO). With that said, I think it might be possible to shim existing code onto it by essentially "replaying" tool calls on each re-attempt.

Say you have something like this in a tool:

1: Begin tool call
2: Validate arguments
3: Send elicitation
4: Receive elicitation results
5: Send tool call result

If any step yields to the client, the entire sequence needs to be repeated from the beginning on the next tool call, because we assume (in general) that servers retain no state. The client makes request 1, and we get to step 3 (+ means the line was executed):

+ 1: Begin tool call
+ 2: Validate arguments
+ 3: Send elicitation
  4: Receive elicitation results
  5: Send tool call result

...and then the server rejects with (some equivalent of) an elicitation request. Then, the client makes request 2 (with an elicitation response squashed into the request this time), and we execute steps 1-3 again, except that at step 3, the server already has the elicitation response and continues to step 5:

++ 1: Begin tool call
++ 2: Validate arguments
++ 3: Send elicitation
+  4: Receive elicitation results
+  5: Send tool call result

This isn't perfect, though, and it'd be safer with a state store of some kind to be able to somehow snapshot the request state after request 1. In my example above (which is not representative of what Mark has in mind), I'm assuming that everything before sending the elicitation is idempotent, however. In reality, it may not be.

Choose a reason for hiding this comment

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

Though actually, I think that my own idea here would conflict directly with how Tasks work, which could complicate things. In general, almost nothing you would want to wrap in a Task is naturally-idempotent like this, unless you choose to front-load elicitation/sampling and have all non-idempotent logic occur after receiving the responses (and typically sampling is used at the end of an operation).

Copy link
Member

Choose a reason for hiding this comment

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

Your speculation is what I've thought, which doesn't seem a good idea. And as you said in your own message at the end, the user may have a different execution by their own logic, so replying the steps will not be safe.

* **State Management (Hidden Fields vs. Cookies):**
* The group debated how to handle state persistence between the terminated
request and the retry.
* Gabriel Zimmerman and Simon Russell argued against using HTTP cookies,
suggesting a "hidden field" mechanism (opaque blob) is more appropriate
for per-tool-call state that should not persist globally.
* Mark Roth agreed, noting that cookies fail when multiple tool calls run in
parallel within the same session.
* **SDK Impact:** Peter Alexander and Simon Russell acknowledged that while
switching to this model will be painful for SDK maintainers to implement
initially, the resulting architecture is significantly easier to maintain and
conceptually simpler.

### Interaction with Tasks
* **Overlap Analysis:** The group identified that the "Tasks" mechanism
currently duplicates some functionality discussed in the multi-roundtrip
proposal, specifically regarding polling and state retention.
* **Unified Vision:** Caitie McCaffrey noted that Tasks and Tool Calls are
similar but necessary; she described Tasks as a "promise over a network" and
suggested that the handling of elicitation responses in both systems needs to
align.
Comment on lines +53 to +59

Choose a reason for hiding this comment

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

The tricky piece here (I think) is that it becomes ambiguous how the "more information needed" rejection works when something is fragmented across multiple discrete polling requests. It makes sense that (for example) if an elicitation is required when creating a task, it would be handled exactly like a normal tool call and the creation step is what would "pause," but if an elicitation is required after that it would need to be delivered some other way.

It should technically be fine to do the rejection on a polling request, but then that becomes stateful in an awkward orthogonal way to how multi-roundtrip is intended to work. In particular, you can't just replay or snapshot an entire Task's state (consider that it's often offloaded to another service altogether), so this puts you in a tangled state situation where it's unclear where the elicitation/sampling result actually needs to go. I'll need to work through some examples to figure this out, I think.

* **Next Steps:** The group agreed to invite Luca (author of Tasks) to the next
meeting to deeply dive into the relationship between the new stateless
proposal and the existing Tasks implementation.

## Action Items
- [ ] **Shaun Smith** to write a Specification Enhancement Proposal (SEP) to
deprecate unsolicited server-to-client requests (sponsored by Peter
Alexander).
- [ ] **Mark Roth** to post the Multi-Roundtrip Request proposal to GitHub as a
Pull Request for community feedback.
- [ ] **Caitie McCaffrey** to write a strawman proposal detailing the overlap
between "Tasks" and the current multi-roundtrip proposal.
- [ ] **Kurtis Van Gent** to invite Luca to the next meeting to discuss the
relationship with tasks.