Troubleshooting
Why is my MCP server broken after the spec update?
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
initialize requirement, missing required headers, a missing resultType, missing caching metadata, a retired error code, or a method that no longer exists. Find your symptom below.Check this first
Before reading further, send one request. This single test separates the most common failure from all the others:
curl -i https://your-server.example.com/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'- A tool list comes back — your server is at least stateless. Skip to the header and result-shape sections.
- An error about sessions or initialization — go to the next section; this is your problem.
- A 400 — likely header validation; see HeaderMismatch.
“Server requires initialization” / “no active session”
MCP-STL-003{
"jsonrpc": "2.0", "id": 1,
"error": { "code": -32002, "message": "Server not initialized" }
}
// or, from the transport layer:
HTTP/1.1 400 Bad Request
Mcp-Session-Id header is requiredThis is the number-one cause. Your server still implements the legacy handshake, and a modern client never sends one. The revision removed sessions from the transport outright:
“Make MCP stateless: remove the initialize/notifications/initialized handshake.”
The fix
params._meta on each request instead. Do not simply make initialize optional — a server that still mints session IDs fails MCP-STL-001 even if it also accepts cold requests. Full walkthrough: statelessness.HTTP 400 with error -32020 HeaderMismatch
MCP-HDR-003HTTP/1.1 400 Bad Request
{
"jsonrpc": "2.0", "id": 2,
"error": {
"code": -32020,
"message": "Header mismatch: Mcp-Method does not match body method"
}
}Three headers became required, and each must agree with the request body. You will see this from a server you are calling when your client sets a header inconsistent with what it sends — a common outcome of adding Mcp-Method in a shared wrapper that does not know which method the caller ultimately used.
“Servers that process the request body MUST reject requests where the values specified in the headers do not match the corresponding values in the request body.”
The fix
MCP-Protocol-Version must equal _meta's io.modelcontextprotocol/protocolVersion, Mcp-Method must equal method, and Mcp-Name must equal params.name or params.uri on tools/call, resources/read and prompts/get. Details: the Mcp-Method header.-32020 but returns -32601 Method not found, your header validation runs after method dispatch. Move it before.Client rejects a valid-looking response / “invalid result”
MCP-RES-001Your server returns what looks like a correct result and the client discards it. Every result now needs a resultType discriminator:
“The result MUST include a resultType field to indicate the type of the result.”
“A resultType of any value unrecognized by the client MUST be considered invalid.”
// Rejected
{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ … ] } }
// Accepted
{ "jsonrpc": "2.0", "id": 1,
"result": { "resultType": "complete", "tools": [ … ] } }The fix
resultType: "complete" to every ordinary result — one line in whatever wraps your responses. Use "input_required" only for an MRTR interim result. This is purely additive: clients treat an absent resultType from an earlier-revision server as "complete", so adding it cannot break older clients.Validator reports missing ttlMs or cacheScope
MCP-CAC-001Nothing visibly breaks here — clients still work — which is why this one survives long after the others are fixed. It is still a MUST-level failure on five methods.
“Require ttlMs and cacheScope fields on results returned by tools/list, prompts/list, resources/list, resources/read, and resources/templates/list via a new CacheableResult interface.”
The fix
tools/list, prompts/list, resources/list, resources/read and resources/templates/list. If you are unsure of a value, ttlMs: 0 with cacheScope: "private" is conformant and honest. Never mark a per-user list "public" — see caching metadata.Method not found (-32601) for ping, logging/setLevel or resources/subscribe
MCP-DEP-001{
"jsonrpc": "2.0", "id": 3,
"error": { "code": -32601, "message": "Method not found: ping" }
}These methods were removed, not renamed. If your client calls them — often in a health check or a keepalive you forgot was there — it will now fail against a conformant server.
“Remove ping, logging/setLevel, and notifications/roots/list_changed.”
“Log level is now set per-request via io.modelcontextprotocol/logLevel in _meta; servers MUST NOT emit notifications/message for requests that did not include this field.”
“Replace the HTTP GET endpoint and resources/subscribe/resources/unsubscribe with subscriptions/listen: a single long-lived POST-response stream for opted-in server-to-client change notifications.”
The fix
ping keepalives — a stateless protocol has no connection to keep alive. Move log level to io.modelcontextprotocol/logLevel in each request's _meta. Replace resources/subscribe with a single subscriptions/listen stream. Note that logging/setLevel going away also means your server MUST NOT emit notifications/message for requests that did not ask for logging.Validator flags an error code you have always used
MCP-ERR-003The server-error range was partitioned in this revision, which retroactively made some perfectly reasonable choices non-conformant.
“-32002 — resource not found (2025-11-25 and earlier; replaced by -32602). Implementations of this protocol version MUST NOT emit these codes.”
“Implementations MUST NOT emit any code from this sub-range that is not defined by this specification and MUST use defined codes only with their specified meanings.”
The fix
-32002 to -32602. Stop emitting -32042. Move any code you invented out of -32020–-32099 — that range now belongs to the specification, so a number that was free when you picked it may collide with a future one. New implementation-defined codes should also avoid -32000–-32019.Which of these is most common?
What we can say without inventing anything is structural: MCP-STL-003 is listed first because a server that fails it cannot be meaningfully tested for anything else — the validator never obtains a successful response to inspect, so every other check is inconclusive. That ordering is a property of the checks, not a claim about frequency.
Get the specific rule instead of the general direction
Paste your server URL and the validator returns exactly which of the 79 rules fail, each with the verbatim spec sentence behind it — which is faster than working down this page.
Validate a server →Frequently asked
Why did my MCP server stop working after the spec update?
Because 2026-07-28 is a breaking revision. The most common single cause is that the server still requires an initialize handshake: a modern client sends its first real request cold, with no handshake, and a server built against 2025-11-25 rejects it. After that, the usual causes are missing Mcp-Method or Mcp-Name headers, a missing resultType field, and missing ttlMs and cacheScope on list responses.
My client says the server requires initialization. What does that mean?
It means your server is still running legacy session semantics. The 2026-07-28 revision removed the initialize handshake entirely, so a modern client never sends one. Remove the requirement that a session exists before other methods are accepted, and read protocol version, client info and capabilities from each request's params._meta instead.
What is error -32020 and why am I suddenly seeing it?
-32020 is HeaderMismatch, added in 2026-07-28. You see it when the Mcp-Method, Mcp-Name or MCP-Protocol-Version header on a request does not match the corresponding value in the request body. If you are receiving it from a server you call, your client is setting a header inconsistent with what it is sending; if your own server should be emitting it but returns -32601 instead, your header validation is running after method dispatch rather than before.
My server works with some clients but not others. Why?
Almost always because the failing clients are modern and the working ones are legacy, or the reverse. The two eras are mutually unintelligible: a modern client sends per-request _meta and required headers with no handshake, while a legacy client opens with initialize and expects a session ID back. A dual-era server supports both by branching on how the request arrives.
Do I have to rewrite my MCP server for 2026-07-28?
No. For most servers this is a set of mechanical edits rather than a rewrite: delete the session store and handshake, add three headers and validate them against the body, add resultType to every result, and add ttlMs and cacheScope to five list and read responses. The architectural change worth planning for is MRTR, which only affects servers that previously sent their own requests to the client.
How do I find out exactly what is wrong with my server?
Run it through a validator rather than reading the changelog and guessing. createmcps.com checks a live endpoint against 79 rules derived from the 2026-07-28 specification and reports each as pass, warn or fail with the exact spec sentence behind it, so you get the specific rule ID rather than a general direction.