Skip to content

fix(mcp): server/discover pins no era — Claude Code probes, then initializes - #43

Merged
Megaprompting merged 2 commits into
mainfrom
fix/mcp-discover-probe
Oct 27, 2026
Merged

Megaprompting merged 2 commits into
mainfrom
fix/mcp-discover-probe

Conversation

@Megaprompting

Copy link
Copy Markdown
Owner

What broke

Since 2026-10-04 Claude Code has failed every connection to the Torque MCP server with -32010. A pass-through stdio shim recorded the handshake from Claude Code 2.1.308 (desktop-bundled) and 2.1.310 (CLI):

  1. The client sends server/discover (id server-discover-probe-1, carrying the modern _meta).
  2. Torque returns the version list 2026-07-28, 2025-11-25, 2025-06-18 and pins the connection modern.
  3. On the same connection, the client sends initialize with protocolVersion: 2025-11-25.
  4. Torque refuses it: "eras never mix".

Discover was already version-exempt because it is the request a client uses to learn which versions exist. Pinning an era on that request turned it into a choice the client never made.

The fix

server/discover still answers with a modern-shaped result and still needs no version, but it pins no era. The era is pinned by the first message that carries one:

  • initialize pins legacy.
  • A request carrying _meta io.modelcontextprotocol/protocolVersion pins modern.

The rest of "eras never mix" is unchanged, and the tests still assert each case:

  • server/discover after initialize is refused.
  • A modern-marked request on a legacy connection is refused (C1).
  • initialize after a real modern request is refused (new test, replacing the old discover-then-initialize one).
  • Ambiguous or incomplete handshakes pin nothing (D1, D3, C10, E2).

The refusal for an unpinned connection no longer names discover as a way to pin an era.

Tests

  • Two tests changed meaning: "discover pins modern" became "discover pins nothing", and "initialize after discover is refused" became "succeeds" plus the new modern-request refusal test.
  • C14 and D10 now pin modern with a real modern request, so their "on modern" claims stay true.
  • New falsifier: the exact recorded two-line handshake, replayed over stdio. It fails with the production -32010 message on the unpatched kernel and passes after the fix.
  • npm test: all 16 suites green (rpc 58/58).

Wire trace: before and after

Before (installed 1.0.0, Claude Code 2.1.310):

C->S {"id":"server-discover-probe-1","method":"server/discover", ...}
S->C {"id":"server-discover-probe-1","result":{"protocolVersions":["2026-07-28","2025-11-25","2025-06-18"], ...}}
C->S {"method":"initialize","params":{"protocolVersion":"2025-11-25", ...},"id":0}
S->C {"id":0,"error":{"code":-32010,"message":"this connection is pinned to the modern era and initialize belongs to the other one — eras never mix on a connection; reconnect to switch"}}

After (this branch, same client, same one-turn claude -p --strict-mcp-config call):

C->S {"id":"server-discover-probe-1","method":"server/discover", ...}
S->C {"id":"server-discover-probe-1","result":{"protocolVersions":["2026-07-28","2025-11-25","2025-06-18"], ...,"resultType":"complete", ...}}
C->S {"method":"initialize","params":{"protocolVersion":"2025-11-25", ...},"id":0}
S->C {"id":0,"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{},"resources":{},"prompts":{}},"serverInfo":{"name":"torque-mcp","version":"1.0.0"}}}
C->S {"method":"notifications/initialized"}
C->S {"method":"tools/list","id":1}   -> result
C->S {"method":"prompts/list","id":2} -> result
C->S {"method":"resources/list","id":3} -> result

In its own system/init event, Claude Code reports {"name":"tq","status":"connected"} and lists the mcp__tq__* tools.

Not in this PR

  • Nothing is installed into the plugin cache. Users get the fix when they update the plugin after this merges.

🤖 Generated with Claude Code

Expresiions and others added 2 commits October 27, 2026 10:23
…ializes

Claude Code 2.1.308/2.1.310 probes with server/discover, reads the version
list, then sends a legacy initialize (2025-11-25) on the same connection.
The kernel pinned modern on the probe and refused that initialize with
-32010, so every Claude Code connect failed from 2026-10-04.

discover now answers (modern-shaped, version-exempt) without pinning; the
first era-bearing message pins: initialize pins legacy, a request carrying
the _meta protocol version pins modern. discover after initialize, a
modern-marked request on legacy, and initialize after a real modern request
all stay refused; ambiguous handshakes still pin nothing.

Falsifier: the recorded two-line handshake replayed over stdio, red (-32010)
against the unpatched kernel, green after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG [Unreleased] -> Fixed conflict only: keep both entries (discover
probe first, #42 store split second); no other text changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Megaprompting
Megaprompting merged commit 45f8aeb into main Oct 27, 2026
6 checks passed
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.

2 participants