fix: honor MCP tool error results

This commit is contained in:
chengyongru
2026-07-01 13:03:47 +08:00
committed by Xubin Ren
parent b0258e8b20
commit 21aa900d64
2 changed files with 38 additions and 3 deletions
+5 -2
View File
@@ -14,7 +14,7 @@ from weakref import WeakKeyDictionary
import httpx import httpx
from loguru import logger from loguru import logger
from nanobot.agent.tools.base import Tool from nanobot.agent.tools.base import Tool, ToolResult
from nanobot.agent.tools.registry import ToolRegistry from nanobot.agent.tools.registry import ToolRegistry
from nanobot.bus.events import ( from nanobot.bus.events import (
INBOUND_META_RUNTIME_CONTROL, INBOUND_META_RUNTIME_CONTROL,
@@ -461,7 +461,10 @@ class MCPToolWrapper(_MCPWrapperBase):
return f"(MCP tool call failed: {type(exc).__name__})" return f"(MCP tool call failed: {type(exc).__name__})"
else: else:
# Success — extract text and persist any image content as artifacts. # Success — extract text and persist any image content as artifacts.
return self._render_call_result(result.content, kwargs) rendered = self._render_call_result(result.content, kwargs)
if getattr(result, "isError", False):
return ToolResult.error(rendered)
return rendered
return "(MCP tool call failed)" # Unreachable, but satisfies type checkers return "(MCP tool call failed)" # Unreachable, but satisfies type checkers
+33 -1
View File
@@ -19,7 +19,7 @@ from nanobot.agent.tools.mcp import (
_sanitize_name, _sanitize_name,
connect_mcp_servers, connect_mcp_servers,
) )
from nanobot.agent.tools.registry import ToolRegistry from nanobot.agent.tools.registry import ToolRegistry, is_tool_error_result
from nanobot.config.schema import MCPServerConfig from nanobot.config.schema import MCPServerConfig
@@ -304,6 +304,38 @@ async def test_execute_returns_text_blocks() -> None:
assert result == "hello\n42" assert result == "hello\n42"
@pytest.mark.asyncio
async def test_execute_wraps_mcp_is_error_result() -> None:
async def call_tool(_name: str, arguments: dict) -> object:
return SimpleNamespace(
content=[_FakeTextContent("Error: server-side MCP failure")],
isError=True,
)
wrapper = _make_wrapper(SimpleNamespace(call_tool=call_tool))
result = await wrapper.execute()
assert result == "Error: server-side MCP failure"
assert is_tool_error_result(wrapper.name, result)
@pytest.mark.asyncio
async def test_execute_preserves_success_text_that_starts_with_error() -> None:
async def call_tool(_name: str, arguments: dict) -> object:
return SimpleNamespace(
content=[_FakeTextContent("Error: generated report successfully")],
isError=False,
)
wrapper = _make_wrapper(SimpleNamespace(call_tool=call_tool))
result = await wrapper.execute()
assert result == "Error: generated report successfully"
assert not is_tool_error_result(wrapper.name, result)
# Smallest valid 1x1 PNG, base64 without the data: prefix. # Smallest valid 1x1 PNG, base64 without the data: prefix.
_PNG_B64 = ( _PNG_B64 = (
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8"