Skip to content

SEP-2322: Multi Round-Trip Requests #2322

Open
CaitieM20 wants to merge 56 commits intomodelcontextprotocol:mainfrom
CaitieM20:merge
Open

SEP-2322: Multi Round-Trip Requests #2322
CaitieM20 wants to merge 56 commits intomodelcontextprotocol:mainfrom
CaitieM20:merge

Conversation

@CaitieM20
Copy link
Copy Markdown
Contributor

This is a draft SEP for Multi Round-Trip Requests (MRTR) from the Transports Working Group. It is one of the changes discussed and planned at the December 2025 Core Maintainer Meetup. Exploring the Future of MCP Transports Blog

This SEP Is still in draft, Schema changes are proposed but not locked, documentation updates, conformance tests and additional work still to come, but its ready to move out of Transports Working Group and open up to broader feedback.

Authors: Mark D. Roth (@markdroth), Caitie McCaffrey (@CaitieM20), Gabriel Zimmerman (@gjz22)

Motivation and Context

See SEP for details.

How Has This Been Tested?

Not tested yet, in progress work

Breaking Changes

Yes, see SEP for details

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally - Work In Progress
  • I have added appropriate error handling - Work In Progress
  • I have added or updated documentation as needed - Work in Progress

Additional context

CaitieM20 and others added 30 commits February 20, 2026 10:36
…uestParams — now available on any

  client-initiated request, not just tool calls
   2. Added typed InputResponse = ElicitResult | CreateMessageResult — InputResponses values are now properly typed
  instead of { result: { [key: string]: unknown } }
   3. Updated examples — removed the extra result wrapper from TaskInputResponseRequest and
  TaskInputResponseRequestParams examples to match the new typed schema

  All checks pass: ✅ TypeScript compiles, ✅ JSON schema up to date, ✅ 140/140 examples valid.

Caitie Note: Need to take a look at task-input-response-params.json looks wrong on cursory inspection.
…itRequets these should only be sent as part of IncompleteResponse Objects now
…a JSONRPCIncompleteResultResponse and add examples
…pper in the SEP because InputRequests maps keys directly to the requests so the InputResponses now follows that pattern and there is no benefit to the added wrapper. The only argument for the wrapper would be if we wanted to carry error's alongside results but we do not in these cases. If there is an error the client would retry until the necessary information was retrieved and then send back to the server.
@gjz22
Copy link
Copy Markdown

gjz22 commented Mar 12, 2026

...

I'd propose explicitly making graceful degradation the policy in this case: old clients can still connect to new servers and use tools normally, but elicitation and sampling aren't available until the client upgrades. The impact of this is scoped to tools that actually use server-initiated requests - I believe the vast majority of tools are simple request/response and would be completely unaffected.

This can be handled in coupled way with what SEP-1442 proposes and can be linked to the backwards compatibility of that SEP. If a server does not receive the protocol version in a tools/list request as required in SEP-1442, then it can assume the client is an old client and respond omitting any elicitation or sampling tools (or even tools that might want to work in concert with those tools)

@kurtisvg
Copy link
Copy Markdown
Contributor

Here are the vote after discussion in 3/4 Core Maintainer's meeting:

  • SEP-2322: Multi Round-Trip Requests
    • Outcome: Accept w/ changes
    • Votes: 🟢 1 | 🟡 7 | 🔴 0

Next steps are to have a reference implementation in one of the SDKs (TS/Python) and collect feedback from SDK maintainers on it.

@CaitieM20
Copy link
Copy Markdown
Contributor Author

Responding to the Python / TypeScript SDK questions @pcarleton @maxisbey @pja-ant @felixweinberger also adding in @halter73 from C#

1. If server or client implementers upgrade the SDK, will their code still compile/work?

I'd argue this is a non-goal. The code changes for tool authors are small and mechanical (see the migration demos in typescript-sdk#1597 or even the python snippet right here). This is the kind of migration that can be documented in a migration.md and automated with AI tooling. Trying to keep this non-breaking adds permanent complexity to the SDKs for a one-time migration cost.

Agree here. Perhaps we just launch with a Skill that can help do the upgrade.

2. New client → old server: what happens?

This seems mostly manageable. The client SDK keeps support for the existing SSE-based elicitation flow alongside the new MRTR path, and uses capability negotiation to determine which to use. It's a bit unfortunate to carry the legacy code, but it's contained to the client SDK (not tool authors) and can be deprecated on a timeline tied to a protocol version bump.

We should make sure the Client SDKs have an option to NOT support the old version. For example we have several Cloud Hosted Clients that can't support elicitations & sampling today because of the requirement for SSE streams. It would be great if we could make this a config parameter to say I only support MRTR elicitation/sampling/listRoots, etc...

3. Old client → new server: what happens?

This is the hard one, and I think it's fundamentally unresolvable. The whole motivation for MRTR is enabling horizontally scaled servers without sticky sessions or long-lived SSE connections. An old client requires those things for elicitation - it expects the server to hold an SSE stream open and send elicitation requests on it. A new server that's deployed for horizontal scalability can't provide that by definition.

We could build a server-side SDK shim that translates new-style tool code back to the old SSE flow, but that only works if the server can actually hold SSE connections - which kind of defeats the purpose of adopting MRTR in the first place. It makes maintenance of such servers more complicated, not less.

I'd propose explicitly making graceful degradation the policy in this case: old clients can still connect to new servers and use tools normally, but elicitation and sampling aren't available until the client upgrades. The impact of this is scoped to tools that actually use server-initiated requests - I believe the vast majority of tools are simple request/response and would be completely unaffected.

How much of this can be handled via version negotiation vs capabilities negotiation. If an MCP server is on the June 2026 release and the client doesn't support it then I think they are fundamentally incompatible? Do we forsee any issues with the proposal above which is essentially saying I support a mixed version?

One potential way to solve this is to add new capabilities elicitation-mrtr, sampling-mrtr, etc... This would allow clients to specify what versions of these features they support. They could support both, one, or none.

@felixweinberger
Copy link
Copy Markdown
Contributor

felixweinberger commented Mar 18, 2026

3. Old client → new server: what happens?

...

How much of this can be handled via version negotiation vs capabilities negotiation. If an MCP server is on the June 2026 release and the client doesn't support it then I think they are fundamentally incompatible? Do we forsee any issues with the proposal above which is essentially saying I support a mixed version?

One potential way to solve this is to add new capabilities elicitation-mrtr, sampling-mrtr, etc... This would allow clients to specify what versions of these features they support. They could support both, one, or none.

I think the problem isn't version or capability negotiation, it's that the new and old forms of elicitation/sampling are fundamentally incompatible because of the differing deployment model between MRTR and session-based requests. And that makes it very hard for SDKs to build in a way that e.g. we support both 2025-11 and 2026-06 elicitation and sampling at the same time on the same server.

Let's say we have a server that advertises it supports both the 2025-11 and 2026-06 specs. It has tools that use elicitation. In theory, when a 2025-11 client connects, it should use SSE. If a 2026-06 client connects, it should use MRTR. Now, there's a scenario that's fundamentally unresolvable in the matrix below (lower left quadrant) if the server is deployed in a way to take advantage of statelessness.

Server Infra 2025-11 client 2026-06 client (with MRTR)
Can hold SSE ⚠️ SDK shim: clunky but theoretically possible - SDK emulates the retry loop internally while connecting to client over SSE, but I'd argue for graceful degradation instead ✅ Just use MRTR directly
MRTR-only (horizontally scaled) ❌ Tool fails - no SDK shim is possible for this case, as the SSE connection infrastructure on the server side just doesn't exist ✅ Just use MRTR directly

The key problem is that no form of capability negotiation can solve the bottom left quadrant. So one resolution could be for servers that support the 2025-11 and 2026-06 spec at the same time, they should build with MRTR and not use elicitation / sampling if the negotiated version is 2025-11. I believe one solution to this would be an addition to the spec in the form:

A server MUST NOT return IncompleteResult to a client that negotiated a protocol version earlier than 2026-06.

Which implies that for elicitation and sampling the server developer has to make an explicit decision to either (A) support both SSE and MRTR (and implement the infra for it somehow) or (B) not use elicitation or sampling with pre-2026-06 clients.

@felixweinberger
Copy link
Copy Markdown
Contributor

3. Old client → new server: what happens?

...

How much of this can be handled via version negotiation vs capabilities negotiation. If an MCP server is on the June 2026 release and the client doesn't support it then I think they are fundamentally incompatible? Do we forsee any issues with the proposal above which is essentially saying I support a mixed version?
One potential way to solve this is to add new capabilities elicitation-mrtr, sampling-mrtr, etc... This would allow clients to specify what versions of these features they support. They could support both, one, or none.

I think the problem isn't version or capability negotiation, it's that the new and old forms of elicitation/sampling are fundamentally incompatible because of the differing deployment model between MRTR and session-based requests. And that makes it very hard for SDKs to build in a way that e.g. we support both 2025-11 and 2026-06 elicitation and sampling at the same time on the same server.

Let's say we have a server that advertises it supports both the 2025-11 and 2026-06 specs. It has tools that use elicitation. In theory, when a 2025-11 client connects, it should use SSE. If a 2026-06 client connects, it should use MRTR. Now, there's a scenario that's fundamentally unresolvable in the matrix below (lower left quadrant) if the server is deployed in a way to take advantage of statelessness.

Server Infra 2025-11 client 2026-06 client (with MRTR)
Can hold SSE ⚠️ SDK shim: clunky but theoretically possible - SDK emulates the retry loop internally while connecting to client over SSE, but I'd argue for graceful degradation instead ✅ Just use MRTR directly
MRTR-only (horizontally scaled) ❌ Tool fails - no SDK shim is possible for this case, as the SSE connection infrastructure on the server side just doesn't exist ✅ Just use MRTR directly
The key problem is that no form of capability negotiation can solve the bottom left quadrant. So one resolution could be for servers that support the 2025-11 and 2026-06 spec at the same time, they should build with MRTR and not use elicitation / sampling if the negotiated version is 2025-11. I believe one solution to this would be an addition to the spec in the form:

A server MUST NOT return IncompleteResult to a client that negotiated a protocol version earlier than 2026-06.

Which implies that for elicitation and sampling the server developer has to make an explicit decision to either (A) support both SSE and MRTR (and implement the infra for it somehow) or (B) not use elicitation or sampling with pre-2026-06 clients.

Following up from the Transports WG call, here's a quick prototype of what backwards compatibility could look like using the typescript sdk as an example: modelcontextprotocol/typescript-sdk#1701

On the server side there are several options for how this could be designed with different degrees of "magic" - on the client side, if we just keep around the "old" sse based code, I think we can pretty much hide it entirely such that a client using a 2026-06 based SDK should still be able to use older servers without having to change any code - the SDK can manage which approach to use with the server based on version negotiation.

@maciej-kisiel
Copy link
Copy Markdown
Contributor

maciej-kisiel commented Mar 19, 2026

Hi all, I played a bit with this proposal to see how it could be incorporated into the Go SDK. Below are my thoughts.

I agree with the statements that wire compatibility is handled by protocol version negotiation (though it will complicate the SDK, details later on) and that client-side story should be largely transparent to developers. On the server side, I see two main compatibility scenarios.

1. Previously implemented server updating the SDK version to be 2026-06 compliant

In our SDK we're committed to backwards compatibility of the SDK APIs. Updating the SDK version must not result in any breakages or degraded functionality. For this reason, supporting handlers that can return IncompleteResult must result in introducing adjusted copies of the existing APIs, because the return type of the handler changes. I created a proof of concept of this for the CallTool method that can be found here: modelcontextprotocol/go-sdk#849.

Now, we can consider what happens with pre-existing code when the new SDK version gets updated. I would say, at a minimum, it should continue to work correctly with <2026-06 protocol versions. It has been stated above that this is a non-goal – I disagree with that. In our SDK, this would basically force implementation of 2026-06 as v2 version of the SDK, something that would be difficult to achieve in the current timeframe. The real question is, can we make it compliant out of the box with MRTR and I believe due to fundamental differences in approach it would be non-obvious to developers (resembling option B from @felixweinberger's PR) and thus likely not worthwhile. We could consider an approach where, if any legacy handler is registered, support of the 2026-06 protocol version is not advertised to the clients. This would mean, however, that the migration is all or nothing – all handlers would need to be migrated before any actual traffic would use the MRTR approach. Maybe that's fine for servers that don't require MRTR to work correctly, but if there's anything else valuable for them in the 2026-06, they would be forced to rewrite their handlers to take advantage of it.

Small digression: protocol version negotiation

I'm not sure how this is handled in other SDKs, but in Go, protocol version negotiation is entirely handled by the SDK, with no user input affecting the set of advertised versions. So far it worked, because the changes were largely backwards compatible and new SDK version could advertise support for new protocol version and rely on capability advertisement to convey what is actually available on the server. Old code using the SDK would continue working in the new protocol version, new functionality would simply not be configured and thus not advertised via capabilities. Determining whether a given protocol version based on the type of handlers used in the SDK would complicate this logic and slow down adoption of the new protocol version, as it would require developers to rewrite the handlers in the new MRTR way.

2. New code being written as the MRTR handler supporting older protocol versions

This is the other side: a developer writes a new server after 2026-06 is released and wants to use the new approach. They should be able to support <2026-06 clients with it. This is doable in a manner similar to option A from @felixweinberger's PR.

@halter73
Copy link
Copy Markdown
Contributor

halter73 commented Mar 20, 2026

EDIT: I deleted my previous response where I was discussing sending requests the old way over the SSE stream being similar more similar to stdio which didn't fully account for the fact stdio is changing with this SEP too. I'm going to spend more time looking at making MRTR work with the existing await based server APIs in the C# SDK.

As long as we can support that, which I think we can, removing the old way of doing things from the protocol is less painful (at least for users of our SDKs). If you use the existing await APIs, the SDK can handle the protocol negotiation downgrade for you.

We'd still need to provide a lower-level API that gives you more control of the MRTR details on the server in particular where we don't want to force developers into session affinity which is always going to be required to support await. This does possibly create some friction in adoption of the new protocol version being required for the servers using MRTR only, but that's unavoidable.

This is the bottom left quadrant @felixweinberger mentions, and I agree that no form of capability negotiation can solve it, so language to the effect "A server MUST NOT return IncompleteResult to a client that negotiated a protocol version earlier than 2026-06" might make sense. I hope it's already implied you shouldn't rely on schemas that aren't defined until a later version of the protocol, but I think it's a good call out. MRTR only servers should definitely fail fast given clients that only support old protocol versions rather than pray the client might support the new protocol despite advertising otherwise.

@halter73
Copy link
Copy Markdown
Contributor

We should make sure the Client SDKs have an option to NOT support the old version. For example we have several Cloud Hosted Clients that can't support elicitations & sampling today because of the requirement for SSE streams. It would be great if we could make this a config parameter to say I only support MRTR elicitation/sampling/listRoots, etc...

@CaitieM20 I agree this makes sense. I think the most sensible way to do this is for the clients to expose some sort of "I don't want SSE streams" (in)capability since that's the root concern. And the most obvious way to do that would be to relax this requirement in the next iteration of the Streamable HTTP spec:

2. The client **MUST** include an `Accept` header, listing both `application/json` and
`text/event-stream` as supported content types.

It could be updated to say something like the following:

  1. The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types, or listing just application/json.

The SDK maintainers will then certainly be asked to expose control over this via some client API, and would be forced to gate this feature to when it connects to a server using Mcp-Protocol-Version: 2026-06-XX and later as a matter of principle. That by itself that would definitely stop any compliant server from trying the old way of elicitation or sampling.

// retries the original request.
// Note: The client must treat this as an opaque blob; it must not
// interpret it in any way.
requestState?: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If this will be serialized JSON, any thoughts about maximum length? It could get large.

Copy link
Copy Markdown
Contributor

@halter73 halter73 Mar 20, 2026

Choose a reason for hiding this comment

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

In general, we've been avoiding putting maximum limits in the protocol. I think it's better to leave it up to the implementations to set limits similar to HTTP headers.

We could consider some specific status/error mechanism for indicating the request state is too large, similar to 431 Request Header Fields Too Large, but I'm not sure that's necessary either. Presumably it'd be the client that'd want to enforce limits (the server is free not to use this after all), and clients don't have an equivalent to 431 to inform the server its HTTP response headers are too long.

Presumably servers are going to try to keep requestState as short as they can for maximum compatibility. That's certainly what we do in ASP.NET Core for response headers and why we use things like the ChunkingCookieManager. I'm not that I'm saying you could chunk requestState, but I'm just pointing out that HTTP servers already have to deal with unspecified client limits for response headers.

If clients/SDK want to enforce some sensible default limit, they can provide a clear error to the developer/user when it's exceeded, and some configuration to change the limit similar to the .NET's HttpClientHandler.MaxResponseHeadersLength property.

I definitely don't think we should be baking any hard limits into the protocol.

Randgalt added a commit to airlift/airlift that referenced this pull request Mar 20, 2026
// present, since the presence of these fields allow the client to
// determine that the result is incomplete.
export interface IncompleteResult extends Result {
status: "incomplete";
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.

Suggested change
status: "incomplete";
result_type: "incomplete";

Comment on lines +363 to +365
// At least one of the the inputRequests and requestState fields must be
// present, since the presence of these fields allow the client to
// determine that the result is incomplete.
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.

Suggested change
// At least one of the the inputRequests and requestState fields must be
// present, since the presence of these fields allow the client to
// determine that the result is incomplete.

values as untrusted input and validate them the same way they would
validate any client-supplied data.

2. **Client Behavior:**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry if this answered elsewhere... The server will know that the client supports MRTR by the Protocol version?

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 that ought to be enough, considering this is the only way to do elicitation, sampling or roots calls using the upcoming protocol version assuming this SEP is accepted. That's how I currently have it working for the C# SDK in modelcontextprotocol/csharp-sdk@main...halter73/mrtr. Note that this is an early prototype which is why there is no PR yet.

Copy link
Copy Markdown
Contributor

@halter73 halter73 Mar 21, 2026

Choose a reason for hiding this comment

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

I created a draft PR for the C# changes so people can comment on individual changes at modelcontextprotocol/csharp-sdk#1458

Here's a key bit from the PR description for how I decided to handle backward compatability.

1. Full Backwards Compatibility via Protocol Negotiation

This was the most debated topic in the spec PR (felixweinberger, maciej-kisiel, CaitieM20). The C# SDK proves all four combinations work seamlessly:

Server Client Behavior
Experimental ✅ Experimental ✅ MRTR — incomplete results with retry cycle
Experimental ✅ Stable ✅ FallbackElicitAsync/SampleAsync automatically send legacy JSON-RPC requests
Stable ✅ Experimental ✅ Client accepts stable protocol; MRTR retry loop is a no-op
Stable ✅ Stable ✅ Standard behavior — no MRTR, no changes

Key insight: The existing await server.ElicitAsync(...) API doesn't change at all. When the connected client supports MRTR, the SDK returns an IncompleteResult with inputRequests instead of sending a elicitation/create JSON-RPC request. When the client doesn't support MRTR, it sends the legacy request. Tool authors don't need to know or care which path is taken.

The determination is made via protocol version negotiation during initialize:

// Server-side check (McpServerImpl.cs)
internal bool ClientSupportsMrtr() =>
    _negotiatedProtocolVersion is not null &&
    _negotiatedProtocolVersion == ServerOptions.ExperimentalProtocolVersion;

It's pretty seamless for users of the old await API which I think will and should continue to remain popular particularly for people using an MCP server locally either via the stdio or HTTP transport (which makes debugging way easier), so they don't need to worry about statefulness being in anyway difficult.

It also provides a new non-await based API that allows you direct to control the IncompleteResult and InputRequests/InputResponses that works in stateless mode which is of course the whole motivation of this feature. I think it's important to support that without breaking elicitation and sampling code that already works though.

It's worth noting that the protocol-level-session was very handy in providing a reasonable lifetime for tracking pending MRTR flows when using the await machinery that's really convenient when you can rely on it, but I'll get into this more in a separate comment.

@halter73
Copy link
Copy Markdown
Contributor

halter73 commented Mar 21, 2026

Why Removing Protocol-Level Sessions Would Block MRTR Adoption

Context: SEP-2322 (MRTR) and the proposal to remove protocol-level sessions are being considered concurrently. This document explains why the C# SDK's MRTR implementation — which preserves full backwards compatibility with existing await-based server APIs — fundamentally depends on protocol-level sessions, and why removing sessions would make it significantly harder for SDKs to deliver the smooth migration path that server developers need.

The Backwards Compatibility Promise

The strongest argument for MRTR is that it doesn't have to be a breaking change for server developers. In the C# SDK's implementation (PR #1458), existing tool handlers that call await server.ElicitAsync(...) or await server.SampleAsync(...) continue to work with zero code changes. When the connected client supports MRTR (via protocol version negotiation), the SDK transparently uses the incomplete result / retry cycle. When the client doesn't, it falls back to legacy JSON-RPC requests. The tool author never needs to know which path is taken.

This backwards compatibility depends on sessions.

How the High-Level API Depends on Sessions

When a tool handler calls await server.ElicitAsync(...), the SDK suspends the handler task, stores the continuation in a ConcurrentDictionary<string, MrtrContinuation> on the per-session McpServerImpl, and returns an IncompleteResult with a correlation ID in requestState. When the client retries with the resolved inputs, the Mcp-Session-Id header routes the retry to the correct McpServerImpl that holds the suspended handler.

Initial request:
  POST /mcp  (Mcp-Session-Id: ABC)
  → Routed to Session ABC → McpServerImpl_ABC
  → Tool handler calls ElicitAsync → handler suspended
  → McpServerImpl_ABC._mrtrContinuations["corr-123"] = suspended handler
  → Returns IncompleteResult { requestState: "corr-123" }

Retry:
  POST /mcp  (Mcp-Session-Id: ABC)
  → Routed to Session ABC → McpServerImpl_ABC  ← SAME instance
  → Looks up _mrtrContinuations["corr-123"] → FOUND
  → Resumes handler with client's response

Without the session header, the retry has no way to find the right McpServerImpl. You could use a global continuation store, but then you lose the lifecycle boundary that IdleTimeout and MaxIdleSessionCount provide for cleanup, you lose isolation between clients, and you lose the rate limiting hook that sessions give you for free.

Per-Session MRTR Governance

The C# SDK's filter system lets developers intercept JSON-RPC messages at the transport boundary. Because the outgoing filter sees IncompleteResult responses and has access to context.Server.SessionId, you can track and limit concurrent MRTR flows per session using nothing but the built-in filter API. This is tested end-to-end in PR #1458.

Why per-session limits and not just per-user?

Per-user limits are usually the primary defense, but per-session limits serve distinct needs:

  • Tiered session plans: A service provider might offer different concurrency limits per session tier. A premium session allows 10 concurrent MRTR flows, a basic one allows 2.
  • Resource isolation: Each suspended handler consumes memory. Per-session limits bound the footprint per session, which is simpler to reason about for capacity planning than a global pool attributed to users.
  • Sub-agent scenarios: In sub-agent architectures, each sub-agent uses its own session. Per-session limits let the parent allocate MRTR budgets independently to each sub-agent.

Per-user limits require authentication infrastructure. Per-session limits work out of the box because session identity is a protocol-level concept. The two are complementary.

The Low-Level API Doesn't Replace This

PR #1458 also provides a low-level API where tool handlers throw IncompleteResultException and manage their own state via requestState. This works without sessions — it's designed for stateless servers. But it requires developers to manually encode/decode state, handle each retry as a fresh invocation, and give up the await pattern entirely.

That's a reasonable API for advanced scenarios, but it's not a migration path for existing servers that use await server.ElicitAsync(...) today. Telling those developers "MRTR is great, but rewrite all your tool handlers" undermines the value proposition.

With sessions, the migration path is:

  1. Set ExperimentalProtocolVersion = "2026-06-XX"
  2. Done. Existing tool handlers automatically use MRTR with compatible clients.

Without sessions, it becomes: rewrite all handlers to use IncompleteResultException, design a state encoding scheme, implement retry-aware logic, add version checks with fallback paths, and manage continuation cleanup. That's a significant barrier, and it risks fragmenting the ecosystem with many servers stuck on the old protocol.

Conclusion

The session removal proposal raises real concerns worth addressing. But those concerns can be addressed incrementally — by making session lifetime more explicit, adding cache-control headers, or allowing multiple state handles per session.

Removing sessions entirely would make it very difficult for SDKs to deliver the seamless MRTR migration experience that server developers need. The C# SDK implementation in PR #1458 shows that with sessions, existing await-based server code works with MRTR without any changes. Without sessions, that guarantee disappears.

If both MRTR and session removal are on the table, I'd recommend advancing MRTR first and keeping sessions as the foundation for the high-level API. If we cannot keep sessions, I believe we should not approve MRTR in its current form — not because the protocol design is wrong, but because the SDK-level developer experience would suffer enough to limit adoption.

For example, a server might send the following input requests:

```json5
"inputRequests": {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is there an implied ordering here? What if the inputRequests contains multiple elicitation/create messages? How is the client to handle that?

[key: string]: InputRequest;
}

export type InputResponse =
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

InputResponse needs to include an optional error JsonRpcResponse, or at minimum the error stanza. I'm working on some initial designs for MRTR and we need a way to inform the tool author that an InputRequest has failed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Or, is the idea that the tool doesn't care? I guess one could make that argument.

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 wouldn't mind putting the error information back for better SDK-level compatibility. I think this is something @mikekistler pointed out too.

@Randgalt
Copy link
Copy Markdown

Randgalt commented Mar 25, 2026

Why Removing Protocol-Level Sessions Would Block MRTR Adoption

[snip]

@halter73 makes a good point here. For our server, we're going to have tool developers adopt MRTR and we will emulate it inside the server (with a blocking thread). However, existing tools must be refactored. This could be a large burden if there are a lot of tools and it they are complex.

@localden localden moved this to Accepted in SEP Review Pipeline Mar 28, 2026
@localden localden added this to the 2026-06-30-RC milestone Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Accepted

Development

Successfully merging this pull request may close these issues.