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:
- Run any MCP server built on swift-sdk 0.12.1 with
StatelessHTTPServerTransport (a single tool is enough).
- 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"] }
}
}
}
}
- Observe an HTTP 200 whose body is a JSON-RPC internal error; the server's initialize handler never runs.
- 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.
Describe the bug
Client.Capabilitiestypesexperimentalas[String: String]?(Sources/MCP/Client/Client.swift, line 128 in 0.12.1) and has noextensionsproperty. The MCP schema definesexperimentalas a map of arbitrary objects ({ [key: string]: object }), andextensionsis a spec-defined ClientCapabilities field as of protocol 2025-11-25 (https://modelcontextprotocol.io/extensions/overview).ChatGPT's MCP client (
openai-mcp, protocol2025-11-25) sends both fields. The SDK's request decoder throws while decodinginitialize— before any server handler runs — andStatelessHTTPServerTransportreturns 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:
StatelessHTTPServerTransport(a single tool is enough).initializerequest (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"] } } } } }Expected behavior
The initialize request decodes and succeeds.
experimental["openai/visibility"]is a nested object, which is valid per the schema, andextensionsis a spec-defined field. More defensively: unknown or unexpectedly-shaped entries insidecapabilitiesshould 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
experimentalas[String: Value]?(or another arbitrary-JSON container) to match the schema, and addextensions: [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-32603actively hides this class of bug.params.capabilities.experimentalandparams.capabilities.extensionsfrominitializebodies before handing the request to the SDK transport (safe when the server doesn't consume them). This fully restores ChatGPT connector functionality.