From 926aacdb4bcd1144bbdaa3c2df2364e63c4c728b Mon Sep 17 00:00:00 2001
From: kooshi <1934337+kooshi@users.noreply.github.com>
Date: Thu, 22 May 2025 23:07:45 -0500
Subject: [PATCH 1/4] Log tool errors
---
.../Server/AIFunctionMcpServerTool.cs | 28 ++++++++++++-------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
index 366eb23cd..94f46a50f 100644
--- a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
+++ b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
@@ -1,5 +1,7 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using ModelContextProtocol.Protocol;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -11,6 +13,8 @@ namespace ModelContextProtocol.Server;
/// Provides an that's implemented via an .
internal sealed class AIFunctionMcpServerTool : McpServerTool
{
+ private readonly ILogger _logger;
+
///
/// Creates an instance for a method, specified via a instance.
///
@@ -19,7 +23,7 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
McpServerToolCreateOptions? options)
{
Throw.IfNull(method);
-
+
options = DeriveOptions(method.Method, options);
return Create(method.Method, method.Target, options);
@@ -172,7 +176,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
{
Name = options?.Name ?? function.Name,
Description = options?.Description ?? function.Description,
- InputSchema = function.JsonSchema,
+ InputSchema = function.JsonSchema,
};
if (options is not null)
@@ -194,7 +198,7 @@ options.OpenWorld is not null ||
}
}
- return new AIFunctionMcpServerTool(function, tool);
+ return new AIFunctionMcpServerTool(function, tool, options?.Services);
}
private static McpServerToolCreateOptions DeriveOptions(MethodInfo method, McpServerToolCreateOptions? options)
@@ -239,10 +243,11 @@ private static McpServerToolCreateOptions DeriveOptions(MethodInfo method, McpSe
internal AIFunction AIFunction { get; }
/// Initializes a new instance of the class.
- private AIFunctionMcpServerTool(AIFunction function, Tool tool)
+ private AIFunctionMcpServerTool(AIFunction function, Tool tool, IServiceProvider? serviceProvider)
{
AIFunction = function;
ProtocolTool = tool;
+ _logger = serviceProvider?.GetService()?.CreateLogger() ?? (ILogger)NullLogger.Instance;
}
///
@@ -277,6 +282,9 @@ public override async ValueTask InvokeAsync(
}
catch (Exception e) when (e is not OperationCanceledException)
{
+ _logger.LogError(e, "Error invoking AIFunction tool '{ToolName}' with arguments '{Args}'.",
+ request.Params?.Name, string.Join(",", request.Params?.Arguments?.Keys ?? Array.Empty()));
+
string errorMessage = e is McpException ?
$"An error occurred invoking '{request.Params?.Name}': {e.Message}" :
$"An error occurred invoking '{request.Params?.Name}'.";
@@ -300,29 +308,29 @@ public override async ValueTask InvokeAsync(
{
Content = []
},
-
+
string text => new()
{
Content = [new() { Text = text, Type = "text" }]
},
-
+
Content content => new()
{
Content = [content]
},
-
+
IEnumerable texts => new()
{
Content = [.. texts.Select(x => new Content() { Type = "text", Text = x ?? string.Empty })]
},
-
+
IEnumerable contentItems => ConvertAIContentEnumerableToCallToolResponse(contentItems),
-
+
IEnumerable contents => new()
{
Content = [.. contents]
},
-
+
CallToolResponse callToolResponse => callToolResponse,
_ => new()
From a87c47cb18032c56b28d6698759fba6bc5a9bbfc Mon Sep 17 00:00:00 2001
From: kooshi <1934337+kooshi@users.noreply.github.com>
Date: Fri, 23 May 2025 15:32:39 -0500
Subject: [PATCH 2/4] add test and implement source generated logging
---
.../Server/AIFunctionMcpServerTool.cs | 226 +++++++++---------
.../Server/McpServerToolTests.cs | 41 ++++
2 files changed, 155 insertions(+), 112 deletions(-)
diff --git a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
index 94f46a50f..e6137d8d7 100644
--- a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
+++ b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
@@ -11,7 +11,7 @@
namespace ModelContextProtocol.Server;
/// Provides an that's implemented via an .
-internal sealed class AIFunctionMcpServerTool : McpServerTool
+internal sealed partial class AIFunctionMcpServerTool : McpServerTool
{
private readonly ILogger _logger;
@@ -19,8 +19,8 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
/// Creates an instance for a method, specified via a instance.
///
public static new AIFunctionMcpServerTool Create(
- Delegate method,
- McpServerToolCreateOptions? options)
+ Delegate method,
+ McpServerToolCreateOptions? options)
{
Throw.IfNull(method);
@@ -33,26 +33,26 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
/// Creates an instance for a method, specified via a instance.
///
public static new AIFunctionMcpServerTool Create(
- MethodInfo method,
- object? target,
- McpServerToolCreateOptions? options)
+ MethodInfo method,
+ object? target,
+ McpServerToolCreateOptions? options)
{
Throw.IfNull(method);
options = DeriveOptions(method, options);
return Create(
- AIFunctionFactory.Create(method, target, CreateAIFunctionFactoryOptions(method, options)),
- options);
+ AIFunctionFactory.Create(method, target, CreateAIFunctionFactoryOptions(method, options)),
+ options);
}
///
/// Creates an instance for a method, specified via a instance.
///
public static new AIFunctionMcpServerTool Create(
- MethodInfo method,
- Func, object> createTargetFunc,
- McpServerToolCreateOptions? options)
+ MethodInfo method,
+ Func, object> createTargetFunc,
+ McpServerToolCreateOptions? options)
{
Throw.IfNull(method);
Throw.IfNull(createTargetFunc);
@@ -60,112 +60,112 @@ internal sealed class AIFunctionMcpServerTool : McpServerTool
options = DeriveOptions(method, options);
return Create(
- AIFunctionFactory.Create(method, args =>
- {
- var request = (RequestContext)args.Context![typeof(RequestContext)]!;
- return createTargetFunc(request);
- }, CreateAIFunctionFactoryOptions(method, options)),
- options);
+ AIFunctionFactory.Create(method, args =>
+ {
+ var request = (RequestContext)args.Context![typeof(RequestContext)]!;
+ return createTargetFunc(request);
+ }, CreateAIFunctionFactoryOptions(method, options)),
+ options);
}
// TODO: Fix the need for this suppression.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers",
- Justification = "AIFunctionFactory ensures that the Type passed to AIFunctionFactoryOptions.CreateInstance has public constructors preserved")]
+ Justification = "AIFunctionFactory ensures that the Type passed to AIFunctionFactoryOptions.CreateInstance has public constructors preserved")]
internal static Func GetCreateInstanceFunc() =>
- static ([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] type, args) => args.Services is { } services ?
- ActivatorUtilities.CreateInstance(services, type) :
- Activator.CreateInstance(type)!;
+ static ([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] type, args) => args.Services is { } services ?
+ ActivatorUtilities.CreateInstance(services, type) :
+ Activator.CreateInstance(type)!;
private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
- MethodInfo method, McpServerToolCreateOptions? options) =>
- new()
+ MethodInfo method, McpServerToolCreateOptions? options) =>
+ new()
+ {
+ Name = options?.Name ?? method.GetCustomAttribute()?.Name,
+ Description = options?.Description,
+ MarshalResult = static (result, _, cancellationToken) => new ValueTask