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

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions