Hermes seeds MCP-Protocol-Version from the handshake era, not the newest revision

Kind: nuance · Status: current · Last verified: 2026-11-09 · Clients: hermes-agent · Revisions: 2025-11-25, 2026-07-28

The HTTP path sets a default MCP-Protocol-Version request header when the config does not already carry one, matching the key case-insensitively so a user's own casing survives. The value is the SDK's LATEST_HANDSHAKE_VERSION, which mcp 2.0.0 and 2.3.0 both resolve to 2025-11-25, not LATEST_PROTOCOL_VERSION, which resolves to 2026-07-28.

The comment in place gives the failure it prevents. Hermes connects over HTTP through ClientSession.initialize(), whose body advertises LATEST_HANDSHAKE_VERSION. A header naming 2026-07-28 puts the request on the server's per-request-envelope handling, which then rejects the handshake body for missing the params._meta keys that era requires. The header and the body have to name the same era.

Hermes also carries its own fallback constant for SDK builds that do not export the newer names. LATEST_PROTOCOL_VERSION falls back to 2025-03-26 — the revision that introduced Streamable HTTP, so it stays valid for the transport regardless — and LATEST_HANDSHAKE_VERSION falls back to whatever LATEST_PROTOCOL_VERSION resolved to, on the reasoning that an SDK predating the split makes no distinction between the two.

This is the concrete cost of the era split for anyone writing an HTTP client: two constants that used to be one, and a header that silently selects which of a server's two request ladders handles the call.

Sources