Skip to content

1.0.81-1 sends legacy initialize after successful modern server/discover, causing -32022 #4525

Description

@dmbutko

Describe the bug

Copilot CLI 1.0.81-1 fails MCP initialization against a stdio server using the Python MCP SDK 2.0.0 dual-era runner.

The CLI opens the connection with a modern server/discover probe whose _meta declares io.modelcontextprotocol/protocolVersion: 2026-07-28. The server replies with a valid successful DiscoverResult (resultType: "complete", supportedVersions: ["2026-07-28"]). The CLI then sends a legacy initialize requesting 2025-11-25 on that same connection, and the server rejects it with -32022.

Per the stdio backward-compatibility rules, a successful DiscoverResult identifies a modern server; legacy initialize fallback is only for a non-modern error or timeout. The CLI declares two protocol eras on one connection.

Related to #4370, but exercises the other probe outcome: #4370 concerns fallback after a non-modern error; this occurs after a successful modern DiscoverResult.

Affected version

1.0.81-1

Verified working on 1.0.80, 1.0.80-1 and 1.0.81-0 with the same server and config. 1.0.81-0 (works) and 1.0.81-1 (fails) were both run via node index.js from the same cache layout, so the difference is the version, not the invocation path. 1.0.81-1 also fails when run as the native binary.

Steps to reproduce the behavior

  1. Configure a stdio MCP server that implements server/discover. Reproduced with scrapling 0.4.14 (scrapling mcp), resolved dependency mcp==2.0.0.
  2. Run Copilot CLI 1.0.81-1.
  3. The server's tools are missing. The CLI log shows:
    failed to initialize MCP client: JSON-RPC error: -32022: connection is serving the 2026-07-28 protocol; the initialize handshake is not accepted({"supported":["2026-07-28"],"requested":"2025-11-25"})

Expected behavior

After a successful DiscoverResult, the CLI should select a mutually supported advertised version and continue in that era without sending legacy initialize on that connection. Legacy initialize fallback should occur only when the probe returns a non-modern error or times out.

Additional context

Wire capture on 1.0.81-1 (abridged — capabilities and instructions elided, marked "..."):

// 1. client -> server
{"jsonrpc":"2.0","id":0,"method":"server/discover","params":{"_meta":{
  "io.modelcontextprotocol/protocolVersion":"2026-07-28",
  "io.modelcontextprotocol/clientInfo":{"name":"copilot-cli","version":"0.0.0"},
  "io.modelcontextprotocol/clientCapabilities":"..."}}}

// 2. server -> client  (valid modern discovery success)
{"jsonrpc":"2.0","id":0,"result":{"resultType":"complete",
  "supportedVersions":["2026-07-28"],"capabilities":"...","instructions":"..."}}

// 3. client -> server  (legacy handshake on a modern-locked connection)
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25",
  "capabilities":"...","clientInfo":{"name":"copilot-cli","version":"0.0.0"}}}

// 4. server -> client  (verbatim)
{"jsonrpc":"2.0","id":1,"error":{"code":-32022,
 "message":"connection is serving the 2026-07-28 protocol; the initialize handshake is not accepted",
 "data":{"supported":["2026-07-28"],"requested":"2025-11-25"}}}

Server side, the Python SDK locks the connection's era from the first message: a non-initialize opening request carrying the reserved io.modelcontextprotocol/protocolVersion key selects the modern runner, which then rejects initialize (mcp/server/runner.py).

Environment: Linux x64, Node v24.15.0, server scrapling 0.4.14 on mcp 2.0.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:mcpMCP server configuration, discovery, connectivity, OAuth, policy, and registry

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions