Skip to content

bug(2.0.0-preview.3): resultType, ttlMs, and cacheScope emitted on all responses regardless of negotiated protocol version (breaks 2025-11-25 clients) #1721

Description

@p-ob

Describe the bug

When a client negotiates "protocolVersion": "2025-11-25" via the initialize handshake, the server (v2.0.0-preview.3) still includes resultType, ttlMs, and cacheScope in every JSON-RPC result object. These fields are exclusive to the 2026-07-28 draft spec and must be absent from responses on a 2025-11-25 session. Clients that strictly validate against the 2025-11-25 schema — such as MCP Inspector 1.0.0 — treat them as unrecognized keys and fail the handshake.

To Reproduce

  1. Start a C# MCP server built with v2.0.0-preview.3 (stateless or stateful).
  2. Send an initialize request with "protocolVersion": "2025-11-25".
  3. Observe that the InitializeResult contains "resultType": "complete" despite the server echoing back "protocolVersion": "2025-11-25".
  4. Send a tools/list request.
  5. Observe that the result contains "resultType": "complete", "ttlMs": 0, and "cacheScope": "private".
  6. Point MCP Inspector 1.0.0 at the server — it rejects the handshake on these unrecognized fields.

Expected behavior

For sessions where the negotiated protocol version is 2025-11-25, resultType, ttlMs, and cacheScope must be absent from all result objects. The serializer already uses WhenWritingNull — if these properties are left null they are correctly omitted. The server should only stamp them when the negotiated (or per-request) version is 2026-07-28 or later.

Logs

InitializeResult wire shape (negotiated version 2025-11-25):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "resultType": "complete",
    "serverInfo": { "..." : "..." },
    "capabilities": {}
  }
}

tools/list wire shape (same session):

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [],
    "resultType": "complete",
    "ttlMs": 0,
    "cacheScope": "private"
  }
}

Additional context

This appears to have been introduced by two PRs without protocol-version gating:

The fix should gate both stampings on IsJuly2026OrLaterProtocolRequest(jsonRpcRequest), which requires moving the logic from the inner McpRequestHandler closures into the outer _requestHandlers.Set(...) lambda where jsonRpcRequest is in scope. SetWithAlternateHandler and SetTaskAugmentedHandler have the same pattern and need the same fix. The hardcoded ResultType = "complete" on InitializeResult at line 613 also needs a version gate — negotiatedProtocolVersion is already in scope at that point.

Metadata

Metadata

Assignees

Labels

P0Critical: core functionality failures or high-severity securitybugSomething isn't workingready for workHas enough information to start

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions