Skip to content
Closed
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
6 changes: 3 additions & 3 deletions src/mcp/server/mcpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from mcp.server.lowlevel.server import LifespanResultT, Server
from mcp.server.lowlevel.server import lifespan as default_lifespan
from mcp.server.mcpserver.context import Context
from mcp.server.mcpserver.exceptions import ResourceError
from mcp.server.mcpserver.prompts import Prompt, PromptManager
from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceManager
from mcp.server.mcpserver.tools import Tool, ToolManager
Expand All @@ -44,6 +43,7 @@
from mcp.server.transport_security import TransportSecuritySettings
from mcp.shared.exceptions import MCPError
from mcp.types import (
INVALID_PARAMS,
Annotations,
BlobResourceContents,
CallToolRequestParams,
Expand Down Expand Up @@ -439,15 +439,15 @@ async def read_resource(
try:
resource = await self._resource_manager.get_resource(uri, context)
except ValueError:
raise ResourceError(f"Unknown resource: {uri}")
raise MCPError(code=INVALID_PARAMS, message=f"Unknown resource: {uri}")

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

def add_tool(
self,
Expand Down
6 changes: 3 additions & 3 deletions tests/issues/test_141_resource_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from mcp import Client
from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.exceptions import ResourceError
from mcp.shared.exceptions import MCPError
from mcp.types import (
ListResourceTemplatesResult,
TextResourceContents,
Expand Down 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(MCPError, match="Unknown resource"):
await mcp.read_resource("resource://users/123/posts") # Missing post_id

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


Expand Down
47 changes: 47 additions & 0 deletions tests/issues/test_1579_resource_error_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Tests for issue #1579: FastMCP read_resource() returns incorrect error code.

FastMCP previously returned error code 0 for resource-not-found because
ResourceError (an MCPServerError subclass) was caught by the generic
Exception handler in the low-level server, which defaults to code 0.

The fix adds a dedicated handler for MCPServerError that maps it to
INVALID_PARAMS (-32602), consistent with the TypeScript SDK and the
emerging spec consensus.
"""

import pytest

from mcp.client import Client
from mcp.server.mcpserver import MCPServer
from mcp.shared.exceptions import MCPError
from mcp.types import INVALID_PARAMS

pytestmark = pytest.mark.anyio


async def test_unknown_resource_returns_invalid_params_error_code():
"""Reading an unknown resource returns INVALID_PARAMS (-32602), not 0."""
mcp = MCPServer()

async with Client(mcp) as client:
with pytest.raises(MCPError) as exc_info:
await client.read_resource("resource://does-not-exist")

assert exc_info.value.code == INVALID_PARAMS
assert "Unknown resource" in exc_info.value.message


async def test_resource_read_error_returns_invalid_params_error_code():
"""A resource that raises during read returns INVALID_PARAMS (-32602)."""
mcp = MCPServer()

@mcp.resource("resource://failing")
def failing_resource():
raise RuntimeError("something broke")

async with Client(mcp) as client:
with pytest.raises(MCPError) as exc_info:
await client.read_resource("resource://failing")

assert exc_info.value.code == INVALID_PARAMS
assert "Error reading resource" in exc_info.value.message
Loading