skip to content

Multi Round-Trip Requests

Rather than call the client back, a server returns an input_required result naming what it needs, and the client retries the same request with the answers. It replaced all server-initiated requests.

part ofAPI stylesoverview, primer and where to startread it →
on this pageshow

questions

5

In MCP 2026-07-28, what replaced server-initiated requests back to the client?

level: juniorimportance: must knowfreq 62%

answer

  1. servers no longer originate requests
  2. the answer comes back on a retry
  3. a result, not a callback
  4. resultType input_required plus requestState
  5. client re-sends with a new id

basics

~20 s

Multi Round-Trip Requests. A server can no longer send its own JSON-RPC request; it answers the client's call with a result whose resultType is "input_required", listing what it needs, and the client re-sends the original call carrying the answers.

solid answer

~40 s

MCP revision 2026-07-28 removed server-initiated JSON-RPC requests entirely — there is no longer a `ServerRequest` union in the schema, so a server never originates a call on its own. Instead it uses the **Multi Round-Trip Request** (MRTR) pattern: while handling a client request it may return an `InputRequiredResult`, a result with `resultType: "input_required"` that carries an `inputRequests` map (what the server still needs) plus an opaque `requestState` blob. That is a final, complete JSON-RPC response to the original id — the server is not blocked waiting. The client gathers the answers, then re-issues the *same* request with a **new** JSON-RPC id, supplying `inputResponses` under the same keys and echoing `requestState` verbatim. The inversion keeps every message client-initiated, which is what lets MCP be a stateless, POST-only protocol.

code

json · 13 lines
json
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "need-roots": {
        "method": "roots/list"
      }
    },
    "requestState": "c3RhdGUtYmxvYi12MQ"
  }
}

go deeper

for a junior

Recall the one-sentence shape: the server answers with an input_required result instead of calling the client back, and the client re-sends the request with the answers attached.

for a middle

Be ready to name the parts — resultType "input_required", the inputRequests map, the opaque requestState, inputResponses on a retry with a new JSON-RPC id — and to say that 2026-07-28 removed server-initiated requests.

for a senior

Explain why the inversion exists: no live per-connection state, a POST-only one-directional transport, and any node behind a load balancer able to serve the retry. Say which revision changed it.

for a principal

Own the tradeoff you have accepted: continuation state now travels over the wire in every round, retries multiply request volume and consent prompts, and a client is free never to come back — so server design must tolerate abandoned exchanges.

## The change Up to and including MCP revision 2025-11-25, the protocol was bidirectional at the request level: a server handling a `tools/call` could pause and send its *own* JSON-RPC request back down the connection — asking the client's LLM to sample a completion, asking for the client's roots, or eliciting a value from the user — and then continue once the client's response arrived. Revision **2026-07-28** deleted that capability. There is no `ServerRequest` union in the current schema; servers emit only responses and notifications. What replaced it is the **Multi Round-Trip Request** pattern, usually abbreviated MRTR. ## How MRTR works The server answers the client's request with an interim-but-complete result: - `resultType: "input_required"` marks it as an `InputRequiredResult` rather than an ordinary `"complete"` result. - `inputRequests` is a **map** from server-assigned keys to the things the server needs. Each value is one member of the union `InputRequest = CreateMessageRequest | ListRootsRequest | ElicitRequest`. - `requestState` is an **opaque** string the server uses to resume; the client must never parse or modify it. Because this is a normal JSON-RPC response, the original request id is now finished. The client does the work described by each entry, then **re-issues the original request with a new JSON-RPC id**, attaching `inputResponses` keyed exactly as `inputRequests` was and echoing `requestState` unchanged. The server matches the answers to the keys it assigned, restores its continuation from `requestState`, and either returns the real result or asks for more — the loop may run several rounds, hence *multi round-trip*. A client that does not want to supply the input simply does not retry. There is no rejection message to send; the exchange is already closed. ## Why the protocol was inverted Three properties fall out of it: 1. **Statelessness.** MCP 2026-07-28 states that every request is self-contained and that servers MUST NOT rely on prior requests over the same connection. A server that could call back mid-request would have to hold live in-memory state tied to one connection. With MRTR, everything the server needs to resume is in the bytes the client sends back. 2. **A single, one-directional transport shape.** Streamable HTTP is POST-only; the client always initiates. Server-initiated requests forced the old standalone listening stream and the session header that went with it, both removed in 2026-07-28. 3. **Horizontal scaling.** Any node behind a load balancer can serve the retry, because the continuation travels in `requestState` rather than living in the process that produced it. ## Which calls can use it Only three methods may answer with an `InputRequiredResult`: `tools/call`, `prompts/get` and `resources/read`. Listing methods, `server/discover` and `completion/complete` always answer with a complete result. ## What it is not MRTR is not a queue, a callback URL, or a push channel. It is also not the same as the `io.modelcontextprotocol/tasks` extension: an MRTR round-trip is about *missing input*, whereas the tasks extension is about long-running work the client polls. And it is not the subscription mechanism — `subscriptions/listen` carries server→client *notifications*, never requests. ## Interview framing Because the deployed fleet still contains servers speaking 2025-11-25 and earlier, interviewers use this question to date a candidate's knowledge. Saying "the server calls `sampling/createMessage` on the client" describes the legacy era; the modern answer is that the server *asks for* a `sampling/createMessage` inside `inputRequests` and the client brings the result back on a retry. Naming the removal revision explicitly is the strongest signal you can give.

  • Is the original request still open while the client gathers the input?
    No. The `InputRequiredResult` is a complete JSON-RPC response to that id, so the exchange is finished and, on Streamable HTTP, the response stream can close. The server holds nothing open; continuation lives in the opaque `requestState` the client will echo back on a fresh request.
  • Can a server still push anything to the client during a request in 2026-07-28?
    Notifications only, not requests. `notifications/progress` and `notifications/message` still flow on the originating request's own response stream, and list-changed notifications flow on an opted-in `subscriptions/listen` stream. Neither is a JSON-RPC request, so neither expects a response from the client.
  • How many rounds can this take?
    The spec does not fix a number — a server may return `input_required` again after receiving the answers, so the pattern is a loop. Clients typically bound it, since each round is a fresh request the user may be asked to approve, and an unbounded loop is a denial-of-service surface.

saying these in an interview costs you the question

  • Says the server sends sampling/createMessage as its own request
  • Claims the client answers on the same JSON-RPC id
  • Thinks input_required is an error the client must handle
  • Calls the connection a session that holds the pending call
  • Confuses MRTR with the long-running tasks extension

context

open as a page

How does an MCP client resume a call after an input_required result?

level: middleimportance: must knowfreq 72%

basics

~20 s

The client re-sends the original method and arguments as a brand-new JSON-RPC request with a new id, adding an inputResponses map keyed exactly like the server's inputRequests and echoing the opaque requestState byte for byte.

open as a page

Which MCP methods can return input_required, and what may they ask for?

level: middleimportance: should knowfreq 44%

basics

~20 s

Only tools/call, prompts/get and resources/read may answer with an input_required result. Each entry in its inputRequests map is one of three request types: a sampling createMessage request, a roots list request, or an elicitation request.

open as a page

Why must an MCP client echo requestState verbatim and never inspect it?

level: seniorimportance: should knowfreq 48%

basics

~20 s

requestState is the server's private continuation for a paused call. MCP 2026-07-28 defines it as opaque so the server may encode, sign or version it freely; clients that parse or rewrite it break the resume and couple themselves to one server's internals.

open as a page

How would you design an MCP tool whose call is retried after input_required?

level: principalimportance: should knowfreq 33%

basics

~20 s

Design each round to be safely re-executable: do no irreversible work before asking for input, hold no locks or open transactions across rounds, expire the continuation, bound how many rounds a call may take, and assume many clients will simply never come back.

open as a page