From 68f8ddde85fcebb0ca981a833a9548fccf0d77a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 04:14:47 +0000 Subject: [PATCH 1/5] Initial plan From 2c07b8fc2b5343cea05785921e791ca9f23402fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 04:28:41 +0000 Subject: [PATCH 2/5] Normalize CallToolResult.StructuredContent from JsonNode? to JsonElement? to match ToolResultContentBlock.StructuredContent Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Protocol/CallToolResult.cs | 4 ++-- .../Server/AIFunctionMcpServerTool.cs | 10 +++++----- .../Client/McpClientToolTests.cs | 2 +- .../Protocol/CallToolResultTests.cs | 4 ++-- .../Protocol/UnknownPropertiesTests.cs | 4 ++-- .../Server/McpServerToolTests.cs | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ModelContextProtocol.Core/Protocol/CallToolResult.cs b/src/ModelContextProtocol.Core/Protocol/CallToolResult.cs index cd4c1abe3..35dba5b6e 100644 --- a/src/ModelContextProtocol.Core/Protocol/CallToolResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/CallToolResult.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Nodes; +using System.Text.Json; using System.Text.Json.Serialization; namespace ModelContextProtocol.Protocol; @@ -41,7 +41,7 @@ public sealed class CallToolResult : Result /// Gets or sets an optional JSON object representing the structured result of the tool call. /// [JsonPropertyName("structuredContent")] - public JsonNode? StructuredContent { get; set; } + public JsonElement? StructuredContent { get; set; } /// /// Gets or sets a value that indicates whether the tool call was unsuccessful. diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs index f72e5a483..8465b3af0 100644 --- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs @@ -267,7 +267,7 @@ public override async ValueTask InvokeAsync( object? result; result = await AIFunction.InvokeAsync(arguments, cancellationToken).ConfigureAwait(false); - JsonNode? structuredContent = CreateStructuredResponse(result); + JsonElement? structuredContent = CreateStructuredResponse(result); return result switch { AIContent aiContent => new() @@ -530,7 +530,7 @@ typeProperty.ValueKind is not JsonValueKind.String || return outputSchema; } - private JsonNode? CreateStructuredResponse(object? aiFunctionResult) + private JsonElement? CreateStructuredResponse(object? aiFunctionResult) { if (ProtocolTool.OutputSchema is null) { @@ -547,16 +547,16 @@ typeProperty.ValueKind is not JsonValueKind.String || if (_structuredOutputRequiresWrapping) { - return new JsonObject + nodeResult = new JsonObject { ["result"] = nodeResult }; } - return nodeResult; + return nodeResult?.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement); } - private static CallToolResult ConvertAIContentEnumerableToCallToolResult(IEnumerable contentItems, JsonNode? structuredContent) + private static CallToolResult ConvertAIContentEnumerableToCallToolResult(IEnumerable contentItems, JsonElement? structuredContent) { List contentList = []; bool allErrorContent = true; diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs index f6818fd4f..e17fb6bc9 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientToolTests.cs @@ -122,7 +122,7 @@ public static CallToolResult StructuredContentTool() => new() { Content = [new TextContentBlock { Text = "Regular content" }], - StructuredContent = JsonNode.Parse("{\"key\":\"value\"}") + StructuredContent = JsonElement.Parse("{\"key\":\"value\"}") }; // Tool that returns CallToolResult with Meta diff --git a/tests/ModelContextProtocol.Tests/Protocol/CallToolResultTests.cs b/tests/ModelContextProtocol.Tests/Protocol/CallToolResultTests.cs index 29ecbcb8e..d66e03b3f 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/CallToolResultTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/CallToolResultTests.cs @@ -12,7 +12,7 @@ public static void CallToolResult_SerializationRoundTrip_PreservesAllProperties( var original = new CallToolResult { Content = [new TextContentBlock { Text = "Result text" }], - StructuredContent = JsonNode.Parse("""{"temperature":72}"""), + StructuredContent = JsonElement.Parse("""{"temperature":72}"""), IsError = false, Task = new McpTask { @@ -32,7 +32,7 @@ public static void CallToolResult_SerializationRoundTrip_PreservesAllProperties( var textBlock = Assert.IsType(deserialized.Content[0]); Assert.Equal("Result text", textBlock.Text); Assert.NotNull(deserialized.StructuredContent); - Assert.Equal(72, deserialized.StructuredContent["temperature"]!.GetValue()); + Assert.Equal(72, deserialized.StructuredContent.Value.GetProperty("temperature").GetInt32()); Assert.False(deserialized.IsError); Assert.NotNull(deserialized.Task); Assert.Equal("task-1", deserialized.Task.TaskId); diff --git a/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs b/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs index 1501553d3..05c25b523 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/UnknownPropertiesTests.cs @@ -252,7 +252,7 @@ public void CallToolResult_WithStructuredContentAtCorrectLevel_PreservesProperty // Assert Assert.NotNull(deserialized); Assert.NotNull(deserialized.StructuredContent); - Assert.Equal("correctly placed here", deserialized.StructuredContent["result"]?.ToString()); - Assert.Equal(42, (int?)deserialized.StructuredContent["value"]); + Assert.Equal("correctly placed here", deserialized.StructuredContent.Value.GetProperty("result").GetString()); + Assert.Equal(42, deserialized.StructuredContent.Value.GetProperty("value").GetInt32()); } } diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs index 552eaed72..7bca39f30 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs @@ -654,11 +654,11 @@ public object InstanceMethod() } } - private static void AssertMatchesJsonSchema(JsonElement schemaDoc, JsonNode? value) + private static void AssertMatchesJsonSchema(JsonElement schemaDoc, JsonElement? value) { JsonSchema schema = JsonSerializer.Deserialize(schemaDoc, JsonContext2.Default.JsonSchema)!; EvaluationOptions options = new() { OutputFormat = OutputFormat.List }; - EvaluationResults results = schema.Evaluate(JsonSerializer.SerializeToElement(value, JsonContext2.Default.JsonNode), options); + EvaluationResults results = schema.Evaluate(value ?? default, options); if (!results.IsValid) { IEnumerable errors = (results.Details ?? []) @@ -670,7 +670,7 @@ Instance JSON document does not match the specified schema. Schema: {JsonSerializer.Serialize(schema)} Instance: - {value?.ToJsonString() ?? "null"} + {value?.ToString() ?? "null"} Errors: {string.Join(Environment.NewLine, errors)} """); From 4b201a4c705bc5c4eb06e2416bb7a67168b379c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 04:33:18 +0000 Subject: [PATCH 3/5] Fix AssertMatchesJsonSchema to use value!.Value instead of value ?? default Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs index 7bca39f30..fd62a05c7 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs @@ -658,7 +658,7 @@ private static void AssertMatchesJsonSchema(JsonElement schemaDoc, JsonElement? { JsonSchema schema = JsonSerializer.Deserialize(schemaDoc, JsonContext2.Default.JsonSchema)!; EvaluationOptions options = new() { OutputFormat = OutputFormat.List }; - EvaluationResults results = schema.Evaluate(value ?? default, options); + EvaluationResults results = schema.Evaluate(value!.Value, options); if (!results.IsValid) { IEnumerable errors = (results.Details ?? []) From 4496e2e39fc766e05c9cd0c5185365fb52f99112 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:32:20 +0000 Subject: [PATCH 4/5] Refactor CreateStructuredResponse switch to target JsonElement? directly Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Server/AIFunctionMcpServerTool.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs index 97d0ef7ca..7f9e318df 100644 --- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs @@ -537,22 +537,26 @@ typeProperty.ValueKind is not JsonValueKind.String || return null; } - JsonNode? nodeResult = aiFunctionResult switch + JsonElement? elementResult = aiFunctionResult switch { - JsonNode node => node, - JsonElement jsonElement => JsonSerializer.SerializeToNode(jsonElement, McpJsonUtilities.JsonContext.Default.JsonElement), - _ => JsonSerializer.SerializeToNode(aiFunctionResult, AIFunction.JsonSerializerOptions.GetTypeInfo(typeof(object))), + JsonElement jsonElement => jsonElement, + JsonNode node => node.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement), + null => null, + _ => JsonSerializer.SerializeToElement(aiFunctionResult, AIFunction.JsonSerializerOptions.GetTypeInfo(typeof(object))), }; if (_structuredOutputRequiresWrapping) { - nodeResult = new JsonObject + JsonNode? resultNode = elementResult is { } je + ? JsonSerializer.SerializeToNode(je, McpJsonUtilities.JsonContext.Default.JsonElement) + : null; + return new JsonObject { - ["result"] = nodeResult - }; + ["result"] = resultNode + }.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement); } - return nodeResult?.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement); + return elementResult; } private static CallToolResult ConvertAIContentEnumerableToCallToolResult(IEnumerable contentItems, JsonElement? structuredContent) From 9832a59f76a5efb1f5fcd32d125b33f767f4cce6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:45:05 +0000 Subject: [PATCH 5/5] Use SerializeToElement instead of Deserialize for JsonNode/JsonObject to JsonElement conversion Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Server/AIFunctionMcpServerTool.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs index 7f9e318df..e91bdd206 100644 --- a/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs @@ -540,7 +540,7 @@ typeProperty.ValueKind is not JsonValueKind.String || JsonElement? elementResult = aiFunctionResult switch { JsonElement jsonElement => jsonElement, - JsonNode node => node.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement), + JsonNode node => JsonSerializer.SerializeToElement(node, McpJsonUtilities.JsonContext.Default.JsonNode), null => null, _ => JsonSerializer.SerializeToElement(aiFunctionResult, AIFunction.JsonSerializerOptions.GetTypeInfo(typeof(object))), }; @@ -550,10 +550,10 @@ typeProperty.ValueKind is not JsonValueKind.String || JsonNode? resultNode = elementResult is { } je ? JsonSerializer.SerializeToNode(je, McpJsonUtilities.JsonContext.Default.JsonElement) : null; - return new JsonObject + return JsonSerializer.SerializeToElement(new JsonObject { ["result"] = resultNode - }.Deserialize(McpJsonUtilities.JsonContext.Default.JsonElement); + }, McpJsonUtilities.JsonContext.Default.JsonObject); } return elementResult;