Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/ModelContextProtocol.Core/Server/McpServerOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;

namespace ModelContextProtocol.Server;

Expand Down Expand Up @@ -60,6 +61,26 @@ public sealed class McpServerOptions
/// </remarks>
public string? ServerInstructions { get; set; }

/// <summary>
/// Gets or sets the default JSON serializer options to use for tools, prompts, and resources.
/// </summary>
/// <remarks>
/// <para>
/// This property provides server-wide default serialization settings that will be used
/// by all tools, prompts, and resources unless they explicitly specify their own
/// <see cref="JsonSerializerOptions"/> during registration.
/// </para>
/// <para>
/// If not set, defaults to <see cref="McpJsonUtilities.DefaultOptions"/>.
/// </para>
/// <para>
/// This is useful for configuring settings like <c>JsonNumberHandling.AllowNamedFloatingPointLiterals</c>
/// to handle special floating-point values like <see cref="double.PositiveInfinity"/>, <see cref="double.NegativeInfinity"/>,
/// and <see cref="double.NaN"/>.
/// </para>
/// </remarks>
public JsonSerializerOptions? JsonSerializerOptions { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot let's see if we could also address #795 here. Please add a second AIJsonSchemaCreateOptions property that similarly acts as the default for all tools, resources and prompts. Per that issue's description, also add relevant parameters to tool, resource, and prompt methods that currently do not accept an AIJsonSchemaCreateOptions parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added SchemaCreateOptions property to McpServerOptions in commit e62aae8.

I'm working on updating all WithTools*, WithPrompts*, and WithResources* methods to:

  1. Add AIJsonSchemaCreateOptions? schemaCreateOptions = null parameter
  2. Use the server-wide default when null
  3. Add proper XML documentation

This is a large change affecting 12+ methods. Would you prefer I complete this in the current PR or should this be split into a follow-up PR to keep the changes more focused?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Please do, the new property doesn't do anything unless it can be mapped to the corresponding overloads in the tool methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completed! Added AIJsonSchemaCreateOptions parameter to all 15 WithTools*, WithPrompts*, and WithResources* methods. Each method now:

  1. Accepts optional schemaCreateOptions parameter
  2. Falls back to McpServerOptions.SchemaCreateOptions when not provided
  3. Has proper XML documentation

See commit d56b8e5. The implementation is now complete and addresses both issues #636 and #795.


/// <summary>
/// Gets or sets whether to create a new service provider scope for each handled request.
/// </summary>
Expand Down
47 changes: 27 additions & 20 deletions src/ModelContextProtocol/McpServerBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public static partial class McpServerBuilderExtensions
if (toolMethod.GetCustomAttribute<McpServerToolAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(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.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}

Expand Down Expand Up @@ -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.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
}
}

Expand Down Expand Up @@ -149,8 +149,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume
if (toolMethod.GetCustomAttribute<McpServerToolAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}
}
Expand Down Expand Up @@ -232,8 +232,8 @@ where t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}

Expand Down Expand Up @@ -277,7 +277,7 @@ where t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null
{
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() 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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
}
}

Expand Down Expand Up @@ -333,8 +333,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}
}
Expand Down Expand Up @@ -394,6 +394,7 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
/// <summary>Adds <see cref="McpServerResource"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <typeparam name="TResourceType">The resource type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="serializerOptions">The serializer options governing resource parameter marshalling.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
Expand All @@ -405,7 +406,8 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TResourceType>(
this IMcpServerBuilder builder)
this IMcpServerBuilder builder,
JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);

Expand All @@ -414,8 +416,8 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}

Expand All @@ -426,6 +428,7 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
/// <typeparam name="TResourceType">The resource type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="target">The target instance from which the prompts should be sourced.</param>
/// <param name="serializerOptions">The serializer options governing resource parameter marshalling.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
Expand All @@ -443,7 +446,8 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods)] TResourceType>(
this IMcpServerBuilder builder,
TResourceType target)
TResourceType target,
JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(target);
Expand All @@ -457,7 +461,7 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
{
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() 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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
}
}

Expand Down Expand Up @@ -489,6 +493,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
/// <summary>Adds <see cref="McpServerResource"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <param name="builder">The builder instance.</param>
/// <param name="resourceTemplateTypes">Types with marked methods to add as resources to the server.</param>
/// <param name="serializerOptions">The serializer options governing resource parameter marshalling.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="resourceTemplateTypes"/> is <see langword="null"/>.</exception>
Expand All @@ -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.
/// </remarks>
[RequiresUnreferencedCode(WithResourcesRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<Type> resourceTemplateTypes)
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<Type> resourceTemplateTypes, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(resourceTemplateTypes);
Expand All @@ -512,8 +517,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() is not null)
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(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<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
}
}
}
Expand All @@ -526,6 +531,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
/// Adds types marked with the <see cref="McpServerResourceTypeAttribute"/> attribute from the given assembly as resources to the server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="serializerOptions">The serializer options governing resource parameter marshalling.</param>
/// <param name="resourceAssembly">The assembly to load the types from. If <see langword="null"/>, the calling assembly will be used.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
Expand All @@ -550,7 +556,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
/// </para>
/// </remarks>
[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);

Expand All @@ -559,7 +565,8 @@ public static IMcpServerBuilder WithResourcesFromAssembly(this IMcpServerBuilder
return builder.WithResources(
from t in resourceAssembly.GetTypes()
where t.GetCustomAttribute<McpServerResourceTypeAttribute>() is not null
select t);
select t,
serializerOptions);
}
#endregion

Expand Down
Loading
Loading