Skip to content

Commit 4f5cb28

Browse files
committed
fix: return INVALID_PARAMS (-32602) error code for resource-not-found
Previously, when a resource was not found or failed to read, MCPServer raised ResourceError (an MCPServerError subclass) which was caught by the generic Exception handler in the low-level server and returned with error code 0. This made it difficult for clients to programmatically distinguish resource errors from other failures. The fix raises MCPError with INVALID_PARAMS (-32602) directly, which is consistent with the TypeScript SDK and the emerging spec consensus (modelcontextprotocol/modelcontextprotocol#1545). Closes #1579
1 parent 92c693b commit 4f5cb28

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

src/mcp/server/mcpserver/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
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
3534
from mcp.server.mcpserver.prompts import Prompt, PromptManager
3635
from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceManager
3736
from mcp.server.mcpserver.tools import Tool, ToolManager
@@ -44,6 +43,7 @@
4443
from mcp.server.transport_security import TransportSecuritySettings
4544
from mcp.shared.exceptions import MCPError
4645
from mcp.types import (
46+
INVALID_PARAMS,
4747
Annotations,
4848
BlobResourceContents,
4949
CallToolRequestParams,
@@ -439,15 +439,15 @@ async def read_resource(
439439
try:
440440
resource = await self._resource_manager.get_resource(uri, context)
441441
except ValueError:
442-
raise ResourceError(f"Unknown resource: {uri}")
442+
raise MCPError(code=INVALID_PARAMS, message=f"Unknown resource: {uri}")
443443

444444
try:
445445
content = await resource.read()
446446
return [ReadResourceContents(content=content, mime_type=resource.mime_type, meta=resource.meta)]
447447
except Exception as exc:
448448
logger.exception(f"Error getting resource {uri}")
449449
# If an exception happens when reading the resource, we should not leak the exception to the client.
450-
raise ResourceError(f"Error reading resource {uri}") from exc
450+
raise MCPError(code=INVALID_PARAMS, message=f"Error reading resource {uri}") from exc
451451

452452
def add_tool(
453453
self,

tests/issues/test_141_resource_templates.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from mcp import Client
44
from mcp.server.mcpserver import MCPServer
5-
from mcp.server.mcpserver.exceptions import ResourceError
5+
from mcp.shared.exceptions import MCPError
66
from mcp.types import (
77
ListResourceTemplatesResult,
88
TextResourceContents,
@@ -55,10 +55,10 @@ def get_user_profile_missing(user_id: str) -> str: # pragma: no cover
5555
assert result_list[0].mime_type == "text/plain"
5656

5757
# Verify invalid parameters raise error
58-
with pytest.raises(ResourceError, match="Unknown resource"):
58+
with pytest.raises(MCPError, match="Unknown resource"):
5959
await mcp.read_resource("resource://users/123/posts") # Missing post_id
6060

61-
with pytest.raises(ResourceError, match="Unknown resource"):
61+
with pytest.raises(MCPError, match="Unknown resource"):
6262
await mcp.read_resource("resource://users/123/posts/456/extra") # Extra path component
6363

6464

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Tests for issue #1579: FastMCP read_resource() returns incorrect error code.
2+
3+
FastMCP previously returned error code 0 for resource-not-found because
4+
ResourceError (an MCPServerError subclass) was caught by the generic
5+
Exception handler in the low-level server, which defaults to code 0.
6+
7+
The fix adds a dedicated handler for MCPServerError that maps it to
8+
INVALID_PARAMS (-32602), consistent with the TypeScript SDK and the
9+
emerging spec consensus.
10+
"""
11+
12+
import pytest
13+
14+
from mcp.client import Client
15+
from mcp.server.mcpserver import MCPServer
16+
from mcp.shared.exceptions import MCPError
17+
from mcp.types import INVALID_PARAMS
18+
19+
pytestmark = pytest.mark.anyio
20+
21+
22+
async def test_unknown_resource_returns_invalid_params_error_code():
23+
"""Reading an unknown resource returns INVALID_PARAMS (-32602), not 0."""
24+
mcp = MCPServer()
25+
26+
@mcp.resource("resource://exists")
27+
def existing_resource():
28+
return "data"
29+
30+
async with Client(mcp) as client:
31+
with pytest.raises(MCPError) as exc_info:
32+
await client.read_resource("resource://does-not-exist")
33+
34+
assert exc_info.value.code == INVALID_PARAMS
35+
assert "Unknown resource" in exc_info.value.message
36+
37+
38+
async def test_resource_read_error_returns_invalid_params_error_code():
39+
"""A resource that raises during read returns INVALID_PARAMS (-32602)."""
40+
mcp = MCPServer()
41+
42+
@mcp.resource("resource://failing")
43+
def failing_resource():
44+
raise RuntimeError("something broke")
45+
46+
async with Client(mcp) as client:
47+
with pytest.raises(MCPError) as exc_info:
48+
await client.read_resource("resource://failing")
49+
50+
assert exc_info.value.code == INVALID_PARAMS
51+
assert "Error reading resource" in exc_info.value.message

0 commit comments

Comments
 (0)