Skip to content

Commit 84cd691

Browse files
Copilothalter73jeffhandley
authored
Mark RunSessionHandler experimental (MCPEXP002) (#1383)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: halter73 <54385+halter73@users.noreply.github.com> Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
1 parent 4e68a4e commit 84cd691

File tree

12 files changed

+82
-4
lines changed

12 files changed

+82
-4
lines changed

docs/list-of-diagnostics.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ If you use experimental APIs, you will get one of the diagnostics shown below. T
2323

2424
| Diagnostic ID | Description |
2525
| :------------ | :---------- |
26-
| `MCPEXP001` | MCP experimental APIs including Tasks and Extensions. Tasks provide a mechanism for asynchronous long-running operations that can be polled for status and results (see [MCP Tasks specification](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks)). Extensions provide a framework for extending the Model Context Protocol while maintaining interoperability (see [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)). |
27-
| `MCPEXP002` | Subclassing `McpClient` and `McpServer` is experimental and subject to change (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)). |
26+
| `MCPEXP001` | Experimental APIs for features in the MCP specification itself, including Tasks and Extensions. Tasks provide a mechanism for asynchronous long-running operations that can be polled for status and results (see [MCP Tasks specification](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks)). Extensions provide a framework for extending the Model Context Protocol while maintaining interoperability (see [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)). |
27+
| `MCPEXP002` | Experimental SDK APIs unrelated to the MCP specification itself, including subclassing `McpClient`/`McpServer` (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)) and `RunSessionHandler`, which may be removed or change signatures in a future release (consider using `ConfigureSessionOptions` instead). |
2828

2929
## Obsolete APIs
3030

samples/EverythingServer/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
.WithHttpTransport(options =>
5252
{
5353
// Add a RunSessionHandler to remove all subscriptions for the session when it ends
54+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
5455
options.RunSessionHandler = async (httpContext, mcpServer, token) =>
5556
{
5657
if (mcpServer.SessionId == null)
@@ -76,6 +77,7 @@
7677
subscriptions.TryRemove(mcpServer.SessionId, out _);
7778
}
7879
};
80+
#pragma warning restore MCPEXP002
7981
})
8082
.WithTools<AddTool>()
8183
.WithTools<AnnotatedMessageTool>()

src/Common/Experimentals.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@ namespace ModelContextProtocol;
66
/// Defines diagnostic IDs, messages, and URLs for APIs annotated with <see cref="ExperimentalAttribute"/>.
77
/// </summary>
88
/// <remarks>
9+
/// Experimental diagnostic IDs are grouped by category:
10+
/// <list type="bullet">
11+
/// <item><description>
12+
/// <c>MCPEXP001</c> covers APIs related to experimental features in the MCP specification itself,
13+
/// such as Tasks and Extensions. These APIs may change as the specification evolves.
14+
/// </description></item>
15+
/// <item><description>
16+
/// <c>MCPEXP002</c> covers experimental SDK APIs that are unrelated to the MCP specification,
17+
/// such as subclassing internal types or SDK-specific extensibility hooks. These APIs may
18+
/// change or be removed based on SDK design feedback.
19+
/// </description></item>
20+
/// </list>
21+
/// <para>
922
/// When an experimental API is associated with an experimental specification, the message
1023
/// should refer to the specification version that introduces the feature and the SEP
1124
/// when available. If there is a SEP associated with the experimental API, the Url should
1225
/// point to the SEP issue.
26+
/// </para>
1327
/// <para>
1428
/// Experimental diagnostic IDs are in the format MCPEXP###.
1529
/// </para>
@@ -58,8 +72,14 @@ internal static class Experimentals
5872
public const string Extensions_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp001";
5973

6074
/// <summary>
61-
/// Diagnostic ID for experimental subclassing of McpClient and McpServer.
75+
/// Diagnostic ID for experimental SDK APIs unrelated to the MCP specification,
76+
/// such as subclassing <c>McpClient</c>/<c>McpServer</c> or referencing <c>RunSessionHandler</c>.
6277
/// </summary>
78+
/// <remarks>
79+
/// This diagnostic ID covers experimental SDK-level extensibility APIs. All constants
80+
/// in this group share the same diagnostic ID so users need only one suppression point
81+
/// for SDK design preview features.
82+
/// </remarks>
6383
public const string Subclassing_DiagnosticId = "MCPEXP002";
6484

6585
/// <summary>
@@ -70,5 +90,24 @@ internal static class Experimentals
7090
/// <summary>
7191
/// URL for experimental subclassing of McpClient and McpServer.
7292
/// </summary>
73-
public const string Subclassing_Url = "https://github.com/modelcontextprotocol/csharp-sdk/pull/1363";
93+
public const string Subclassing_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp002";
94+
95+
/// <summary>
96+
/// Diagnostic ID for the experimental <c>RunSessionHandler</c> API.
97+
/// </summary>
98+
/// <remarks>
99+
/// This uses the same diagnostic ID as <see cref="Subclassing_DiagnosticId"/> because
100+
/// both are experimental SDK APIs unrelated to the MCP specification.
101+
/// </remarks>
102+
public const string RunSessionHandler_DiagnosticId = "MCPEXP002";
103+
104+
/// <summary>
105+
/// Message for the experimental <c>RunSessionHandler</c> API.
106+
/// </summary>
107+
public const string RunSessionHandler_Message = "RunSessionHandler is experimental and may be removed or changed in a future release. Consider using ConfigureSessionOptions instead.";
108+
109+
/// <summary>
110+
/// URL for the experimental <c>RunSessionHandler</c> API.
111+
/// </summary>
112+
public const string RunSessionHandler_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp002";
74113
}

src/ModelContextProtocol.AspNetCore/HttpServerTransportOptions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@ public class HttpServerTransportOptions
2424
/// </summary>
2525
/// <remarks>
2626
/// This callback is useful for running logic before a session starts and after it completes.
27+
/// <para>
28+
/// The <see cref="HttpContext"/> parameter comes from the request that initiated the session (e.g., the
29+
/// initialize request) and may not be usable after <see cref="McpServer.RunAsync"/> starts, since that
30+
/// request will have already completed.
31+
/// </para>
32+
/// <para>
33+
/// Consider using <see cref="ConfigureSessionOptions"/> instead, which provides access to the
34+
/// <see cref="HttpContext"/> of the initializing request with fewer known issues.
35+
/// </para>
36+
/// <para>
37+
/// This API is experimental and may be removed or change signatures in a future release.
38+
/// </para>
2739
/// </remarks>
40+
[System.Diagnostics.CodeAnalysis.Experimental(Experimentals.RunSessionHandler_DiagnosticId, UrlFormat = Experimentals.RunSessionHandler_Url)]
2841
public Func<HttpContext, McpServer, CancellationToken, Task>? RunSessionHandler { get; set; }
2942

3043
/// <summary>

src/ModelContextProtocol.AspNetCore/ModelContextProtocol.AspNetCore.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
<None Include="README.md" pack="true" PackagePath="\" />
2121
</ItemGroup>
2222

23+
<ItemGroup>
24+
<Compile Include="..\Common\Experimentals.cs" Link="Experimentals.cs" />
25+
</ItemGroup>
26+
2327
<ItemGroup>
2428
<ProjectReference Include="..\ModelContextProtocol\ModelContextProtocol.csproj" />
2529
</ItemGroup>

src/ModelContextProtocol.AspNetCore/SseHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ public async Task HandleSseRequestAsync(HttpContext context)
5656
await using var mcpServer = McpServer.Create(transport, mcpServerOptions, loggerFactory, context.RequestServices);
5757
context.Features.Set(mcpServer);
5858

59+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
5960
var runSessionAsync = httpMcpServerOptions.Value.RunSessionHandler ?? StreamableHttpHandler.RunSessionAsync;
61+
#pragma warning restore MCPEXP002
6062
await runSessionAsync(context, mcpServer, cancellationToken);
6163
}
6264
finally

src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,9 @@ private async ValueTask<StreamableHttpSession> CreateSessionAsync(
390390
var userIdClaim = GetUserIdClaim(context.User);
391391
var session = new StreamableHttpSession(sessionId, transport, server, userIdClaim, sessionManager);
392392

393+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
393394
var runSessionAsync = HttpServerTransportOptions.RunSessionHandler ?? RunSessionAsync;
395+
#pragma warning restore MCPEXP002
394396
session.ServerRunTask = runSessionAsync(context, server, session.SessionClosed);
395397

396398
return session;

tests/ModelContextProtocol.AspNetCore.Tests/MapMcpStreamableHttpTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,14 @@ public async Task CanResumeSessionWithMapMcpAndRunSessionHandler()
213213
}).WithHttpTransport(opts =>
214214
{
215215
ConfigureStateless(opts);
216+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
216217
opts.RunSessionHandler = async (context, server, cancellationToken) =>
217218
{
218219
Interlocked.Increment(ref runSessionCount);
219220
serverTcs.TrySetResult(server);
220221
await server.RunAsync(cancellationToken);
221222
};
223+
#pragma warning restore MCPEXP002
222224
}).WithTools<EchoHttpContextUserTools>();
223225

224226
await using var app = Builder.Build();
@@ -481,11 +483,13 @@ public async Task DisposeAsync_DoesNotHang_WhenOwnsSessionIsFalse_WithUnsolicite
481483
Builder.Services.AddMcpServer().WithHttpTransport(opts =>
482484
{
483485
ConfigureStateless(opts);
486+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
484487
opts.RunSessionHandler = async (context, server, cancellationToken) =>
485488
{
486489
serverTcs.TrySetResult(server);
487490
await server.RunAsync(cancellationToken);
488491
};
492+
#pragma warning restore MCPEXP002
489493
}).WithTools<ClaimsPrincipalTools>();
490494

491495
await using var app = Builder.Build();

tests/ModelContextProtocol.AspNetCore.Tests/ResumabilityIntegrationTestsBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,13 @@ public virtual async Task Client_CanResumeUnsolicitedMessageStream_AfterDisconne
253253

254254
await using var app = await CreateServerAsync(configureTransport: options =>
255255
{
256+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
256257
options.RunSessionHandler = (httpContext, mcpServer, cancellationToken) =>
257258
{
258259
serverTcs.TrySetResult(mcpServer);
259260
return mcpServer.RunAsync(cancellationToken);
260261
};
262+
#pragma warning restore MCPEXP002
261263
});
262264

263265
await using var client = await ConnectClientAsync();

tests/ModelContextProtocol.AspNetCore.Tests/SseIntegrationTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public async Task ConnectAndReceiveNotification_InMemoryServer()
8383
Builder.Services.AddMcpServer()
8484
.WithHttpTransport(httpTransportOptions =>
8585
{
86+
#pragma warning disable MCPEXP002 // RunSessionHandler is experimental
8687
httpTransportOptions.RunSessionHandler = (httpContext, mcpServer, cancellationToken) =>
8788
{
8889
// We could also use ServerCapabilities.NotificationHandlers, but it's good to have some test coverage of RunSessionHandler.
@@ -93,6 +94,7 @@ public async Task ConnectAndReceiveNotification_InMemoryServer()
9394
});
9495
return mcpServer.RunAsync(cancellationToken);
9596
};
97+
#pragma warning restore MCPEXP002
9698
});
9799

98100
await using var app = Builder.Build();

0 commit comments

Comments
 (0)