Align protocol error codes with MCP 2026-07-28
Contributed by pushpak1300
Part of the mcp v1.0.1 release
Before and after
In v0.9.6, a resources/read request pointing at a missing or unregistered URI produced:
{ "code": -32002, "message": "Resource [file://resources/nope] not found." }
After upgrading to 1.x (via this PR), the same request now returns:
{ "code": -32602, "message": "Resource [file://resources/nope] not found." }
The message is unchanged — only the code moved. Error codes are how MCP clients branch their behavior, so this is a breaking change for any consumer pinned to -32002.
The new ErrorCode enum
The PR also introduces a central Laravel\Mcp\Enums\ErrorCode backed enum:
enum ErrorCode: int
{
case PARSE_ERROR = -32700;
case INVALID_REQUEST = -32600;
case METHOD_NOT_FOUND = -32601;
case INVALID_PARAMS = -32602;
case INTERNAL_ERROR = -32603;
case HEADER_MISMATCH = -32020;
case MISSING_REQUIRED_CLIENT_CAPABILITY = -32021;
case UNSUPPORTED_PROTOCOL_VERSION = -32022;
}
Previously, error codes were scattered as magic integers across server method classes (you can see the raw -32002 inline in ReadResource.php before this diff). The enum gives the package a single source of truth.
Note the three new spec-reserved codes — -32020, -32021, -32022 — are defined here even though they aren't emitted yet. Later PRs in the 2026-07-28 stack will use them for header mismatch, missing client capability, and unsupported protocol version errors.
Why the spec made this change
This isn't arbitrary renumbering. The MCP 2026-07-28 specification's changelog includes:
- Minor change 6: the bespoke
-32002code is removed;resources/readfailures for unknown URIs must use the standard JSON-RPC-32602(Invalid params) instead. - Minor change 12: a code range policy, plus renumbering of the three reserved server codes to the contiguous
-32020–-32022block.
The spec forbids emitting -32002, so keeping it wasn't an option for a conformant server.
It's also a semantic improvement. Under JSON-RPC, -32602 means the client sent invalid parameters — and a URI the server can't resolve is a parameter problem. The old bespoke code made resources/read the odd one out.
What this means for your upgrade
- If you only define resources/tools and never inspect error codes: nothing to do. Your resource classes and handlers are untouched; the remapping happens internally in
ReadResource. - If you branch on error codes (in a custom client, integration test, or proxy), update any
-32002checks:
// Before
if ($error['code'] === -32002) {
// treat as "resource not found"
}
// After
if ($error['code'] === -32602) {
// invalid params — includes missing/unknown resource URIs
}
- If you throw
JsonRpcExceptionmanually in custom code, prefer the enum over a raw integer:
use Laravel\Mcp\Enums\ErrorCode;
throw new JsonRpcException(
'Resource [file://resources/nope] not found.',
ErrorCode::INVALID_PARAMS->value,
$request->id,
);
- Your own test suite may assert the old code if you copied the package's test patterns; the package's tests now assert the code explicitly.
Takeaways
resources/readwith a missing or unknown URI returns-32602(Invalid params) instead of-32002, per MCP2026-07-28.- The spec forbids emitting
-32002; the old code could not stay. - A new
ErrorCodeenum is now the canonical source for JSON-RPC codes in the package. - Spec-reserved codes
-32020,-32021,-32022are declared for upcoming PRs in the 2026-07-28 series. - Update any client logic, proxy, or tests pinned to
-32002; resource definitions need no changes.