Spec explainer
MCP error codes in 2026-07-28: -32020, -32021, -32022
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
-32020 HeaderMismatch, -32021 MissingRequiredClientCapability and -32022 UnsupportedProtocolVersion. Two were retired — -32002 and -32042 — and must not be emitted.Every code at a glance
| Code | Name | HTTP | When | Status |
|---|---|---|---|---|
| -32020 | HeaderMismatch | 400 | A header value contradicts the request body | new |
| -32021 | MissingRequiredClientCapability | 400 | The request needs a capability the client did not declare | new |
| -32022 | UnsupportedProtocolVersion | 400 | The requested protocol version is not implemented | new |
| -32601 | Method not found | 404 | The RPC method is not implemented | kept |
| -32602 | Invalid params | 400 | Malformed params — including resource-not-found | kept |
| -32002 | Resource not found | — | Replaced by -32602 | retired |
| -32042 | URL elicitation required | — | 2025-11-25 only | retired |
The partitioned range
-32000 … -32019 legacy sub-range
→ new codes MUST NOT be allocated here
→ new implementations SHOULD NOT use it at all
-32020 … -32099 reserved by the MCP specification
→ only spec-defined codes, only with their
specified meanings“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.”
“New codes MUST NOT be allocated in this sub-range, and new implementations SHOULD NOT use codes from this sub-range at all.”
This is the change most likely to make previously-correct code non-conformant. Picking a free number in the -32020+ band for your own error was reasonable before this revision; afterwards that band belongs to the spec, and a number that is free today may be defined tomorrow.
The three new codes
“If the server does not implement the requested version, it MUST respond with an UnsupportedProtocolVersionError listing the versions it does support.”
-32022 is the one that makes version negotiation work without a handshake. Because there is no initialize exchange to agree a version in, the error itself has to carry the list — otherwise a client whose version is refused has nothing to try next.
HTTP/1.1 400 Bad Request
{
"jsonrpc": "2.0", "id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": { "supported": ["2026-07-28"] }
}
}“If processing a request requires a capability the client did not include, the server MUST return a MissingRequiredClientCapabilityError whose data.requiredCapabilities lists the missing capabilities.”
-32021 follows the same principle: name what was missing, in data.requiredCapabilities, so the client can act rather than guess. -32020 HeaderMismatch is covered in depth on the Mcp-Method page.
data. If you add your own errors, copy that.The two retired codes
“-32002 — resource not found (2025-11-25 and earlier; replaced by -32602). Implementations of this protocol version MUST NOT emit these codes.”
“-32042 — URL elicitation required (2025-11-25 only).”
// Before
{ "error": { "code": -32002, "message": "Resource not found" } }
// After — resource-not-found is a params problem
{ "error": { "code": -32602, "message": "Resource not found: file:///…" } }MUST NOT emit is stronger than deprecated. A server still returning -32002 is non-conformant even though the number is not reused, because a modern client has no definition for it.
Pairing the JSON-RPC code with an HTTP status
The revision is specific about this in two places, and both are easy to miss if you treat JSON-RPC as transport-agnostic:
“If the server does not implement the requested RPC method, it MUST respond with 404 Not Found and a JSON-RPC error with code -32601 (Method not found).”
“A request missing any required field is malformed; the server MUST reject it with JSON-RPC error code -32602 (Invalid params). On HTTP, the response status MUST be 400 Bad Request.”
200 carrying -32601 is a finding, not a stylistic choice — every intermediary between you and the client reads the status, and none of them parse your body.Defining your own error codes
- Not in
-32020–-32099. Reserved by the specification. - Not in
-32000–-32019. Legacy;SHOULD NOTbe used by new implementations at all. - Carry recovery data. Follow the
data.supported/data.requiredCapabilitiespattern rather than relying on a human readingmessage.
The 8 error-code rules we check
| Rule | What we check | Level | If it fails |
|---|---|---|---|
| MCP-ERR-001 | Unsupported version returns -32022 with data.supported[] | MUST | fail |
| MCP-ERR-002 | Unknown method returns HTTP 404 and JSON-RPC -32601 | MUST | fail |
| MCP-ERR-003 | Resource-not-found uses -32602, not the retired -32002 | MUST_NOT | fail |
| MCP-ERR-004 | The retired -32042 code is never emitted | MUST_NOT | fail |
| MCP-ERR-005 | No undefined code is emitted in the reserved -32020…-32099 range | MUST_NOT | fail |
| MCP-ERR-006 | Missing required _meta field returns HTTP 400 + JSON-RPC -32602 | MUST | fail |
| MCP-ERR-007 | Missing client capability returns -32021 with data.requiredCapabilities | MUST | fail |
| MCP-ERR-008 | New implementation-defined codes avoid the legacy -32000…-32019 range | SHOULD_NOT | warn |
All eight carry a verbatim spec sentence.
Check your error codes
The validator deliberately sends unknown methods, unsupported versions and malformed requests, then checks both the JSON-RPC code and the HTTP status you answer with.
Validate a server →Frequently asked
What is MCP error -32020?
HeaderMismatch, added in 2026-07-28. A server returns it with HTTP 400 when the Mcp-Method, Mcp-Name or MCP-Protocol-Version header on a request does not match the corresponding value in the request body. It exists because those headers are routing metadata that intermediaries act on, so a header allowed to disagree with the body would let policy be bypassed.
What is error -32022 in MCP?
UnsupportedProtocolVersion. If a server does not implement the protocol version a client requested, it must respond with this code and list the versions it does support in the error's data.supported field, so the client can retry rather than guess.
Why can't I use -32002 any more?
It was retired in 2026-07-28 and replaced by -32602 (Invalid params) for resource-not-found. The spec states that implementations of this revision must not emit it. Servers built against 2025-11-25 or earlier commonly still do, which is why it's one of the more frequent findings when an older server is first validated.
Which error codes can I define myself in MCP?
Not -32020 to -32099, which the specification reserves for itself, and preferably not -32000 to -32019, which is the legacy sub-range that new implementations should avoid entirely. That leaves codes outside the JSON-RPC server-error band for implementation-defined use.
What HTTP status should an MCP error use?
It depends on the error. An unknown method is HTTP 404 alongside JSON-RPC -32601. A malformed request — including a missing required _meta field — is HTTP 400 alongside -32602. A header mismatch is HTTP 400 with -32020. Returning HTTP 200 with a JSON-RPC error in the body is not conformant for these cases.
What is -32021 MissingRequiredClientCapability?
The code a server returns when processing a request requires a capability the client did not include in its per-request capabilities. The error's data.requiredCapabilities must list what was missing, so the client can decide whether it can retry with them declared.