From 250b4d9069283f2bcca4693450c3b547b63f9300 Mon Sep 17 00:00:00 2001 From: arimu1 <19286898+arimu1@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:25:07 +0700 Subject: [PATCH 1/2] Rename MCPEXP002 Subclassing_* constants to Extensibility_* Align internal constant names and documentation with MCPEXP002's actual scope: experimental C# SDK extensibility APIs beyond subclassing alone. Diagnostic ID and suppression behavior are unchanged. Fixes #1736 --- .../references/classification.md | 2 +- docs/list-of-diagnostics.md | 2 +- src/Common/Experimentals.cs | 24 ++++++++++--------- .../Client/McpClient.cs | 2 +- .../Protocol/JsonRpcMessageContext.cs | 2 +- .../Protocol/ResultOrAlternate.cs | 2 +- .../Server/McpRequestFilters.cs | 2 +- .../Server/McpRequestInvocationFilter.cs | 2 +- .../Server/McpServer.Methods.cs | 2 +- .../Server/McpServer.cs | 2 +- .../Server/McpServerHandlers.cs | 2 +- .../Server/McpServerOptions.cs | 2 +- .../Server/McpServerRequestHandler.cs | 4 ++-- 13 files changed, 26 insertions(+), 24 deletions(-) diff --git a/.github/skills/breaking-changes/references/classification.md b/.github/skills/breaking-changes/references/classification.md index e58897214..9c34c3790 100644 --- a/.github/skills/breaking-changes/references/classification.md +++ b/.github/skills/breaking-changes/references/classification.md @@ -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.") diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index 284f24f94..0faf9de02 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -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 extensibility points in the C# SDK implementation that are unrelated to the MCP specification itself, including subclassing `McpClient`/`McpServer` (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)), custom request handlers, alternate handlers and filters, outgoing request interception, routing metadata, and `RunSessionHandler`, which may be removed or change signatures in a future release (consider using `ConfigureSessionOptions` instead). | | `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 diff --git a/src/Common/Experimentals.cs b/src/Common/Experimentals.cs index 1af5ce5a3..2e5c90fa8 100644 --- a/src/Common/Experimentals.cs +++ b/src/Common/Experimentals.cs @@ -13,9 +13,10 @@ namespace ModelContextProtocol; /// These APIs may change as the specification evolves. /// /// -/// MCPEXP002 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. +/// MCPEXP002 covers experimental extensibility points in the C# SDK implementation +/// that are unrelated to the MCP specification, such as subclassing internal types, custom +/// request handlers, alternate handlers and filters, outgoing request interception, and +/// routing metadata. These APIs may change or be removed based on SDK design feedback. /// /// /// @@ -65,31 +66,32 @@ internal static class Experimentals public const string Apps_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp003"; /// - /// Diagnostic ID for experimental SDK APIs unrelated to the MCP specification, - /// such as subclassing McpClient/McpServer or referencing RunSessionHandler. + /// Diagnostic ID for experimental extensibility points in the C# SDK implementation, + /// such as subclassing McpClient/McpServer, custom request handlers, + /// alternate handlers and filters, outgoing request interception, and routing metadata. /// /// /// 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. /// - public const string Subclassing_DiagnosticId = "MCPEXP002"; + public const string Extensibility_DiagnosticId = "MCPEXP002"; /// - /// Message for experimental subclassing of McpClient and McpServer. + /// Message for experimental extensibility points in the C# SDK implementation. /// - 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."; /// - /// URL for experimental subclassing of McpClient and McpServer. + /// URL for experimental extensibility points in the C# SDK implementation. /// - 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"; /// /// Diagnostic ID for the experimental RunSessionHandler API. /// /// - /// This uses the same diagnostic ID as because + /// This uses the same diagnostic ID as because /// both are experimental SDK APIs unrelated to the MCP specification. /// public const string RunSessionHandler_DiagnosticId = "MCPEXP002"; diff --git a/src/ModelContextProtocol.Core/Client/McpClient.cs b/src/ModelContextProtocol.Core/Client/McpClient.cs index d683172da..6107201f2 100644 --- a/src/ModelContextProtocol.Core/Client/McpClient.cs +++ b/src/ModelContextProtocol.Core/Client/McpClient.cs @@ -12,7 +12,7 @@ public abstract partial class McpClient : McpSession /// /// Initializes a new instance of the class. /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] protected McpClient() { } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs index fae0684d9..def9b89e4 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs @@ -83,7 +83,7 @@ public sealed class JsonRpcMessageContext /// Streamable HTTP transports emit this value in the Mcp-Name header. This enables /// extension methods to identify the named resource targeted by a request. /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] [JsonIgnore] public string? RoutingName { get; set; } diff --git a/src/ModelContextProtocol.Core/Protocol/ResultOrAlternate.cs b/src/ModelContextProtocol.Core/Protocol/ResultOrAlternate.cs index 0944ba135..8303978fe 100644 --- a/src/ModelContextProtocol.Core/Protocol/ResultOrAlternate.cs +++ b/src/ModelContextProtocol.Core/Protocol/ResultOrAlternate.cs @@ -20,7 +20,7 @@ namespace ModelContextProtocol.Protocol; /// for the immediate result or for the alternate. /// /// -[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] +[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public class ResultOrAlternate where TResult : Result { private readonly TResult? _result; diff --git a/src/ModelContextProtocol.Core/Server/McpRequestFilters.cs b/src/ModelContextProtocol.Core/Server/McpRequestFilters.cs index df34d48a7..a1aad7112 100644 --- a/src/ModelContextProtocol.Core/Server/McpRequestFilters.cs +++ b/src/ModelContextProtocol.Core/Server/McpRequestFilters.cs @@ -84,7 +84,7 @@ public IList> CallToolFi /// asynchronously, filters registered after it execute as part of that asynchronous operation. /// /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public IList>> CallToolWithAlternateFilters { get => field ??= []; diff --git a/src/ModelContextProtocol.Core/Server/McpRequestInvocationFilter.cs b/src/ModelContextProtocol.Core/Server/McpRequestInvocationFilter.cs index f9871fca5..baacf4fcc 100644 --- a/src/ModelContextProtocol.Core/Server/McpRequestInvocationFilter.cs +++ b/src/ModelContextProtocol.Core/Server/McpRequestInvocationFilter.cs @@ -11,7 +11,7 @@ namespace ModelContextProtocol.Server; /// The next request handler in the pipeline for this invocation. /// The cancellation token for the current request. /// The result of the filtered request invocation. -[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] +[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public delegate ValueTask McpRequestInvocationFilter( RequestContext context, McpRequestHandler next, diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs index a4a245043..a9a5dddfb 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs @@ -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. /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public McpServer WithOutgoingRequestInterceptor(Func> interceptor) { Throw.IfNull(interceptor); diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index a51010dc0..4797ce151 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -11,7 +11,7 @@ public abstract partial class McpServer : McpSession /// /// Initializes a new instance of the class. /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] protected McpServer() { } diff --git a/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs b/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs index d7510a932..e8718626e 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs @@ -78,7 +78,7 @@ public McpRequestHandler? CallToolHandler /// /// /// is already set. - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public McpRequestHandler>? CallToolWithAlternateHandler { get; diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index a84bd600e..f3d9b5f74 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -217,6 +217,6 @@ public McpServerFilters Filters /// Handlers registered here take precedence over built-in handlers for the same method. /// /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public IList? RequestHandlers { get; set; } } diff --git a/src/ModelContextProtocol.Core/Server/McpServerRequestHandler.cs b/src/ModelContextProtocol.Core/Server/McpServerRequestHandler.cs index 2da6d0eb9..af8a94091 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerRequestHandler.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerRequestHandler.cs @@ -16,7 +16,7 @@ namespace ModelContextProtocol.Server; /// response, giving extensions full control over request/response serialization. /// /// -[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] +[Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public sealed class McpServerRequestHandler { /// @@ -32,7 +32,7 @@ public sealed class McpServerRequestHandler /// When set, Streamable HTTP servers require the request to include an Mcp-Name /// header whose decoded value matches the string value of this parameter. /// - [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)] + [Experimental(Experimentals.Extensibility_DiagnosticId, UrlFormat = Experimentals.Extensibility_Url)] public string? RoutingNameParameter { get; init; } /// From f41f2b73453b583131c96af2c8fe8f6d48b0e0af Mon Sep 17 00:00:00 2001 From: arimu1 <19286898+arimu1@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:59:51 +0700 Subject: [PATCH 2/2] Clarify MCPEXP002 purpose and expected lifetime Apply review suggestions: describe MCPEXP002 as the extensibility surface that lets features ship in standalone packages without Core awareness (Tasks being the current consumer), and state that the APIs stay experimental until more extensibility scenarios validate the design. --- docs/list-of-diagnostics.md | 2 +- src/Common/Experimentals.cs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index 0faf9de02..a90e0e8cc 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -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 extensibility points in the C# SDK implementation that are unrelated to the MCP specification itself, including subclassing `McpClient`/`McpServer` (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)), custom request handlers, alternate handlers and filters, outgoing request interception, routing metadata, 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 diff --git a/src/Common/Experimentals.cs b/src/Common/Experimentals.cs index 2e5c90fa8..e61f84199 100644 --- a/src/Common/Experimentals.cs +++ b/src/Common/Experimentals.cs @@ -13,10 +13,10 @@ namespace ModelContextProtocol; /// These APIs may change as the specification evolves. /// /// -/// MCPEXP002 covers experimental extensibility points in the C# SDK implementation -/// that are unrelated to the MCP specification, such as subclassing internal types, custom -/// request handlers, alternate handlers and filters, outgoing request interception, and -/// routing metadata. These APIs may change or be removed based on SDK design feedback. +/// MCPEXP002 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. /// /// /// @@ -66,9 +66,8 @@ internal static class Experimentals public const string Apps_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp003"; /// - /// Diagnostic ID for experimental extensibility points in the C# SDK implementation, - /// such as subclassing McpClient/McpServer, custom request handlers, - /// alternate handlers and filters, outgoing request interception, and routing metadata. + /// Diagnostic ID for SDK extensibility APIs that enable independently packaged features, + /// such as Tasks, without requiring Core awareness of those features. /// /// /// This diagnostic ID covers experimental SDK-level extensibility APIs. All constants