Multi Round-Trip Requests (MRTR)#1458
Draft
halter73 wants to merge 8 commits into
Draft
Conversation
9 tasks
f1dd4c4 to
5845866
Compare
This was referenced Mar 23, 2026
9 tasks
tarekgh
reviewed
May 26, 2026
tarekgh
reviewed
May 26, 2026
Resolves conflicts from rebasing the MRTR work (originally branched from 4140c6d) onto the current main (b8c4d95). Key conflict resolutions: - McpClientImpl.SendRequestAsync: combine SEP-2243 tool-context attachment with MRTR retry loop for IncompleteResult. - McpSessionHandler.SendRequestAsync: take MRTR's outgoing filter and request logging. - McpServerImpl.InvokeHandlerAsync: take MRTR's CreateDestinationBoundServer. - docs/concepts/index.md: combine main's Tasks entry with MRTR additions. - MapMcpTests.cs: keep main's new IncomingFilter/OutgoingFilter tests in full, drop MRTR's outdated overload usage by going through configureClient. - MrtrIntegrationTests.cs: gate with #if !NET472 (uses ReadLineAsync(CT)). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- IncompleteResult/IncompleteResultException -> InputRequiredResult/InputRequiredException - Wire format: result_type -> resultType, `incomplete` -> `input_required` - Drop ExperimentalProtocolVersion option; opt in via ProtocolVersion = `DRAFT-2026-v1` - Add DraftProtocolVersion constant and include in SupportedProtocolVersions - Restrict implicit MRTR continuation path to legacy stateful sessions; DRAFT-2026-v1 and stateless sessions always use the exception-based path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Implicit MRTR (handler suspension via ElicitAsync) requires both client support (DRAFT-2026-v1) and a stateful session. All other cases fall through to the exception-based path, which transparently resolves InputRequiredException via legacy JSON-RPC requests for clients that don't speak MRTR. - Drop the now-redundant ProtocolVersion pin from ConfigureExperimentalServer in MapMcpTests.Mrtr; server uses the negotiated version like any other server. - Rewrite the obsolete WithoutExperimental low-level test now that the experimental flag is gone; it now verifies retry exhaustion when no input requests are supplied. - Update other test assertions to use the literal DRAFT-2026-v1 string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…er draft Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ve input requests with WhenAll+CTS Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t protocol ElicitAsync/SampleAsync/RequestRootsAsync now throw only when the server is stateless (the existing ThrowIf*Unsupported guards already handled this). Stdio + DRAFT-2026-v1 keeps working via the legacy server-to-client JSON-RPC path; stateless Streamable HTTP throws regardless of protocol revision. A follow-up will force DRAFT-2026-v1 Streamable HTTP to stateless mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements SEP-2322: Multi Round-Trip Requests (MRTR) for the C# SDK.
MRTR lets a server tool ask the client for input — elicitation, sampling, or roots — as part of a single tool call by returning an incomplete result instead of a final one. The client resolves the input requests and retries the original
tools/callwithinputResponsesattached, until the tool returns a final result.This PR follows the ratified draft wire format and gates the new behavior on the negotiated protocol revision
DRAFT-2026-v1. There are no experimental opt-in flags.The API
InputRequiredExceptionis the only way to do MRTR. A tool throws it with anInputRequiredResultcontaininginputRequestsand/orrequestState, and the SDK turns that into the right wire response for the negotiated protocol.McpServer.IsMrtrSupportedreturnstruewhenever the SDK can satisfyInputRequiredException— either natively (draft) or via the legacy resolver (current+stateful).Compatibility matrix
DRAFT-2026-v1InputRequiredResultis serialized straight to the wire.2025-06-18and earlier)elicitation/create/sampling/createMessage/roots/listrequests, collects responses, retries the handler withinputResponses. Capped at 10 rounds.InputRequiredExceptionraises anMcpException.Breaking changes under
DRAFT-2026-v1The draft revision removes the server-to-client
elicitation/create,sampling/createMessage, androots/listrequest methods. The SDK fails fast:McpServer.ElicitAsync,SampleAsync,RequestRootsAsync,AsSamplingChatClient,ElicitAsTaskAsync,SampleAsTaskAsyncall throwInvalidOperationExceptionafter aDRAFT-2026-v1session is negotiated. The exception message points to theInputRequest.ForElicitation/ForSampling/ForRootsListreplacement.Removed
ElicitAsync/SampleAsynccalls and suspended the handler across MRTR rounds. (Replaced by the explicitInputRequiredExceptioncontract.)DeferTaskCreationon[McpServerTool]/McpServerToolCreateOptionsand the server-sideCreateTaskAsyncAPI tied to it. Long-running tasks still useIMcpTaskStoreas before.ExperimentalProtocolVersionopt-in — replaced by negotiatingDRAFT-2026-v1directly.Follow-ups (intentionally left out of this PR)
Mcp-Session-Idand theStatefulmode. When that lands, the current-protocol-stateful row of the matrix collapses into the stateless row, and the legacyelicitation/create/sampling/createMessage/roots/listresolver path can be deleted. The code has// TODO(stateless-draft):markers where that simplification will go.tests/ModelContextProtocol.AspNetCore.Tests/ServerConformanceTestsand gated onNodeHelpers.HasMrtrScenarios(). Once conformance#188 merges, the gate can be removed.Tests
ModelContextProtocol.Tests: 1980 passed, 0 failed, 4 skippedModelContextProtocol.AspNetCore.Tests: 410 passed, 0 failed, 33 skippedIncludes new coverage:
DraftProtocolGuardTests— verifies the legacy methods throw underDRAFT-2026-v1.MrtrLowLevelApiTests,MrtrSerializationTests— exerciseInputRequiredExceptionand its wire format.MapMcpTests.Mrtr— end-to-end Streamable HTTP coverage.ServerConformanceTests(8 ephemeral + 3 task-based deferred).Docs
docs/concepts/mrtr/mrtr.mdrewritten aroundInputRequiredExceptionwith the new compatibility matrix.docs/concepts/elicitation/,sampling/,roots/updated to call out theDRAFT-2026-v1behavior change.docs/concepts/tasks/tasks.md—DeferTaskCreationsection removed.