mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 20:44:17 +02:00
fix: ProxyTool crashes on non-TextContent error responses (#3926)
* fix: handle non-TextContent error responses in ProxyTool 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Avoid serializing binary content into ToolError messages Use type name instead of str(content) to prevent dumping large base64 payloads into error messages. 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ruff format fix 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
757678bc7e
commit
4ea102b433
2 changed files with 37 additions and 1 deletions
|
|
@ -158,7 +158,13 @@ class ProxyTool(Tool):
|
|||
name=backend_name, arguments=arguments, meta=meta
|
||||
)
|
||||
if result.isError:
|
||||
raise ToolError(cast(mcp.types.TextContent, result.content[0]).text)
|
||||
first = result.content[0] if result.content else None
|
||||
if isinstance(first, mcp.types.TextContent):
|
||||
raise ToolError(first.text)
|
||||
elif first is None:
|
||||
raise ToolError("Tool returned an error with no content")
|
||||
else:
|
||||
raise ToolError(f"Tool returned an error ({type(first).__name__})")
|
||||
# Preserve backend's meta (includes task metadata for background tasks)
|
||||
return ToolResult(
|
||||
content=result.content,
|
||||
|
|
|
|||
|
|
@ -330,6 +330,36 @@ class TestTools:
|
|||
async with Client(proxy_server) as client:
|
||||
await client.call_tool("error_tool", {})
|
||||
|
||||
async def test_error_tool_with_image_content(self, proxy_server):
|
||||
"""Non-TextContent error responses should not crash with AttributeError."""
|
||||
error_result = mcp_types.CallToolResult(
|
||||
content=[
|
||||
mcp_types.ImageContent(
|
||||
type="image", data="abc123", mimeType="image/png"
|
||||
)
|
||||
],
|
||||
isError=True,
|
||||
)
|
||||
with patch.object(
|
||||
Client, "call_tool_mcp", new_callable=AsyncMock, return_value=error_result
|
||||
):
|
||||
with pytest.raises(ToolError):
|
||||
async with Client(proxy_server) as client:
|
||||
await client.call_tool("error_tool", {})
|
||||
|
||||
async def test_error_tool_with_empty_content(self, proxy_server):
|
||||
"""Error responses with empty content should not crash."""
|
||||
error_result = mcp_types.CallToolResult(
|
||||
content=[],
|
||||
isError=True,
|
||||
)
|
||||
with patch.object(
|
||||
Client, "call_tool_mcp", new_callable=AsyncMock, return_value=error_result
|
||||
):
|
||||
with pytest.raises(ToolError):
|
||||
async with Client(proxy_server) as client:
|
||||
await client.call_tool("error_tool", {})
|
||||
|
||||
async def test_call_tool_forwards_meta(self, fastmcp_server, proxy_server):
|
||||
"""Test that metadata from proxied tool results is properly forwarded."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue