Spec explainer
What is MRTR (Multi Round-Trip Requests) in MCP?
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
resultType: "input_required" describing what it needs, and the client retries the original call with the answers attached. Two responses to one logical operation, never a second conversation.The short answer
// Legacy: the server starts its OWN request
Client → Server: tools/call (id: 1)
Server → Client: elicitation/create (a NEW request, id: 99)
Client → Server: response to id: 99
Server → Client: final result for id: 1
// Two conversations interleaved on one connection.// Modern: the server only ever REPLIES
Client → Server: POST tools/call (id: 1)
Server → Client: { "result": {
"resultType": "input_required",
"inputRequests": [ … ] } }
// client gathers the input, then re-sends the SAME call
Client → Server: POST tools/call (id: 2, same params
+ inputResponses)
Server → Client: { "result": { "resultType": "complete", … } }“The result MUST include a resultType field to indicate the type of the result.”
Why callbacks had to go
A server sending its own request assumes a channel that outlives a single request-response pair — something to send into, and a client listening on it. That is a session by another name, and the stateless core removed it.
“The server MUST NOT send independent JSON-RPC requests on this stream.”
MRTR expresses the same need using only replies. Everything the server wants to say arrives as the answer to something the client asked, which means a load balancer can route each leg independently, a serverless function can handle each one cold, and nothing has to stay warm between them.
input_required is not the server pausing — it is the server returning, having told you what a complete request would look like.The shape of an MRTR exchange
{
"jsonrpc": "2.0", "id": 1,
"result": {
"resultType": "input_required",
"inputRequests": [
{
"type": "elicitation/create",
"message": "Which workspace should I export?",
"requestedSchema": {
"type": "object",
"properties": { "workspace": { "type": "string" } },
"required": ["workspace"]
}
}
]
}
}{
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {
"name": "export_data",
"arguments": { "format": "csv" },
"inputResponses": [
{ "type": "elicitation/create", "content": { "workspace": "acme" } }
],
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28"
}
}
}“A resultType of any value unrecognized by the client MUST be considered invalid.”
Note the retry carries a new JSON-RPC id and the same arguments. It is a fresh request as far as the transport is concerned — which is the point. Nothing links the two except the data the client chose to carry forward.
What this changed for sampling, elicitation and roots
Three features previously worked by the server issuing a request. All three moved into inputRequests:
| The server wants | Legacy request | Now |
|---|---|---|
| An LLM completion | sampling/createMessage | an entry in inputRequests |
| Input from the user | elicitation/create | an entry in inputRequests |
| The client's roots | roots/list | an entry in inputRequests |
“Deprecate the Roots, Sampling, and Logging features. These features remain fully functional during the deprecation window but new implementations should not add support for them.”
Worth reading twice: Roots, Sampling and Logging are also deprecated as features, independently of the mechanism change. If you are building new, the answer is not “port my callback to MRTR” but “do I need this at all?”
What a response stream may still carry
MRTR does not ban streaming. A server may still answer with text/event-stream — it just may not use that stream to start its own conversation.
“The server MUST return either Content-Type: application/json (a single JSON object) or Content-Type: text/event-stream (an SSE response stream).”
“When initiating an SSE stream, servers SHOULD include the X-Accel-Buffering: no header in the HTTP response.”
X-Accel-Buffering: no is the one that bites in production rather than in testing: nginx buffers proxied responses by default, which holds an SSE stream until it completes and makes a working server look like it is hanging.
“If the server accepts it, the server MUST return HTTP status code 202 Accepted with no body.”
State between round trips
The server does not get to remember what it was doing. If the first leg did expensive work before discovering it needed input, that work is gone unless you deliberately carry it.
// A. Redo it. Simplest, and correct when it's cheap.
// Ask for everything you might need BEFORE doing the work.
// B. Hand the client an explicit handle to pass back.
{
"resultType": "input_required",
"inputRequests": [ … ],
"_meta": {
"com.example/draftId": "draft_01HZY…" // your own namespace
}
}
// What you MUST NOT do: key the work to the connection,
// the client's IP, or an implicit "current operation" —
// that is MCP-STL-004, and it will work in testing and
// fail behind a load balancer.The 7 result-shape rules we check
| Rule | What we check | Level | If it fails |
|---|---|---|---|
| MCP-RES-001 | Every result includes resultType | MUST | fail |
| MCP-RES-002 | resultType is a value defined by core or an advertised extension | MUST | fail |
| MCP-RES-003 | Server sends no independent JSON-RPC requests on a response stream | MUST_NOT | fail |
| MCP-RES-004 | Accepted notification POST returns 202 with no body | MUST | fail |
| MCP-RES-005 | Response is application/json or text/event-stream; SSE terminates with the final response | MUST | fail |
| MCP-RES-006 | SSE responses set X-Accel-Buffering: no | SHOULD | warn |
| MCP-RES-007 | tools/list returns a deterministic order | SHOULD | warn |
All seven carry a verbatim spec sentence. MCP-RES-003 is the one that catches a server still sending its own requests.
Check your result shapes
The validator inspects every result for resultType, checks that error and notification responses use the right status and content type, and flags a server that sends independent requests on a response stream.
Frequently asked
What is MRTR in MCP?
Multi Round-Trip Request — the pattern that replaced server-initiated requests in the 2026-07-28 revision. Instead of the server sending its own JSON-RPC request mid-flight to ask the client for something, it returns an interim result with resultType "input_required" listing what it needs. The client gathers that input and retries the original request with the answers attached.
Why did MCP remove server-initiated requests?
Because they require a session. A server sending its own request mid-flight assumes a bidirectional channel that outlives a single request-response pair, which is exactly what the stateless core removed. MRTR expresses the same need — the server wants input before it can finish — using only responses to requests the client made.
What happened to sampling, elicitation and roots?
The needs remain; the mechanism changed. A server that previously sent sampling/createMessage, elicitation/create or roots/list as its own request now names those in the inputRequests array of an input_required result. Separately, the Roots, Sampling and Logging features are formally deprecated under the 12-month lifecycle policy, so new implementations should not adopt them.
What is resultType input_required?
One of the two result types in 2026-07-28. "complete" means the result is final; "input_required" means the server cannot finish without something from the client and is describing what it needs. A client that receives input_required is expected to satisfy the requests and re-send the original call.
Can a server still open an SSE stream?
Yes, for its response to a request the client made — but it must not send independent JSON-RPC requests on that stream. The stream carries the response and any notifications relating to it, and terminates with the final response. Long-lived server-to-client notifications now go through a single subscriptions/listen stream the client opts into.
Does MRTR mean the server loses its work between round trips?
It means the server must not rely on remembering it. If a tool call needs expensive setup before it asks for input, either redo it on the retry or hand the client an explicit handle to pass back — the same rule as any other cross-request state under a stateless protocol.