From fbb0b03f403859c08c7aabaab14aefba154b83cf Mon Sep 17 00:00:00 2001 From: CHENYI ZHANG Date: Tue, 29 Apr 2025 22:58:43 -0700 Subject: [PATCH] add structure proceessing --- src/agents/mcp/util.py | 15 ++++++++----- tests/mcp/test_mcp_util.py | 45 +++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/agents/mcp/util.py b/src/agents/mcp/util.py index bbfe1885..f8a67dfa 100644 --- a/src/agents/mcp/util.py +++ b/src/agents/mcp/util.py @@ -113,13 +113,16 @@ async def invoke_mcp_tool( # The MCP tool result is a list of content items, whereas OpenAI tool outputs are a single # string. We'll try to convert. - if len(result.content) == 1: - tool_output = result.content[0].model_dump_json() - elif len(result.content) > 1: - tool_output = json.dumps([item.model_dump() for item in result.content]) + if type(result) == list: + tool_output = json.dumps([item.model_dump() for item in result]) else: - logger.error(f"Errored MCP tool result: {result}") - tool_output = "Error running tool." + if len(result.content) == 1: + tool_output = result.content[0].model_dump_json() + elif len(result.content) > 1: + tool_output = json.dumps([item.model_dump() for item in result.content]) + else: + logger.error(f"Errored MCP tool result: {result}") + tool_output = "Error running tool." current_span = get_current_span() if current_span: diff --git a/tests/mcp/test_mcp_util.py b/tests/mcp/test_mcp_util.py index 74356a16..bc18eadb 100644 --- a/tests/mcp/test_mcp_util.py +++ b/tests/mcp/test_mcp_util.py @@ -1,9 +1,10 @@ import logging from typing import Any +import json import pytest from inline_snapshot import snapshot -from mcp.types import Tool as MCPTool +from mcp.types import Tool as MCPTool, CallToolResult, TextContent from pydantic import BaseModel, TypeAdapter from agents import Agent, FunctionTool, RunContextWrapper @@ -75,6 +76,19 @@ async def test_get_all_function_tools(): assert all(tool.name in names for tool in tools) +@pytest.mark.asyncio +async def test_invoke_mcp_tool(): + """Test that the invoke_mcp_tool function invokes an MCP tool and returns the result.""" + server = FakeMCPServer() + server.add_tool("test_tool_1", {}) + + ctx = RunContextWrapper(context=None) + tool = MCPTool(name="test_tool_1", inputSchema={}) + + await MCPUtil.invoke_mcp_tool(server, tool, ctx, "") + # Just making sure it doesn't crash + + @pytest.mark.asyncio async def test_invoke_mcp_tool(): """Test that the invoke_mcp_tool function invokes an MCP tool and returns the result.""" @@ -125,6 +139,35 @@ async def test_mcp_invocation_crash_causes_error(caplog: pytest.LogCaptureFixtur await MCPUtil.invoke_mcp_tool(server, tool, ctx, "") assert "Error invoking MCP tool test_tool_1" in caplog.text + +class ResultTestingServer(FakeMCPServer): + def __init__(self): + super().__init__() + self.return_empty = False + self.return_multiple = False + + async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None) -> CallToolResult: + if self.return_empty: + return CallToolResult(content=[]) + elif self.return_multiple: + return CallToolResult(content=[ + TextContent(text="result1", type="text"), + TextContent(text="result2", type="text") + ]) + return CallToolResult(content=[TextContent(text=f"result_{tool_name}", type="text")]) + + +@pytest.mark.asyncio +async def test_mcp_tool_result_conversion(): + """Test that MCP tool results with empty content return error message.""" + server = ResultTestingServer() + server.return_empty = True + server.add_tool("test_tool", {}) + ctx = RunContextWrapper(context=None) + tool = MCPTool(name="test_tool", inputSchema={}) + + result = await MCPUtil.invoke_mcp_tool(server, tool, ctx, "{}") + assert result == "Error running tool." @pytest.mark.asyncio