fix: honor MCP tool error results
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user