GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — 2.4.0: tools/call results with annotated content blocks fail to serialize (InvalidOperationException in ContentBlock.Converter.CreateNestedOptions) · Issue #2012 · modelcontextprotocol/csharp-sdk Open DescriptionAfter moving from 2.3.1 to 2.4.0, any tool that returns a content block with ReproA server with three tools: plain text, the same text with annotations, and a resource link with annotations. It starts, calls each tool over HTTP, prints what came back, and exits.
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup><TargetFramework>net10.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings></PropertyGroup> <ItemGroup><PackageReference Include="ModelContextProtocol.AspNetCore" Version="$(McpVer)" /></ItemGroup> </Project>
// Minimal MCP server, public API only. Two tools that differ only in Annotations. using System.ComponentModel; using System.Text; using ModelContextProtocol.AspNetCore; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; var stateless = args.Contains("--stateless"); var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseUrls("http://127.0.0.1:5599"); builder.Logging.SetMinimumLevel(LogLevel.Warning); builder.Services.AddMcpServer() .WithHttpTransport(o => { if (stateless) o.SessionMode = HttpServerSessionMode.Stateless; }) .WithTools<DemoTools>(); var app = builder.Build(); app.MapMcp("/mcp"); await app.StartAsync(); using var http = new HttpClient { BaseAddress = new Uri("http://127.0.0.1:5599") }; string? session = null; async Task<string> Post(string json) { var req = new HttpRequestMessage(HttpMethod.Post, "/mcp") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; req.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream"); req.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2025-11-25"); if (session is not null) req.Headers.TryAddWithoutValidation("Mcp-Session-Id", session); var res = await http.SendAsync(req); if (res.Headers.TryGetValues("Mcp-Session-Id", out var s)) session = s.First(); var body = (await res.Content.ReadAsStringAsync()).Replace("\r", ""); var data = body.Split('\n').FirstOrDefault(l => l.StartsWith("data: "))?[6..] ?? body; return $"{(int)res.StatusCode} {data}"; } Console.WriteLine($"mode: {(stateless ? "stateless" : "stateful (default)")}, SDK {typeof(McpServerToolAttribute).Assembly.GetName().Version}"); if (!stateless) { Console.WriteLine("initialize -> " + (await Post("""{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"repro","version":"1"}}}"""))[..3]); await Post("""{"jsonrpc":"2.0","method":"notifications/initialized"}"""); } foreach (var tool in new[] { "plain", "annotated", "annotated_link" }) { Console.WriteLine($"{tool} -> " + await Post("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"" + tool + "\",\"arguments\":{}}}")); } await app.StopAsync(); [McpServerToolType] public sealed class DemoTools { [McpServerTool(Name = "plain"), Description("Text block without annotations.")] public static CallToolResult Plain() => new() { Content = [new TextContentBlock { Text = "x" }] }; [McpServerTool(Name = "annotated"), Description("Same text block with annotations.")] public static CallToolResult Annotated() => new() { Content = [new TextContentBlock { Text = "x", Annotations = new Annotations { Audience = [Role.User] } }], }; [McpServerTool(Name = "annotated_link"), Description("Resource link with annotations.")] public static CallToolResult AnnotatedLink() => new() { Content = [new ResourceLinkBlock { Uri = "https://example.com/x", Name = "x", Annotations = new Annotations { Audience = [Role.User] } }], }; } Run it against each version: Add What I get2.3.1: 2.4.0: Server log on 2.4.0: Where it seems to come from
JsonSerializerOptions nestedOptions = new(callerOptions); nestedOptions.TypeInfoResolverChain.Add(McpJsonUtilities.JsonContext.Default); The server writes the tool result with The method came in with #1934 and is the same on Notes
.NET SDK 10, macOS arm64. Happy to test a fix. Note This issue was prepared with AI assistance (Claude Code). I reviewed the repro and its results before filing. Activity
MetadataMetadataAssigneesLabelsNo labels TypeProjectsMilestoneRelationshipsDevelopmentIssue actions |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |