Skip to content

_meta field not serialized in HTTP transport - blocks ChatGPT Apps SDK #959

@xavier-musy

Description

@xavier-musy

_meta field not serialized in HTTP transport - blocks ChatGPT Apps SDK

Summary

Issue #856 was closed as complete, but HTTP serialization is an issue. While PR #857 added the API to SET metadata (McpMetaAttribute and Meta properties), the _meta field is not serialized when using HTTP transport (Streamable HTTP/SSE).

This is a critical blocker for ChatGPT Apps SDK integration, as ChatGPT only supports HTTP-based transports (Streamable HTTP and SSE, not stdio).

Impact

  • ChatGPT Apps SDK requires HTTP-based transports: Streamable HTTP or SSE (doesn't support stdio)
  • ChatGPT requires _meta.openai/outputTemplate in tool descriptors for custom UI
  • Issue Tools and resources should support _meta additions #856 closed prematurely - API added but serialization is a problem
  • All ChatGPT Apps SDK users that have custom UX are affected
  • Both implementation approaches affected:
    • Dynamic: McpServerToolCreateOptions.Meta
    • Attribute: [McpMeta("key", "value")]

Reproduction

Setup

// Using dynamic approach
var toolOptions = new McpServerToolCreateOptions
{
    Meta = new JsonObject
    {
        ["openai/outputTemplate"] = "ui://task.html",
        ["openai/widgetAccessible"] = true
    }
};
var tool = McpServerTool.Create(myFunction, options: toolOptions);

// OR using attribute approach
[McpServerTool]
[McpMeta("openai/outputTemplate", "ui://task.html")]
public static string GetTasks() { }

Test with HTTP

curl -X POST http://localhost:5047 \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[0]._meta'

Expected: {"openai/outputTemplate": "ui://task.html", ...}
Actual: null

Debug Logging Confirms Meta Is Set

// Added logging in DynamicToolService.cs
_logger.LogInformation("Meta is {Status}: {Meta}", 
    tool.ProtocolTool.Meta != null ? "SET" : "NULL",
    tool.ProtocolTool.Meta);

Output: Meta is SET: {"openai/outputTemplate":"ui://task.html",...}

Conclusion: Meta exists on tool.ProtocolTool.Meta internally, but HTTP serialization strips it.

Root Cause

The SDK uses source-generated JSON serialization (McpJsonUtilities.JsonContext):

  1. Tool.cs line 125: [JsonPropertyName("_meta")] public JsonObject? Meta { get; set; }
  2. McpJsonUtilities.cs line 134: [JsonSerializable(typeof(ListToolsResult))]
  3. Missing: [JsonSerializable(typeof(Tool))]

Source generators use compile-time metadata. When Tool.Meta was added in PR #857, the source generator wasn't updated. The HTTP layer serializes using the source-generated context, which doesn't include metadata for Tool, so _meta gets stripped.

Compare with ResourceContents: Has a custom JsonConverter that explicitly writes _meta (ResourceContents.cs lines 173-178). Tool doesn't have this.

Why Tests Pass But HTTP Fails

PR #857 added extensive lines of tests (McpMetaAttributeTests.cs) - all pass! But:

  • Tests serialize directly using McpJsonUtilities.DefaultOptions
  • Tests don't go through HTTP transport layer
  • HTTP serialization uses the source-generated JsonContext which lacks Tool metadata
  • Bug only manifests in HTTP transport

Environment

  • SDK: ModelContextProtocol 0.4.0-preview.3, ModelContextProtocol.AspNetCore 0.4.0-preview.3
  • .NET: 8.0
  • OS: macOS
  • Transport: HTTP (Streamable HTTP on port 5047)
  • Confirmed via: MCP Inspector, direct curl testing, debug logging

Suggested Fix

Option 1: Add explicit serialization metadata:

[JsonSerializable(typeof(Tool))]
[JsonSerializable(typeof(ListToolsResult))]

Option 2: Add custom JsonConverter for Tool (like ResourceContents has) that explicitly writes _meta

Related

Additional Context

#856 may have been closed prematurely:

  • All tests pass because they don't use HTTP transport
  • HTTP serialization bug was never addressed
  • Issue closed without testing the only transport ChatGPT supports

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions