Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/mcp/server/mcpserver/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class ValidationError(MCPServerError):
class ResourceError(MCPServerError):
"""Error in resource operations."""

def __init__(self, message: str, code: int | None = None):
super().__init__(message)
self.code = code


class ToolError(MCPServerError):
"""Error in tool operations."""
Expand Down
10 changes: 8 additions & 2 deletions src/mcp/server/mcpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,13 @@ async def _handle_read_resource(
self, ctx: ServerRequestContext[LifespanResultT], params: ReadResourceRequestParams
) -> ReadResourceResult:
context = Context(request_context=ctx, mcp_server=self)
results = await self.read_resource(params.uri, context)
try:
results = await self.read_resource(params.uri, context)
except ResourceError as exc:
if exc.code is not None:
message = f"Resource not found: {params.uri}" if exc.code == -32002 else str(exc)
raise MCPError(code=exc.code, message=message) from exc
raise
contents: list[TextResourceContents | BlobResourceContents] = []
for item in results:
if isinstance(item.content, bytes):
Expand Down Expand Up @@ -448,7 +454,7 @@ async def read_resource(
try:
resource = await self._resource_manager.get_resource(uri, context)
except ValueError as exc:
raise ResourceError(f"Unknown resource: {uri}") from exc
raise ResourceError(f"Resource not found: {uri}", code=-32002) from exc

try:
content = await resource.read()
Expand Down
6 changes: 0 additions & 6 deletions tests/interaction/_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,12 +901,6 @@ def __post_init__(self) -> None:
"mcpserver:resource:unknown-uri": Requirement(
source=f"{SPEC_BASE_URL}/server/resources#error-handling",
behavior="resources/read for a URI matching no registered resource returns JSON-RPC error -32002.",
divergence=Divergence(
note=(
"The spec reserves -32002 for resource-not-found; MCPServer raises ResourceError, which "
"the low-level server converts to error code 0."
),
),
),
# ═══════════════════════════════════════════════════════════════════════════
# Prompts
Expand Down
4 changes: 2 additions & 2 deletions tests/interaction/mcpserver/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def user_profile(user_id: str) -> str:
async def test_read_unknown_uri_is_error(connect: Connect) -> None:
"""Reading a URI that matches no registered resource fails with a JSON-RPC error.

The spec reserves -32002 for resource-not-found; see the divergence note on the requirement.
The spec reserves -32002 for resource-not-found.
"""
mcp = MCPServer("library")

Expand All @@ -127,7 +127,7 @@ def app_config() -> str:
with pytest.raises(MCPError) as exc_info:
await client.read_resource("config://missing")

assert exc_info.value.error == snapshot(ErrorData(code=0, message="Unknown resource: config://missing"))
assert exc_info.value.error == snapshot(ErrorData(code=-32002, message="Resource not found: config://missing"))


@requirement("mcpserver:resource:read-throws-surfaced")
Expand Down
4 changes: 2 additions & 2 deletions tests/issues/test_141_resource_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ def get_user_profile_missing(user_id: str) -> str: # pragma: no cover
assert result_list[0].mime_type == "text/plain"

# Verify invalid parameters raise error
with pytest.raises(ResourceError, match="Unknown resource"):
with pytest.raises(ResourceError, match="Resource not found"):
await mcp.read_resource("resource://users/123/posts") # Missing post_id

with pytest.raises(ResourceError, match="Unknown resource"):
with pytest.raises(ResourceError, match="Resource not found"):
await mcp.read_resource("resource://users/123/posts/456/extra") # Extra path component


Expand Down
2 changes: 1 addition & 1 deletion tests/server/mcpserver/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ async def test_read_unknown_resource(self):
mcp = MCPServer()

async with Client(mcp) as client:
with pytest.raises(MCPError, match="Unknown resource: unknown://missing"):
with pytest.raises(MCPError, match="Resource not found: unknown://missing"):
await client.read_resource("unknown://missing")

async def test_read_resource_error(self):
Expand Down
Loading