Skip to content

Commit 8930269

Browse files
Copilotstephentoub
andcommitted
Remove ICallToolResultTyped, move conversion logic to AIFunctionMcpServerTool
Replace the ICallToolResultTyped interface (which contained serialization logic) with a minimal ICallToolResultTypedContent interface that only exposes raw content data. The serialization/conversion logic now lives in AIFunctionMcpServerTool's ConvertCallToolResultOfT method where it's consumed. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent dd3e73b commit 8930269

2 files changed

Lines changed: 37 additions & 24 deletions

File tree

src/ModelContextProtocol.Core/Protocol/CallToolResultOfT.cs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Text.Json;
21
using System.Text.Json.Nodes;
32

43
namespace ModelContextProtocol.Protocol;
@@ -23,7 +22,7 @@ namespace ModelContextProtocol.Protocol;
2322
/// the SDK to handle serialization of a strongly-typed result.
2423
/// </para>
2524
/// </remarks>
26-
public sealed class CallToolResult<T> : ICallToolResultTyped
25+
public sealed class CallToolResult<T> : ICallToolResultTypedContent
2726
{
2827
/// <summary>
2928
/// Gets or sets the typed content returned by the tool.
@@ -58,30 +57,15 @@ public sealed class CallToolResult<T> : ICallToolResultTyped
5857
public JsonObject? Meta { get; set; }
5958

6059
/// <inheritdoc/>
61-
CallToolResult ICallToolResultTyped.ToCallToolResult(JsonSerializerOptions serializerOptions)
62-
{
63-
var typeInfo = serializerOptions.GetTypeInfo(typeof(T));
64-
65-
string json = JsonSerializer.Serialize(Content, typeInfo);
66-
JsonNode? structuredContent = JsonSerializer.SerializeToNode(Content, typeInfo);
67-
68-
return new()
69-
{
70-
Content = [new TextContentBlock { Text = json }],
71-
StructuredContent = structuredContent,
72-
IsError = IsError,
73-
Meta = Meta,
74-
};
75-
}
60+
(object? Content, Type ContentType, bool? IsError, JsonObject? Meta) ICallToolResultTypedContent.GetContent() =>
61+
(Content, typeof(T), IsError, Meta);
7662
}
7763

7864
/// <summary>
79-
/// Internal interface for converting strongly-typed tool results to <see cref="CallToolResult"/>.
65+
/// Internal interface for accessing the content of a <see cref="CallToolResult{T}"/> without reflection.
8066
/// </summary>
81-
internal interface ICallToolResultTyped
67+
internal interface ICallToolResultTypedContent
8268
{
83-
/// <summary>
84-
/// Converts the strongly-typed result to a <see cref="CallToolResult"/>.
85-
/// </summary>
86-
CallToolResult ToCallToolResult(JsonSerializerOptions serializerOptions);
69+
/// <summary>Gets the content, its type, the error flag, and metadata.</summary>
70+
(object? Content, Type ContentType, bool? IsError, JsonObject? Meta) GetContent();
8771
}

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ public override async ValueTask<CallToolResult> InvokeAsync(
320320

321321
CallToolResult callToolResponse => callToolResponse,
322322

323-
ICallToolResultTyped typedResult => typedResult.ToCallToolResult(AIFunction.JsonSerializerOptions),
323+
_ when ConvertCallToolResultOfT(result, AIFunction.JsonSerializerOptions) is { } converted => converted,
324324

325325
_ => new()
326326
{
@@ -644,4 +644,33 @@ private static CallToolResult ConvertAIContentEnumerableToCallToolResult(IEnumer
644644
IsError = allErrorContent && hasAny
645645
};
646646
}
647+
648+
/// <summary>
649+
/// If <paramref name="result"/> is a <see cref="CallToolResult{T}"/>, converts it to a <see cref="CallToolResult"/>.
650+
/// </summary>
651+
private static CallToolResult? ConvertCallToolResultOfT(object result, JsonSerializerOptions serializerOptions)
652+
{
653+
Type type = result.GetType();
654+
if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(CallToolResult<>))
655+
{
656+
return null;
657+
}
658+
659+
// Use the internal accessor to get the untyped content, IsError, and Meta
660+
// without reflection, avoiding trimming warnings.
661+
var (content, contentType, isError, meta) = ((ICallToolResultTypedContent)result).GetContent();
662+
663+
var typeInfo = serializerOptions.GetTypeInfo(contentType);
664+
665+
string json = JsonSerializer.Serialize(content, typeInfo);
666+
JsonNode? structuredContent = JsonSerializer.SerializeToNode(content, typeInfo);
667+
668+
return new()
669+
{
670+
Content = [new TextContentBlock { Text = json }],
671+
StructuredContent = structuredContent,
672+
IsError = isError,
673+
Meta = meta,
674+
};
675+
}
647676
}

0 commit comments

Comments
 (0)