Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions docs/concepts/capabilities/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ if (client.ServerCapabilities.Resources is { Subscribe: true })
// Check if the server supports prompts with list-changed notifications
if (client.ServerCapabilities.Prompts is { ListChanged: true })
{
mcpClient.RegisterNotificationHandler(
client.RegisterNotificationHandler(
NotificationMethods.PromptListChangedNotification,
async (notification, ct) =>
{
var prompts = await mcpClient.ListPromptsAsync(cancellationToken: ct);
var prompts = await client.ListPromptsAsync(cancellationToken: ct);
});
}

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ builder.Services.AddMcpServer()
{
// Stateless mode is recommended for servers that don't need
// server-to-client requests like sampling or elicitation.
// See the Sessions documentation for details.
// See the Stateless and Stateful documentation for details.
options.Stateless = true;
})
.WithToolsFromAssembly();
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ To install the SDK and build your first MCP client and server, see [Getting star
| - | - |
| [Capabilities](capabilities/capabilities.md) | Learn how client and server capabilities are negotiated during initialization, including protocol version negotiation. |
| [Transports](transports/transports.md) | Learn how to configure stdio, Streamable HTTP, and SSE transports for client-server communication. |
| [Stateless and Stateful](stateless/stateless.md) | Learn when to use stateless vs. stateful mode for HTTP servers and how to configure sessions. |
| [Ping](ping/ping.md) | Learn how to verify connection health using the ping mechanism. |
| [Progress tracking](progress/progress.md) | Learn how to track progress for long-running operations through notification messages. |
| [Cancellation](cancellation/cancellation.md) | Learn how to cancel in-flight MCP requests using cancellation tokens and notifications. |
Expand All @@ -43,7 +44,6 @@ To install the SDK and build your first MCP client and server, see [Getting star
| [Completions](completions/completions.md) | Learn how to implement argument auto-completion for prompts and resource templates. |
| [Logging](logging/logging.md) | Learn how to implement logging in MCP servers and how clients can consume log messages. |
| [Pagination](pagination/pagination.md) | Learn how to use cursor-based pagination when listing tools, prompts, and resources. |
| [Stateless and Stateful](stateless/stateless.md) | Learn when to use stateless vs. stateful mode for HTTP servers and how to configure sessions. |
| [HTTP Context](httpcontext/httpcontext.md) | Learn how to access the underlying `HttpContext` for a request. |
| [MCP Server Handler Filters](filters.md) | Learn how to add filters to the handler pipeline. Filters let you wrap the original handler with additional functionality. |

Expand Down
7 changes: 5 additions & 2 deletions docs/concepts/logging/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ MCP servers can expose log messages to clients through the [Logging utility].

[Logging utility]: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging

> [!IMPORTANT]
> Logging is **deprecated** as of MCP specification revision `2026-07-28` ([SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577), [MCP9005](xref:list-of-diagnostics#obsolete-apis)) and may be removed in a future version.

This document describes how to implement logging in MCP servers and how clients can consume log messages.

### Logging levels
Expand All @@ -32,8 +35,8 @@ different set of logging levels. The following table shows the levels and how th
| `emergency` | | System is unusable | |

**Note:** .NET's [ILogger] also supports a `Trace` level (more verbose than Debug) log level.
As there is no equivalent level in the MCP logging levels, Trace level logs messages are silently
dropped when sending messages to the client.
As there is no more verbose level in the MCP logging levels, Trace level log messages are mapped to
the MCP `debug` level when sent to the client.

[ILogger]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger
[ILoggerProvider]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerprovider
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/logging/samples/server/Tools/LoggingTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static async Task<string> LoggingTool(
var progressToken = context.Params.ProgressToken;
var stepDuration = duration / steps;

// <snippet_LoggingConfiguration >
// <snippet_LoggingConfiguration>
ILoggerProvider loggerProvider = context.Server.AsClientLoggerProvider();
ILogger logger = loggerProvider.CreateLogger("LoggingTools");
// </snippet_LoggingConfiguration>
Expand Down
99 changes: 93 additions & 6 deletions docs/concepts/mrtr/mrtr.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,107 @@ public static string WizardTool(
}
```

### Providing custom error messages
### Supporting down-level clients

When MRTR is not supported, you can provide domain-specific guidance:
When <xref:ModelContextProtocol.Server.McpServer.IsMrtrSupported> is `false`, the tool
can't obtain client input through an input request — but that doesn't have to be a dead
end. Handle down-level and stateless callers along a spectrum, from a helpful message to a
fully functional non-interactive path.

#### Return actionable guidance

At minimum, tell the caller how to proceed instead of failing opaquely. Explain what kind
of session would enable the interactive path, and — when the tool exposes one — how to
invoke the non-interactive path described below:

```csharp
if (!server.IsMrtrSupported)
{
return "This tool requires interactive input. To use it:\n"
+ "1. Connect with a client that negotiates MCP protocol revision 2026-07-28, or\n"
+ "2. Use a stateful session using protocol revision 2025-11-25 so the server can resolve the input requests for you.\n"
+ "\nStateless sessions using protocol revision 2025-11-25 and earlier cannot resolve MRTR input requests.";
return "This tool needs interactive input. Connect with a client that negotiates MCP "
+ "protocol revision 2026-07-28, or use a stateful session using revision 2025-11-25 "
+ "so the server can resolve the input requests for you.";
}
```

#### Offer a non-interactive fallback

When a tool *can* complete without a prompt, expose an explicit argument that lets a
down-level or stateless caller opt in directly. The tool stays usable everywhere: it
elicits confirmation when MRTR is available, and otherwise returns guidance the caller
(or its model) can act on by resending with the argument set.

The confirmation tool below is just an example of a tool that has a sensible
non-interactive fallback. It also shows how to branch on the elicitation **action** rather
than assuming acceptance: the deserialized <xref:ModelContextProtocol.Protocol.ElicitResult>
carries an <xref:ModelContextProtocol.Protocol.ElicitResult.Action> of `"accept"`,
`"decline"`, or `"cancel"`, surfaced through the
<xref:ModelContextProtocol.Protocol.ElicitResult.IsAccepted> shorthand.

```csharp
[McpServerTool, Description("Deletes a file (with required confirmation).")]
public static string DeleteFile(
McpServer server,
RequestContext<CallToolRequestParams> context,
[Description("The path of the file to delete")] string path,
[Description("User confirmation to delete the file")] bool confirm = false)
{
// Handles four client scenarios:
// 1. Explicit opt-in: client sends `confirm: true`
// 2. MRTR round-trip request: client sends `InputResponses["confirm"]` with action `accept`
// 3. MRTR initial request: server elicits input (with automatic SDK down-level bridge)
// 4. Session-less down-level: server returns a guidance message requesting explicit opt-in

// (1) Explicit opt-in. Works on any client, including down-level session-less
// because a previous response gave instructions for passing `confirm: true`.
// These requests are typically sent after (4) returns a guidance message.
var deletionConfirmed = confirm;

// (2) MRTR round-trip request. Works with native MRTR support or the automatic down-level
// SDK bridge after (3) throws an `InputRequiredException` to elicit input.
if (!confirm && context.Params?.InputResponses?.TryGetValue("confirm", out var confirmResponse) is true)
{
var confirmResult = confirmResponse.Deserialize(InputResponse.ElicitResultJsonTypeInfo);
deletionConfirmed = confirmResult?.IsAccepted is true;

if (!deletionConfirmed) return "Deletion cancelled";
}

// (1) or (2) Explicit opt-in or confirmation input received; proceed with the deletion
if (deletionConfirmed)
return $"Deleted {path}.";

// (3) MRTR initial request: elicit input to confirm the deletion. This uses the
// 2026-07-28 MRTR input request, but the SDK provides an automatic bridge to
// a legacy elicitation on a down-level, stateful session. When the bridge can
// be provided, `server.IsMrtrSupported` is `true` and the exception leads to
// a legacy elicitation response automatically.
if (server.IsMrtrSupported)
{
throw new InputRequiredException(
inputRequests: new Dictionary<string, InputRequest>
{
["confirm"] = InputRequest.ForElicitation(new ElicitRequestParams
{
Message = $"Delete {path}? This cannot be undone.",
RequestedSchema = new(),
})
},
requestState: path); // opaque; echoed back to us on the retry
}

// (4) Down-level and stateless: we can't prompt an elicitation through an MRTR
// round-trip request or an elicitation. Return a natural language response
// with guidance for sending an explicit opt-in.
return $"Deletion requires user confirmation. Confirm by resending with `confirm: true`.";
}
```

> [!IMPORTANT]
> A confirmation prompt collects no form fields, but you must still set
> `RequestedSchema = new()`. The empty schema is accepted by native `2026-07-28` MRTR, and
> it's **required** by the down-level stateful elicitation bridge under `2025-11-25`:
> omitting it throws `ArgumentException: "Form mode elicitation requests require a requested schema."`.

## Compatibility

The SDK supports `InputRequiredException` across two protocol revisions and two session modes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static async Task<string> LongRunningTool(
{
await Task.Delay(stepDuration * 1000);

// <snippet_SendProgress >
// <snippet_SendProgress>
if (progressToken is not null)
{
await server.SendNotificationAsync("notifications/progress", new ProgressNotificationParams
Expand All @@ -36,7 +36,7 @@ public static async Task<string> LongRunningTool(
},
});
}
// </snippet_SendProgress >
// </snippet_SendProgress>
}

return $"Long running tool completed. Duration: {duration} seconds. Steps: {steps}.";
Expand Down
5 changes: 4 additions & 1 deletion docs/concepts/roots/roots.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ MCP [roots] allow clients to inform servers about the relevant locations in the

[roots]: https://modelcontextprotocol.io/specification/2025-11-25/client/roots

> [!IMPORTANT]
> Roots are **deprecated** as of MCP specification revision `2026-07-28` ([SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577), [MCP9005](xref:list-of-diagnostics#obsolete-apis)) and may be removed in a future version.

### Overview

Roots provide a mechanism for the client to tell the server which directories, projects, or repositories are relevant to the current session. A server might use roots to:
Expand Down Expand Up @@ -57,7 +60,7 @@ await using var client = await McpClient.CreateAsync(transport, options);

### Requesting roots from the server

Servers can request the client's root list using <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>. This is a server-to-client request, so it requires [stateful mode or stdio](xref:stateless) — it is not available in [stateless mode](xref:stateless#stateless-mode-recommended).
Servers can request the client's root list using <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>. This is a server-to-client request, so it requires [stateful mode or stdio](xref:stateless) — it is not available in [stateless mode](xref:stateless#stateless-mode-recommended). For a stateless-compatible alternative, throw `InputRequiredException` from your handler through MRTR — see [Multi round-trip requests (MRTR)](#multi-round-trip-requests-mrtr).

```csharp
[McpServerTool, Description("Lists the user's project roots")]
Expand Down
5 changes: 4 additions & 1 deletion docs/concepts/sampling/sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ MCP [sampling] allows servers to request LLM completions from the client. This e

[sampling]: https://modelcontextprotocol.io/specification/2025-11-25/client/sampling

> [!IMPORTANT]
> Sampling is **deprecated** as of MCP specification revision `2026-07-28` ([SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577), [MCP9005](xref:list-of-diagnostics#obsolete-apis)) and may be removed in a future version.

> [!NOTE]
> Sampling is a **server-to-client request** — the server sends a request back to the client over an open connection. This requires [stateful mode or stdio](xref:stateless). Sampling is not available in [stateless mode](xref:stateless#stateless-mode-recommended) because stateless servers cannot send requests to clients.
> Sampling is a **server-to-client request** — the server sends a request back to the client over an open connection. This requires [stateful mode or stdio](xref:stateless). Sampling is not available in [stateless mode](xref:stateless#stateless-mode-recommended) because stateless servers cannot send requests to clients. For a stateless-compatible alternative, throw `InputRequiredException` from your handler through MRTR — see [Multi round-trip requests (MRTR)](#multi-round-trip-requests-mrtr).

### How sampling works

Expand Down
38 changes: 19 additions & 19 deletions docs/concepts/tasks/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,6 @@ Tasks are provided by the `ModelContextProtocol.Extensions.Tasks` package and re
version `2026-07-28` or later. The implementation follows
[SEP-2663 (Tasks Extension)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/seps/2663-tasks-extension.md).

### Compatibility with v1 experimental Tasks

The Tasks extension in v2.0.0 replaces the experimental Tasks implementation shipped in v1.3.0 and
v1.4.x. The implementations are not compatible at either the API or protocol level. A v2 Tasks
client or server must use a connection negotiated to `2026-07-28` or later; it cannot fall back to
the down-level implementation.

On a connection negotiated to the down-level `2025-11-25` protocol:

- A v2 client calling a v1 server receives an ordinary tool result. The v2 client does not opt in
to the down-level Tasks protocol, and `GetTaskAsync` rejects use before a `2026-07-28`
connection is negotiated.
- A v1 client calling a v2 server likewise receives an ordinary tool result. The v2 server does
not create Tasks on a down-level connection, and its `tasks/get` endpoint rejects the legacy
request with a method-not-found error.

Upgrade both peers to the v2 Tasks extension before using Tasks. The extension provides no
compatibility bridge for the previous experimental API.

### Overview

A client opts into tasks on a per-request basis by including the `io.modelcontextprotocol/tasks`
Expand Down Expand Up @@ -346,6 +327,25 @@ scope, the SDK intentionally skips the normal client-capability negotiation chec
the client opted in by including the extension marker in the originating request, so it's
responsible for handling — or rejecting — the input requests surfaced through `tasks/get`.

#### Compatibility with v1 experimental Tasks

The Tasks extension in v2.0.0 replaces the experimental Tasks implementation shipped in v1.3.0 and
v1.4.x. The implementations are not compatible at either the API or protocol level. A v2 Tasks
client or server must use a connection negotiated to `2026-07-28` or later; it cannot fall back to
the down-level implementation.

On a connection negotiated to the down-level `2025-11-25` protocol:

- A v2 client calling a v1 server receives an ordinary tool result. The v2 client does not opt in
to the down-level Tasks protocol, and `GetTaskAsync` rejects use before a `2026-07-28`
connection is negotiated.
- A v1 client calling a v2 server likewise receives an ordinary tool result. The v2 server does
not create Tasks on a down-level connection, and its `tasks/get` endpoint rejects the legacy
request with a method-not-found error.

Upgrade both peers to the v2 Tasks extension before using Tasks. The extension provides no
compatibility bridge for the previous experimental API.

### Known limitations

- **Server-push task status notifications (SEP-2575)**: not yet implemented. Clients rely on
Expand Down
Loading