Skip to content

Commit 1a53bc7

Browse files
committed
fix: log exceptions in _handle_call_tool at ERROR level
Resource and prompt handlers already call logger.exception() when an error occurs. Tool calls were silently swallowing exceptions into CallToolResult(is_error=True) with no server-side log, making it impossible for operators to diagnose tool failures without client-side visibility. Adds logger.exception() before the is_error return in _handle_call_tool, matching the pattern used in _handle_read_resource and _handle_get_prompt. Fixes #3266 Signed-off-by: Radhakrishnan Panchayappan <gingeekrishna@gmail.com> Signed-off-by: Radhakrishnan Pachyappan <gingeekrishna@gmail.com>
1 parent a4f4ccd commit 1a53bc7

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

src/mcp/server/mcpserver/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ async def _handle_call_tool(
421421
except MCPError:
422422
raise
423423
except Exception as e:
424+
logger.exception(f"Error calling tool {params.name}")
424425
return CallToolResult(content=[TextContent(type="text", text=str(e))], is_error=True)
425426

426427
async def _handle_list_resources(

tests/interaction/mcpserver/test_tools.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,32 @@ def flux() -> str:
119119
)
120120

121121

122+
@requirement("mcpserver:tool:handler-throws")
123+
async def test_call_tool_exception_is_logged_server_side(
124+
connect: Connect, caplog: pytest.LogCaptureFixture
125+
) -> None:
126+
"""A tool exception is logged at ERROR level on the server so operators can diagnose failures.
127+
128+
The is_error result reaches the client, but without server-side logging the root cause is
129+
invisible in server logs. This test asserts the log record is emitted alongside the result.
130+
"""
131+
mcp = MCPServer("errors")
132+
133+
@mcp.tool()
134+
def boom() -> str:
135+
raise RuntimeError("something went wrong")
136+
137+
with caplog.at_level(logging.ERROR, logger="mcp.server.mcpserver.server"):
138+
async with connect(mcp) as client:
139+
result = await client.call_tool("boom", {})
140+
141+
assert result.is_error is True
142+
assert any(
143+
rec.levelno == logging.ERROR and "boom" in rec.message
144+
for rec in caplog.records
145+
)
146+
147+
122148
@requirement("mcpserver:tool:unknown-name")
123149
async def test_call_tool_unknown_name_returns_error_result(connect: Connect, unstamped: Unstamp) -> None:
124150
"""Calling a tool name that was never registered is reported as an is_error result.

0 commit comments

Comments
 (0)