Update M.E.AI.Abstractions to 9.5.0-preview.1.25262.9 (#412)

This commit is contained in:
Stephen Toub 2025-05-14 06:24:24 -04:00 • committed by GitHub
commit ca3a8ab921
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 68 additions and 58 deletions

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<System10Version>10.0.0-preview.3.25171.5</System10Version>
<MicrosoftExtensionsAIVersion>9.4.4-preview.1.25259.16</MicrosoftExtensionsAIVersion>
<MicrosoftExtensionsAIVersion>9.5.0-preview.1.25262.9</MicrosoftExtensionsAIVersion>
</PropertyGroup>
<!-- Product dependencies netstandard -->
@ -31,7 +31,7 @@
<!-- Product dependencies shared -->
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.4.4-preview.1.25259.16" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MicrosoftExtensionsAIVersion)" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="$(System10Version)" />
</ItemGroup>

View file

@ -48,7 +48,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(toolMethod.IsStatic ?
services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, typeof(TToolType), new() { Services = services, SerializerOptions = serializerOptions })));
services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
@ -105,7 +105,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(toolMethod.IsStatic ?
services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions }) :
services => McpServerTool.Create(toolMethod, toolType, new() { Services = services , SerializerOptions = serializerOptions })));
services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions })));
}
}
}
@ -188,7 +188,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(promptMethod.IsStatic ?
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerPrompt.Create(promptMethod, typeof(TPromptType), new() { Services = services, SerializerOptions = serializerOptions })));
services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
@ -245,7 +245,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(promptMethod.IsStatic ?
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions }) :
services => McpServerPrompt.Create(promptMethod, promptType, new() { Services = services, SerializerOptions = serializerOptions })));
services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions })));
}
}
}
@ -325,7 +325,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(resourceTemplateMethod.IsStatic ?
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) :
services => McpServerResource.Create(resourceTemplateMethod, typeof(TResourceType), new() { Services = services })));
services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services })));
}
}
@ -381,7 +381,7 @@ public static partial class McpServerBuilderExtensions
{
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(resourceTemplateMethod.IsStatic ?
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services }) :
services => McpServerResource.Create(resourceTemplateMethod, resourceTemplateType, new() { Services = services })));
services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services })));
}
}
}
@ -775,4 +775,13 @@ public static partial class McpServerBuilderExtensions
});
}
#endregion
#region Helpers
/// <summary>Creates an instance of the target object.</summary>
private static object CreateTarget(
IServiceProvider? services,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) =>
services is not null ? ActivatorUtilities.CreateInstance(services, type) :
Activator.CreateInstance(type)!;
#endregion
}

View file

@ -4,7 +4,6 @@ using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Utils;
using ModelContextProtocol.Utils.Json;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
@ -49,15 +48,20 @@ internal sealed class AIFunctionMcpServerPrompt : McpServerPrompt
/// </summary>
public static new AIFunctionMcpServerPrompt Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<GetPromptRequestParams>, object> createTargetFunc,
McpServerPromptCreateOptions? options)
{
Throw.IfNull(method);
Throw.IfNull(createTargetFunc);
options = DeriveOptions(method, options);
return Create(
AIFunctionFactory.Create(method, targetType, CreateAIFunctionFactoryOptions(method, options)),
AIFunctionFactory.Create(method, args =>
{
var request = (RequestContext<GetPromptRequestParams>)args.Context![typeof(RequestContext<GetPromptRequestParams>)]!;
return createTargetFunc(request);
}, CreateAIFunctionFactoryOptions(method, options)),
options);
}
@ -69,7 +73,6 @@ internal sealed class AIFunctionMcpServerPrompt : McpServerPrompt
Description = options?.Description,
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
CreateInstance = AIFunctionMcpServerTool.GetCreateInstanceFunc(),
ConfigureParameterBinding = pi =>
{
if (pi.ParameterType == typeof(RequestContext<GetPromptRequestParams>))

View file

@ -5,7 +5,6 @@ using ModelContextProtocol.Utils;
using ModelContextProtocol.Utils.Json;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Text;
@ -56,15 +55,20 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
/// </summary>
public static new AIFunctionMcpServerResource Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<ReadResourceRequestParams>, object> createTargetFunc,
McpServerResourceCreateOptions? options)
{
Throw.IfNull(method);
Throw.IfNull(createTargetFunc);
options = DeriveOptions(method, options);
return Create(
AIFunctionFactory.Create(method, targetType, CreateAIFunctionFactoryOptions(method, options)),
AIFunctionFactory.Create(method, args =>
{
var request = (RequestContext<ReadResourceRequestParams>)args.Context![typeof(RequestContext<ReadResourceRequestParams>)]!;
return createTargetFunc(request);
}, CreateAIFunctionFactoryOptions(method, options)),
options);
}
@ -76,7 +80,6 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
Description = options?.Description,
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
SerializerOptions = McpJsonUtilities.DefaultOptions,
CreateInstance = AIFunctionMcpServerTool.GetCreateInstanceFunc(),
ConfigureParameterBinding = pi =>
{
if (pi.ParameterType == typeof(RequestContext<ReadResourceRequestParams>))

View file

@ -49,15 +49,20 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
/// </summary>
public static new AIFunctionMcpServerTool Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<CallToolRequestParams>, object> createTargetFunc,
McpServerToolCreateOptions? options)
{
Throw.IfNull(method);
Throw.IfNull(createTargetFunc);
options = DeriveOptions(method, options);
return Create(
AIFunctionFactory.Create(method, targetType, CreateAIFunctionFactoryOptions(method, options)),
AIFunctionFactory.Create(method, args =>
{
var request = (RequestContext<CallToolRequestParams>)args.Context![typeof(RequestContext<CallToolRequestParams>)]!;
return createTargetFunc(request);
}, CreateAIFunctionFactoryOptions(method, options)),
options);
}
@ -77,7 +82,6 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
Description = options?.Description,
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
CreateInstance = GetCreateInstanceFunc(),
ConfigureParameterBinding = pi =>
{
if (pi.ParameterType == typeof(RequestContext<CallToolRequestParams>))

View file

@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
@ -184,21 +183,19 @@ public abstract class McpServerPrompt : IMcpServerPrimitive
/// instantiate each time the method is invoked.
/// </summary>
/// <param name="method">The instance method to be represented via the created <see cref="AIFunction"/>.</param>
/// <param name="targetType">
/// The <see cref="Type"/> to construct an instance of on which to invoke <paramref name="method"/> when
/// the resulting <see cref="AIFunction"/> is invoked. If services are provided,
/// ActivatorUtilities.CreateInstance will be used to construct the instance using those services; otherwise,
/// <see cref="Activator.CreateInstance(Type)"/> is used, utilizing the type's public parameterless constructor.
/// If an instance can't be constructed, an exception is thrown during the function's invocation.
/// <param name="createTargetFunc">
/// Callback used on each function invocation to create an instance of the type on which the instance method <paramref name="method"/>
/// will be invoked. If the returned instance is <see cref="IAsyncDisposable"/> or <see cref="IDisposable"/>, it will
/// be disposed of after method completes its invocation.
/// </param>
/// <param name="options">Optional options used in the creation of the <see cref="McpServerPrompt"/> to control its behavior.</param>
/// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
public static McpServerPrompt Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<GetPromptRequestParams>, object> createTargetFunc,
McpServerPromptCreateOptions? options = null) =>
AIFunctionMcpServerPrompt.Create(method, targetType, options);
AIFunctionMcpServerPrompt.Create(method, createTargetFunc, options);
/// <summary>Creates an <see cref="McpServerPrompt"/> that wraps the specified <see cref="AIFunction"/>.</summary>
/// <param name="function">The function to wrap.</param>

View file

@ -2,7 +2,6 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace ModelContextProtocol.Server;
@ -205,21 +204,19 @@ public abstract class McpServerResource : IMcpServerPrimitive
/// instantiate each time the method is invoked.
/// </summary>
/// <param name="method">The instance method to be represented via the created <see cref="AIFunction"/>.</param>
/// <param name="targetType">
/// The <see cref="Type"/> to construct an instance of on which to invoke <paramref name="method"/> when
/// the resulting <see cref="AIFunction"/> is invoked. If services are provided,
/// ActivatorUtilities.CreateInstance will be used to construct the instance using those services; otherwise,
/// <see cref="Activator.CreateInstance(Type)"/> is used, utilizing the type's public parameterless constructor.
/// If an instance can't be constructed, an exception is thrown during the function's invocation.
/// <param name="createTargetFunc">
/// Callback used on each function invocation to create an instance of the type on which the instance method <paramref name="method"/>
/// will be invoked. If the returned instance is <see cref="IAsyncDisposable"/> or <see cref="IDisposable"/>, it will
/// be disposed of after method completes its invocation.
/// </param>
/// <param name="options">Optional options used in the creation of the <see cref="McpServerResource"/> to control its behavior.</param>
/// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
public static McpServerResource Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<ReadResourceRequestParams>, object> createTargetFunc,
McpServerResourceCreateOptions? options = null) =>
AIFunctionMcpServerResource.Create(method, targetType, options);
AIFunctionMcpServerResource.Create(method, createTargetFunc, options);
/// <summary>Creates an <see cref="McpServerResource"/> that wraps the specified <see cref="AIFunction"/>.</summary>
/// <param name="function">The function to wrap.</param>

View file

@ -4,7 +4,6 @@ using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Utils.Json;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
@ -187,21 +186,19 @@ public abstract class McpServerTool : IMcpServerPrimitive
/// instantiate each time the method is invoked.
/// </summary>
/// <param name="method">The instance method to be represented via the created <see cref="AIFunction"/>.</param>
/// <param name="targetType">
/// The <see cref="Type"/> to construct an instance of on which to invoke <paramref name="method"/> when
/// the resulting <see cref="AIFunction"/> is invoked. If services are provided,
/// ActivatorUtilities.CreateInstance will be used to construct the instance using those services; otherwise,
/// <see cref="Activator.CreateInstance(Type)"/> is used, utilizing the type's public parameterless constructor.
/// If an instance can't be constructed, an exception is thrown during the function's invocation.
/// <param name="createTargetFunc">
/// Callback used on each function invocation to create an instance of the type on which the instance method <paramref name="method"/>
/// will be invoked. If the returned instance is <see cref="IAsyncDisposable"/> or <see cref="IDisposable"/>, it will
/// be disposed of after method completes its invocation.
/// </param>
/// <param name="options">Optional options used in the creation of the <see cref="McpServerTool"/> to control its behavior.</param>
/// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
public static McpServerTool Create(
MethodInfo method,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType,
Func<RequestContext<CallToolRequestParams>, object> createTargetFunc,
McpServerToolCreateOptions? options = null) =>
AIFunctionMcpServerTool.Create(method, targetType, options);
AIFunctionMcpServerTool.Create(method, createTargetFunc, options);
/// <summary>Creates an <see cref="McpServerTool"/> that wraps the specified <see cref="AIFunction"/>.</summary>
/// <param name="function">The function to wrap.</param>

View file

@ -15,8 +15,8 @@ public class McpServerPromptTests
{
Assert.Throws<ArgumentNullException>("function", () => McpServerPrompt.Create((AIFunction)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerPrompt.Create((MethodInfo)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerPrompt.Create((MethodInfo)null!, typeof(object)));
Assert.Throws<ArgumentNullException>("targetType", () => McpServerPrompt.Create(typeof(McpServerPromptTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, (Type)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerPrompt.Create((MethodInfo)null!, _ => new object()));
Assert.Throws<ArgumentNullException>("createTargetFunc", () => McpServerPrompt.Create(typeof(McpServerPromptTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerPrompt.Create((Delegate)null!));
}
@ -96,7 +96,7 @@ public class McpServerPromptTests
{
McpServerPrompt prompt1 = McpServerPrompt.Create(
typeof(DisposablePromptType).GetMethod(nameof(DisposablePromptType.InstanceMethod))!,
typeof(DisposablePromptType));
_ => new DisposablePromptType());
var result = await prompt1.GetAsync(
new RequestContext<GetPromptRequestParams>(new Mock<IMcpServer>().Object),
@ -109,7 +109,7 @@ public class McpServerPromptTests
{
McpServerPrompt prompt1 = McpServerPrompt.Create(
typeof(AsyncDisposablePromptType).GetMethod(nameof(AsyncDisposablePromptType.InstanceMethod))!,
typeof(AsyncDisposablePromptType));
_ => new AsyncDisposablePromptType());
var result = await prompt1.GetAsync(
new RequestContext<GetPromptRequestParams>(new Mock<IMcpServer>().Object),
@ -122,7 +122,7 @@ public class McpServerPromptTests
{
McpServerPrompt prompt1 = McpServerPrompt.Create(
typeof(AsyncDisposableAndDisposablePromptType).GetMethod(nameof(AsyncDisposableAndDisposablePromptType.InstanceMethod))!,
typeof(AsyncDisposableAndDisposablePromptType));
_ => new AsyncDisposableAndDisposablePromptType());
var result = await prompt1.GetAsync(
new RequestContext<GetPromptRequestParams>(new Mock<IMcpServer>().Object),

View file

@ -109,8 +109,8 @@ public partial class McpServerResourceTests
{
Assert.Throws<ArgumentNullException>("function", () => McpServerResource.Create((AIFunction)null!, new() { UriTemplate = "test://hello" }));
Assert.Throws<ArgumentNullException>("method", () => McpServerResource.Create((MethodInfo)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerResource.Create((MethodInfo)null!, typeof(object)));
Assert.Throws<ArgumentNullException>("targetType", () => McpServerResource.Create(typeof(McpServerResourceTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, (Type)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerResource.Create((MethodInfo)null!, _ => new object()));
Assert.Throws<ArgumentNullException>("createTargetFunc", () => McpServerResource.Create(typeof(McpServerResourceTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerResource.Create((Delegate)null!));
Assert.NotNull(McpServerResource.Create(typeof(DisposableResourceType).GetMethod(nameof(DisposableResourceType.InstanceMethod))!, new DisposableResourceType()));
@ -415,7 +415,7 @@ public partial class McpServerResourceTests
McpServerResource resource1 = McpServerResource.Create(
typeof(DisposableResourceType).GetMethod(nameof(DisposableResourceType.InstanceMethod))!,
typeof(DisposableResourceType));
_ => new DisposableResourceType());
var result = await resource1.ReadAsync(
new RequestContext<ReadResourceRequestParams>(new Mock<IMcpServer>().Object) { Params = new() { Uri = "test://static/resource/instanceMethod" } },

View file

@ -18,7 +18,7 @@ public partial class McpServerToolTests
Assert.Throws<ArgumentNullException>("function", () => McpServerTool.Create((AIFunction)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerTool.Create((MethodInfo)null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerTool.Create((MethodInfo)null!, typeof(object)));
Assert.Throws<ArgumentNullException>("targetType", () => McpServerTool.Create(typeof(McpServerToolTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, (Type)null!));
Assert.Throws<ArgumentNullException>("createTargetFunc", () => McpServerTool.Create(typeof(McpServerToolTests).GetMethod(nameof(Create_InvalidArgs_Throws))!, null!));
Assert.Throws<ArgumentNullException>("method", () => McpServerTool.Create((Delegate)null!));
Assert.NotNull(McpServerTool.Create(typeof(DisposableToolType).GetMethod(nameof(DisposableToolType.InstanceMethod))!, new DisposableToolType()));
@ -129,7 +129,7 @@ public partial class McpServerToolTests
McpServerToolCreateOptions options = new() { SerializerOptions = JsonContext2.Default.Options };
McpServerTool tool1 = McpServerTool.Create(
typeof(DisposableToolType).GetMethod(nameof(DisposableToolType.InstanceMethod))!,
typeof(DisposableToolType),
_ => new DisposableToolType(),
options);
var result = await tool1.InvokeAsync(
@ -144,7 +144,7 @@ public partial class McpServerToolTests
McpServerToolCreateOptions options = new() { SerializerOptions = JsonContext2.Default.Options };
McpServerTool tool1 = McpServerTool.Create(
typeof(AsyncDisposableToolType).GetMethod(nameof(AsyncDisposableToolType.InstanceMethod))!,
typeof(AsyncDisposableToolType),
_ => new AsyncDisposableToolType(),
options);
var result = await tool1.InvokeAsync(
@ -163,7 +163,7 @@ public partial class McpServerToolTests
McpServerToolCreateOptions options = new() { SerializerOptions = JsonContext2.Default.Options };
McpServerTool tool1 = McpServerTool.Create(
typeof(AsyncDisposableAndDisposableToolType).GetMethod(nameof(AsyncDisposableAndDisposableToolType.InstanceMethod))!,
typeof(AsyncDisposableAndDisposableToolType),
static r => ActivatorUtilities.CreateInstance(r.Services!, typeof(AsyncDisposableAndDisposableToolType)),
options);
var result = await tool1.InvokeAsync(