Skip to content

Send current MCP protocol version in readiness probe - #5940

Merged
JAORMX merged 1 commit into
mainfrom
readiness-probe-protocol-version-5764
Jul 23, 2026
Merged

Send current MCP protocol version in readiness probe#5940
JAORMX merged 1 commit into
mainfrom
readiness-probe-protocol-version-5764

Conversation

@JAORMX

@JAORMX JAORMX commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Why: The runner's readiness probe hardcoded "protocolVersion":"2024-11-05" in its initialize request (and repeated it in the MCP-Protocol-Version header). MCP servers that reject that ancient revision report as never-ready even when healthy, so ToolHive waits out the full readiness timeout before failing to start them. This got more likely as backends move to newer, stricter SDKs.

What:

  • Advertise a pinned, current revision — probeProtocolVersion = "2025-11-25" — in the initialize body instead of the hardcoded 2024-11-05.
  • The version is deliberately pinned rather than tracking mcp.LATEST_PROTOCOL_VERSION. The upcoming 2026-07-28 revision removes the initialize method entirely (SEP-2575, replaced by server/discover + per-request _meta), so coupling the probe to a moving "latest" constant would eventually emit a method:"initialize" request stamped with a version that no longer defines it. The 2026-07-28 migration is deferred to Adopt go-sdk v1.7 for MCP 2026-07-28 stateless support #5754.
  • Stop sending the MCP-Protocol-Version header on the initialize request. The spec scopes that header to post-initialization requests carrying the negotiated version, and requires a server to reject an unsupported value with HTTP 400. Sending it on initialize would let a backend that only supports an older revision fail the probe, whereas body-based version negotiation degrades gracefully (the server answers HTTP 200 with its own supported version). This makes the probe robust across backend revisions in both directions.

Addresses item 1 of #5764 (readiness probe protocol version). Items 2 (JSON-RPC error for filtered tool calls) and 3 (version-agnostic proxying docs + strict mode) remain, so this does not close the issue.

Type of change

  • Bug fix

Test plan

  • Unit tests — go test -race ./pkg/runner/ (full package) passes, including a new TestWaitForInitializeSuccess subtest that captures the probe request and asserts: the body carries "protocolVersion":"2025-11-25" (independent literal, so the deliberate pin can't silently drift), the body contains neither 2024-11-05 nor 2026-07-28, and no MCP-Protocol-Version header is sent.
  • Linting — task lint reports 0 issues; task build succeeds.

Does this introduce a user-facing change?

No behavioral change users configure directly. Operationally, MCP servers that only accept recent protocol revisions now pass ToolHive's readiness probe instead of timing out at startup.

Implementation plan

Approved implementation plan

Planned with Claude Code via an architect → worker → cross-axis-review pipeline:

  1. Architect: source the probe version from a current revision instead of the hardcoded 2024-11-05. Initially proposed mcp.LATEST_PROTOCOL_VERSION.
  2. Spec check (mcp-protocol-expert): verified against the 2025-11-25 spec and the 2026-07-28 draft changelog. Found that (a) tracking LATEST is unsafe because 2026-07-28 removes initialize, so the version was pinned to 2025-11-25 behind a documented probeProtocolVersion constant; and (b) the MCP-Protocol-Version header on initialize can trigger a spec-mandated HTTP 400 from older backends, so the header was dropped in favor of graceful body-based negotiation.
  3. Worker: implemented the constant, the fmt.Sprintf body, header removal, and the regression test.
  4. Cross-axis review (security / correctness+conventions+tests / MCP-spec): security clean; applied the header-drop finding, strengthened the test to assert an independent literal, and tightened the doc comment (2026-07-28 draft caveat).

Special notes for reviewers

  • Deviation from the issue text: 2025-11-25 compliance polish: probe version, filter errors, version stance #5764 item 1 literally says to update "the MCP-Protocol-Version header". This PR instead removes that header from the probe, because the review found keeping it (even at a current version) can falsely mark older-but-healthy backends as not-ready. The body-based version is the spec-correct negotiation channel.
  • Pre-existing items in waitForInitializeSuccess intentionally left out of scope: the defer resp.Body.Close() inside the retry loop and closing the body without draining. Worth a separate cleanup PR.

Generated with Claude Code

@JAORMX
JAORMX requested a review from amirejaz as a code owner July 23, 2026 12:24
@github-actions github-actions Bot added the size/XS Extra small PR: < 100 lines changed label Jul 23, 2026
@JAORMX

JAORMX commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

CI note: the only red check here is Tests / Test Go Code, failing on an unrelated pre-existing flake — TestForwarding_Progress_RealBackend in pkg/vmcp/server (times out on a 5s async-notification wait under CI load; passes 3/3 locally). It has no connection to this readiness-probe change (pkg/runner); it failed identically on the initial run and a re-run of failed jobs.

Root-cause fix split out to #5941 (raise the wait deadline to 15s). Once that merges, I'll rebase this branch on main so its Tests job picks up the fix and goes green. All 20 other checks here (lint, E2E lifecycle ×3, CodeQL, govulncheck, Helm, CRD compat, etc.) are passing.

The readiness probe hardcoded protocolVersion "2024-11-05" in its
initialize request. MCP servers that reject that ancient revision report
as never-ready even when healthy, so ToolHive waits out the full timeout
before failing to start them.

Advertise a pinned, current revision (2025-11-25) in the initialize body
instead. The version is deliberately pinned rather than tracking
mcp.LATEST_PROTOCOL_VERSION: the upcoming 2026-07-28 revision removes the
initialize method entirely (SEP-2575), so coupling the probe to a moving
"latest" constant would eventually emit a method:"initialize" request
stamped with a version that no longer defines it. The 2026-07-28 path
(server/discover) is deferred to #5754.

Also stop sending the MCP-Protocol-Version header on the initialize
request. The spec scopes that header to post-initialization requests
carrying the negotiated version and requires a server to reject an
unsupported value with HTTP 400 -- so a header would let a backend that
only supports an older revision fail the probe, whereas body-based
version negotiation degrades gracefully to HTTP 200. This closes item 1
of #5764.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JAORMX
JAORMX force-pushed the readiness-probe-protocol-version-5764 branch from 1f801bf to 2dddbbd Compare July 23, 2026 13:19
@github-actions github-actions Bot added size/XS Extra small PR: < 100 lines changed and removed size/XS Extra small PR: < 100 lines changed labels Jul 23, 2026
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.76%. Comparing base (4529806) to head (2dddbbd).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5940      +/-   ##
==========================================
- Coverage   71.77%   71.76%   -0.02%     
==========================================
  Files         705      705              
  Lines       72157    72159       +2     
==========================================
- Hits        51794    51783      -11     
- Misses      16662    16675      +13     
  Partials     3701     3701              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JAORMX
JAORMX merged commit 1d3e866 into main Jul 23, 2026
80 of 81 checks passed
@JAORMX
JAORMX deleted the readiness-probe-protocol-version-5764 branch July 23, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XS Extra small PR: < 100 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants