From 958d1fd6f1b81b12799ab91622165dc2faf838aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:18:06 +0000 Subject: [PATCH 01/10] Initial plan From 2f84c3ea8cc1009f8f8a126c325a641f16fd1913 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:50:06 +0000 Subject: [PATCH 02/10] Add JsonSerializerOptions property to McpServerOptions and update builder extensions Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../Server/McpServerOptions.cs | 21 +++++ .../McpServerBuilderExtensions.cs | 47 ++++++---- .../McpServerJsonSerializerOptionsTests.cs | 94 +++++++++++++++++++ 3 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index 618e87d58..f3e9dcc68 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -1,4 +1,5 @@ using ModelContextProtocol.Protocol; +using System.Text.Json; namespace ModelContextProtocol.Server; @@ -60,6 +61,26 @@ public sealed class McpServerOptions /// public string? ServerInstructions { get; set; } + /// + /// Gets or sets the default JSON serializer options to use for tools, prompts, and resources. + /// + /// + /// + /// This property provides server-wide default serialization settings that will be used + /// by all tools, prompts, and resources unless they explicitly specify their own + /// during registration. + /// + /// + /// If not set, defaults to . + /// + /// + /// This is useful for configuring settings like JsonNumberHandling.AllowNamedFloatingPointLiterals + /// to handle special floating-point values like , , + /// and . + /// + /// + public JsonSerializerOptions? JsonSerializerOptions { get; set; } + /// /// Gets or sets whether to create a new service provider scope for each handled request. /// diff --git a/src/ModelContextProtocol/McpServerBuilderExtensions.cs b/src/ModelContextProtocol/McpServerBuilderExtensions.cs index d4c338262..2f4ff2a5c 100644 --- a/src/ModelContextProtocol/McpServerBuilderExtensions.cs +++ b/src/ModelContextProtocol/McpServerBuilderExtensions.cs @@ -45,8 +45,8 @@ public static partial class McpServerBuilderExtensions if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) : - services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } @@ -93,7 +93,7 @@ public static partial class McpServerBuilderExtensions builder.Services.AddSingleton(services => McpServerTool.Create( toolMethod, toolMethod.IsStatic ? null : target, - new() { Services = services, SerializerOptions = serializerOptions })); + new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); } } @@ -149,8 +149,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions }) : - services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } } @@ -232,8 +232,8 @@ where t.GetCustomAttribute() is not null if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) : - services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } @@ -277,7 +277,7 @@ where t.GetCustomAttribute() is not null { if (promptMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions })); + builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); } } @@ -333,8 +333,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) : - services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } } @@ -394,6 +394,7 @@ where t.GetCustomAttribute() is not null /// Adds instances to the service collection backing . /// The resource type. /// The builder instance. + /// The serializer options governing resource parameter marshalling. /// The builder provided in . /// is . /// @@ -405,7 +406,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] TResourceType>( - this IMcpServerBuilder builder) + this IMcpServerBuilder builder, + JsonSerializerOptions? serializerOptions = null) { Throw.IfNull(builder); @@ -414,8 +416,8 @@ where t.GetCustomAttribute() is not null if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) : - services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } @@ -426,6 +428,7 @@ where t.GetCustomAttribute() is not null /// The resource type. /// The builder instance. /// The target instance from which the prompts should be sourced. + /// The serializer options governing resource parameter marshalling. /// The builder provided in . /// is . /// @@ -443,7 +446,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] TResourceType>( this IMcpServerBuilder builder, - TResourceType target) + TResourceType target, + JsonSerializerOptions? serializerOptions = null) { Throw.IfNull(builder); Throw.IfNull(target); @@ -457,7 +461,7 @@ where t.GetCustomAttribute() is not null { if (resourceTemplateMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services })); + builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); } } @@ -489,6 +493,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// Adds instances to the service collection backing . /// The builder instance. /// Types with marked methods to add as resources to the server. + /// The serializer options governing resource parameter marshalling. /// The builder provided in . /// is . /// is . @@ -498,7 +503,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// instance for each. For instance methods, an instance will be constructed for each invocation of the resource. /// [RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes) + public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes, JsonSerializerOptions? serializerOptions = null) { Throw.IfNull(builder); Throw.IfNull(resourceTemplateTypes); @@ -512,8 +517,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) : - services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); } } } @@ -526,6 +531,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// Adds types marked with the attribute from the given assembly as resources to the server. /// /// The builder instance. + /// The serializer options governing resource parameter marshalling. /// The assembly to load the types from. If , the calling assembly will be used. /// The builder provided in . /// is . @@ -550,7 +556,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// /// [RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder builder, Assembly? resourceAssembly = null) + public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder builder, Assembly? resourceAssembly = null, JsonSerializerOptions? serializerOptions = null) { Throw.IfNull(builder); @@ -559,7 +565,8 @@ public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder return builder.WithResources( from t in resourceAssembly.GetTypes() where t.GetCustomAttribute() is not null - select t); + select t, + serializerOptions); } #endregion diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs new file mode 100644 index 000000000..038676627 --- /dev/null +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs @@ -0,0 +1,94 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol; +using ModelContextProtocol.Server; +using ModelContextProtocol.Tests.Utils; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ModelContextProtocol.Tests.Configuration; + +public class McpServerJsonSerializerOptionsTests : ClientServerTestBase +{ + public McpServerJsonSerializerOptionsTests(ITestOutputHelper testOutputHelper) + : base(testOutputHelper) + { + } + + private class SpecialNumbers + { + public double PositiveInfinity { get; set; } + public double NegativeInfinity { get; set; } + public double NotANumber { get; set; } + } + + protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder) + { + // Configure server-wide JsonSerializerOptions to allow named floating point literals + var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) + { + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals + }; + + services.Configure(options => + { + options.JsonSerializerOptions = customOptions; + }); + + // Register a tool that will use the server-wide JsonSerializerOptions + // The null serializerOptions parameter should cause it to use McpServerOptions.JsonSerializerOptions + services.AddSingleton(sp => + { + var serverOptions = sp.GetRequiredService>().Value; + return McpServerTool.Create( + () => new SpecialNumbers + { + PositiveInfinity = double.PositiveInfinity, + NegativeInfinity = double.NegativeInfinity, + NotANumber = double.NaN + }, + new McpServerToolCreateOptions + { + Name = "GetSpecialNumbers", + Description = "Returns special floating point values", + UseStructuredContent = true, + SerializerOptions = serverOptions.JsonSerializerOptions, + Services = sp + }); + }); + } + + [Fact] + public async Task ServerWide_JsonSerializerOptions_Applied_To_Tools() + { + // Arrange + McpClient client = await CreateMcpClientForServer(); + + // Act + IList tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); + CallToolResult result = await client.CallToolAsync("GetSpecialNumbers", cancellationToken: TestContext.Current.CancellationToken); + + // Assert + Assert.NotNull(tools); + Assert.Single(tools); + Assert.Equal("GetSpecialNumbers", tools[0].Name); + + // Verify the result contains structured content with special numbers + Assert.NotNull(result); + Assert.NotNull(result.StructuredContent); + + var structuredContent = JsonSerializer.Deserialize( + result.StructuredContent.ToString(), + new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) + { + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals + }); + + Assert.NotNull(structuredContent); + Assert.True(double.IsPositiveInfinity(structuredContent.PositiveInfinity)); + Assert.True(double.IsNegativeInfinity(structuredContent.NegativeInfinity)); + Assert.True(double.IsNaN(structuredContent.NotANumber)); + } +} From bb8d2a5302f257dd1db573873cd48ff307c2d037 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:08:26 +0000 Subject: [PATCH 03/10] Fix service resolution to use GetService instead of GetRequiredService Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../McpServerBuilderExtensions.cs | 30 +++--- .../McpServerJsonSerializerOptionsTests.cs | 101 +++++++----------- 2 files changed, 55 insertions(+), 76 deletions(-) diff --git a/src/ModelContextProtocol/McpServerBuilderExtensions.cs b/src/ModelContextProtocol/McpServerBuilderExtensions.cs index 2f4ff2a5c..57ba5d854 100644 --- a/src/ModelContextProtocol/McpServerBuilderExtensions.cs +++ b/src/ModelContextProtocol/McpServerBuilderExtensions.cs @@ -45,8 +45,8 @@ public static partial class McpServerBuilderExtensions if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } @@ -93,7 +93,7 @@ public static partial class McpServerBuilderExtensions builder.Services.AddSingleton(services => McpServerTool.Create( toolMethod, toolMethod.IsStatic ? null : target, - new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); + new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); } } @@ -149,8 +149,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } } @@ -232,8 +232,8 @@ where t.GetCustomAttribute() is not null if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } @@ -277,7 +277,7 @@ where t.GetCustomAttribute() is not null { if (promptMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); + builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); } } @@ -333,8 +333,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } } @@ -416,8 +416,8 @@ where t.GetCustomAttribute() is not null if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } @@ -461,7 +461,7 @@ where t.GetCustomAttribute() is not null { if (resourceTemplateMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions })); + builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); } } @@ -517,8 +517,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }) : - services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService>().Value.JsonSerializerOptions }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); } } } diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs index 038676627..dcbca957b 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs @@ -1,32 +1,47 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; -using ModelContextProtocol.Tests.Utils; -using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; namespace ModelContextProtocol.Tests.Configuration; -public class McpServerJsonSerializerOptionsTests : ClientServerTestBase +public class McpServerJsonSerializerOptionsTests { - public McpServerJsonSerializerOptionsTests(ITestOutputHelper testOutputHelper) - : base(testOutputHelper) + [Fact] + public void McpServerOptions_JsonSerializerOptions_DefaultsToNull() { + // Arrange & Act + var options = new McpServerOptions(); + + // Assert + Assert.Null(options.JsonSerializerOptions); } - private class SpecialNumbers + [Fact] + public void McpServerOptions_JsonSerializerOptions_CanBeSet() { - public double PositiveInfinity { get; set; } - public double NegativeInfinity { get; set; } - public double NotANumber { get; set; } + // Arrange + var customOptions = new JsonSerializerOptions + { + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals + }; + var options = new McpServerOptions(); + + // Act + options.JsonSerializerOptions = customOptions; + + // Assert + Assert.NotNull(options.JsonSerializerOptions); + Assert.Equal(JsonNumberHandling.AllowNamedFloatingPointLiterals, options.JsonSerializerOptions.NumberHandling); } - protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder) + [Fact] + public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided() { - // Configure server-wide JsonSerializerOptions to allow named floating point literals + // Arrange + var services = new ServiceCollection(); var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals @@ -37,58 +52,22 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer options.JsonSerializerOptions = customOptions; }); - // Register a tool that will use the server-wide JsonSerializerOptions - // The null serializerOptions parameter should cause it to use McpServerOptions.JsonSerializerOptions - services.AddSingleton(sp => - { - var serverOptions = sp.GetRequiredService>().Value; - return McpServerTool.Create( - () => new SpecialNumbers - { - PositiveInfinity = double.PositiveInfinity, - NegativeInfinity = double.NegativeInfinity, - NotANumber = double.NaN - }, - new McpServerToolCreateOptions - { - Name = "GetSpecialNumbers", - Description = "Returns special floating point values", - UseStructuredContent = true, - SerializerOptions = serverOptions.JsonSerializerOptions, - Services = sp - }); - }); - } + var builder = services.AddMcpServer(); - [Fact] - public async Task ServerWide_JsonSerializerOptions_Applied_To_Tools() - { - // Arrange - McpClient client = await CreateMcpClientForServer(); + // Act - WithTools should pick up the server-wide options + builder.WithTools(); + var serviceProvider = services.BuildServiceProvider(); - // Act - IList tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); - CallToolResult result = await client.CallToolAsync("GetSpecialNumbers", cancellationToken: TestContext.Current.CancellationToken); - - // Assert - Assert.NotNull(tools); + // Assert - Verify the tool was registered + var tools = serviceProvider.GetServices().ToList(); Assert.Single(tools); - Assert.Equal("GetSpecialNumbers", tools[0].Name); - - // Verify the result contains structured content with special numbers - Assert.NotNull(result); - Assert.NotNull(result.StructuredContent); - - var structuredContent = JsonSerializer.Deserialize( - result.StructuredContent.ToString(), - new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) - { - NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals - }); + Assert.Equal("TestTool", tools[0].ProtocolTool.Name); + } - Assert.NotNull(structuredContent); - Assert.True(double.IsPositiveInfinity(structuredContent.PositiveInfinity)); - Assert.True(double.IsNegativeInfinity(structuredContent.NegativeInfinity)); - Assert.True(double.IsNaN(structuredContent.NotANumber)); + [McpServerToolType] + private class TestTools + { + [McpServerTool] + public static string TestTool() => "test"; } } From f7b9deec2601e624e7621987777aae9bc4eb40ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:09:09 +0000 Subject: [PATCH 04/10] Add usage documentation and example for JSON serialization configuration Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- USAGE_EXAMPLE.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 USAGE_EXAMPLE.md diff --git a/USAGE_EXAMPLE.md b/USAGE_EXAMPLE.md new file mode 100644 index 000000000..88c301846 --- /dev/null +++ b/USAGE_EXAMPLE.md @@ -0,0 +1,87 @@ +# JSON Serialization Configuration for MCP Server + +This document demonstrates how to configure JSON serialization options for your MCP server to handle special cases like `double.Infinity` or `NaN` values. + +## Problem + +By default, JSON serialization in .NET doesn't support special floating-point values like positive/negative infinity and NaN. When a tool returns such values, you would get an error: + +``` +System.ArgumentException: .NET number values such as positive and negative infinity cannot be written as valid JSON. +To make it work when using 'JsonSerializer', consider specifying 'JsonNumberHandling.AllowNamedFloatingPointLiterals' +``` + +## Solution + +Configure server-wide JSON serialization options when setting up your MCP server: + +```csharp +using Microsoft.Extensions.DependencyInjection; +using ModelContextProtocol.Server; +using System.Text.Json; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +// Configure server-wide JSON serialization options +builder.Services.AddMcpServer(options => +{ + options.JsonSerializerOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) + { + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals + }; +}) +.WithTools(); + +var app = builder.Build(); +app.Run(); + +[McpServerToolType] +public class MyTools +{ + [McpServerTool] + public static double[] GetSpecialNumbers() + { + // These values will now serialize correctly as "Infinity", "-Infinity", and "NaN" + return new[] { double.PositiveInfinity, double.NegativeInfinity, double.NaN }; + } +} +``` + +## How It Works + +1. The `JsonSerializerOptions` property on `McpServerOptions` provides server-wide default serialization settings +2. All tools, prompts, and resources registered via `WithTools*`, `WithPrompts*`, and `WithResources*` will use these options by default +3. Individual registrations can still override with their own specific options if needed + +## Override for Specific Tools + +If you need different serialization options for specific tools, you can still provide them explicitly: + +```csharp +var customOptions = new JsonSerializerOptions +{ + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower +}; + +builder.Services.AddMcpServer(options => +{ + options.JsonSerializerOptions = McpJsonUtilities.DefaultOptions; // Default for most tools +}) +.WithTools() // Uses server-wide options +.WithTools(customOptions); // Uses custom options +``` + +## Additional Configuration Options + +You can configure other JSON serialization settings as needed: + +```csharp +options.JsonSerializerOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) +{ + NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, + PropertyNameCaseInsensitive = true, + AllowTrailingCommas = true, + WriteIndented = true // For debugging +}; +``` From eca0585e3ea057f7a0fdbf1034390a0f1bb3d359 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:51:29 +0000 Subject: [PATCH 05/10] Improve test to verify JsonSerializerOptions are actually used by checking schema naming policy Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../McpServerJsonSerializerOptionsTests.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs index dcbca957b..cdcb7b3e4 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs @@ -44,7 +44,7 @@ public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided() var services = new ServiceCollection(); var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) { - NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; services.Configure(options => @@ -54,20 +54,30 @@ public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided() var builder = services.AddMcpServer(); - // Act - WithTools should pick up the server-wide options + // Act - WithTools should pick up the server-wide options with snake_case naming policy builder.WithTools(); var serviceProvider = services.BuildServiceProvider(); - // Assert - Verify the tool was registered + // Assert - Verify the tool schema uses snake_case property naming var tools = serviceProvider.GetServices().ToList(); Assert.Single(tools); - Assert.Equal("TestTool", tools[0].ProtocolTool.Name); + + var tool = tools[0]; + Assert.Equal("ToolWithParameters", tool.ProtocolTool.Name); + + // Check that the input schema uses snake_case for property names + var inputSchema = tool.ProtocolTool.InputSchema; + + // The schema should have a "properties" object with snake_case property names + var propertiesElement = inputSchema.GetProperty("properties"); + Assert.True(propertiesElement.TryGetProperty("my_parameter", out _), "Schema should have 'my_parameter' property (snake_case)"); + Assert.False(propertiesElement.TryGetProperty("MyParameter", out _), "Schema should not have 'MyParameter' property (PascalCase)"); } [McpServerToolType] private class TestTools { [McpServerTool] - public static string TestTool() => "test"; + public static string ToolWithParameters(string myParameter) => myParameter; } } From 542fa910cc5f2578fa3175364efa08120516bf74 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:54:15 +0000 Subject: [PATCH 06/10] Remove USAGE_EXAMPLE.md file Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- USAGE_EXAMPLE.md | 87 ------------------------------------------------ 1 file changed, 87 deletions(-) delete mode 100644 USAGE_EXAMPLE.md diff --git a/USAGE_EXAMPLE.md b/USAGE_EXAMPLE.md deleted file mode 100644 index 88c301846..000000000 --- a/USAGE_EXAMPLE.md +++ /dev/null @@ -1,87 +0,0 @@ -# JSON Serialization Configuration for MCP Server - -This document demonstrates how to configure JSON serialization options for your MCP server to handle special cases like `double.Infinity` or `NaN` values. - -## Problem - -By default, JSON serialization in .NET doesn't support special floating-point values like positive/negative infinity and NaN. When a tool returns such values, you would get an error: - -``` -System.ArgumentException: .NET number values such as positive and negative infinity cannot be written as valid JSON. -To make it work when using 'JsonSerializer', consider specifying 'JsonNumberHandling.AllowNamedFloatingPointLiterals' -``` - -## Solution - -Configure server-wide JSON serialization options when setting up your MCP server: - -```csharp -using Microsoft.Extensions.DependencyInjection; -using ModelContextProtocol.Server; -using System.Text.Json; -using System.Text.Json.Serialization; - -var builder = WebApplication.CreateBuilder(args); - -// Configure server-wide JSON serialization options -builder.Services.AddMcpServer(options => -{ - options.JsonSerializerOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) - { - NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals - }; -}) -.WithTools(); - -var app = builder.Build(); -app.Run(); - -[McpServerToolType] -public class MyTools -{ - [McpServerTool] - public static double[] GetSpecialNumbers() - { - // These values will now serialize correctly as "Infinity", "-Infinity", and "NaN" - return new[] { double.PositiveInfinity, double.NegativeInfinity, double.NaN }; - } -} -``` - -## How It Works - -1. The `JsonSerializerOptions` property on `McpServerOptions` provides server-wide default serialization settings -2. All tools, prompts, and resources registered via `WithTools*`, `WithPrompts*`, and `WithResources*` will use these options by default -3. Individual registrations can still override with their own specific options if needed - -## Override for Specific Tools - -If you need different serialization options for specific tools, you can still provide them explicitly: - -```csharp -var customOptions = new JsonSerializerOptions -{ - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower -}; - -builder.Services.AddMcpServer(options => -{ - options.JsonSerializerOptions = McpJsonUtilities.DefaultOptions; // Default for most tools -}) -.WithTools() // Uses server-wide options -.WithTools(customOptions); // Uses custom options -``` - -## Additional Configuration Options - -You can configure other JSON serialization settings as needed: - -```csharp -options.JsonSerializerOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) -{ - NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, - PropertyNameCaseInsensitive = true, - AllowTrailingCommas = true, - WriteIndented = true // For debugging -}; -``` From dfcd1ecbc774265661b12aaae2157a82b7737d18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:02:08 +0000 Subject: [PATCH 07/10] Add tests for prompts and resources using server-wide JsonSerializerOptions Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../McpServerJsonSerializerOptionsTests.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs index cdcb7b3e4..d00c7f745 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using ModelContextProtocol.Protocol; @@ -74,10 +75,90 @@ public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided() Assert.False(propertiesElement.TryGetProperty("MyParameter", out _), "Schema should not have 'MyParameter' property (PascalCase)"); } + [Fact] + public void WithPrompts_UsesServerWideOptions_WhenNoExplicitOptionsProvided() + { + // Arrange + var services = new ServiceCollection(); + var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + services.Configure(options => + { + options.JsonSerializerOptions = customOptions; + }); + + var builder = services.AddMcpServer(); + + // Act - WithPrompts should pick up the server-wide options with snake_case naming policy + builder.WithPrompts(); + var serviceProvider = services.BuildServiceProvider(); + + // Assert - Verify the prompt schema uses snake_case property naming + var prompts = serviceProvider.GetServices().ToList(); + Assert.Single(prompts); + + var prompt = prompts[0]; + Assert.Equal("PromptWithParameters", prompt.ProtocolPrompt.Name); + + // Check that the arguments schema uses snake_case for property names + var arguments = prompt.ProtocolPrompt.Arguments; + Assert.NotNull(arguments); + Assert.Single(arguments); + Assert.Equal("my_argument", arguments[0].Name); + } + + [Fact] + public void WithResources_UsesServerWideOptions_WhenNoExplicitOptionsProvided() + { + // Arrange + var services = new ServiceCollection(); + var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions) + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + services.Configure(options => + { + options.JsonSerializerOptions = customOptions; + }); + + var builder = services.AddMcpServer(); + + // Act - WithResources should pick up the server-wide options with snake_case naming policy + builder.WithResources(); + var serviceProvider = services.BuildServiceProvider(); + + // Assert - Verify the resource was registered (resources don't expose schema in the same way) + var resources = serviceProvider.GetServices().ToList(); + Assert.Single(resources); + + var resource = resources[0]; + Assert.Equal("resource://test/{myParameter}", resource.ProtocolResourceTemplate.UriTemplate); + } + [McpServerToolType] private class TestTools { [McpServerTool] public static string ToolWithParameters(string myParameter) => myParameter; } + + [McpServerPromptType] + private class TestPrompts + { + [McpServerPrompt] + public static ChatMessage PromptWithParameters(string myArgument) => + new(ChatRole.User, $"Prompt with: {myArgument}"); + } + + [McpServerResourceType] + private class TestResources + { + [McpServerResource(UriTemplate = "resource://test/{myParameter}")] + public static string ResourceWithParameters(string myParameter) => + $"Resource content: {myParameter}"; + } } From e62aae87f0293ef5bae73ce11f1a5fc835e7cb6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:38:27 +0000 Subject: [PATCH 08/10] Add SchemaCreateOptions property to McpServerOptions Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../Server/McpServerOptions.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index f3e9dcc68..3f323bbe3 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -81,6 +81,21 @@ public sealed class McpServerOptions /// public JsonSerializerOptions? JsonSerializerOptions { get; set; } + /// + /// Gets or sets the default JSON schema creation options to use for tools, prompts, and resources. + /// + /// + /// + /// This property provides server-wide default schema creation settings that will be used + /// by all tools, prompts, and resources unless they explicitly specify their own + /// during registration. + /// + /// + /// If not set, defaults to . + /// + /// + public Microsoft.Extensions.AI.AIJsonSchemaCreateOptions? SchemaCreateOptions { get; set; } + /// /// Gets or sets whether to create a new service provider scope for each handled request. /// From d56b8e541054f1e037cb48dd6fd7a83669823d4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 06:08:30 +0000 Subject: [PATCH 09/10] Add AIJsonSchemaCreateOptions parameter to all WithTools/Prompts/Resources methods Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../McpServerBuilderExtensions.cs | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/src/ModelContextProtocol/McpServerBuilderExtensions.cs b/src/ModelContextProtocol/McpServerBuilderExtensions.cs index 57ba5d854..fde2a8991 100644 --- a/src/ModelContextProtocol/McpServerBuilderExtensions.cs +++ b/src/ModelContextProtocol/McpServerBuilderExtensions.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -24,6 +25,7 @@ public static partial class McpServerBuilderExtensions /// The tool type. /// The builder instance. /// The serializer options governing tool parameter marshalling. + /// The JSON schema creation options governing tool schema generation. /// The builder provided in . /// is . /// @@ -36,7 +38,8 @@ public static partial class McpServerBuilderExtensions DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] TToolType>( this IMcpServerBuilder builder, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -45,8 +48,8 @@ public static partial class McpServerBuilderExtensions if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions, SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions, SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } @@ -58,6 +61,7 @@ public static partial class McpServerBuilderExtensions /// The builder instance. /// The target instance from which the tools should be sourced. /// The serializer options governing tool parameter marshalling. + /// The JSON schema creation options governing tool schema generation. /// The builder provided in . /// is . /// @@ -76,7 +80,8 @@ public static partial class McpServerBuilderExtensions DynamicallyAccessedMemberTypes.NonPublicMethods)] TToolType>( this IMcpServerBuilder builder, TToolType target, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(target); @@ -93,7 +98,7 @@ public static partial class McpServerBuilderExtensions builder.Services.AddSingleton(services => McpServerTool.Create( toolMethod, toolMethod.IsStatic ? null : target, - new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); + new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions })); } } @@ -126,6 +131,7 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume /// The builder instance. /// Types with -attributed methods to add as tools to the server. /// The serializer options governing tool parameter marshalling. + /// The JSON schema creation options governing tool schema generation. /// The builder provided in . /// is . /// is . @@ -135,7 +141,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume /// instance for each. For instance methods, an instance will be constructed for each invocation of the tool. /// [RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable toolTypes, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable toolTypes, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(toolTypes); @@ -149,8 +156,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } } @@ -164,6 +171,7 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume /// /// The builder instance. /// The serializer options governing tool parameter marshalling. + /// The JSON schema creation options governing tool schema generation. /// The assembly to load the types from. If , the calling assembly will be used. /// The builder provided in . /// is . @@ -188,7 +196,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume /// /// [RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -198,7 +207,8 @@ public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder bui from t in toolAssembly.GetTypes() where t.GetCustomAttribute() is not null select t, - serializerOptions); + serializerOptions, + schemaCreateOptions); } #endregion @@ -211,6 +221,7 @@ where t.GetCustomAttribute() is not null /// The prompt type. /// The builder instance. /// The serializer options governing prompt parameter marshalling. + /// The JSON schema creation options governing prompt schema generation. /// The builder provided in . /// is . /// @@ -223,7 +234,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] TPromptType>( this IMcpServerBuilder builder, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -232,8 +244,8 @@ where t.GetCustomAttribute() is not null if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } @@ -245,6 +257,7 @@ where t.GetCustomAttribute() is not null /// The builder instance. /// The target instance from which the prompts should be sourced. /// The serializer options governing prompt parameter marshalling. + /// The JSON schema creation options governing prompt schema generation. /// The builder provided in . /// is . /// @@ -263,7 +276,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.NonPublicMethods)] TPromptType>( this IMcpServerBuilder builder, TPromptType target, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(target); @@ -277,7 +291,7 @@ where t.GetCustomAttribute() is not null { if (promptMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); + builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions })); } } @@ -310,6 +324,7 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu /// The builder instance. /// Types with marked methods to add as prompts to the server. /// The serializer options governing prompt parameter marshalling. + /// The JSON schema creation options governing prompt schema generation. /// The builder provided in . /// is . /// is . @@ -319,7 +334,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu /// instance for each. For instance methods, an instance will be constructed for each invocation of the prompt. /// [RequiresUnreferencedCode(WithPromptsRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnumerable promptTypes, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnumerable promptTypes, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(promptTypes); @@ -333,8 +349,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu if (promptMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(promptMethod.IsStatic ? - services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } } @@ -348,6 +364,7 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu /// /// The builder instance. /// The serializer options governing prompt parameter marshalling. + /// The JSON schema creation options governing prompt schema generation. /// The assembly to load the types from. If , the calling assembly will be used. /// The builder provided in . /// is . @@ -372,7 +389,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu /// /// [RequiresUnreferencedCode(WithPromptsRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithPromptsFromAssembly(this IMcpServerBuilder builder, Assembly? promptAssembly = null, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithPromptsFromAssembly(this IMcpServerBuilder builder, Assembly? promptAssembly = null, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -382,7 +400,8 @@ public static IMcpServerBuilder WithPromptsFromAssembly(this IMcpServerBuilder b from t in promptAssembly.GetTypes() where t.GetCustomAttribute() is not null select t, - serializerOptions); + serializerOptions, + schemaCreateOptions); } #endregion @@ -395,6 +414,7 @@ where t.GetCustomAttribute() is not null /// The resource type. /// The builder instance. /// The serializer options governing resource parameter marshalling. + /// The JSON schema creation options governing resource schema generation. /// The builder provided in . /// is . /// @@ -407,7 +427,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] TResourceType>( this IMcpServerBuilder builder, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -416,8 +437,8 @@ where t.GetCustomAttribute() is not null if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } @@ -429,6 +450,7 @@ where t.GetCustomAttribute() is not null /// The builder instance. /// The target instance from which the prompts should be sourced. /// The serializer options governing resource parameter marshalling. + /// The JSON schema creation options governing resource schema generation. /// The builder provided in . /// is . /// @@ -447,7 +469,8 @@ where t.GetCustomAttribute() is not null DynamicallyAccessedMemberTypes.NonPublicMethods)] TResourceType>( this IMcpServerBuilder builder, TResourceType target, - JsonSerializerOptions? serializerOptions = null) + JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(target); @@ -461,7 +484,7 @@ where t.GetCustomAttribute() is not null { if (resourceTemplateMethod.GetCustomAttribute() is not null) { - builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions })); + builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions })); } } @@ -494,6 +517,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// The builder instance. /// Types with marked methods to add as resources to the server. /// The serializer options governing resource parameter marshalling. + /// The JSON schema creation options governing resource schema generation. /// The builder provided in . /// is . /// is . @@ -503,7 +527,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// instance for each. For instance methods, an instance will be constructed for each invocation of the resource. /// [RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); Throw.IfNull(resourceTemplateTypes); @@ -517,8 +542,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE if (resourceTemplateMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(resourceTemplateMethod.IsStatic ? - services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }) : - services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions }))); + services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : + services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); } } } @@ -532,6 +557,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// /// The builder instance. /// The serializer options governing resource parameter marshalling. + /// The JSON schema creation options governing resource schema generation. /// The assembly to load the types from. If , the calling assembly will be used. /// The builder provided in . /// is . @@ -556,7 +582,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE /// /// [RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)] - public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder builder, Assembly? resourceAssembly = null, JsonSerializerOptions? serializerOptions = null) + public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder builder, Assembly? resourceAssembly = null, JsonSerializerOptions? serializerOptions = null, + AIJsonSchemaCreateOptions? schemaCreateOptions = null) { Throw.IfNull(builder); @@ -566,7 +593,8 @@ public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder from t in resourceAssembly.GetTypes() where t.GetCustomAttribute() is not null select t, - serializerOptions); + serializerOptions, + schemaCreateOptions); } #endregion From 750708180960106693a7a928f5e10722b78ee946 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:31:50 +0000 Subject: [PATCH 10/10] Fix stack overflow by using McpServerDefaultOptions instead of resolving IOptions during tool creation Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../McpServerBuilderExtensions.cs | 32 +++++++++++++++++-- .../McpServerDefaultOptions.cs | 21 ++++++++++++ .../McpServerServiceCollectionExtensions.cs | 13 ++++++++ .../McpServerJsonSerializerOptionsTests.cs | 12 ++----- 4 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/ModelContextProtocol/McpServerDefaultOptions.cs diff --git a/src/ModelContextProtocol/McpServerBuilderExtensions.cs b/src/ModelContextProtocol/McpServerBuilderExtensions.cs index fde2a8991..693f74f65 100644 --- a/src/ModelContextProtocol/McpServerBuilderExtensions.cs +++ b/src/ModelContextProtocol/McpServerBuilderExtensions.cs @@ -48,8 +48,36 @@ public static partial class McpServerBuilderExtensions if (toolMethod.GetCustomAttribute() is not null) { builder.Services.AddSingleton((Func)(toolMethod.IsStatic ? - services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions, SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }) : - services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService>()?.Value.JsonSerializerOptions, SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }))); + services => + { + var effectiveSerializerOptions = serializerOptions; + var effectiveSchemaCreateOptions = schemaCreateOptions; + + // Try to get server-wide defaults if not explicitly provided + if (effectiveSerializerOptions is null || effectiveSchemaCreateOptions is null) + { + var defaultOptions = services.GetService(); + effectiveSerializerOptions ??= defaultOptions?.JsonSerializerOptions; + effectiveSchemaCreateOptions ??= defaultOptions?.SchemaCreateOptions; + } + + return McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = effectiveSerializerOptions, SchemaCreateOptions = effectiveSchemaCreateOptions }); + } : + services => + { + var effectiveSerializerOptions = serializerOptions; + var effectiveSchemaCreateOptions = schemaCreateOptions; + + // Try to get server-wide defaults if not explicitly provided + if (effectiveSerializerOptions is null || effectiveSchemaCreateOptions is null) + { + var defaultOptions = services.GetService(); + effectiveSerializerOptions ??= defaultOptions?.JsonSerializerOptions; + effectiveSchemaCreateOptions ??= defaultOptions?.SchemaCreateOptions; + } + + return McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = effectiveSerializerOptions, SchemaCreateOptions = effectiveSchemaCreateOptions }); + })); } } diff --git a/src/ModelContextProtocol/McpServerDefaultOptions.cs b/src/ModelContextProtocol/McpServerDefaultOptions.cs new file mode 100644 index 000000000..e1d3a1e22 --- /dev/null +++ b/src/ModelContextProtocol/McpServerDefaultOptions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.AI; +using System.Text.Json; + +namespace ModelContextProtocol.Server; + +/// +/// Holds default options for MCP server primitives (tools, prompts, resources). +/// This is separate from McpServerOptions to avoid circular dependencies during service resolution. +/// +internal sealed class McpServerDefaultOptions +{ + /// + /// Gets or sets the default JSON serializer options. + /// + public JsonSerializerOptions? JsonSerializerOptions { get; set; } + + /// + /// Gets or sets the default JSON schema creation options. + /// + public AIJsonSchemaCreateOptions? SchemaCreateOptions { get; set; } +} diff --git a/src/ModelContextProtocol/McpServerServiceCollectionExtensions.cs b/src/ModelContextProtocol/McpServerServiceCollectionExtensions.cs index d0072002c..9910ce84e 100644 --- a/src/ModelContextProtocol/McpServerServiceCollectionExtensions.cs +++ b/src/ModelContextProtocol/McpServerServiceCollectionExtensions.cs @@ -21,10 +21,23 @@ public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, A { services.AddOptions(); services.TryAddEnumerable(ServiceDescriptor.Transient, McpServerOptionsSetup>()); + + // Capture default options from the configuration callback to avoid circular dependencies + // when resolving IOptions from within tool/prompt/resource factories + var defaultOptions = new McpServerDefaultOptions(); if (configureOptions is not null) { + var tempOptions = new McpServerOptions(); + configureOptions(tempOptions); + defaultOptions.JsonSerializerOptions = tempOptions.JsonSerializerOptions; + defaultOptions.SchemaCreateOptions = tempOptions.SchemaCreateOptions; + services.Configure(configureOptions); } + + // Register the default options as a singleton that can be safely resolved + // without circular dependencies + services.TryAddSingleton(defaultOptions); return new DefaultMcpServerBuilder(services); } diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs index d00c7f745..e2cd323e0 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs @@ -48,13 +48,11 @@ public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided() PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; - services.Configure(options => + var builder = services.AddMcpServer(options => { options.JsonSerializerOptions = customOptions; }); - var builder = services.AddMcpServer(); - // Act - WithTools should pick up the server-wide options with snake_case naming policy builder.WithTools(); var serviceProvider = services.BuildServiceProvider(); @@ -85,13 +83,11 @@ public void WithPrompts_UsesServerWideOptions_WhenNoExplicitOptionsProvided() PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; - services.Configure(options => + var builder = services.AddMcpServer(options => { options.JsonSerializerOptions = customOptions; }); - var builder = services.AddMcpServer(); - // Act - WithPrompts should pick up the server-wide options with snake_case naming policy builder.WithPrompts(); var serviceProvider = services.BuildServiceProvider(); @@ -120,13 +116,11 @@ public void WithResources_UsesServerWideOptions_WhenNoExplicitOptionsProvided() PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; - services.Configure(options => + var builder = services.AddMcpServer(options => { options.JsonSerializerOptions = customOptions; }); - var builder = services.AddMcpServer(); - // Act - WithResources should pick up the server-wide options with snake_case naming policy builder.WithResources(); var serviceProvider = services.BuildServiceProvider();