Skip to content
Merged
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@
"group": "Draft",
"pages": [
"seps/2106-json-schema-2020-12",
"seps/2164-resource-not-found-error",
"seps/2484-conformance-tests-required-for-final-seps"
]
}
Expand Down
102 changes: 102 additions & 0 deletions docs/seps/2164-resource-not-found-error.mdx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion docs/seps/index.mdx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/specification/draft/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ the previous revision, [2025-11-25](/specification/2025-11-25).
3. Servers **SHOULD** return tools from `tools/list` in a deterministic order to enable client-side caching and improve LLM prompt cache hit rates.
4. Require standard MCP request headers (`Mcp-Method`, `Mcp-Name`) on Streamable HTTP POST requests, and add support for custom headers from tool parameters via `x-mcp-header` ([SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)).
5. Add `ttlMs` and `cacheScope` fields to results returned by `tools/list`, `prompts/list`, `resources/list`, `resources/read`, and `resources/templates/list` via a new `CacheableResult` interface. `ttlMs` is a freshness hint (in milliseconds) allowing clients to cache responses and reduce polling; `cacheScope` (`"public"` or `"private"`) controls whether shared intermediaries may cache the response. Both fields complement existing `listChanged` notifications ([SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)).
6. Change resource not found error code from `-32002` to `-32602` (Invalid Params) to align with JSON-RPC specification.

## Other schema changes

Expand Down
9 changes: 7 additions & 2 deletions docs/specification/draft/server/resources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -378,17 +378,22 @@ taking the above guidance in to account.

Servers **SHOULD** return standard JSON-RPC errors for common failure cases:

- Resource not found: `-32002`
- Resource not found: `-32602` (Invalid Params)
- Internal errors: `-32603`

For backwards compatibility, clients **SHOULD** also accept `-32002` as a
resource not found error, as earlier protocol versions used this code.

Servers **MUST NOT** return an empty `contents` array for a non-existent resource. An empty array is ambiguous—it could mean the resource exists but has no content, or that it doesn't exist at all.

Example error:

```json
{
"jsonrpc": "2.0",
"id": 5,
"error": {
"code": -32002,
"code": -32602,
"message": "Resource not found",
"data": {
"uri": "file:///nonexistent.txt"
Expand Down
83 changes: 83 additions & 0 deletions seps/2164-resource-not-found-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# SEP-2164: Standardize Resource Not Found Error Code

- **Status**: Draft
- **Type**: Standards Track
- **Created**: 2026-01-28
- **Author(s)**: Peter Alexander (@pja-ant)
- **Sponsor**: None (seeking sponsor)
- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2164

## Abstract

The current MCP specification [recommends `-32002`](https://modelcontextprotocol.io/specification/draft/server/resources#error-handling) as the error code for resource not found. However, `-32002` falls within the JSON-RPC "server error" range (`-32000` to `-32099`) which is reserved for implementation-defined errors, not protocol-level semantics. Additionally, SDK implementations are inconsistent — only 4 of 6 official SDKs use `-32002`, while the TypeScript SDK uses `-32602` and the Python SDK uses `0`.

This SEP standardizes on `-32602` (Invalid Params), the correct JSON-RPC error code for this case, and aligns the specification with the JSON-RPC standard.

## Motivation

Current SDK implementations vary in their error handling for resource not found:

| SDK | Current Error Code | Source |
| ---------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TypeScript | `-32602` (InvalidParams) | [mcp.ts#L561](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/server/src/server/mcp.ts#L561) |
| Python | `0` (generic) | [server.py#L790](https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/server/lowlevel/server.py#L790) |
| C# | `-32002` (custom RESOURCE_NOT_FOUND) | [McpServerImpl.cs#L289](https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.Core/Server/McpServerImpl.cs#L289) |
| Rust | `-32002` (custom RESOURCE_NOT_FOUND) | [model.rs#L450](https://github.com/modelcontextprotocol/rust-sdk/blob/main/crates/rmcp/src/model.rs#L450) |
| Java | `-32002` (custom RESOURCE_NOT_FOUND) | [McpAsyncServer.java#L732](https://github.com/modelcontextprotocol/java-sdk/blob/main/mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java#L732) |
| Go | `-32002` (custom RESOURCE_NOT_FOUND) | [server.go#L786](https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/server.go#L786) |
| Kotlin | `-32603` (INTERNAL_ERROR) | [Server.kt#L618-L621](https://github.com/modelcontextprotocol/kotlin-sdk/blob/main/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt#L618-L621) |
| PHP | `-32002` (custom RESOURCE_NOT_FOUND) | [Error.php#L37](https://github.com/modelcontextprotocol/php-sdk/blob/main/src/Schema/JsonRpc/Error.php#L37) |
| Ruby | N/A (left to implementor) | [server.rb#L375-L379](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/lib/mcp/server.rb#L375-L379) |
| Swift | N/A (no built-in handler) | N/A |

This inconsistency means clients cannot reliably detect resource-not-found conditions across implementations. Of the 8 SDKs with built-in resource handling, four different error codes are used: `-32002` (C#, Rust, Java, Go, PHP), `-32602` (TypeScript), `-32603` (Kotlin), and `0` (Python). Ruby and Swift leave error handling to the server implementor. Clients that need to distinguish "resource not found" from other errors must handle all variants.

## Specification

If the requested resource does not exist, servers MUST return a JSON-RPC error with code `-32602` (Invalid Params):

```json
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32602,
"message": "Resource not found",
"data": {
"uri": "file:///nonexistent.txt"
}
}
}
```

The `data` field SHOULD include the `uri` that was not found.

Servers MUST NOT return an empty `contents` array for a non-existent resource. An empty array is ambiguous — it could mean the resource exists but has no content, or that it doesn't exist at all.

## Rationale

### Why `-32602` (Invalid Params)?

`-32602` is the standard JSON-RPC error code for invalid parameters. A non-existent URI is semantically an invalid parameter — the client provided a URI that doesn't correspond to any resource. This aligns with the TypeScript SDK's existing behavior and avoids introducing custom error codes outside the JSON-RPC reserved range.

### Why Not a Custom Error Code?

Several SDKs use `-32002` (RESOURCE_NOT_FOUND), but:

- Custom codes in the `-32000` to `-32099` range are "reserved for implementation-defined server errors" per JSON-RPC spec, not for protocol-level semantics
- Adding a protocol-defined custom code requires all clients to be updated to recognize it
- `-32602` already has the correct meaning and is universally understood by JSON-RPC libraries

## Backward Compatibility

This changes what is specified — the current spec recommends `-32002`, and this SEP changes it to `-32602`. However, since the current recommendation is not consistently followed across SDKs (only 5 of 10 use `-32002`), clients cannot rely on any single error code today. This means the practical impact on clients is minimal — any client robust enough to work across existing SDKs already handles multiple error codes or treats all errors generically.

### Migration Path

1. SDKs should update their resource-not-found error code to `-32602`
2. During the transition, clients SHOULD handle both `-32602` and `-32002` as resource-not-found
3. The specification should document `-32602` as the canonical error code

## Security Implications

None. This change only affects error code values, not access control or data exposure.
Loading