diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs
index 618e87d5..3f323bbe 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,41 @@ 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 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.
///
diff --git a/src/ModelContextProtocol/McpServerBuilderExtensions.cs b/src/ModelContextProtocol/McpServerBuilderExtensions.cs
index d4c33826..693f74f6 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,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 => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions })));
+ 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 });
+ }));
}
}
@@ -58,6 +89,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 +108,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 +126,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.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }));
}
}
@@ -126,6 +159,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 +169,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 +184,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.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 +199,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 +224,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 +235,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 +249,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 +262,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 +272,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.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 +285,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 +304,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 +319,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.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }));
}
}
@@ -310,6 +352,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 +362,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 +377,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.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 +392,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 +417,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 +428,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
@@ -394,6 +441,8 @@ 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 JSON schema creation options governing resource schema generation.
/// The builder provided in .
/// is .
///
@@ -405,7 +454,9 @@ where t.GetCustomAttribute() is not null
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TResourceType>(
- this IMcpServerBuilder builder)
+ this IMcpServerBuilder builder,
+ JsonSerializerOptions? serializerOptions = null,
+ AIJsonSchemaCreateOptions? schemaCreateOptions = null)
{
Throw.IfNull(builder);
@@ -414,8 +465,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.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 })));
}
}
@@ -426,6 +477,8 @@ 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 JSON schema creation options governing resource schema generation.
/// The builder provided in .
/// is .
///
@@ -443,7 +496,9 @@ where t.GetCustomAttribute() is not null
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods)] TResourceType>(
this IMcpServerBuilder builder,
- TResourceType target)
+ TResourceType target,
+ JsonSerializerOptions? serializerOptions = null,
+ AIJsonSchemaCreateOptions? schemaCreateOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(target);
@@ -457,7 +512,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.GetService>()?.Value.JsonSerializerOptions , SchemaCreateOptions = schemaCreateOptions ?? services.GetService>()?.Value.SchemaCreateOptions }));
}
}
@@ -489,6 +544,8 @@ 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 JSON schema creation options governing resource schema generation.
/// The builder provided in .
/// is .
/// is .
@@ -498,7 +555,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)
+ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable resourceTemplateTypes, JsonSerializerOptions? serializerOptions = null,
+ AIJsonSchemaCreateOptions? schemaCreateOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(resourceTemplateTypes);
@@ -512,8 +570,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.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 })));
}
}
}
@@ -526,6 +584,8 @@ 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 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 .
@@ -550,7 +610,8 @@ 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,
+ AIJsonSchemaCreateOptions? schemaCreateOptions = null)
{
Throw.IfNull(builder);
@@ -559,7 +620,9 @@ 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,
+ schemaCreateOptions);
}
#endregion
diff --git a/src/ModelContextProtocol/McpServerDefaultOptions.cs b/src/ModelContextProtocol/McpServerDefaultOptions.cs
new file mode 100644
index 00000000..e1d3a1e2
--- /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 d0072002..9910ce84 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
new file mode 100644
index 00000000..e2cd323e
--- /dev/null
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerJsonSerializerOptionsTests.cs
@@ -0,0 +1,158 @@
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using ModelContextProtocol.Protocol;
+using ModelContextProtocol.Server;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace ModelContextProtocol.Tests.Configuration;
+
+public class McpServerJsonSerializerOptionsTests
+{
+ [Fact]
+ public void McpServerOptions_JsonSerializerOptions_DefaultsToNull()
+ {
+ // Arrange & Act
+ var options = new McpServerOptions();
+
+ // Assert
+ Assert.Null(options.JsonSerializerOptions);
+ }
+
+ [Fact]
+ public void McpServerOptions_JsonSerializerOptions_CanBeSet()
+ {
+ // 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);
+ }
+
+ [Fact]
+ public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions)
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
+ };
+
+ var builder = services.AddMcpServer(options =>
+ {
+ options.JsonSerializerOptions = customOptions;
+ });
+
+ // Act - WithTools should pick up the server-wide options with snake_case naming policy
+ builder.WithTools();
+ var serviceProvider = services.BuildServiceProvider();
+
+ // Assert - Verify the tool schema uses snake_case property naming
+ var tools = serviceProvider.GetServices().ToList();
+ Assert.Single(tools);
+
+ 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)");
+ }
+
+ [Fact]
+ public void WithPrompts_UsesServerWideOptions_WhenNoExplicitOptionsProvided()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions)
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
+ };
+
+ var builder = services.AddMcpServer(options =>
+ {
+ options.JsonSerializerOptions = customOptions;
+ });
+
+ // 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
+ };
+
+ var builder = services.AddMcpServer(options =>
+ {
+ options.JsonSerializerOptions = customOptions;
+ });
+
+ // 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}";
+ }
+}