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: 2 additions & 4 deletions src/mcp/server/mcpserver/tools/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from mcp.server.mcpserver.utilities.context_injection import find_context_parameter
from mcp.server.mcpserver.utilities.func_metadata import FuncMetadata, func_metadata
from mcp.shared._callable_inspection import is_async_callable
from mcp.shared.exceptions import UrlElicitationRequiredError
from mcp.shared.exceptions import MCPError
from mcp.shared.tool_name_validation import validate_and_warn_tool_name
from mcp.types import Icon, ToolAnnotations

Expand Down Expand Up @@ -111,9 +111,7 @@ async def run(
result = self.fn_metadata.convert_result(result)

return result
except UrlElicitationRequiredError:
# Re-raise UrlElicitationRequiredError so it can be properly handled
# as an MCP error response with code -32042
except MCPError:
raise
except Exception as e:
raise ToolError(f"Error executing tool {self.name}: {e}") from e
17 changes: 17 additions & 0 deletions tests/server/mcpserver/test_url_elicitation_error_throw.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ async def multi_auth(ctx: Context) -> str:
assert url_error.elicitations[1].elicitation_id == "gdrive-auth"


@pytest.mark.anyio
async def test_mcp_error_propagates_as_json_rpc_error():
"""Test that MCPError raised from a tool propagates as a JSON-RPC error, not isError result."""
mcp = MCPServer(name="McpErrorServer")

@mcp.tool(description="A tool that raises a plain MCPError")
async def failing_tool(ctx: Context) -> str:
raise MCPError(-32000, "custom MCP error")

async with Client(mcp) as client:
with pytest.raises(MCPError) as exc_info:
await client.call_tool("failing_tool", {})

assert exc_info.value.error.code == -32000
assert exc_info.value.error.message == "custom MCP error"


@pytest.mark.anyio
async def test_normal_exceptions_still_return_error_result():
"""Test that normal exceptions still return CallToolResult with is_error=True."""
Expand Down
Loading