AI Native  

MCP Multi-Round Requests: Designing Reliable Tool Calls

Traditional API calls usually follow a simple pattern:

Client
   ↓
Request
   ↓
Server
   ↓
Response

That model works well when the server has everything it needs to complete an operation.

AI tools are often different.

A tool may need additional information before it can safely continue:

Client
   ↓
Call tool
   ↓
Server needs input
   ↓
Client provides input
   ↓
Server continues
   ↓
Final result

This is the problem addressed by Multi Round-Trip Requests (MRTR) in the July 28, 2026 MCP specification revision.

MRTR allows an MCP tool to return an input-required result and continue the operation after the client supplies the requested information. Crucially, this interaction does not require a long-lived MCP session when using the new stateless protocol model. The MCP C# SDK 2.0 provides server-side support through InputRequiredException and the corresponding request-state mechanism.

This changes how developers should design interactive MCP tools.

Instead of trying to recreate conversational state inside the server process, the tool can explicitly describe what it needs and carry the required continuation state between requests.

Why Multi-Round Tool Calls Matter

Consider a support-ticket tool:

close_support_ticket(ticketId)

The server receives:

ticketId = 12345

But closing a ticket also requires a reason.

A traditional API might require the caller to know this beforehand:

close_support_ticket(
    ticketId,
    closeReason
)

MRTR allows the tool to handle the missing information interactively:

Round 1
Client → close ticket 12345

Server → Input required:
         "What is the closing reason?"

Round 2
Client → "Issue resolved"

Server → Ticket closed

This is particularly useful for AI agents because the client can obtain the missing information from the user or another model interaction before resubmitting the tool call.

MRTR Versus Traditional Session State

The older mental model was:

Client
  ↓
Initialize
  ↓
Session created
  ↓
Server remembers context
  ↓
Additional request
  ↓
Session continues

The newer stateless model is:

Request 1
  ↓
InputRequiredResult
  ↓
Client gathers input
  ↓
Request 2
  ↓
Final result

The continuity is carried by the request data rather than a hidden transport session.

The MCP C# SDK 2.0 documentation specifically describes the new protocol as stateless by default and introduces MRTR so interactive operations can work without requiring a long-lived session.

This is important for horizontally scaled deployments:

Round 1 → Server A

Round 2 → Server C

The second request does not need to return to Server A simply because Server A processed the first request.

The Basic MRTR Flow

The conceptual flow is:

1. Client calls tool
        ↓
2. Tool determines additional input is required
        ↓
3. Server returns input_required
        ↓
4. Client collects input
        ↓
5. Client sends inputResponses + requestState
        ↓
6. Tool resumes
        ↓
7. Server returns final result

The important design property is that the server does not need to keep the continuation in process memory.

Implementing InputRequiredException in C#

The MCP C# SDK 2.0 provides a server-side pattern based on InputRequiredException.

A simplified example looks like:

[McpServerTool]
public static string CloseSupportTicket(
    string ticketId,
    string? closeReason)
{
    if (string.IsNullOrWhiteSpace(closeReason))
    {
        throw new InputRequiredException(
            inputs: new[]
            {
                // Define the information required
            },
            requestState: ticketId);
    }

    return $"Closed ticket {ticketId}: {closeReason}";
}

The exact input-definition types and tool signature should follow the version of the MCP C# SDK being used.

The important concept is:

Missing input
      ↓
InputRequiredException
      ↓
Client obtains input
      ↓
Tool called again

The SDK serializes the appropriate MRTR response for a compatible client.

Request State Is Not a Server Session

One of the most important concepts in MRTR is requestState.

The server can provide an opaque state value that the client echoes back on the next round.

Conceptually:

Round 1

requestState = "opaque-continuation-state"

Then:

Round 2

requestState = "opaque-continuation-state"
inputResponses = ...

The state should be treated as an opaque continuation token.

Do not design it as:

requestState =
"server=A;ticket=12345;user=abc;secret=..."

Instead, use a safe representation appropriate for your application.

Do Not Put Sensitive Data Into Request State

Request state can travel through the client.

Therefore, avoid putting secrets directly into it.

Bad design:

requestState =
"accessToken=eyJ..."

Better:

requestState =
"opaque-reference"

with the actual sensitive state stored securely on the server side when necessary.

For example:

Client
  ↓
Opaque state token
  ↓
Server
  ↓
Secure state store

This allows the continuation mechanism to remain small while sensitive application data remains under server control.

When Should You Use MRTR?

MRTR is useful when the missing information is discovered during tool execution.

Good examples include:

Confirm a destructive action
Request a missing business reason
Ask for a required configuration value
Request user approval
Collect additional tool parameters
Resolve an interactive choice

For example:

delete_customer
      ↓
Customer has active subscription
      ↓
Input required:
"Confirm cancellation?"
      ↓
User confirms
      ↓
Continue

The important characteristic is that the server discovers the need for additional input as part of the operation.

MRTR Is Not a Replacement for Tool Schemas

If the required parameter is known before the operation starts, put it in the tool schema.

For example:

[McpServerTool]
public static string CreateInvoice(
    string customerId,
    decimal amount,
    string currency)
{
    // ...
}

There is no reason to start the operation and then ask for currency.

The client should provide known required arguments from the beginning.

Use MRTR for information that genuinely becomes necessary during execution.

Design Tools to Be Resumable

An MRTR-enabled tool should be designed around explicit stages.

For example:

Validate request
      ↓
Check business rules
      ↓
Need additional input?
      ↓
Yes → InputRequired
      ↓
Receive response
      ↓
Validate response
      ↓
Perform operation
      ↓
Return result

Avoid putting irreversible operations before the input boundary.

Bad design:

Charge credit card
      ↓
Ask for confirmation

A confirmation should happen before the irreversible operation.

Better:

Validate payment
      ↓
Ask for confirmation
      ↓
Receive confirmation
      ↓
Charge credit card

This is especially important for AI tools.

Treat Each Round as a Trust Boundary

Input returned during the second round should be treated as untrusted input.

Do not assume:

Round 1 validated everything

means:

Round 2 can be trusted

Validate again.

For example:

if (string.IsNullOrWhiteSpace(closeReason))
{
    throw new ArgumentException(
        "A closing reason is required.");
}

Also validate:

Length
Format
Allowed values
Authorization
Business rules
Resource ownership

The exact validation depends on the tool.

Authorization Must Be Rechecked

Suppose Round 1 determines:

User can modify ticket 12345

Then the user waits five minutes.

Round 2 arrives.

Do not assume the authorization decision from Round 1 is still valid.

Re-evaluate authorization where appropriate:

Round 2
  ↓
Authenticate
  ↓
Authorize
  ↓
Validate state
  ↓
Perform operation

This is particularly important for privileged or destructive tools.

Make Continuation State Tamper-Resistant

If the server relies on request state for a security-sensitive workflow, do not trust a client-provided value blindly.

A continuation token can be:

Opaque database identifier

or:

Signed token

or another server-verifiable representation.

For example, a signed continuation structure might conceptually contain:

{
  "operation": "close-ticket",
  "ticketId": "12345",
  "expires": "..."
}

The actual implementation should protect integrity and avoid unnecessary sensitive data.

The client should not be able to change:

ticketId = 12345

into:

ticketId = 99999

without the server detecting the modification.

Add Expiration

Continuation state should not live forever.

Consider:

Round 1
   ↓
Input required
   ↓
RequestState created
   ↓
Expires after configured period

If Round 2 arrives after expiration:

Expired continuation
        ↓
Reject
        ↓
Ask client to restart

The appropriate expiration period depends on the workflow.

Short-lived approval workflows may need minutes.

Long-running business processes may require a durable workflow mechanism instead.

Prevent Replay

A continuation request may be retried.

The server should consider whether executing the same continuation twice could produce duplicate side effects.

For example:

Round 2
   ↓
Charge customer
   ↓
Network timeout
   ↓
Client retries Round 2

Without idempotency, the operation could potentially execute twice.

For important side effects, use an idempotency mechanism.

For example:

OperationId = close-ticket-12345-abc

Then the server can determine whether the operation has already completed.

Use Durable State for Long Operations

MRTR solves interactive input.

It does not automatically solve every long-running workflow problem.

Suppose a tool takes:

30 seconds
5 minutes
2 hours

If the operation itself is long-running, the MCP Tasks extension may be a better architectural fit.

The MCP C# SDK 2.0 provides Tasks as a separate extension package for long-running tools, with pluggable task persistence. Microsoft specifically recommends durable shared storage when tasks must survive process restarts or operate across multiple server instances.

A useful distinction is:

MRTR
→ "I need more input."

Tasks
→ "This operation takes time."

They can also work together.

MRTR and Tasks Can Work Together

A long-running operation might look like:

Start task
   ↓
Task running
   ↓
Input required
   ↓
MRTR round
   ↓
Task continues
   ↓
Task completed

The MCP SDK documentation notes that MRTR can flow through the Tasks extension for long-running tools.

This gives developers two independent concepts:

Interaction state
+
Execution state

Do not confuse the two.

Handle Client Cancellation

The client may cancel a request.

Tool implementations should respect cancellation where possible.

For example:

public async Task<string> ProcessAsync(
    string input,
    CancellationToken cancellationToken)
{
    await ProcessStepAsync(
        input,
        cancellationToken);

    return "Completed";
}

Downstream operations should also receive the cancellation token:

await httpClient.GetAsync(
    url,
    cancellationToken);

This prevents unnecessary work when the client no longer needs the result.

Make Tool Operations Idempotent Where Possible

Consider a tool:

create_customer

If the client retries the final round, you do not want:

Customer A
Customer B
Customer C

created from the same logical request.

Use an operation identifier:

operationId

and store the result:

operationId
      ↓
Existing result?
      ↓
Yes → return previous result
No  → execute operation

Not every tool can be made fully idempotent, but important side-effecting tools should have a deliberate retry strategy.

Design for Multiple Rounds

MRTR is not necessarily limited to two requests.

Conceptually:

Round 1
 ↓
Input A required

Round 2
 ↓
Input A received
 ↓
Input B required

Round 3
 ↓
Input B received
 ↓
Complete

The tool should therefore behave like a state machine:

Initial
  ↓
AwaitingInputA
  ↓
AwaitingInputB
  ↓
Executing
  ↓
Completed

This is more reliable than scattering continuation logic across unrelated code paths.

Model the State Machine Explicitly

For complex tools, define states:

public enum TicketCloseState
{
    Initial,
    AwaitingReason,
    ReadyToClose,
    Completed
}

Then process the current state deliberately.

For example:

switch (state)
{
    case TicketCloseState.Initial:
        // Validate ticket.
        break;

    case TicketCloseState.AwaitingReason:
        // Validate supplied reason.
        break;

    case TicketCloseState.ReadyToClose:
        // Perform operation.
        break;
}

The exact design depends on the workflow.

The important point is that multi-round behavior should be explicit.

Keep Request State Small

Do not use request state as a database.

Avoid:

requestState =
entire customer record
+
entire order
+
permissions
+
tool output
+
configuration

Instead:

requestState =
small opaque continuation reference

Then retrieve additional state when the next request arrives.

This reduces payload size and makes state easier to secure.

Separate User Input From Server State

Suppose the server needs:

closeReason

and also needs:

ticketId
authorization context
workflow state

Do not merge all of them into a single string.

Conceptually:

InputResponses
    ↓
User-provided values

RequestState
    ↓
Server continuation context

This separation makes validation and security easier.

Validate Input Responses

The server should validate every response.

For example:

var reason =
    inputResponse?.Trim();

if (string.IsNullOrWhiteSpace(reason))
{
    throw new InvalidOperationException(
        "A close reason is required.");
}

if (reason.Length > 500)
{
    throw new InvalidOperationException(
        "The close reason is too long.");
}

For enumerated values:

var allowedReasons =
    new[]
    {
        "Resolved",
        "Duplicate",
        "Cancelled"
    };

if (!allowedReasons.Contains(reason))
{
    throw new InvalidOperationException(
        "Unsupported closing reason.");
}

Never assume that an LLM-generated response is valid simply because it looks reasonable.

Avoid Ambiguous Input Requests

The server should ask for exactly what it needs.

Poor request:

Please provide additional information.

Better:

Provide the reason for closing ticket 12345.

Even better, when the protocol supports structured input:

Field:
closeReason

Required:
true

Allowed values:
Resolved
Duplicate
Cancelled

Structured requests are easier for clients and agents to process consistently.

Design Safe Confirmation Flows

Destructive tools are an obvious MRTR use case.

Consider:

delete_database

The first round can perform validation:

Database exists
User authorized
Dependencies checked

Then request confirmation:

Input required:
"Confirm deletion."

Only after confirmation:

Delete database

This creates a clear safety boundary:

Validation
   ↓
Confirmation
   ↓
Side effect

Do not place the irreversible action before the confirmation boundary.

Add Business Validation After Confirmation

Even after confirmation, revalidate critical conditions.

For example:

Round 1
 ↓
Account status = Active

User confirms

Round 2
 ↓
Recheck account status
 ↓
Execute

This avoids relying on stale state.

Handle State Expiration Gracefully

If request state expires:

Client
  ↓
Old requestState
  ↓
Server
  ↓
Expired

return a clear error.

The client should know that it needs to restart the workflow rather than endlessly retrying the same invalid state.

For example:

"The continuation has expired. Please start the operation again."

The exact protocol-level error handling should follow the MCP SDK and specification behavior.

Test MRTR Like an API Contract

An MRTR tool has more than one successful path.

Test:

Initial request
Missing input
Valid input
Invalid input
Expired state
Tampered state
Duplicate continuation
Cancellation
Authorization failure
Dependency failure

A useful test matrix is:

ScenarioExpected Result
Complete input providedImmediate success
Missing inputInput required
Valid continuationFinal result
Invalid responseValidation error
Expired stateRestart required
Tampered stateReject
Duplicate requestIdempotent behavior
CancellationOperation cancelled
Unauthorized userAuthorization failure

This is more valuable than testing only the happy path.

Unit Test the State Transitions

For a state-machine-style implementation:

Initial
 ↓
AwaitingReason
 ↓
ReadyToClose
 ↓
Completed

test every transition.

For example:

[Fact]
public void MissingReason_RequiresInput()
{
    // Arrange
    // Act
    // Assert
}

Then:

[Fact]
public void ValidReason_CompletesOperation()
{
    // Arrange
    // Act
    // Assert
}

Keep these tests independent from network infrastructure where possible.

Add Integration Tests

Unit tests cannot verify the complete MCP wire behavior.

Integration tests should verify:

MCP client
   ↓
HTTP transport
   ↓
MCP server
   ↓
Input-required response
   ↓
Second request
   ↓
Final result

This confirms that:

  • Request state is serialized correctly.

  • Input responses are accepted.

  • The tool resumes correctly.

  • The final result has the expected shape.

Test Stateless Load Balancing

This is one of the most important MRTR tests.

Deploy:

Server A
Server B
Server C

Then intentionally route:

Round 1 → Server A
Round 2 → Server B
Round 3 → Server C

The operation should continue when the state model is designed correctly.

If it only works when Round 2 returns to Server A, you have accidentally introduced hidden process-local state.

Test Server Restart

Run:

Round 1
 ↓
Input required
 ↓
Restart server
 ↓
Round 2

If the workflow is supposed to survive a restart, the continuation state must not depend on process memory.

For workflows that require durable state, use a shared durable store.

Test Concurrent Continuations

Two clients may independently execute the same tool.

For example:

Client A
  ↓
Workflow A

Client B
  ↓
Workflow B

Ensure that:

Workflow A state

cannot accidentally be applied to:

Workflow B

This is particularly important when state is stored in caches or databases.

Use unique operation or workflow identifiers.

Test Duplicate Requests

Network failures can cause retries.

Test:

Round 2
 ↓
Server completes operation
 ↓
Response lost
 ↓
Client retries Round 2

The application should have a deliberate behavior.

For idempotent operations:

Retry
 ↓
Return existing result

For non-idempotent operations:

Retry
 ↓
Detect duplicate
 ↓
Do not repeat side effect

The exact strategy depends on the operation.

Add Observability to Every Round

Use distributed tracing for individual requests and an application-level workflow identifier when multiple requests form one operation.

For example:

Workflow: WF123

Trace A
 └── close_support_ticket
      └── Input required

Trace B
 └── close_support_ticket
      └── Completed

This is preferable to recreating a hidden MCP session merely for observability.

The stateless MCP design is specifically intended to work with ordinary HTTP infrastructure and horizontal scaling.

Monitor MRTR Metrics

Useful metrics include:

mcp.mrtr.started
mcp.mrtr.input_required
mcp.mrtr.completed
mcp.mrtr.expired
mcp.mrtr.cancelled
mcp.mrtr.failed

Break them down by low-cardinality dimensions:

ToolName
Outcome

Avoid putting:

UserId
OrderId
WorkflowId
TraceId

into metric labels.

Use logs and traces for those values.

Common Mistakes

Using MRTR for Every Missing Parameter

If the required parameter is known at the beginning, put it in the tool schema.

Storing Continuation State in Memory

This breaks when requests move between instances or the process restarts.

Putting Secrets in Request State

Request state can travel through the client.

Use opaque references instead.

Performing Side Effects Before Confirmation

Do not delete, charge, publish, or send before required confirmation.

Trusting Second-Round Input

Validate it like any other external input.

Ignoring Replay

Design idempotency for important operations.

Treating MRTR as a Long-Running Job System

Use the Tasks extension when execution itself is long-running.

Assuming Two Rounds Are Always Enough

Design the tool as an explicit workflow when multiple input stages are possible.

Troubleshooting

The Second Request Cannot Continue

Check:

RequestState
InputResponses
Tool arguments
Protocol version
Client capability

Also verify that the server is not depending on process-local state.

MRTR Works Locally but Fails Behind a Load Balancer

Check whether continuation state is stored only in memory.

The second request may be reaching another instance.

Old Clients Cannot Complete the Interactive Flow

MCP C# SDK 2.0 includes compatibility behavior for older clients when stateful sessions are available. However, a down-level client operating without a session cannot use the new MRTR interaction. The SDK documentation explicitly describes these compatibility cases.

Provide a non-interactive argument path when backward compatibility requires it.

The Tool Executes Twice

Investigate retry behavior and implement idempotency for side-effecting operations.

Request State Is Too Large

Store durable information externally and use a small opaque continuation reference.

Input Is Valid but Authorization Fails

This is usually desirable.

Authorization should be evaluated at the point where the operation is actually performed.

Backward Compatibility

MCP C# SDK 2.0 is designed to preserve compatibility with earlier clients and servers.

For MRTR specifically, the compatibility behavior depends on both protocol version and session mode.

Conceptually:

ClientSessionMRTR
New protocolStatelessNative MRTR
New protocolStatefulNative MRTR
Older protocolStatefulSDK compatibility bridge
Older protocolStatelessMRTR unavailable

The official SDK documentation describes this matrix and notes that older clients can use compatibility behavior when stateful sessions are available.

This means a migration should not assume that every connected client immediately understands MRTR.

Recommended Architecture

A robust MRTR-enabled service can be modeled as:

                    MCP Client
                         |
                         v
                 Load Balancer
                         |
            +------------+------------+
            |            |            |
            v            v            v
        Server A      Server B      Server C
            |            |            |
            +------------+------------+
                         |
                         v
                  Tool Workflow
                         |
             +-----------+-----------+
             |                       |
             v                       v
       Durable State          External APIs
             |
             v
        Database / Cache

The request flow is:

Request 1
   ↓
Validate
   ↓
Input required
   ↓
Request state
   ↓
Client collects input
   ↓
Request 2
   ↓
Validate again
   ↓
Authorize again
   ↓
Execute
   ↓
Final result

This architecture works naturally with stateless HTTP.

Best Practices

  1. Use MRTR when additional input is discovered during tool execution.

  2. Put known required parameters directly in the tool schema.

  3. Treat requestState as opaque continuation state.

  4. Never place secrets directly into request state.

  5. Keep continuation state small.

  6. Use durable storage when workflows must survive restarts.

  7. Revalidate authorization on continuation.

  8. Validate every input response.

  9. Protect continuation state from tampering.

  10. Add expiration to sensitive workflows.

  11. Design side-effecting tools for idempotency.

  12. Prevent duplicate execution after retries.

  13. Separate MRTR interaction state from long-running task state.

  14. Use Tasks for operations whose execution itself is long-running.

  15. Test different server instances handling different rounds.

  16. Test process restarts.

  17. Test cancellation and retries.

  18. Add tracing to every round.

  19. Use workflow identifiers when several requests form one business operation.

  20. Test older clients if backward compatibility is required.

Frequently Asked Questions

What is Multi Round-Trip Request in MCP?

MRTR allows an MCP tool to request additional input during execution and continue after the client sends that input back. The July 28, 2026 MCP specification introduced this mechanism so interactive tools do not require a long-lived session.

Does MRTR require a session?

No.

Native MRTR is designed to work with stateless MCP HTTP requests. Continuation information is carried through the request/response flow rather than requiring a server-side transport session.

What is requestState?

It is continuation information supplied by the server and returned by the client on the subsequent request. It allows the server to associate the later request with the appropriate operation without requiring hidden transport session state.

Should request state contain the complete workflow?

Usually no.

Keep it small and opaque. Store larger or sensitive state in a secure server-side data store.

Is MRTR the same as a long-running task?

No.

MRTR addresses additional input during an operation.

The MCP Tasks extension addresses long-running execution and task lifecycle. The two mechanisms can be combined when a long-running operation also requires interactive input.

Can Round 1 and Round 2 reach different server instances?

Yes.

That is one of the important advantages of the stateless model. The continuation should not depend on the process that handled the previous request.

Should I use MRTR for confirmation dialogs?

It can be a good fit when confirmation is discovered as part of tool execution, particularly for destructive or sensitive actions.

How should I handle retries?

Use idempotency for side-effecting operations and associate the logical operation with a unique identifier.

Can older MCP clients use MRTR?

Compatibility depends on the protocol version and transport/session mode. The MCP C# SDK 2.0 provides a compatibility bridge for older clients in stateful scenarios, while a down-level stateless client cannot perform the new interactive MRTR flow.

Conclusion

Multi Round-Trip Requests change an important assumption in MCP tool design.

Previously, an interactive tool could depend on a long-lived connection or session:

Session
   ↓
Tool
   ↓
Server asks
   ↓
Client responds

The new model is more aligned with modern distributed HTTP systems:

Request
   ↓
Input required
   ↓
Client responds
   ↓
New request
   ↓
Continue

The MCP C# SDK 2.0 implements this model through InputRequiredException, input-required results, and request-state continuation. The July 28, 2026 specification makes this possible without requiring protocol-level session state.

The key engineering challenge is therefore not simply learning how to throw InputRequiredException.

It is designing the tool so that the entire workflow remains reliable when:

Requests are retried
Requests reach different instances
Servers restart
Users provide invalid input
Authorization changes
Continuation state expires
Clients cancel operations
Dependencies fail

A production-ready MRTR implementation should follow:

Explicit workflow
      ↓
Small continuation state
      ↓
Strong validation
      ↓
Authorization
      ↓
Idempotency
      ↓
Durable state when required
      ↓
Distributed tracing
      ↓
Failure handling

The most important principle is:

Use MRTR to make interaction explicit, not to recreate hidden session state.

When designed this way, MCP tools can remain interactive while still benefiting from stateless HTTP, load balancing, horizontal scaling, and ordinary ASP.NET Core infrastructure.

That combination makes multi-round AI tool execution much closer to a conventional distributed application architecture—and significantly easier to reason about, test, and operate in production.