Skip to content

Commit 6bc310e

Browse files
committed
chore(server): address review nits on resource error handling
- Document Raises: on ResourceManager.get_resource() (AGENTS.md docstring rule) - Log template-handler crashes via logger.exception() and chain from e, so operators get a server-side traceback (matches the resource.read() path) - Move the two new SEP-2164 regression tests to top-level functions per AGENTS.md (no Test* classes for new tests)
1 parent 0172696 commit 6bc310e

3 files changed

Lines changed: 43 additions & 32 deletions

File tree

src/mcp/server/mcpserver/resources/resource_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ def add_template(
8080
return template
8181

8282
async def get_resource(self, uri: AnyUrl | str, context: Context[LifespanContextT, RequestT]) -> Resource:
83-
"""Get resource by URI, checking concrete resources first, then templates."""
83+
"""Get resource by URI, checking concrete resources first, then templates.
84+
85+
Raises:
86+
ResourceNotFoundError: If no resource or template matches the URI.
87+
ResourceError: If a matching template fails to create the resource.
88+
"""
8489
uri_str = str(uri)
8590
logger.debug("Getting resource", extra={"uri": uri_str})
8691

src/mcp/server/mcpserver/resources/templates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
from mcp.server.mcpserver.resources.types import FunctionResource, Resource
1616
from mcp.server.mcpserver.utilities.context_injection import find_context_parameter, inject_context
1717
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
18+
from mcp.server.mcpserver.utilities.logging import get_logger
1819
from mcp.shared._callable_inspection import is_async_callable
1920
from mcp.types import Annotations, Icon
2021

22+
logger = get_logger(__name__)
23+
2124
if TYPE_CHECKING:
2225
from mcp.server.context import LifespanContextT, RequestT
2326
from mcp.server.mcpserver.context import Context
@@ -133,4 +136,5 @@ async def create_resource(
133136
except ResourceError:
134137
raise
135138
except Exception as e:
136-
raise ResourceError(f"Error creating resource from template: {e}")
139+
logger.exception("Error creating resource from template")
140+
raise ResourceError(f"Error creating resource from template: {e}") from e

tests/server/mcpserver/test_server.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -751,36 +751,6 @@ def failing_resource():
751751
with pytest.raises(MCPError, match="Error reading resource resource://failing"):
752752
await client.read_resource("resource://failing")
753753

754-
async def test_read_resource_template_error(self):
755-
"""Template-creation failure must surface as INTERNAL_ERROR, not INVALID_PARAMS (not-found)."""
756-
mcp = MCPServer()
757-
758-
@mcp.resource("resource://item/{item_id}")
759-
def get_item(item_id: str) -> str:
760-
raise RuntimeError("backend unavailable")
761-
762-
async with Client(mcp) as client:
763-
with pytest.raises(MCPError, match="Error creating resource from template") as exc_info:
764-
await client.read_resource("resource://item/42")
765-
766-
assert exc_info.value.error.code == INTERNAL_ERROR
767-
assert exc_info.value.error.code != INVALID_PARAMS
768-
769-
async def test_read_resource_template_not_found(self):
770-
"""A template handler raising ResourceNotFoundError must surface as INVALID_PARAMS per SEP-2164."""
771-
mcp = MCPServer()
772-
773-
@mcp.resource("resource://users/{user_id}")
774-
def get_user(user_id: str) -> str:
775-
raise ResourceNotFoundError(f"no user {user_id}")
776-
777-
async with Client(mcp) as client:
778-
with pytest.raises(MCPError, match="no user 999") as exc_info:
779-
await client.read_resource("resource://users/999")
780-
781-
assert exc_info.value.error.code == INVALID_PARAMS
782-
assert exc_info.value.error.data == {"uri": "resource://users/999"}
783-
784754
async def test_binary_resource(self):
785755
mcp = MCPServer()
786756

@@ -1551,3 +1521,35 @@ async def test_report_progress_passes_related_request_id():
15511521
message="halfway",
15521522
related_request_id="req-abc-123",
15531523
)
1524+
1525+
1526+
async def test_read_resource_template_error():
1527+
"""Template-creation failure must surface as INTERNAL_ERROR, not INVALID_PARAMS (not-found)."""
1528+
mcp = MCPServer()
1529+
1530+
@mcp.resource("resource://item/{item_id}")
1531+
def get_item(item_id: str) -> str:
1532+
raise RuntimeError("backend unavailable")
1533+
1534+
async with Client(mcp) as client:
1535+
with pytest.raises(MCPError, match="Error creating resource from template") as exc_info:
1536+
await client.read_resource("resource://item/42")
1537+
1538+
assert exc_info.value.error.code == INTERNAL_ERROR
1539+
assert exc_info.value.error.code != INVALID_PARAMS
1540+
1541+
1542+
async def test_read_resource_template_not_found():
1543+
"""A template handler raising ResourceNotFoundError must surface as INVALID_PARAMS per SEP-2164."""
1544+
mcp = MCPServer()
1545+
1546+
@mcp.resource("resource://users/{user_id}")
1547+
def get_user(user_id: str) -> str:
1548+
raise ResourceNotFoundError(f"no user {user_id}")
1549+
1550+
async with Client(mcp) as client:
1551+
with pytest.raises(MCPError, match="no user 999") as exc_info:
1552+
await client.read_resource("resource://users/999")
1553+
1554+
assert exc_info.value.error.code == INVALID_PARAMS
1555+
assert exc_info.value.error.data == {"uri": "resource://users/999"}

0 commit comments

Comments
 (0)