Support MCP 2026-07-28 spec (go-sdk v1.7 via toolhive-core)#5993
Open
JAORMX wants to merge 2 commits into
Open
Support MCP 2026-07-28 spec (go-sdk v1.7 via toolhive-core)#5993JAORMX wants to merge 2 commits into
JAORMX wants to merge 2 commits into
Conversation
Move ToolHive onto toolhive-core v0.0.34, which embeds go-sdk v1.7.0-pre.3 — the SDK that implements the new MCP 2026-07-28 "stateless" revision. This lets vMCP negotiate and speak both revisions per backend: Legacy 2025-11-25 and Modern 2026-07-28. The build is unchanged (the mcpcompat shim absorbs the go-sdk API change); this fixes the behavioral gaps that only surface once the real v1.7 SDK is imported, without regressing 2025-11-25 (dual-revision is preserved). Supersedes the prior "hand-rolled Modern envelope, do not import go-sdk v1.7 into the root module" approach: importing the SDK is what makes the new spec reachable. CC @jhrozek (probe/classification and #5981 author). - Revision classification (the crux): decide a backend's revision by whether its server/discover result advertises 2026-07-28 in supportedVersions — the SEP-2575 negotiation signal, and exactly what go-sdk's own reference client keys on. The old "any clean discover is Modern" heuristic mis-classified stateful 2025-11-25 backends, which under v1.7 answer discover too (negotiating down). New sentinel errModernNegotiatedDown wires the negotiate-down case through the existing reclassify machinery. - Strip reserved io.modelcontextprotocol/* _meta at the Legacy backend tool-call egresses (httpBackendClient and the session client): these per-hop protocol-control keys must not cross onto the vMCP->backend hop (a stateful backend 400s them under v1.7). No-op for Legacy traffic. - Test adaptations for the real v1.7 client (server/discover-first over SSE; SSE-forwarding fake answers discover with -32601), a nil-registry test-construction fix, and new real-backend pin tests that assert stateful->Legacy / stateless->Modern so a future SDK discover-semantics change fails loudly here. Follow-up: #5992 (revision-cache TTL re-probe). Part of #5743 (#5754). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WG3mSjVGWNc8nfgbkdd79
JAORMX
requested review from
ChrisJBurns,
amirejaz,
blkt,
jerm-dro,
jhrozek,
rdimitrov and
tgrunnagle
as code owners
July 25, 2026 16:39
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5993 +/- ##
==========================================
- Coverage 72.06% 72.06% -0.01%
==========================================
Files 718 718
Lines 74462 74477 +15
==========================================
+ Hits 53662 53669 +7
- Misses 16965 16975 +10
+ Partials 3835 3833 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The MCPRevision backend-status field (added by the earlier per-backend revision work) was missing from the generated docs/operator/crd-api.md; regenerate via task crdref-gen so the Operator CI docs check passes.
Collaborator
Author
Context vs the #5754 gate (proceeding on pre.3 — deliberate maintainer decision)Flagging this explicitly for reviewers since it touches the go/no-go analysis on #5754:
Reviewed by a 3-way Opus panel (protocol-correctness/dual-revision headline) with every load-bearing claim verified against the real go-sdk v1.7.0-pre.3 source. cc @jhrozek |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of #5743 (Workstream C). Advances #5754.
The goal is support for the new MCP spec revision, 2026-07-28 ("stateless") — not a Go SDK for its own sake. Bumping
toolhive-coreto v0.0.34 pulls in go-sdk v1.7.0-pre.3, the SDK that implements 2026-07-28, so vMCP can negotiate and speak both revisions per backend (Legacy 2025-11-25 + Modern 2026-07-28). The build is unchanged (themcpcompatshim absorbs the go-sdk API change); this PR fixes the behavioral gaps that only surface once the real v1.7 SDK is imported, without regressing 2025-11-25 (dual-revision is a hard requirement).What changed
server/discoverresult advertises2026-07-28insupportedVersions— the SEP-2575 negotiation signal, and exactly what go-sdk's own reference client keys on (mcp/client.go:428-444). The old "any cleanserver/discover⇒ Modern" heuristic mis-classified stateful 2025-11-25 backends, which under v1.7 answer discover too (negotiating down) → their Legacytools/listwas parsed as Modern → sessions died. New sentinelerrModernNegotiatedDownroutes the negotiate-down case through the existing reclassify-on-mismatch machinery (retry-safe: only read-only discover/list can produce it)._metaegress strip. Strip the per-hopio.modelcontextprotocol/*control keys at both Legacy backend tool-call egresses (httpBackendClient.CallTool,mcpSession.CallTool) — a stateful backend 400s them under v1.7. Exact keys only, copy-before-mutate, no-op for Legacy.server/discover-first over SSE (parser assertion made transport-aware); the SSE-forwarding fake now answers discover with-32601; a nil-registry test-construction fix; and new real-backend pin tests assertingstateful → Legacy/stateless → Modernso a future SDK discover-semantics change fails loudly here, not as mystery failures.ponytail:comments toNOTE:.Type of change
Test plan
task test)task lint-fix)The full suite goes from 137 failures to exactly the 3 pre-existing, sandbox-environment failures (no container runtime ×2, no keyring ×1 — none in touched packages). New/changed tests pass under
-race.Special notes for reviewers
SupportsProtocolVersionstateless gate atmcp/streamable.go:871, thesupportedVersionsJSON tag, the reference-client fallback). Verdict: SHIP / SHIP-WITH-NITS ×2, no MUST-FIX. All actionable nits addressed (added aStripReservedModernMetaunit test, a version-exact-match tripwire comment, and filed the cache-staleness follow-up).probeRevisiondeliberately builds its own discover client (rather than delegating) so a transport-misconfiguration hard-fails instead of being mis-cached as Legacy — see the doc comment.🤖 Generated with Claude Code