modelcontextprotocol / modelcontextprotocol/kotlin-sdk
[Proposal] SEP-2322: Multi Round-Trip Requests (MRTR) architecture & flow design (#808)
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 1.5k
- Forks
- 248
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 23
Description
Proposal: SEP-2322 Multi Round-Trip Requests (MRTR) Architecture & Flow Design (#808)
Motivation & Context
Tracking implementation of SEP-2322 in kotlin-sdk for the 2026-07-28 MCP specification release (aligned with umbrella issue #842).
In the original MCP design, interactive flows (such as tool-driven elicitation or server-initiated sampling) relied on in-line coroutine suspension waiting on an active bidirectional SSE stream. While workable for single-process local servers, this model failed in modern distributed deployments:
- Stateless HTTP & Scaled Backends: Multi-instance servers behind load balancers cannot guarantee that the in-line callback returns to the identical server process holding the suspended thread/coroutine.
- Compatibility with SEP-2260: SEP-2260 strictly prohibits unsolicited server-to-client requests; intermediate requests must be formally linked to the parent client call.
- Connection Resilience: Network dropouts during long user elicitation prompts would terminate the entire tool execution and fail the operation unrecoverably.
SEP-2322 defines Multi Round-Trip Requests (MRTR):
- When a tool requires additional client input (e.g.
elicitation/createorsampling/createMessage), the server returns an intermediate response withresultType: "input_required", containing namedinputRequests: Map<String, ServerRequest>and an opaque, tamper-evidentrequestState. - The client resolves the requested inputs and resumes the execution:
- Ephemeral flow: Re-invokes
tools/callwithinputResponses: Map<String, InputResponse>andrequestState. - Task-backed flow: Updates the task via
tasks/update(under SEP-2663).
- Ephemeral flow: Re-invokes
- The server decrypts/validates
requestState, processesinputResponses, and either produces the finalCallToolResultor requests further input if needed.
This proposal outlines the Kotlin Multiplatform architecture to implement MRTR in kotlin-sdk across kotlin-sdk-core, kotlin-sdk-server, and kotlin-sdk-client.
Proposed Architecture & Component Design
1. Wire Models & Data Types (kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/)
- Input Required Result Wire Shape:
@Serializable @SerialName("input_required") public data class InputRequiredResult( val inputRequests: Map<String, ServerRequest>, val requestState: String? = null, @SerialName("_meta") override val meta: JsonObject? = null, ) : CallToolResponse @Serializable public data class InputResponse( val action: String, // "accept" | "decline" | "cancel" val content: JsonElement? = null, ) - Extended CallToolRequestParams:
UpdateCallToolRequestParamsto support round-trip continuations:@Serializable public data class CallToolRequestParams( val name: String, val arguments: JsonObject? = null, val inputResponses: Map<String, InputResponse>? = null, val requestState: String? = null, @SerialName("_meta") override val meta: RequestMeta? = null, ) : RequestParams
2. State Serialization & Integrity (kotlin-sdk-core / kotlin-sdk-server)
Because requestState passes through untrusted client hands:
- Stateless Continuation Token:
Provide an encrypted or HMAC-signed state serializer interface:
This ensures that state tampering by compromised clients is rejected immediately with an invalid parameter error (public interface RequestStateCodec { public fun <T> encode(state: T, serializer: KSerializer<T>): String public fun <T> decode(token: String, serializer: KSerializer<T>): T? } public class HmacSignedStateCodec(private val secretKey: ByteArray) : RequestStateCodec { ... }-32602).
3. Server Tool Authoring DSL & Programming Model (kotlin-sdk-server)
Support both continuation-based tools and high-level coroutine helpers:
- Explicit Round-Trip Tool Handler:
server.addTool( name = "book_flight", description = "Books a flight with optional confirmation" ) { params -> val state = params.requestState?.let { codec.decode(it, BookingState.serializer()) } val confirmation = params.inputResponses?.get("user_confirm") if (state == null) { // First round: ask for user confirmation via elicitation val initialBooking = createBookingDraft(params.arguments) InputRequiredResult( inputRequests = mapOf( "user_confirm" to ElicitationCreateRequest( message = "Confirm booking for ${initialBooking.destination} for \$${initialBooking.price}?" ) ), requestState = codec.encode(BookingState(initialBooking.id), BookingState.serializer()) ) } else { // Resumed round: finalize booking if (confirmation?.action == "accept") { val receipt = finalizeBooking(state.draftId) CallToolResult(content = listOf(TextContent(receipt))) } else { CallToolResult(content = listOf(TextContent("Booking cancelled by user.")), isError = true) } } }
4. Client-Side Round-Trip Automation (kotlin-sdk-client)
Provide automatic resolution options in ClientSession:
- Automated Handler Hook:
public suspend fun ClientSession.callToolAutoRoundTrip( name: String, arguments: JsonObject? = null, maxRounds: Int = 5, elicitationHandler: suspend (String, ElicitationCreateRequest) -> InputResponse ): CallToolResult- When the server returns
InputRequiredResult, the client invokeselicitationHandlerfor each requested prompt, collects theinputResponses, and automatically re-issuestools/callwithrequestStateandinputResponses. - Terminates gracefully when a final
CallToolResultis received ormaxRoundsis reached.
- When the server returns
5. Conformance & Verification Plan
- Unit Tests:
- Serialization and deserialization of
InputRequiredResultandCallToolRequestParamswithinputResponses. - HMAC state codec validation and tampering rejection.
- Multi-round simulated tool invocation flow in in-memory transport.
- Serialization and deserialization of
- Conformance Suite:
- Validate against https://github.com/modelcontextprotocol/conformance/pull/188 scenarios covering MRTR wire specifications and error recovery.
Next Steps
Upon review and consensus on the MRTR design:
- Introduce
InputRequiredResult,InputResponse, and updatedCallToolRequestParamsinkotlin-sdk-core. - Implement
RequestStateCodecinkotlin-sdk-server. - Provide
callToolAutoRoundTripinkotlin-sdk-client. - Validate against official MRTR conformance tests.
AI assistance disclosure: AI was used to discover this opportunity and draft the change or text. The submission was checked against the prepared artifact and recorded verification evidence.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with SEP-2322 and the wire-model directory at kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/. Review the proposed boundaries across kotlin-sdk-core, kotlin-sdk-server, and kotlin-sdk-client, then inspect the listed serialization, HMAC, in-memory flow, and conformance tests. Done requires agreement on the architecture, implementation across the three modules, and successful MRTR verification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- backend-api-design, distributed-systems, security, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100