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:
| Scenario | Expected Result |
|---|
| Complete input provided | Immediate success |
| Missing input | Input required |
| Valid continuation | Final result |
| Invalid response | Validation error |
| Expired state | Restart required |
| Tampered state | Reject |
| Duplicate request | Idempotent behavior |
| Cancellation | Operation cancelled |
| Unauthorized user | Authorization 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:
| Client | Session | MRTR |
|---|
| New protocol | Stateless | Native MRTR |
| New protocol | Stateful | Native MRTR |
| Older protocol | Stateful | SDK compatibility bridge |
| Older protocol | Stateless | MRTR 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
Use MRTR when additional input is discovered during tool execution.
Put known required parameters directly in the tool schema.
Treat requestState as opaque continuation state.
Never place secrets directly into request state.
Keep continuation state small.
Use durable storage when workflows must survive restarts.
Revalidate authorization on continuation.
Validate every input response.
Protect continuation state from tampering.
Add expiration to sensitive workflows.
Design side-effecting tools for idempotency.
Prevent duplicate execution after retries.
Separate MRTR interaction state from long-running task state.
Use Tasks for operations whose execution itself is long-running.
Test different server instances handling different rounds.
Test process restarts.
Test cancellation and retries.
Add tracing to every round.
Use workflow identifiers when several requests form one business operation.
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.