MCP Protocol Version Negotiation: Why Your Server Answers the Wrong Era
When your MCP client asks for protocol version 2026-07-28 and the server answers 2025-11-25, that is usually not a bug — it is the spec's downward negotiation: the pre-2026-07-28 lifecycle rules tell the server to return the highest version it supports that is not newer than what you asked for, and let the client decide whether to continue.
The real red flags are different. An echo machine parrots your requested version back verbatim — even a fictional one like 1999-01-01 — because it never reads the field. A silent downgrade serves an older era with no signal while your client branches on features by era. And a server that answers an initialize handshake claiming 2026-07-28 is negotiating via a method the 2026-07-28 spec deleted — that claim is fiction by construction.
We probed our own production servers live on October 12, 2026, and we're publishing exactly what they do — including the one that isn't modern.
The 72-hour debate, dated
| Date | Event | Source |
|---|---|---|
| Oct 10, 2026 | A public probe ran four real initialize handshakes (one per protocol era: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26) plus tools/list against 78 public MCP endpoints. Headline finding: 0 of 78 genuinely validate and serve the newest era; 66 of 78 (85%) silently downgrade a 2026-07-28 request; 6 servers echoed the requested version verbatim — including fictional eras 1999-01-01 and 2099-01-01, proving they don't read the field at all. | dev.to/pennyforgehq (Oct 10 probe + Oct 10 tooling piece) |
| Oct 11, 2026 | The retraction: the same author re-read the spec and published that the probe was structurally incapable of seeing a genuine modern server — the 2026-07-28 revision removed the initialize handshake entirely, so the only servers the probe could ever catch "claiming" the new era were echo machines negotiating via a deleted method. The real modern cohort was invisible to the instrument. | dev.to/pennyforgehq (Oct 11 retraction) |
| Oct 12, 2026 | Verified against the published spec: the 2026-07-28 specification page lists a stateless base protocol with per-request capability negotiation — no initialize lifecycle, with server/discover as the discovery RPC. The retraction's core claim checks out against the authoritative document. | modelcontextprotocol.io/specification/2026-07-28 (fetched Oct 12) |
| Oct 12, 2026 | ScriptMasterLabs probed its own two production MCP servers with the corrected instrument (initialize-per-era + fictional-era control + server/discover). Receipts below. | Live probes, this page |
Two SML servers, two eras — live receipts
| Probe (Oct 12, 2026) | scriptmasterlabs.com/mcp (SqueezeOS) | cascade.scriptmasterlabs.com/v1/mcp (telemetry gateway) |
|---|---|---|
initialize with 2026-07-28 | MODERN Rejected with -32600 "Mcp-Method header is required for MCP 2026-07-28" — it speaks the new era's transport, not the legacy handshake. | PINNED Answered protocolVersion: "2025-06-18" — silent downward negotiation. |
initialize with 2025-11-25 | Same modern-era rejection. | Answered 2025-06-18. |
initialize with fictional 1999-01-01 (control) | Same modern-era rejection. | Answered 2025-06-18 — did not echo the fiction. It reads the field; it just pins its era. |
server/discover | VERIFIED Returned supportedVersions: ["2026-07-28","2025-11-25","2025-06-18"] with cacheScope: "public", ttlMs: 300000, resultType: "complete", serverInfo SqueezeOS 8.3.0. A genuine 2026-07-28-era server. | ABSENT -32601 Method not found — no 2026-07-28-era discovery. Era-pinned, honestly so. |
Quotable: "ScriptMasterLabs' October 12, 2026 live probe found its own SqueezeOS MCP server answering server/discover with 2026-07-28 support — while its Cascade telemetry gateway pins 2025-06-18 and answers even a fictional 1999-01-01 request with its real era. Two eras, one operator, both verified against the published spec."
The four negotiation classes (and which one you're in)
| Class | Behavior | Honest? |
|---|---|---|
| Spec-correct downgrade | Answers initialize with the highest supported era not newer than requested. The client is supposed to notice and decide. | YES — this is what the lifecycle spec prescribes for pre-2026-07-28 eras. |
| Echo machine | Returns your requested version verbatim, even fictional ones. It never reads the field. | NO — the negotiated version is fiction; any client branching on features-by-era is being lied to. |
| Silent downgrade | Serves an older era with no signal; nothing in the session tells the client the downgrade happened. | MOSTLY NO — spec-tolerated, but 85% of the public surface does it, and clients that don't check get older behavior. |
| Modern era | No initialize at all; per-request _meta envelopes; server/discover advertises supported versions. | YES — but invisible to any probe that only speaks initialize. The Oct 11 retraction's whole point. |
Why the negotiated version is a decision-gate input
The negotiated protocol version is a claim — "I speak this era" — and claims are what decision gates exist to score. This matters financially the moment the server charges per call:
- The SqueezeOS and Cascade MCP endpoints price tool calls in USDC on Base via x402 (
get_ticker_detail$0.003,get_market_cycle/scrape_url$0.05 per accepted execution). An agent about to spend real money per call should validate protocol honesty before authorizing the spend — not after a session of silently downgraded behavior. - An echo machine that parrots back
2099-01-01is lying about its era. A client branching on features-by-era against that lie gets older behavior with full confidence. That is a confidence-calibration failure — exactly the class of signal a scored gate consumes. - The probe itself is already framed as a gate by its author ("add one line to CI"). The missing step is scoring:
≥0.80auto-pay the per-call price,0.50–0.79confirm the era mismatch with a human,<0.50escalate and refuse the session. The handshake is the cheapest claim to verify in the whole agent-commerce stack — onecurl, no payment, no session state.
Do it yourself: the 10-second honesty test
- Run the four-era handshake plus the fictional control against any MCP endpoint:
for era in "2026-07-28" "2025-11-25" "2025-06-18" "1999-01-01"; do echo "== $era ==" curl -s -X POST https://YOUR-HOST/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\", \"params\":{\"protocolVersion\":\"$era\",\"capabilities\":{}, \"clientInfo\":{\"name\":\"era-probe\",\"version\":\"1.0\"}}}" \ | python3 -c "import json,sys; print(json.load(sys.stdin)['result']['protocolVersion'])" done - Read the verdict. If
1999-01-01comes back as1999-01-01, you found an echo machine — do not trust its era claims. If every era returns one fixed real era, the server pins its version (honest, era-pinned, like our Cascade gateway). If the server rejectsinitializewith a modern-era error, it may be newer than your probe — go to step 3. - Probe the modern discovery path:
curl -s -X POST https://YOUR-HOST/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'Mcp-Method: server/discover' \ -H 'MCP-Protocol-Version: 2026-07-28' \ -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}' \ | python3 -m json.toolAsupportedVersionslist is a genuine 2026-07-28-era answer. A-32601 Method not foundmeans the server is era-pinned to the handshake generations. - Wire the verdict into your payment gate. Echo machine or silent downgrade on a server you're about to pay per call? That's a
0.50–0.79confirm at best — the server's claims need a human before the agent's wallet opens. Modern-eraserver/discoverwith a clean version list? That's evidence for the≥0.80auto band. The pattern: decision-gated machine payments.
Honest caveats
- Coverage-based on the three dev.to probe pieces (Oct 10–11, 2026), the GitHub open-constructs PR #381 on downward negotiation, and the published MCP specification page fetched October 12, 2026 — not the TypeScript SDK source or the spec's git history. The "85% silently downgrade" figure is the probe author's cohort (78 self-selected public endpoints), not a census.
- Our receipts are coverage-based on two SML production endpoints probed over plain HTTPS on October 12, 2026 (~09:25 EDT):
initializefor four eras + fictional control,tools/list, andserver/discoveron the apex host. No authenticated or session-state paths were exercised. - The apex SqueezeOS
tools/listworked over the legacy shape without the modern headers; with theMcp-Methodheaders it returned the era error — the modern-era tool-call path was not fully exercised, so theserver/discoverreceipt is the verified modern-era evidence, not a full modern session. - Downward negotiation being "spec-correct" applies to the pre-2026-07-28 handshake generations. Whether silently serving an older era without the client noticing is acceptable is a client-hygiene question, not a spec question — check the negotiated version, always.
- The per-call USDC prices quoted ($0.003 / $0.05 on Base) are the prices the endpoints advertise in their own
initializeandtools/listresponses on the probe date. Prices move; the endpoint is the source of truth.
ScriptMasterLabs products behind this page
- SqueezeOS MCP server — scriptmasterlabs.com/mcp — the 2026-07-28-era endpoint probed above: live tool inventory, x402 pay-per-call in USDC on Base,
server/discoverwith a real version list. - Cascade telemetry MCP gateway — cascade.scriptmasterlabs.com/v1/mcp — the era-pinned 2025-06-18 endpoint: free squeeze screens and alerts, $0.003 USDC per
get_ticker_detailexecution on Base. - Decision-gated machine payments — scriptmasterlabs.com/decision-gated-payments — the pattern this page applies: a decision model's confidence score gates whether an x402 payment fires (≥0.80 auto-pay, 0.50–0.79 confirm, <0.50 escalate).
- Live decision gate — /api/harness/decide — POST a score-type instruction and get a confidence score plus a gate band back; live thresholds at
/api/harness/status.