diff --git a/src/mcp/server/mcpserver/exceptions.py b/src/mcp/server/mcpserver/exceptions.py index dd1b75e82..f1cc31df5 100644 --- a/src/mcp/server/mcpserver/exceptions.py +++ b/src/mcp/server/mcpserver/exceptions.py @@ -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.""" diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index fdb69571d..907bc1714 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -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): @@ -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() diff --git a/tests/interaction/_requirements.py b/tests/interaction/_requirements.py index caed8905d..bf0bf2363 100644 --- a/tests/interaction/_requirements.py +++ b/tests/interaction/_requirements.py @@ -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 diff --git a/tests/interaction/mcpserver/test_resources.py b/tests/interaction/mcpserver/test_resources.py index 57b0fdc86..3d546bffa 100644 --- a/tests/interaction/mcpserver/test_resources.py +++ b/tests/interaction/mcpserver/test_resources.py @@ -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") @@ -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") diff --git a/tests/issues/test_141_resource_templates.py b/tests/issues/test_141_resource_templates.py index f5c5081c3..61d1a30b3 100644 --- a/tests/issues/test_141_resource_templates.py +++ b/tests/issues/test_141_resource_templates.py @@ -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 diff --git a/tests/server/mcpserver/test_server.py b/tests/server/mcpserver/test_server.py index 21352b5f2..00d4ec777 100644 --- a/tests/server/mcpserver/test_server.py +++ b/tests/server/mcpserver/test_server.py @@ -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):