Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f15eb86
Add SEP-2575 + SEP-2567 draft protocol support
halter73 May 28, 2026
64aaf70
Add draft-mode tests and update XML docs
halter73 May 28, 2026
bd5c624
Rename ExperimentalProtocolVersion to ProtocolVersion + sync draft ve…
halter73 Jun 5, 2026
cb23930
Flip Stateless default to true and obsolete stateful Streamable HTTP …
halter73 Jun 5, 2026
04da5c8
Add fallback negotiation, MinProtocolVersion, server-side _meta proto…
halter73 Jun 6, 2026
30782f6
Add raw HTTP conformance tests + document MCP9005
halter73 Jun 6, 2026
fb9225e
Expose DraftProtocolVersion publicly and update stateless concept doc
halter73 Jun 6, 2026
dd84298
Merge origin/main into halter73/remove-session-id-draft
halter73 Jun 6, 2026
c2878b7
Fix draft client rejecting downgraded version after legacy fallback
halter73 Jun 6, 2026
c4662a2
Gate built-in ping handler to legacy protocol versions
halter73 Jun 6, 2026
9ff24d0
Merge origin/main into halter73/remove-session-id-draft
halter73 Jun 11, 2026
1b8dd92
Merge per-request client capabilities instead of overwriting them
halter73 Jun 11, 2026
51571c6
Add SEP-2549 caching hints (ttlMs and cacheScope) to cacheable results
Jun 3, 2026
58512d8
Address review feedback on cacheScope converter and conformance gate
Jun 3, 2026
ed8561f
Drop TimeSpanMillisecondsConverterSharedTests after McpTask removal
halter73 Jun 11, 2026
8ff1735
Add ttlMs and cacheScope to DiscoverResult per spec PR #2855
halter73 Jun 11, 2026
e162a8a
Add regression tests for SEP-2575 _meta envelope on list-style requests
halter73 Jun 11, 2026
ae1edef
Clarify stdio fallback comment per spec PR #2844
halter73 Jun 11, 2026
ccdd422
Accept null-id JSON-RPC error responses per spec
halter73 Jun 11, 2026
00d57f7
Surface JSON-RPC errors in HTTP 400 bodies as McpProtocolException
halter73 Jun 11, 2026
276bde4
Surface HeaderMismatch errors instead of falling back to legacy
halter73 Jun 11, 2026
3778e00
Recognize JSON-RPC errors in AutoDetect HTTP transport (no SSE fallback)
halter73 Jun 11, 2026
d539e7f
Bump @modelcontextprotocol/conformance pin from 0.1.16 to 0.2.0-alpha.2
halter73 Jun 12, 2026
f3698c7
Tighten conformance gates with HasMatchingDraftWireVersion guard
halter73 Jun 12, 2026
d9277e0
tests: align conformance fixtures with @modelcontextprotocol/conforma…
halter73 Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ Here's an example implementation of how a console application might handle elici

### Multi Round-Trip Requests (MRTR)

[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `DRAFT-2026-v1`. Under the draft protocol, the server-to-client `elicitation/create` request method is removed; the recommended way to ask the user for input from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.
[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `2026-07-28`. Under the draft protocol, the server-to-client `elicitation/create` request method is removed; the recommended way to ask the user for input from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.

> [!IMPORTANT]
> `ElicitAsync` throws `InvalidOperationException("Elicitation is not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `DRAFT-2026-v1` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `elicitation/create` request flow. For code that needs to run on stateless servers — including all `DRAFT-2026-v1` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.
> `ElicitAsync` throws `InvalidOperationException("Elicitation is not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `2026-07-28` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `elicitation/create` request flow. For code that needs to run on stateless servers — including all `2026-07-28` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.

For example:

Expand All @@ -196,7 +196,7 @@ public static string ElicitWithMrtr(

if (!server.IsMrtrSupported)
{
return "This tool requires MRTR support (DRAFT-2026-v1, or a stateful current-protocol session).";
return "This tool requires MRTR support (2026-07-28, or a stateful current-protocol session).";
}

// First call — request user input
Expand Down
24 changes: 12 additions & 12 deletions docs/concepts/mrtr/mrtr.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ uid: mrtr

<!-- mlc-disable-next-line -->
> [!WARNING]
> MRTR is part of the **`DRAFT-2026-v1`** revision of the MCP specification ([SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)). The wire format and API surface may change before the revision is ratified. See the [Experimental APIs](../../experimental.md) documentation for details on working with experimental APIs.
> MRTR is part of the **`2026-07-28`** revision of the MCP specification ([SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)). The wire format and API surface may change before the revision is ratified. See the [Experimental APIs](../../experimental.md) documentation for details on working with experimental APIs.

Multi Round-Trip Requests (MRTR) let a server tool request input from the client — such as [elicitation](xref:elicitation), [sampling](xref:sampling), or [roots](xref:roots) — as part of a single tool call, without requiring a separate server-to-client JSON-RPC request for each interaction. Instead of returning a final result, the server returns an **incomplete result** containing one or more input requests. The client fulfills those requests and retries the original tool call with the responses attached.

Expand All @@ -33,13 +33,13 @@ MRTR is useful when:

## Opting in

MRTR activates when both peers negotiate protocol revision **`DRAFT-2026-v1`** during `initialize`. The C# SDK opts in by listing `DRAFT-2026-v1` as a supported protocol version on the client; servers automatically accept it when offered. No experimental flags are required.
MRTR activates when both peers negotiate protocol revision **`2026-07-28`** during `initialize`. The C# SDK opts in by listing `2026-07-28` as a supported protocol version on the client; servers automatically accept it when offered. No experimental flags are required.

```csharp
// Client
var clientOptions = new McpClientOptions
{
ProtocolVersion = "DRAFT-2026-v1",
ProtocolVersion = "2026-07-28",
Handlers = new McpClientHandlers
{
ElicitationHandler = HandleElicitationAsync,
Expand All @@ -48,7 +48,7 @@ var clientOptions = new McpClientOptions
};
```

Under `DRAFT-2026-v1`, MRTR is the recommended way to obtain client input from a server handler. The spec removes the legacy server-to-client `elicitation/create`, `sampling/createMessage`, and `roots/list` request methods, so any code that needs to work on a `DRAFT-2026-v1` Streamable HTTP server (which will be stateless-only in a future revision) must use `InputRequiredException` rather than <xref:ModelContextProtocol.Server.McpServer.ElicitAsync*>, <xref:ModelContextProtocol.Server.McpServer.SampleAsync*>, or <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>. The legacy methods still work on stateful sessions — that's how stdio servers keep working under draft today — but they throw `InvalidOperationException("X is not supported in stateless mode.")` on any stateless session, current or draft.
Under `2026-07-28`, MRTR is the recommended way to obtain client input from a server handler. The spec removes the legacy server-to-client `elicitation/create`, `sampling/createMessage`, and `roots/list` request methods, so any code that needs to work on a `2026-07-28` Streamable HTTP server (which will be stateless-only in a future revision) must use `InputRequiredException` rather than <xref:ModelContextProtocol.Server.McpServer.ElicitAsync*>, <xref:ModelContextProtocol.Server.McpServer.SampleAsync*>, or <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>. The legacy methods still work on stateful sessions — that's how stdio servers keep working under draft today — but they throw `InvalidOperationException("X is not supported in stateless mode.")` on any stateless session, current or draft.

Under the current protocol revision (`2025-06-18` and earlier), `InputRequiredException` is still supported in stateful sessions via a backward-compatibility resolver — see [Compatibility](#compatibility) below.

Expand All @@ -60,7 +60,7 @@ A tool participates in MRTR by throwing <xref:ModelContextProtocol.Protocol.Inpu

Tools should check <xref:ModelContextProtocol.Server.McpServer.IsMrtrSupported> before throwing `InputRequiredException`. It returns `true` when either:

- The negotiated protocol revision is `DRAFT-2026-v1` (MRTR is native), or
- The negotiated protocol revision is `2026-07-28` (MRTR is native), or
- The session is stateful under the current protocol (the SDK can resolve input requests via legacy JSON-RPC and retry the handler).

```csharp
Expand All @@ -71,7 +71,7 @@ public static string MyTool(
{
if (!server.IsMrtrSupported)
{
return "This tool requires a client that negotiates DRAFT-2026-v1, "
return "This tool requires a client that negotiates 2026-07-28, "
+ "or a stateful current-protocol session.";
}

Expand Down Expand Up @@ -258,7 +258,7 @@ When MRTR is not supported, you can provide domain-specific guidance:
if (!server.IsMrtrSupported)
{
return "This tool requires interactive input. To use it:\n"
+ "1. Connect with a client that negotiates MCP protocol revision DRAFT-2026-v1, or\n"
+ "1. Connect with a client that negotiates MCP protocol revision 2026-07-28, or\n"
+ "2. Use a stateful current-protocol session so the server can resolve the input requests for you.\n"
+ "\nStateless current-protocol sessions cannot resolve MRTR input requests.";
}
Expand All @@ -270,22 +270,22 @@ The SDK supports `InputRequiredException` across two protocol revisions and two

| Negotiated protocol | Session mode | Behavior |
|---|---|---|
| `DRAFT-2026-v1` | Stateful | Native MRTR — `InputRequiredResult` is serialized directly to the wire. |
| `DRAFT-2026-v1` | Stateless | Native MRTR — `InputRequiredResult` is serialized directly to the wire. No server-side handler state needed. |
| `2026-07-28` | Stateful | Native MRTR — `InputRequiredResult` is serialized directly to the wire. |
| `2026-07-28` | Stateless | Native MRTR — `InputRequiredResult` is serialized directly to the wire. No server-side handler state needed. |
| Current (`2025-06-18` and earlier) | Stateful | Backward-compatibility resolver — the SDK sends standard `elicitation/create` / `sampling/createMessage` / `roots/list` JSON-RPC requests to the client, collects the responses, and retries the handler with `inputResponses` populated. Up to 10 retry rounds. |
| Current (`2025-06-18` and earlier) | Stateless | **Not supported** — `InputRequiredException` raises an `McpException`. The client doesn't speak MRTR, and the server can't resolve input requests via JSON-RPC without a persistent session. |

> [!NOTE]
> The backcompat resolver is intentionally limited to 10 retry rounds. Tools that need more rounds should require `DRAFT-2026-v1` (check `IsMrtrSupported`).
> The backcompat resolver is intentionally limited to 10 retry rounds. Tools that need more rounds should require `2026-07-28` (check `IsMrtrSupported`).

### Why `ElicitAsync` / `SampleAsync` / `RequestRootsAsync` throw on stateless servers

`ElicitAsync` / `SampleAsync` / `RequestRootsAsync` issue a JSON-RPC request to the client and wait for the response on the same session. Stateless servers don't have a persistent session to wait on, so the SDK fails fast with `InvalidOperationException("X is not supported in stateless mode.")` (the check is `McpServer.ClientCapabilities is null`, which is the SDK's proxy for stateless).

Under the current protocol revision (`2025-06-18` and earlier), stdio and stateful Streamable HTTP keep `ClientCapabilities` populated, so the legacy methods work normally and remain the recommended way to do one-shot client interactions. Under `DRAFT-2026-v1`, the spec removes those request methods from Streamable HTTP entirely; the SDK still allows the legacy methods on draft stdio sessions because stdio is implicitly single-process / stateful and the client handler is wired up regardless of negotiated revision. `InputRequiredException` is the way to write tools that work on every supported configuration.
Under the current protocol revision (`2025-06-18` and earlier), stdio and stateful Streamable HTTP keep `ClientCapabilities` populated, so the legacy methods work normally and remain the recommended way to do one-shot client interactions. Under `2026-07-28`, the spec removes those request methods from Streamable HTTP entirely; the SDK still allows the legacy methods on draft stdio sessions because stdio is implicitly single-process / stateful and the client handler is wired up regardless of negotiated revision. `InputRequiredException` is the way to write tools that work on every supported configuration.

### Future direction

The `DRAFT-2026-v1` revision is moving toward a stateless-only model: `Mcp-Session-Id` is being removed, and Streamable HTTP servers will run statelessly by default under the draft revision. When that lands, the `Stateful` row for `DRAFT-2026-v1` in the compatibility matrix above collapses into the `Stateless` row (Streamable HTTP under draft becomes stateless-only), and `InputRequiredException` becomes uniformly required for non-stdio servers. The current-protocol resolver path will remain for backward compatibility with older clients and stateful servers.
The `2026-07-28` revision is moving toward a stateless-only model: `Mcp-Session-Id` is being removed, and Streamable HTTP servers will run statelessly by default under the draft revision. When that lands, the `Stateful` row for `2026-07-28` in the compatibility matrix above collapses into the `Stateless` row (Streamable HTTP under draft becomes stateless-only), and `InputRequiredException` becomes uniformly required for non-stdio servers. The current-protocol resolver path will remain for backward compatibility with older clients and stateful servers.

This work is a follow-up to the present PR.
6 changes: 3 additions & 3 deletions docs/concepts/roots/roots.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ server.RegisterNotificationHandler(

### Multi Round-Trip Requests (MRTR)

[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `DRAFT-2026-v1`. Under the draft protocol, the server-to-client `roots/list` request method is removed; the recommended way to ask the client for its roots from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.
[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `2026-07-28`. Under the draft protocol, the server-to-client `roots/list` request method is removed; the recommended way to ask the client for its roots from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.

> [!IMPORTANT]
> `RequestRootsAsync` throws `InvalidOperationException("Roots are not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `DRAFT-2026-v1` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `roots/list` request flow. For code that needs to run on stateless servers — including all `DRAFT-2026-v1` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.
> `RequestRootsAsync` throws `InvalidOperationException("Roots are not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `2026-07-28` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `roots/list` request flow. For code that needs to run on stateless servers — including all `2026-07-28` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.

For example:

Expand All @@ -128,7 +128,7 @@ public static string ListRootsWithMrtr(

if (!server.IsMrtrSupported)
{
return "This tool requires MRTR support (DRAFT-2026-v1, or a stateful current-protocol session).";
return "This tool requires MRTR support (2026-07-28, or a stateful current-protocol session).";
}

// First call — request the client's root list
Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/sampling/sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ Sampling requires the client to advertise the `sampling` capability. This is han

### Multi Round-Trip Requests (MRTR)

[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `DRAFT-2026-v1`. Under the draft protocol, the server-to-client `sampling/createMessage` request method is removed; the recommended way to ask the client to sample from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.
[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `2026-07-28`. Under the draft protocol, the server-to-client `sampling/createMessage` request method is removed; the recommended way to ask the client to sample from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.

> [!IMPORTANT]
> `SampleAsync` and `AsSamplingChatClient` throw `InvalidOperationException("Sampling is not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `DRAFT-2026-v1` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `sampling/createMessage` request flow. For code that needs to run on stateless servers — including all `DRAFT-2026-v1` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.
> `SampleAsync` and `AsSamplingChatClient` throw `InvalidOperationException("Sampling is not supported in stateless mode.")` whenever the server is running stateless — which includes every Streamable HTTP server under `2026-07-28` once that revision is forced to stateless-only in a future PR. Stdio servers and current-protocol stateful Streamable HTTP servers continue to work via the legacy server-to-client `sampling/createMessage` request flow. For code that needs to run on stateless servers — including all `2026-07-28` Streamable HTTP servers going forward — throw `InputRequiredException` from your handler instead. It works under both protocols and both session modes.

For example:

Expand All @@ -146,7 +146,7 @@ public static string SampleWithMrtr(

if (!server.IsMrtrSupported)
{
return "This tool requires MRTR support (DRAFT-2026-v1, or a stateful current-protocol session).";
return "This tool requires MRTR support (2026-07-28, or a stateful current-protocol session).";
}

// First call — request LLM completion from the client
Expand Down
Loading
Loading