diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md
index 43574929b..ae4cab4fb 100644
--- a/docs/list-of-diagnostics.md
+++ b/docs/list-of-diagnostics.md
@@ -24,6 +24,7 @@ If you use experimental APIs, you will get one of the diagnostics shown below. T
| Diagnostic ID | Description |
| :------------ | :---------- |
| `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)). |
+| `MCPEXP002` | Subclassing `McpClient` and `McpServer` is experimental and subject to change (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)). |
## Obsolete APIs
@@ -34,4 +35,4 @@ When APIs are marked as obsolete, a diagnostic is emitted to warn users that the
| Diagnostic ID | Status | Description |
| :------------ | :----- | :---------- |
| `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. |
-| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. |
\ No newline at end of file
+| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. |
diff --git a/src/Common/Experimentals.cs b/src/Common/Experimentals.cs
index ec2c7c550..dd37a0766 100644
--- a/src/Common/Experimentals.cs
+++ b/src/Common/Experimentals.cs
@@ -56,4 +56,19 @@ internal static class Experimentals
/// URL for the experimental MCP Extensions feature.
///
public const string Extensions_Url = "https://github.com/modelcontextprotocol/csharp-sdk/blob/main/docs/list-of-diagnostics.md#mcpexp001";
+
+ ///
+ /// Diagnostic ID for experimental subclassing of McpClient and McpServer.
+ ///
+ public const string Subclassing_DiagnosticId = "MCPEXP002";
+
+ ///
+ /// Message for experimental subclassing of McpClient and McpServer.
+ ///
+ public const string Subclassing_Message = "Subclassing McpClient and McpServer is experimental and subject to change.";
+
+ ///
+ /// URL for experimental subclassing of McpClient and McpServer.
+ ///
+ public const string Subclassing_Url = "https://github.com/modelcontextprotocol/csharp-sdk/pull/1363";
}
diff --git a/src/ModelContextProtocol.Core/Client/McpClient.cs b/src/ModelContextProtocol.Core/Client/McpClient.cs
index 28a37f258..acd0bb12d 100644
--- a/src/ModelContextProtocol.Core/Client/McpClient.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClient.cs
@@ -1,4 +1,5 @@
-using ModelContextProtocol.Protocol;
+using System.Diagnostics.CodeAnalysis;
+using ModelContextProtocol.Protocol;
namespace ModelContextProtocol.Client;
@@ -7,6 +8,14 @@ namespace ModelContextProtocol.Client;
///
public abstract partial class McpClient : McpSession
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
+ protected McpClient()
+ {
+ }
+
///
/// Gets the capabilities supported by the connected server.
///
diff --git a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
index 9375e2760..8317656d0 100644
--- a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
@@ -6,6 +6,7 @@
namespace ModelContextProtocol.Client;
///
+#pragma warning disable MCPEXP002
internal sealed partial class McpClientImpl : McpClient
{
private static Implementation DefaultImplementation { get; } = new()
@@ -37,6 +38,7 @@ internal sealed partial class McpClientImpl : McpClient
/// Options for the client, defining protocol version and capabilities.
/// The logger factory.
internal McpClientImpl(ITransport transport, string endpointName, McpClientOptions? options, ILoggerFactory? loggerFactory)
+#pragma warning restore MCPEXP002
{
options ??= new();
diff --git a/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs b/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
index 784e0f9a6..957f58a51 100644
--- a/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
+++ b/src/ModelContextProtocol.Core/Server/DestinationBoundMcpServer.cs
@@ -3,7 +3,9 @@
namespace ModelContextProtocol.Server;
+#pragma warning disable MCPEXP002
internal sealed class DestinationBoundMcpServer(McpServerImpl server, ITransport? transport) : McpServer
+#pragma warning restore MCPEXP002
{
public override string? SessionId => transport?.SessionId ?? server.SessionId;
public override string? NegotiatedProtocolVersion => server.NegotiatedProtocolVersion;
diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs
index 2d8ea6826..b8b41bdc3 100644
--- a/src/ModelContextProtocol.Core/Server/McpServer.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServer.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using ModelContextProtocol.Protocol;
namespace ModelContextProtocol.Server;
@@ -7,6 +8,14 @@ namespace ModelContextProtocol.Server;
///
public abstract partial class McpServer : McpSession
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
+ protected McpServer()
+ {
+ }
+
///
/// Gets the capabilities supported by the client.
///
diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 52e538e7e..39feae5d6 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -9,6 +9,7 @@
namespace ModelContextProtocol.Server;
///
+#pragma warning disable MCPEXP002
internal sealed partial class McpServerImpl : McpServer
{
internal static Implementation DefaultImplementation { get; } = new()
@@ -54,6 +55,7 @@ internal sealed partial class McpServerImpl : McpServer
/// Optional service provider to use for dependency injection
/// The server was incorrectly configured.
public McpServerImpl(ITransport transport, McpServerOptions options, ILoggerFactory? loggerFactory, IServiceProvider? serviceProvider)
+#pragma warning restore MCPEXP002
{
Throw.IfNull(transport);
Throw.IfNull(options);
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
index 046f08e8b..53fd50685 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
@@ -943,7 +943,9 @@ private static async Task InitializeServerAsync(TestServerTransport transport, C
await tcs.Task.WaitAsync(TestConstants.DefaultTimeout, cancellationToken);
}
+#pragma warning disable MCPEXP002
private sealed class TestServerForIChatClient(bool supportsSampling) : McpServer
+#pragma warning restore MCPEXP002
{
public override ClientCapabilities? ClientCapabilities =>
supportsSampling ? new ClientCapabilities { Sampling = new SamplingCapability() } :