diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 49fadb5..d9fd0e2 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -90,7 +90,7 @@ internal sealed partial class McpServerImpl : McpServer
ConfigureTasks(options);
ConfigureLogging(options);
ConfigureCompletion(options);
- ConfigureExperimental(options);
+ ConfigureExperimentalAndExtensions(options);
// Register any notification handlers that were provided.
if (options.Handlers.NotificationHandlers is { } notificationHandlers)
@@ -138,7 +138,7 @@ internal sealed partial class McpServerImpl : McpServer
public override string? NegotiatedProtocolVersion => _negotiatedProtocolVersion;
///
- public ServerCapabilities ServerCapabilities { get; } = new();
+ public ServerCapabilities ServerCapabilities { get; }
///
public override ClientCapabilities? ClientCapabilities => _clientCapabilities;
@@ -262,9 +262,10 @@ internal sealed partial class McpServerImpl : McpServer
McpJsonUtilities.JsonContext.Default.CompleteResult);
}
- private void ConfigureExperimental(McpServerOptions options)
+ private void ConfigureExperimentalAndExtensions(McpServerOptions options)
{
ServerCapabilities.Experimental = options.Capabilities?.Experimental;
+ ServerCapabilities.Extensions = options.Capabilities?.Extensions;
}
private void ConfigureResources(McpServerOptions options)
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
index 53fd506..ea680ec 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
@@ -290,6 +290,95 @@ public class McpServerTests : LoggedTest
});
}
+ [Fact]
+ public async Task Initialize_IncludesExtensionsInResponse()
+ {
+ await Can_Handle_Requests(
+ serverCapabilities: new ServerCapabilities
+ {
+ Extensions = new Dictionary { ["io.myext"] = new JsonObject { ["required"] = true } },
+ },
+ method: RequestMethods.Initialize,
+ configureOptions: null,
+ assertResult: (_, response) =>
+ {
+ var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
+ Assert.NotNull(result);
+ Assert.NotNull(result.Capabilities.Extensions);
+ Assert.True(result.Capabilities.Extensions.ContainsKey("io.myext"));
+ });
+ }
+
+ [Fact]
+ public async Task Initialize_IncludesExperimentalInResponse()
+ {
+ await Can_Handle_Requests(
+ serverCapabilities: new ServerCapabilities
+ {
+ Experimental = new Dictionary { ["customFeature"] = new JsonObject { ["enabled"] = true } },
+ },
+ method: RequestMethods.Initialize,
+ configureOptions: null,
+ assertResult: (_, response) =>
+ {
+ var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
+ Assert.NotNull(result);
+ Assert.NotNull(result.Capabilities.Experimental);
+ Assert.True(result.Capabilities.Experimental.ContainsKey("customFeature"));
+ });
+ }
+
+ [Fact]
+ public async Task Initialize_CopiesAllCapabilityProperties()
+ {
+ // Set every public property on ServerCapabilities to a non-null value.
+ // If a new property is added to ServerCapabilities in the future but the
+ // server fails to copy it, this reflection-based test will automatically
+ // detect the missing property and fail.
+ var inputCapabilities = new ServerCapabilities
+ {
+ Experimental = new Dictionary { ["test"] = new JsonObject() },
+ Logging = new LoggingCapability(),
+ Prompts = new PromptsCapability(),
+ Resources = new ResourcesCapability(),
+ Tools = new ToolsCapability(),
+ Completions = new CompletionsCapability(),
+ Tasks = new McpTasksCapability(),
+ Extensions = new Dictionary { ["io.test"] = new JsonObject() },
+ };
+
+ await Can_Handle_Requests(
+ serverCapabilities: inputCapabilities,
+ method: RequestMethods.Initialize,
+ configureOptions: options =>
+ {
+ // Tasks capability requires a TaskStore
+ options.TaskStore = new InMemoryMcpTaskStore();
+ },
+ assertResult: (_, response) =>
+ {
+ var result = JsonSerializer.Deserialize(response, McpJsonUtilities.DefaultOptions);
+ Assert.NotNull(result);
+
+ // Use reflection to verify every public property on ServerCapabilities is non-null.
+ // This catches cases where new capability properties are added but not copied
+ // from options in McpServerImpl.
+ foreach (var property in typeof(ServerCapabilities).GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ if (!property.CanRead)
+ {
+ continue;
+ }
+
+ Assert.True(
+ property.GetValue(result.Capabilities) is not null,
+ $"ServerCapabilities.{property.Name} was set on options but is null in the initialize response. " +
+ $"Ensure the property is copied in McpServerImpl's Configure* methods.");
+ }
+ });
+ }
+#pragma warning restore MCPEXP001
+
[Fact]
public async Task Can_Handle_Completion_Requests()
{