Disable STJ reflection for testing. (#266)

* Disable STJ reflection for testing.

* Remove params method and redundant overload.

---------

Co-authored-by: Stephen Toub <stoub@microsoft.com>
This commit is contained in:
Eirik Tsarpalis 2025-04-10 16:02:00 +01:00 • committed by GitHub
commit e3aa54eb3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 155 additions and 75 deletions

View file

@ -8,6 +8,7 @@ using ModelContextProtocol.Server;
using ModelContextProtocol.Utils;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
namespace Microsoft.Extensions.DependencyInjection;
@ -24,6 +25,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <typeparam name="TToolType">The tool type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter marshalling.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
@ -35,7 +37,8 @@ public static partial class McpServerBuilderExtensions
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TToolType>(
this IMcpServerBuilder builder)
this IMcpServerBuilder builder,
JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
@ -44,8 +47,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 }) :
services => McpServerTool.Create(toolMethod, typeof(TToolType), new() { Services = services })));
services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, typeof(TToolType), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
@ -55,6 +58,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <param name="builder">The builder instance.</param>
/// <param name="toolTypes">Types with marked methods to add as tools to the server.</param>
/// <param name="serializerOptions">The serializer options governing tool 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="toolTypes"/> is <see langword="null"/>.</exception>
@ -64,7 +68,7 @@ public static partial class McpServerBuilderExtensions
/// instance for each. For instance methods, an instance will be constructed for each invocation of the tool.
/// </remarks>
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, params IEnumerable<Type> toolTypes)
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable<Type> toolTypes, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
Throw.IfNull(toolTypes);
@ -78,8 +82,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 }) :
services => McpServerTool.Create(toolMethod, toolType, new() { Services = services })));
services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, toolType, new() { Services = services , SerializerOptions = serializerOptions })));
}
}
}
@ -92,6 +96,7 @@ public static partial class McpServerBuilderExtensions
/// Adds types marked with the <see cref="McpServerToolTypeAttribute"/> attribute from the given assembly as tools to the server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter marshalling.</param>
/// <param name="toolAssembly">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>
@ -116,7 +121,7 @@ public static partial class McpServerBuilderExtensions
/// </para>
/// </remarks>
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null)
public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null, JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(builder);
@ -125,7 +130,8 @@ public static partial class McpServerBuilderExtensions
return builder.WithTools(
from t in toolAssembly.GetTypes()
where t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null
select t);
select t,
serializerOptions);
}
#endregion

View file

@ -67,6 +67,7 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
Name = options?.Name ?? method.GetCustomAttribute<McpServerToolAttribute>()?.Name,
Description = options?.Description,
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
ConfigureParameterBinding = pi =>
{
if (pi.ParameterType == typeof(RequestContext<CallToolRequestParams>))
@ -314,7 +315,7 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
{
Content = [new()
{
Text = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))),
Text = JsonSerializer.Serialize(result, AIFunction.JsonSerializerOptions.GetTypeInfo(typeof(object))),
Type = "text"
}]
},

View file

@ -1,4 +1,6 @@
using ModelContextProtocol.Utils.Json;
using System.ComponentModel;
using System.Text.Json;
namespace ModelContextProtocol.Server;
@ -122,11 +124,19 @@ public sealed class McpServerToolCreateOptions
/// </remarks>
public bool? ReadOnly { get; set; }
/// <summary>
/// Gets or sets the JSON serializer options to use when marshalling data to/from JSON.
/// </summary>
/// <remarks>
/// Defaults to <see cref="McpJsonUtilities.DefaultOptions"/> if left unspecified.
/// </remarks>
public JsonSerializerOptions? SerializerOptions { get; set; }
/// <summary>
/// Creates a shallow clone of the current <see cref="McpServerToolCreateOptions"/> instance.
/// </summary>
internal McpServerToolCreateOptions Clone() =>
new McpServerToolCreateOptions()
new McpServerToolCreateOptions
{
Services = Services,
Name = Name,
@ -135,6 +145,7 @@ public sealed class McpServerToolCreateOptions
Destructive = Destructive,
Idempotent = Idempotent,
OpenWorld = OpenWorld,
ReadOnly = ReadOnly
ReadOnly = ReadOnly,
SerializerOptions = SerializerOptions,
};
}