Skip to content

Commit 8c0dcea

Browse files
committed
fix: set isError=True on error ToolResults
1 parent e796787 commit 8c0dcea

File tree

2 files changed

+16
-2
lines changed

2 files changed

+16
-2
lines changed

mcp_proxy_for_aws/middleware/error_handling.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
logger = logging.getLogger(__name__)
2424

2525

26+
class _FailedToolResult(ToolResult):
27+
"""A ToolResult that signals an error via the MCP isError flag."""
28+
29+
def to_mcp_result(self) -> mt.CallToolResult:
30+
return mt.CallToolResult(content=self.content, isError=True)
31+
32+
2633
class ConnectionErrorMiddleware(Middleware):
2734
"""Middleware that ensures tool calls never hang and always return a response.
2835
@@ -78,6 +85,6 @@ def _is_credential_error(error: Exception) -> bool:
7885

7986
@staticmethod
8087
def _error_result(message: str) -> ToolResult:
81-
return ToolResult(
88+
return _FailedToolResult(
8289
content=[mt.TextContent(type='text', text=message)],
8390
)

tests/unit/test_error_handling_middleware.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from fastmcp.tools.tool import ToolResult
2323
from mcp import McpError
2424
from mcp.types import ErrorData
25-
from mcp_proxy_for_aws.middleware.error_handling import ConnectionErrorMiddleware
25+
from mcp_proxy_for_aws.middleware.error_handling import ConnectionErrorMiddleware, _FailedToolResult
2626
from typing import Optional
2727
from unittest.mock import AsyncMock, Mock
2828

@@ -67,6 +67,7 @@ async def test_passes_through_on_success(self):
6767
result = await middleware.on_call_tool(context, call_next)
6868

6969
assert result is expected
70+
assert not isinstance(result, _FailedToolResult)
7071
call_next.assert_awaited_once_with(context)
7172

7273
@pytest.mark.asyncio
@@ -80,6 +81,8 @@ async def test_catches_exception_returns_error_result(self):
8081

8182
result = await middleware.on_call_tool(context, call_next)
8283

84+
assert isinstance(result, _FailedToolResult)
85+
assert result.to_mcp_result().isError is True
8386
assert len(result.content) == 1
8487
text = _get_text(result)
8588
assert 'Connection closed' in text
@@ -97,6 +100,8 @@ async def hang_forever(context: MiddlewareContext[mt.CallToolRequestParams]) ->
97100

98101
result = await middleware.on_call_tool(context, hang_forever)
99102

103+
assert isinstance(result, _FailedToolResult)
104+
assert result.to_mcp_result().isError is True
100105
assert len(result.content) == 1
101106
text = _get_text(result)
102107
assert 'slow_tool' in text
@@ -114,6 +119,7 @@ async def test_credential_error_suggests_profile(self):
114119

115120
result = await middleware.on_call_tool(context, call_next)
116121

122+
assert result.to_mcp_result().isError is True
117123
text = _get_text(result)
118124
assert 'expired or invalid AWS credentials' in text
119125
assert '--profile' in text
@@ -127,6 +133,7 @@ async def test_non_credential_error_no_suggestion(self):
127133

128134
result = await middleware.on_call_tool(context, call_next)
129135

136+
assert result.to_mcp_result().isError is True
130137
text = _get_text(result)
131138
assert '--profile' not in text
132139

0 commit comments

Comments
 (0)