Skip to content

LATEST_PROTOCOL_VERSION misdescribes what the shim requests, and Initialize silently discards the caller's version #193

Description

@JAORMX

Rescoped. This issue originally bundled two conformance findings that turned out to be upstream-only; those are now go-sdk#1112 and go-sdk#1113. See the correction comment for why neither was fixable here. What remains below is shim-side.

The defect

mcpcompat exports a constant that no longer describes what the shim does:

// mcpcompat/mcp/jsonrpc.go:15
const LATEST_PROTOCOL_VERSION = "2025-11-25"

Meanwhile go-sdk's latestProtocolVersion is 2026-07-28, and client.Initialize calls Connect(ctx, tr, nil) — so the shim always requests 2026-07-28 on the wire, whatever the caller asked for. request.Params.ProtocolVersion is read nowhere in Initialize.

The combination is what makes this worth fixing: a consumer reads LATEST_PROTOCOL_VERSION, reasonably concludes the shim speaks 2025-11-25, passes it to Initialize, and gets 2026-07-28 requested — silently.

That is not hypothetical. Every ToolHive production initialize does exactly this:

  • pkg/transport/bridge.go:197
  • pkg/vmcp/client/client.go:852
  • pkg/vmcp/session/internal/backend/mcp_session.go:553

Important: do not "fix" this by honouring the request

The substitution is load-bearing for ToolHive today. Because the wire request is 2026-07-28, a Modern-capable backend answers initialize with 2026-07-28, which is precisely how ToolHive's legacyInit detects a mis-cached backend and flips its revision cache Legacy→Modern (stacklok/toolhive#5997, pinned by TestListCapabilities_MisCachedLegacy_SelfHealsViaSDKNegotiation).

Honouring a requested 2025-11-25 would make that backend negotiate 2025-11-25, the self-heal would never fire, and vMCP's Legacy→Modern correction would silently regress. Rejecting an unhonourable request would break all three production call sites immediately, since they all pass a version that cannot be honoured.

Honouring is also not currently possible: ClientSessionOptions.protocolVersion is unexported upstream (go-sdk#1113).

Proposed work — documentation and validation only, no behaviour change

  • Document client.Initialize: state plainly that Params.ProtocolVersion does not influence the wire version, that go-sdk requests its own latest and negotiates down, and link go-sdk#1113. This removes the silence, which is the actual harm.
  • Fix or document LATEST_PROTOCOL_VERSION. Two options, and this is the decision worth discussing:
    • Document it as an mcp-go compatibility constant that reflects neither the revision the shim requests nor the newest it supports. Zero risk.
    • Change it to 2026-07-28. Behaviourally a no-op for Initialize (the value is discarded anyway), but it is exported and consumers may compare against it, so it needs a consumer sweep first. toolhive uses it in at least the three production sites above plus several tests.
  • Validate genuinely invalid input: a requested version that go-sdk does not recognise at all is unambiguously a caller bug and can return an error, with no effect on any current caller.

Non-goal

Making the shim honour arbitrary requested versions. That is blocked on go-sdk#1113 and, per the above, would need a coordinated change in toolhive because the current behaviour is depended upon.

Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions