BYOKchat Blog

MCP Multi-Round-Trip Requests Explained

Understand MCP Multi Round-Trip Requests (MRTR): input_required, inputResponses, requestState, stateless retries, elicitation, cancellation, security, and recovery.

· 7 min read

On this page
  1. The basic flow
  2. What changed from older MCP designs
  3. A conceptual input-required result
  4. The client retries the original request
  5. One logical operation can span several HTTP requests
  6. Give the logical operation its own ID
  7. requestState must be treated as untrusted
  8. Servers should protect important state themselves
  9. Bind requestState to the original operation
  10. Input responses need stable keys
  11. Validate every response before retrying
  12. MRTR can represent more than elicitation
  13. A server may require multiple rounds
  14. Set explicit limits
  15. Cancellation is part of the protocol workflow even if it is host-owned
  16. App restarts should not destroy the workflow accidentally
  17. Permission and MRTR state are separate
  18. Input responses may themselves be sensitive
  19. Retry safety still matters
  20. The server should not perform irreversible work before required input when avoidable
  21. MRTR fits stateless load balancing
  22. Explicit handles can complement requestState
  23. Trace each round
  24. Do not expose opaque requestState to the model by default
  25. MRTR response handling should be type-safe
  26. Version-gate MRTR behavior
  27. A practical state machine
  28. Handle duplicate UI submission
  29. Timeouts should pause appropriately while waiting for the user
  30. Test malformed MRTR payloads
  31. Test cross-instance continuation
  32. Test crash recovery
  33. Where BYOKchat fits
  34. Further reading

Multi Round-Trip Requests, or MRTR, are one of the most important changes in the 2026-07-28 MCP protocol generation.

They solve a problem created by the move to a stateless protocol core:

How can a server ask the client for more information while an operation is in progress without depending on a persistent server-to-client request channel?

The answer is to end the current request with an explicit input-required result, let the client collect what is needed, and then retry the original operation with the answers attached.

The basic flow

Diagram illustrating the surrounding section

This keeps the protocol stateless while still supporting interactive workflows.

What changed from older MCP designs

Earlier MCP revisions had server-to-client requests such as elicitation or sampling that depended on a live bidirectional interaction model.

The 2026-07-28 design removes the general server-to-client JSON-RPC request channel from the modern core.

Instead, the server returns a structured result that says:

I cannot complete this request yet.
I need these inputs.
Here is continuation state to echo back.

The client then reissues the original operation.

A conceptual input-required result

A simplified example:

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete the selected files?",
      "schema": {
        "type": "boolean"
      }
    }
  },
  "requestState": "opaque-continuation"
}

The exact request types and schema depend on the protocol/extension in use.

The important pieces are:

  • resultType tells the client this is not a terminal success;
  • inputRequests describes what is needed;
  • requestState lets the server carry continuation information through the client.

The client retries the original request

The next request is not a new arbitrary operation.

It is the same logical operation continued with additional input.

Conceptually:

{
  "method": "tools/call",
  "params": {
    "name": "delete_files",
    "arguments": {
      "paths": ["a.txt", "b.txt"]
    },
    "inputResponses": {
      "confirm": true
    },
    "requestState": "opaque-continuation"
  }
}

A good host preserves the logical operation identity across these HTTP requests.

One logical operation can span several HTTP requests

This is the key architecture lesson.

Do not model:

HTTP request = tool operation

Model:

logical MCP operation
  attempt 1 -> input_required
  attempt 2 -> input_required
  attempt 3 -> completed

This makes persistence, cancellation, tracing, and UI much clearer.

Give the logical operation its own ID

For example:

operation_id = op_abc123

Then record individual attempts:

op_abc123 / attempt 1
op_abc123 / attempt 2
op_abc123 / attempt 3

The protocol’s JSON-RPC request IDs can still identify wire exchanges, but your application should not depend on them as the durable workflow identity.

requestState must be treated as untrusted

The server may ask the client to echo requestState back.

The client should generally not interpret it.

Do not treat it as proof that:

  • the user approved an action;
  • a side effect occurred;
  • the active account is correct;
  • the server identity is unchanged;
  • permission was granted.

Those are host-owned facts.

requestState is server-owned continuation data.

Servers should protect important state themselves

If a server encodes business-critical state inside requestState, it should protect that state appropriately.

For example, if tampering would be dangerous, the server can use an integrity-protected token rather than trusting arbitrary client modifications.

From the client perspective, the safest policy is:

store opaque
bound size
return only to same logical operation/server
never use as host authorization

Bind requestState to the original operation

A client should never accidentally attach continuation state from one workflow to another.

Persist metadata such as:

server connection ID
protocol revision
operation ID
original method
original name/URI/task ID
requestState
MRTR round number

Before retrying, verify that those still match.

Input responses need stable keys

If the server asks for:

confirm
workspace

then the client must return values under the corresponding identifiers.

Do not infer mappings from human-readable labels.

Use the structured keys.

Validate every response before retrying

A host should validate:

  • expected field exists;
  • type matches;
  • enum value is allowed;
  • ranges and lengths are valid;
  • unsupported response types are rejected;
  • sensitive-input policy permits collection.

The server will likely validate too, but client-side validation gives better UX and avoids avoidable retries.

MRTR can represent more than elicitation

Elicitation is the clearest example, but the modern design generalizes the pattern of an operation requiring additional input.

The host should avoid hard-coding MRTR as:

show one confirmation dialog

Instead, model:

operation paused for structured input

Then route supported input request types to the appropriate host behavior.

A server may require multiple rounds

Example:

round 1 -> choose account
round 2 -> choose project
round 3 -> confirm destructive action
round 4 -> final result

The client should maintain a bounded round counter.

Without limits, a broken or malicious server could create an infinite interaction loop.

Set explicit limits

Useful limits include:

max MRTR rounds
max total elapsed time
max total input payload
max requestState size
max number of input requests per round

Do not rely on UI patience as your only loop breaker.

Cancellation is part of the protocol workflow even if it is host-owned

If the user cancels while the operation is waiting for input, persist a terminal application state:

cancelled_by_user

Do not silently retry with empty values.

Do not assume the server interprets missing input the same way your UI does.

App restarts should not destroy the workflow accidentally

A mobile/desktop client may be terminated while waiting for input.

Persist enough non-secret state to recover:

operation ID
server identity
original request
current input-required payload
requestState
round number
collected responses
permission state

On relaunch, validate that the operation is still safe to resume.

Permission and MRTR state are separate

Suppose a tool is configured as Ask.

The host may need to approve the tool before the initial request.

Then the server may later return an input-required confirmation.

These are two distinct decisions:

Host permission: may delete_files execute?
Server workflow input: proceed with these exact 3 files?

Do not use one as a substitute for the other.

See How to Build an MCP Client Permission System.

Input responses may themselves be sensitive

A server might ask for:

  • account identifiers;
  • email addresses;
  • file paths;
  • business secrets;
  • credentials.

Apply the same privacy policy you would to any outbound request.

Avoid persisting sensitive answers in normal analytics or conversation exports unless the user expects that behavior.

Retry safety still matters

MRTR explicitly requires another request, but that does not mean arbitrary retries are safe.

Consider:

attempt 2 reaches server
server performs side effect
network drops before response

The host sees an unknown outcome.

If it simply replays attempt 2, the side effect may happen twice.

Idempotency/reconciliation is still required for side-effecting operations.

See Idempotency for AI Tool Execution.

The server should not perform irreversible work before required input when avoidable

A well-designed server generally obtains needed confirmation/input before committing irreversible side effects.

For example:

prepare deletion plan
-> input_required confirmation
-> retry
-> perform deletion

rather than:

delete half the files
-> ask user what to do next

The protocol cannot force good business semantics, but clients should prefer tools with understandable transaction boundaries.

MRTR fits stateless load balancing

Because the continuation data and responses travel with the retry, the next request can land on a different server instance.

Diagram illustrating the surrounding section

That is one of the main design goals.

If the server requires hidden process-local state from instance A, it defeats the stateless architecture.

Explicit handles can complement requestState

A server may also expose application state through normal tool arguments/results.

Example:

create_draft -> draft_id
edit_draft(draft_id, ...)

That state is model-visible and application-visible.

requestState is better viewed as continuation state for the current multi-round operation, not as a replacement for every business object handle.

Trace each round

A useful tool trace can show:

Round 1
  request -> input_required(workspace)
Round 2
  response(workspace=work)
  request -> input_required(confirm)
Round 3
  response(confirm=true)
  request -> success

This is invaluable when debugging servers and user reports.

Do not expose opaque requestState to the model by default

The model generally does not need to see the opaque continuation token.

Keep it in protocol/application state.

Exposing it wastes context and may encourage the model to reason about something it should not interpret.

MRTR response handling should be type-safe

Instead of one generic dictionary everywhere, normalize supported request types into an internal model.

For example:

type InputRequest =
  | { kind: "elicitation"; key: string; schema: JSONSchema; message: string }
  | { kind: "unsupported"; rawType: string }

Unsupported types should fail explicitly rather than being silently ignored.

Version-gate MRTR behavior

Do not send modern inputResponses behavior to a legacy MCP path that expects the older server-to-client request lifecycle.

The protocol adapter should know which revision is active.

This is another reason to isolate version behavior below the conversation layer.

A practical state machine

created
-> sending
-> awaiting_result
-> input_required
-> collecting_input
-> ready_to_retry
-> sending
-> completed

Possible terminal/exception states:

cancelled
expired
permission_denied
validation_failed
auth_failed
protocol_failed
outcome_unknown

Persist state transitions that matter for recovery.

Handle duplicate UI submission

A user may tap Submit twice or the UI may restore after a crash.

Give each collected response set an internal identity and ensure the host sends at most one active retry unless deliberate retry policy says otherwise.

Do not let UI duplication become network duplication.

Timeouts should pause appropriately while waiting for the user

A 30-second HTTP timeout should not imply:

user has 30 seconds to answer

Separate:

  • network-attempt timeout;
  • user-input expiration/deadline;
  • total logical operation deadline.

A user-facing workflow may reasonably wait minutes while each HTTP attempt remains short-lived.

Test malformed MRTR payloads

Include fixtures for:

missing resultType
empty inputRequests
unsupported request type
invalid schema
duplicate keys
huge requestState
round count exceeded
malformed inputResponses
server changes requested fields on retry

The host should fail safely and preserve diagnostic context.

Test cross-instance continuation

A strong integration test forces:

attempt 1 -> server instance A
attempt 2 -> server instance B

If the workflow only works on sticky routing, the server is relying on hidden state that modern MCP is designed to avoid.

Test crash recovery

Simulate:

input_required received
state persisted
app terminated
app relaunched
user submits input
retry succeeds

Then test the harder case:

retry sent
server may have executed
app terminated before response

The client should represent the outcome as unknown until it can reconcile.

Where BYOKchat fits

A provider-neutral MCP host can treat MRTR as part of its tool-round state machine rather than tying it to one model provider.

The application can persist:

  • logical operation identity;
  • server/tool identity;
  • input-required rounds;
  • requestState;
  • user responses;
  • approval state;
  • final tool result.

That makes interactive MCP workflows recoverable and auditable while preserving the stateless protocol model.

Further reading

Keep reading