Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Before dismissing a potential break, review the PR description and all PR commen
Every dismissed potential break must be reported to the user with enough detail for them to verify the conclusion. The audit must:

1. **Identify what would normally be breaking and why** (e.g., "CP0005 — adding abstract member `Completion` to abstract class `McpClient`")
2. **Explain the specific reason for dismissal** (e.g., "Bug fix correcting incorrect behavior per the MCP spec" or "`McpClient`'s only accessible constructor is `protected` and marked `[Experimental(MCPEXP002)]` with message 'Subclassing McpClient and McpServer is experimental and subject to change.'")
2. **Explain the specific reason for dismissal** (e.g., "Bug fix correcting incorrect behavior per the MCP spec" or "`McpClient`'s only accessible constructor is `protected` and marked `[Experimental(MCPEXP002)]` with message 'This C# SDK extensibility API is experimental and subject to change.'")
3. **Cite any supporting discussion** from the PR description or comments (e.g., "Reviewers discussed the addition and did not flag it as a breaking concern; compatibility suppressions were added for CP0005")
4. **Conclude with the dismissal and its category** (e.g., "Dismissed — bug fix correcting spec-non-compliant behavior" or "Dismissed — exclusively gated by `[Experimental]` API. Do not apply the `breaking-change` label.")

Expand Down
2 changes: 1 addition & 1 deletion docs/list-of-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ If you use experimental APIs, you will get one of the diagnostics shown below. T
| Diagnostic ID | Description |
| :------------ | :---------- |
| `MCPEXP001` | Experimental APIs tied to MCP specification features. Reuse this ID for newly introduced experimental spec features, and add feature-specific messages/URLs in `Experimentals`. |
| `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). |
| `MCPEXP002` | Experimental SDK extensibility APIs used to implement features in standalone packages without requiring Core to understand those features. For example, the Tasks package uses these APIs to extend the SDK while keeping the Tasks concept out of Core. This includes `McpClient`/`McpServer` subclassing, custom request handlers, alternate handlers and filters, outgoing request interception, and `RunSessionHandler`. These APIs remain experimental until additional extensibility scenarios validate the design. |
| `MCPEXP003` | Experimental MCP Apps extension APIs. MCP Apps is the first official MCP extension (`io.modelcontextprotocol/ui`), enabling servers to deliver interactive UIs inside AI clients (see [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx)). |

## Obsolete APIs
Expand Down
23 changes: 12 additions & 11 deletions src/Common/Experimentals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ namespace ModelContextProtocol;
/// These APIs may change as the specification evolves.
/// </description></item>
/// <item><description>
/// <c>MCPEXP002</c> covers experimental SDK APIs that are unrelated to the MCP specification,
/// such as subclassing internal types or SDK-specific extensibility hooks. These APIs may
/// change or be removed based on SDK design feedback.
/// <c>MCPEXP002</c> covers SDK extensibility APIs that enable features to be implemented
/// in standalone packages without requiring Core to understand those features. The Tasks
/// package is one such consumer. These APIs remain experimental until additional
/// extensibility scenarios validate the design.
/// </description></item>
/// </list>
/// <para>
Expand Down Expand Up @@ -65,31 +66,31 @@ internal static class Experimentals
public const string Apps_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp003";

/// <summary>
/// Diagnostic ID for experimental SDK APIs unrelated to the MCP specification,
/// such as subclassing <c>McpClient</c>/<c>McpServer</c> or referencing <c>RunSessionHandler</c>.
/// Diagnostic ID for SDK extensibility APIs that enable independently packaged features,
/// such as Tasks, without requiring Core awareness of those features.
/// </summary>
/// <remarks>
/// This diagnostic ID covers experimental SDK-level extensibility APIs. All constants
/// in this group share the same diagnostic ID so users need only one suppression point
/// for SDK design preview features.
/// </remarks>
public const string Subclassing_DiagnosticId = "MCPEXP002";
public const string Extensibility_DiagnosticId = "MCPEXP002";

/// <summary>
/// Message for experimental subclassing of McpClient and McpServer.
/// Message for experimental extensibility points in the C# SDK implementation.
/// </summary>
public const string Subclassing_Message = "Subclassing McpClient and McpServer is experimental and subject to change.";
public const string Extensibility_Message = "This C# SDK extensibility API is experimental and subject to change.";

/// <summary>
/// URL for experimental subclassing of McpClient and McpServer.
/// URL for experimental extensibility points in the C# SDK implementation.
/// </summary>
public const string Subclassing_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp002";
public const string Extensibility_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp002";

/// <summary>
/// Diagnostic ID for the experimental <c>RunSessionHandler</c> API.
/// </summary>
/// <remarks>
/// This uses the same diagnostic ID as <see cref="Subclassing_DiagnosticId"/> because
/// This uses the same diagnostic ID as <see cref="Extensibility_DiagnosticId"/> because
/// both are experimental SDK APIs unrelated to the MCP specification.
/// </remarks>
public const string RunSessionHandler_DiagnosticId = "MCPEXP002";
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Client/McpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract partial class McpClient : McpSession
/// <summary>
/// Initializes a new instance of the <see cref="McpClient"/> class.
/// </summary>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
protected McpClient()
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public sealed class JsonRpcMessageContext
/// Streamable HTTP transports emit this value in the <c>Mcp-Name</c> header. This enables
/// extension methods to identify the named resource targeted by a request.
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
[JsonIgnore]
public string? RoutingName { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace ModelContextProtocol.Protocol;
/// <see cref="Result"/> for the immediate result or <see cref="Alternate"/> for the alternate.
/// </para>
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public class ResultOrAlternate<TResult> where TResult : Result
{
private readonly TResult? _result;
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpRequestFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public IList<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFi
/// asynchronously, filters registered after it execute as part of that asynchronous operation.
/// </para>
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public IList<McpRequestInvocationFilter<CallToolRequestParams, ResultOrAlternate<CallToolResult>>> CallToolWithAlternateFilters
{
get => field ??= [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace ModelContextProtocol.Server;
/// <param name="next">The next request handler in the pipeline for this invocation.</param>
/// <param name="cancellationToken">The cancellation token for the current request.</param>
/// <returns>The result of the filtered request invocation.</returns>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public delegate ValueTask<TResult> McpRequestInvocationFilter<TParams, TResult>(
RequestContext<TParams> context,
McpRequestHandler<TParams, TResult> next,
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public abstract partial class McpServer : McpSession
/// On the returned facade, redirected methods skip their client-capability checks,
/// because the alternate channel is responsible for delivering the request to the client.
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public McpServer WithOutgoingRequestInterceptor(Func<string, JsonNode?, CancellationToken, ValueTask<JsonNode?>> interceptor)
{
Throw.IfNull(interceptor);
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public abstract partial class McpServer : McpSession
/// <summary>
/// Initializes a new instance of the <see cref="McpServer"/> class.
/// </summary>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
protected McpServer()
{
}
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public McpRequestHandler<CallToolRequestParams, CallToolResult>? CallToolHandler
/// </para>
/// </remarks>
/// <exception cref="InvalidOperationException"><see cref="CallToolHandler"/> is already set.</exception>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public McpRequestHandler<CallToolRequestParams, ResultOrAlternate<CallToolResult>>? CallToolWithAlternateHandler
{
get;
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol.Core/Server/McpServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,6 @@ public McpServerFilters Filters
/// Handlers registered here take precedence over built-in handlers for the same method.
/// </para>
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public IList<McpServerRequestHandler>? RequestHandlers { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace ModelContextProtocol.Server;
/// <see cref="JsonNode"/> response, giving extensions full control over request/response serialization.
/// </para>
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public sealed class McpServerRequestHandler
{
/// <summary>
Expand All @@ -32,7 +32,7 @@ public sealed class McpServerRequestHandler
/// When set, Streamable HTTP servers require the request to include an <c>Mcp-Name</c>
/// header whose decoded value matches the string value of this parameter.
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)]
public string? RoutingNameParameter { get; init; }

/// <summary>
Expand Down