Skip to content

Commit d065e9b

Browse files
committed
fix(server): return -32602 for resource not found (SEP-2164)
The SDK previously returned error code 0 for resource-not-found, making it impossible for clients to distinguish this from other errors. Per SEP-2164, servers should return -32602 (Invalid Params) with the uri in the error data field. Adds ResourceNotFoundError subclass of ResourceError so existing code catching ResourceError continues to work. The internal handler converts it to MCPError with INVALID_PARAMS before it reaches the JSON-RPC layer. Spec: modelcontextprotocol/modelcontextprotocol#2164
1 parent 7ba4fb8 commit d065e9b

File tree

3 files changed

+17
-5
lines changed

3 files changed

+17
-5
lines changed

src/mcp/server/mcpserver/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class ResourceError(MCPServerError):
1313
"""Error in resource operations."""
1414

1515

16+
class ResourceNotFoundError(ResourceError):
17+
"""Resource does not exist."""
18+
19+
1620
class ToolError(MCPServerError):
1721
"""Error in tool operations."""
1822

src/mcp/server/mcpserver/server.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from mcp.server.lowlevel.server import LifespanResultT, Server
3232
from mcp.server.lowlevel.server import lifespan as default_lifespan
3333
from mcp.server.mcpserver.context import Context
34-
from mcp.server.mcpserver.exceptions import ResourceError
34+
from mcp.server.mcpserver.exceptions import ResourceError, ResourceNotFoundError
3535
from mcp.server.mcpserver.prompts import Prompt, PromptManager
3636
from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceManager
3737
from mcp.server.mcpserver.tools import Tool, ToolManager
@@ -44,6 +44,7 @@
4444
from mcp.server.transport_security import TransportSecuritySettings
4545
from mcp.shared.exceptions import MCPError
4646
from mcp.types import (
47+
INVALID_PARAMS,
4748
Annotations,
4849
BlobResourceContents,
4950
CallToolRequestParams,
@@ -332,7 +333,10 @@ async def _handle_read_resource(
332333
self, ctx: ServerRequestContext[LifespanResultT], params: ReadResourceRequestParams
333334
) -> ReadResourceResult:
334335
context = Context(request_context=ctx, mcp_server=self)
335-
results = await self.read_resource(params.uri, context)
336+
try:
337+
results = await self.read_resource(params.uri, context)
338+
except ResourceNotFoundError as err:
339+
raise MCPError(code=INVALID_PARAMS, message=str(err), data={"uri": str(params.uri)})
336340
contents: list[TextResourceContents | BlobResourceContents] = []
337341
for item in results:
338342
if isinstance(item.content, bytes):
@@ -439,7 +443,7 @@ async def read_resource(
439443
try:
440444
resource = await self._resource_manager.get_resource(uri, context)
441445
except ValueError:
442-
raise ResourceError(f"Unknown resource: {uri}")
446+
raise ResourceNotFoundError(f"Unknown resource: {uri}")
443447

444448
try:
445449
content = await resource.read()

tests/server/mcpserver/test_server.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from mcp.server.transport_security import TransportSecuritySettings
2121
from mcp.shared.exceptions import MCPError
2222
from mcp.types import (
23+
INVALID_PARAMS,
2324
AudioContent,
2425
BlobResourceContents,
2526
Completion,
@@ -692,13 +693,16 @@ def get_text():
692693
assert result.contents[0].text == "Hello, world!"
693694

694695
async def test_read_unknown_resource(self):
695-
"""Test that reading an unknown resource raises MCPError."""
696+
"""Test that reading an unknown resource returns -32602 with uri in data (SEP-2164)."""
696697
mcp = MCPServer()
697698

698699
async with Client(mcp) as client:
699-
with pytest.raises(MCPError, match="Unknown resource: unknown://missing"):
700+
with pytest.raises(MCPError, match="Unknown resource: unknown://missing") as exc_info:
700701
await client.read_resource("unknown://missing")
701702

703+
assert exc_info.value.error.code == INVALID_PARAMS
704+
assert exc_info.value.error.data == {"uri": "unknown://missing"}
705+
702706
async def test_read_resource_error(self):
703707
"""Test that resource read errors are properly wrapped in MCPError."""
704708
mcp = MCPServer()

0 commit comments

Comments
 (0)