From 782c2fcd5dc7427bcb89e412f4fb6cdaa9c23edc Mon Sep 17 00:00:00 2001 From: andyst-dev <150129844+andyst-dev@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:14:12 +0200 Subject: [PATCH] Add tool result marshaling option --- .../Server/AIFunctionMcpServerTool.cs | 2 +- .../Server/McpServerToolCreateOptions.cs | 10 ++++++++ .../Server/McpServerToolTests.cs | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs index 82b6ceb9d..2caa461be 100644 --- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs @@ -74,7 +74,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions( { Name = options?.Name ?? method.GetCustomAttribute()?.Name ?? DeriveName(method), Description = options?.Description, - MarshalResult = static (result, _, cancellationToken) => new ValueTask(result), + MarshalResult = options?.MarshalResult ?? (static (result, _, cancellationToken) => new ValueTask(result)), SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions, JsonSchemaCreateOptions = options?.SchemaCreateOptions, ConfigureParameterBinding = pi => diff --git a/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs index b0b6b3de7..fdd6babbe 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerToolCreateOptions.cs @@ -157,6 +157,15 @@ public sealed class McpServerToolCreateOptions /// public JsonSerializerOptions? SerializerOptions { get; set; } + /// + /// Gets or sets a delegate used to marshal the result returned by the tool method. + /// + /// + /// The delegate receives the result, its declared return type, and a cancellation token. + /// If , the result is passed through unchanged. + /// + public Func>? MarshalResult { get; set; } + /// /// Gets or sets the JSON schema options when creating an from a method. /// @@ -214,6 +223,7 @@ internal McpServerToolCreateOptions Clone() => UseStructuredContent = UseStructuredContent, OutputSchema = OutputSchema, SerializerOptions = SerializerOptions, + MarshalResult = MarshalResult, SchemaCreateOptions = SchemaCreateOptions, Metadata = Metadata, Icons = Icons, diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs index 8fd1d9954..81b6359f3 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs @@ -354,6 +354,29 @@ public async Task CanReturnString() Assert.Equal("42", Assert.IsType(result.Content[0]).Text); } + [Fact] + public async Task SupportsCustomResultMarshaling() + { + Mock mockServer = new(); + McpServerTool tool = McpServerTool.Create(() => 42, new() + { + MarshalResult = (result, resultType, cancellationToken) => + { + Assert.Equal(42, result); + Assert.Equal(typeof(int), resultType); + Assert.Equal(TestContext.Current.CancellationToken, cancellationToken); + return new($"marshaled:{result}"); + }, + }); + + var result = await tool.InvokeAsync( + new RequestContext(mockServer.Object, CreateTestJsonRpcRequest(), new() { Name = "" }), + TestContext.Current.CancellationToken); + + Assert.Single(result.Content); + Assert.Equal("marshaled:42", Assert.IsType(result.Content[0]).Text); + } + [Fact] public async Task CanReturnCollectionOfStrings() {