stacklok / stacklok/toolhive-core
LATEST_PROTOCOL_VERSION misdescribes what the shim requests, and Initialize silently discards the caller's version
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2
- Forks
- 4
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 46
Description
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:197pkg/vmcp/client/client.go:852pkg/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 thatParams.ProtocolVersiondoes 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 forInitialize(the value is discarded anyway), but it is exported and consumers may compare against it, so it needs a consumer sweep first.toolhiveuses 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
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with mcpcompat/mcp/jsonrpc.go and client.Initialize, then inspect the three production call sites in pkg/transport/bridge.go, pkg/vmcp/client/client.go, and pkg/vmcp/session/internal/backend/mcp_session.go. Review TestListCapabilities_MisCachedLegacy_SelfHealsViaSDKNegotiation and the linked go-sdk limitation before choosing how to document LATEST_PROTOCOL_VERSION. Done means the Initialize behavior and constant semantics are explicit, with validation covered without changing current negotiation behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend
- Issue type
- Documentation
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100