Skip to content

Server fails to decode ChatGPT's initialize (experimental typed [String: String], extensions unmodeled); error is returned inside an HTTP 200 #262

Description

@nstrm

Describe the bug
Client.Capabilities types experimental as [String: String]? (Sources/MCP/Client/Client.swift, line 128 in 0.12.1) and has no extensions property. The MCP schema defines experimental as a map of arbitrary objects ({ [key: string]: object }), and extensions is a spec-defined ClientCapabilities field as of protocol 2025-11-25 (https://modelcontextprotocol.io/extensions/overview).

ChatGPT's MCP client (openai-mcp, protocol 2025-11-25) sends both fields. The SDK's request decoder throws while decoding initialize — before any server handler runs — and StatelessHTTPServerTransport returns the JSON-RPC error inside an HTTP 200 response, making the failed handshake indistinguishable from success at the HTTP layer.

Practical impact: every Swift-SDK server fails ChatGPT Developer-Mode connector setup. ChatGPT shows the connector as "Connected" (OAuth succeeds) but imports zero actions and displays a generic error banner. Because nothing 4xx/5xx appears anywhere, this took us a two-day investigation across OAuth, transports, and tool schemas before we inspected the JSON-RPC body of a "200 OK" initialize.

To Reproduce
Steps to reproduce the behavior:

  1. Run any MCP server built on swift-sdk 0.12.1 with StatelessHTTPServerTransport (a single tool is enough).
  2. POST the following initialize request (captured verbatim from ChatGPT Developer Mode, July 2026) to the server's MCP endpoint:
{
  "jsonrpc": "2.0",
  "method": "initialize",
  "id": 1,
  "params": {
    "protocolVersion": "2025-11-25",
    "clientInfo": { "name": "openai-mcp", "version": "1.0.0" },
    "capabilities": {
      "experimental": {
        "openai/visibility": { "enabled": true }
      },
      "extensions": {
        "io.modelcontextprotocol/ui": { "mimeTypes": ["text/html;profile=mcp-app"] }
      }
    }
  }
}
  1. Observe an HTTP 200 whose body is a JSON-RPC internal error; the server's initialize handler never runs.
  2. Real-world trigger: add the server as a ChatGPT Developer-Mode custom connector — OAuth completes, then setup fails with no actions imported.

Expected behavior
The initialize request decodes and succeeds. experimental["openai/visibility"] is a nested object, which is valid per the schema, and extensions is a spec-defined field. More defensively: unknown or unexpectedly-shaped entries inside capabilities should arguably never fail the entire initialize — capability objects evolve faster than typed models, and a server that ignores capabilities it does not consume is spec-safe.

Logs
Response to the request above (HTTP status 200):

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: The operation could not be completed. The data isn’t in the correct format."
  }
}

Server-side request logging shows only a successful-looking POST; no 4xx/5xx is emitted anywhere in the exchange.

Additional context

  • Suggested fix: type experimental as [String: Value]? (or another arbitrary-JSON container) to match the schema, and add extensions: [String: Value]? per the 2025-11-25 spec. Also worth considering: surfacing request-decode failures distinctly from handler errors, so servers can log them — the 200-wrapped -32603 actively hides this class of bug.
  • Workaround we run in production: strip params.capabilities.experimental and params.capabilities.extensions from initialize bodies before handing the request to the SDK transport (safe when the server doesn't consume them). This fully restores ChatGPT connector functionality.
  • Happy to open a PR for the capability typing if maintainers agree with the direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions