Skip to content

Commit 1e87786

Browse files
authored
Merge branch 'main' into dependabot/nuget/Microsoft.Extensions.Caching.Abstractions-10.0.5
2 parents 44f8fa8 + 1ad11bf commit 1e87786

90 files changed

Lines changed: 2487 additions & 389 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ jobs:
4646

4747
- name: Deploy to GitHub Pages
4848
id: deployment
49-
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
49+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0

Directory.Packages.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@
7676
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.1.0" />
7777
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
7878
<PackageVersion Include="Moq" Version="4.20.72" />
79-
<PackageVersion Include="OpenTelemetry" Version="1.15.0" />
80-
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.0" />
81-
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
79+
<PackageVersion Include="OpenTelemetry" Version="1.15.1" />
80+
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.15.1" />
81+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.1" />
8282
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
83-
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
84-
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" />
83+
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.1" />
84+
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.1" />
8585
<PackageVersion Include="Serilog.Extensions.Hosting" Version="10.0.0" />
8686
<PackageVersion Include="Serilog.Extensions.Logging" Version="10.0.0" />
8787
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />

docs/concepts/cancellation/cancellation.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ MCP supports [cancellation] of in-flight requests. Either side can cancel a prev
1212
[cancellation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation
1313
[task cancellation]: https://learn.microsoft.com/dotnet/standard/parallel-programming/task-cancellation
1414

15+
> [!NOTE]
16+
> The source and lifetime of the `CancellationToken` provided to server handlers depends on the transport and session mode. In [stateless mode](xref:stateless#stateless-mode-recommended), the token is tied to the HTTP request — if the client disconnects, the handler is cancelled. In [stateful mode](xref:stateless#stateful-mode-sessions), the token is tied to the session lifetime. See [Cancellation and disposal](xref:stateless#cancellation-and-disposal) for details.
17+
1518
### How cancellation maps to MCP notifications
1619

1720
When a `CancellationToken` passed to a client method (such as <xref:ModelContextProtocol.Client.McpClient.CallToolAsync*>) is cancelled, a `notifications/cancelled` notification is sent to the server with the request ID. On the server side, the `CancellationToken` provided to the tool method is then triggered, allowing the handler to stop work gracefully. This same mechanism works in reverse for server-to-client requests.

docs/concepts/completions/completions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Register a completion handler when building the server. The handler receives a r
2626

2727
```csharp
2828
builder.Services.AddMcpServer()
29-
.WithHttpTransport()
29+
.WithHttpTransport(o => o.Stateless = true)
3030
.WithPrompts<MyPrompts>()
3131
.WithResources<MyResources>()
3232
.WithCompleteHandler(async (ctx, ct) =>

docs/concepts/elicitation/elicitation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ Here's an example implementation of how a console application might handle elici
172172

173173
### URL Elicitation Required Error
174174

175-
When a tool cannot proceed without first completing a URL-mode elicitation (for example, when third-party OAuth authorization is needed), and calling `ElicitAsync` is not practical (for example in <xref: ModelContextProtocol.AspNetCore.HttpServerTransportOptions.Stateless> is enabled disabling server-to-client requests), the server may throw a <xref:ModelContextProtocol.UrlElicitationRequiredException>. This is a specialized error (JSON-RPC error code `-32042`) that signals to the client that one or more URL-mode elicitations must be completed before the original request can be retried.
175+
When a tool cannot proceed without first completing a URL-mode elicitation (for example, when third-party OAuth authorization is needed), and calling `ElicitAsync` is not practical (for example in [stateless](xref:stateless) mode where server-to-client requests are disabled), the server may throw a <xref:ModelContextProtocol.UrlElicitationRequiredException>. This is a specialized error (JSON-RPC error code `-32042`) that signals to the client that one or more URL-mode elicitations must be completed before the original request can be retried.
176176

177177
#### Throwing UrlElicitationRequiredException on the Server
178178

docs/concepts/elicitation/samples/server/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66

77
builder.Services.AddMcpServer()
88
.WithHttpTransport(options =>
9-
options.IdleTimeout = Timeout.InfiniteTimeSpan // Never timeout
10-
)
9+
{
10+
// Elicitation requires stateful mode because it sends server-to-client requests.
11+
// Set Stateless = false explicitly for forward compatibility in case the default changes.
12+
options.Stateless = false;
13+
})
1114
.WithTools<InteractiveTools>();
1215

1316
builder.Logging.AddConsole(options =>

docs/concepts/filters.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
317317
{
318318
var logger = context.Services?.GetService<ILogger<Program>>();
319319

320-
logger?.LogInformation($"Processing request from {context.Params?.ProgressToken}");
320+
logger?.LogInformation($"Processing request from {context.Params.ProgressToken}");
321321
var result = await next(context, cancellationToken);
322322
logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools");
323323
return result;
@@ -339,7 +339,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
339339
catch (Exception ex)
340340
{
341341
var logger = context.Services?.GetService<ILogger<Program>>();
342-
logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params?.ProgressToken);
342+
logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params.ProgressToken);
343343

344344
return new CallToolResult
345345
{
@@ -401,7 +401,7 @@ To enable authorization support, call `AddAuthorizationFilters()` when configuri
401401

402402
```csharp
403403
services.AddMcpServer()
404-
.WithHttpTransport()
404+
.WithHttpTransport(o => o.Stateless = true)
405405
.AddAuthorizationFilters() // Enable authorization filter support
406406
.WithTools<WeatherTools>();
407407
```
@@ -501,7 +501,7 @@ This allows you to implement logging, metrics, or other cross-cutting concerns t
501501

502502
```csharp
503503
services.AddMcpServer()
504-
.WithHttpTransport()
504+
.WithHttpTransport(o => o.Stateless = true)
505505
.WithRequestFilters(requestFilters =>
506506
{
507507
requestFilters.AddListToolsFilter(next => async (context, cancellationToken) =>
@@ -544,7 +544,10 @@ builder.Services.AddAuthentication("Bearer")
544544
builder.Services.AddAuthorization();
545545

546546
builder.Services.AddMcpServer()
547-
.WithHttpTransport()
547+
.WithHttpTransport(options =>
548+
{
549+
options.Stateless = true;
550+
})
548551
.AddAuthorizationFilters() // Required for authorization support
549552
.WithTools<WeatherTools>()
550553
.WithRequestFilters(requestFilters =>

docs/concepts/getting-started.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,13 @@ using System.ComponentModel;
7979

8080
var builder = WebApplication.CreateBuilder(args);
8181
builder.Services.AddMcpServer()
82-
.WithHttpTransport()
82+
.WithHttpTransport(options =>
83+
{
84+
// Stateless mode is recommended for servers that don't need
85+
// server-to-client requests like sampling or elicitation.
86+
// See the Sessions documentation for details.
87+
options.Stateless = true;
88+
})
8389
.WithToolsFromAssembly();
8490
var app = builder.Build();
8591

docs/concepts/httpcontext/httpcontext.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,16 @@ The following code snippet shows the `ContextTools` class accepting an [IHttpCon
2929
and the `GetHttpHeaders` method accessing the current [HttpContext] to retrieve the HTTP headers from the current request.
3030

3131
[!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)]
32+
33+
### SSE transport and stale HttpContext
34+
35+
When using the legacy SSE transport, be aware that the `HttpContext` returned by `IHttpContextAccessor` references the long-lived SSE connection request — not the individual `POST` request that triggered the tool call. This means:
36+
37+
- The `HttpContext.User` may contain stale claims if the client's token was refreshed after the SSE connection was established.
38+
- Request headers, query strings, and other per-request metadata will reflect the initial SSE connection, not the current operation.
39+
40+
The Streamable HTTP transport does not have this issue because each tool call is its own HTTP request, so `IHttpContextAccessor.HttpContext` always reflects the current request. In [stateless](xref:stateless) mode, this is guaranteed since every request creates a fresh server context.
41+
42+
<!-- mlc-disable-next-line -->
43+
> [!NOTE]
44+
> The server validates that the user identity has not changed between the session-initiating request and subsequent requests (using the `sub`, `NameIdentifier`, or `UPN` claim). If the user identity changes, the request is rejected with `403 Forbidden`. However, other claims (roles, permissions, custom claims) are not re-validated and may become stale over the lifetime of a session.

docs/concepts/httpcontext/samples/Program.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
// Add services to the container.
66

77
builder.Services.AddMcpServer()
8-
.WithHttpTransport()
8+
.WithHttpTransport(options =>
9+
{
10+
options.Stateless = true;
11+
})
912
.WithTools<ContextTools>();
1013

1114
// <snippet_AddHttpContextAccessor>

0 commit comments

Comments
 (0)