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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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