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

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions