Skip to content

Model nested experimental values and extensions in Client.Capabilities - #276

Open
nstrm wants to merge 1 commit into
modelcontextprotocol:mainfrom
nstrm:fix/client-capabilities-experimental-extensions
Open

Model nested experimental values and extensions in Client.Capabilities#276
nstrm wants to merge 1 commit into
modelcontextprotocol:mainfrom
nstrm:fix/client-capabilities-experimental-extensions

Conversation

@nstrm

@nstrm nstrm commented Aug 19, 2026

Copy link
Copy Markdown

Fixes #262.

Client.Capabilities.experimental is typed [String: String]?, but the spec defines it as a map of arbitrary objects. Clients that send nested values there — ChatGPT sends {"openai/visibility": {"enabled": true}} — fail decoding inside TypedRequestHandler, and the server returns JSON-RPC -32603 to a valid initialize. The extensions capability map was also unmodeled (ignored on decode).

Note for reviewers: this changes the public type of experimental. Dictionary literals with string values still compile (Value is ExpressibleByStringLiteral); no call sites in the repo needed updating.

Type experimental as [String: Value] per the spec and add the
extensions capability map, so initialize requests carrying nested
JSON (e.g. ChatGPT's) decode instead of failing with -32603.

Fixes modelcontextprotocol#262
@dergachoff

Copy link
Copy Markdown

Second reproducer for #262: Codex CLI 0.154.0 (released 2026-09-09) sends "capabilities":{"experimental":{"codex/auth-change":{}}} on initialize, so every server on 0.12.1 fails the handshake with -32603. Rebuilt my server (Epistates/MCPSafari) against f7077e0 and the same request succeeds.

ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 11, 2026
…nt.Capabilities (upstream modelcontextprotocol#276, nstrm)

Manifest entry 7. Upstream refs/pull/276/head at f7077e0, unmodified.

Client.Capabilities.experimental was typed [String: String], but the
2025-11-25 schema defines it as a map of arbitrary objects. A ChatGPT-shaped
initialize carrying {"openai/visibility": {"enabled": true}} failed to
decode and the server answered -32603 to a valid request. Reproduced before
the merge and confirmed fixed after.

experimental becomes [String: Value]; extensions is added as [String: Value].
Note that extensions is a draft-schema capability, absent from 2025-11-25 —
an unmodeled key already decoded fine, so that half is a feature, not a fix.

This is a source-breaking public type change: dictionary literals with string
values still compile, but a typed [String: String] variable and reading a
value back as String do not. A deprecated compatibility initializer follows
as entry 7a.

Fixes modelcontextprotocol#262.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb9yGSXH1TVkg5Afsu9phk
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 11, 2026
modelcontextprotocol#276 changes Client.Capabilities.experimental from [String: String] to
[String: Value], which is source-breaking: a dictionary literal with string
values still compiles because Value is ExpressibleByStringLiteral, but a
typed [String: String] variable does not.

This restores that call site behind a deprecation warning. experimental is
deliberately non-optional and without a default: with every parameter
defaulted, overload resolution routes the bare Client.Capabilities() call to
this initializer and warns on it, which is noise unrelated to experimental.
Requiring the parameter keeps that call on the designated initializer.

Reading a value back as String is not restored — experimental is a stored
property and Swift has no second property of the same name, so consumers that
read values out still need updating.

Follows the deprecated compatibility factories in Sources/MCP/Server/Tools.swift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb9yGSXH1TVkg5Afsu9phk
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 11, 2026
…nt.Capabilities

Manifest entry 7a. Fork addition at 604afd2, one clean commit stacked on
modelcontextprotocol#276's f7077e0, offerable upstream as submitted.

modelcontextprotocol#276's public type change is source-breaking for a typed [String: String]
variable. This restores that call site behind a deprecation warning, with
experimental required and undefaulted so the bare Client.Capabilities() call
stays on the designated initializer instead of warning. Reading a value back
as String is still unsupported and cannot be shimmed by an initializer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb9yGSXH1TVkg5Afsu9phk
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 11, 2026
… as entry 7a

Client.Capabilities.experimental was [String: String] where the 2025-11-25
schema defines a map of arbitrary objects, so a ChatGPT-shaped initialize
carrying {"openai/visibility": {"enabled": true}} failed to decode and the
server answered -32603 to a valid request. Reproduced on the wire before the
merge and confirmed fixed after.

The entry records what the PR description understates: the type change is
source-breaking for a typed [String: String] variable and for reading a value
back as String, and the extensions field it adds is a draft-schema capability
absent from 2025-11-25 — an unmodeled key already decoded fine, so that half
is a feature rather than a fix.

7a restores the typed-variable call site behind a deprecation warning. The
read direction cannot be shimmed by an initializer and still needs updating
downstream.

Remaining candidate modelcontextprotocol#278 renumbered 7 to 6.

Tracking: fork issue #11.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb9yGSXH1TVkg5Afsu9phk
ianegordon added a commit to ianegordon/swift-sdk that referenced this pull request Sep 11, 2026
First fork tag carrying a library change since .5: modelcontextprotocol#276 fixes the -32603 a
server returns to a ChatGPT-shaped initialize, and 7a adds the deprecated
[String: String] compatibility initializer. Tags .5 and .6 differ only in the
conformance harness, so this is the first tag downstream has a reason to move
to since .5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eb9yGSXH1TVkg5Afsu9phk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants