Fix mcp tool cloning for handoff pattern (#1883)

This commit is contained in:
Evan Mattson
2025-11-05 07:49:34 +09:00
committed by GitHub
Unverified
parent 9e1b3c9b85
commit d5040236c9
2 changed files with 42 additions and 1 deletions
@@ -80,6 +80,14 @@ def _clone_chat_agent(agent: ChatAgent) -> ChatAgent:
options = agent.chat_options
middleware = list(agent.middleware or [])
# Reconstruct the original tools list by combining regular tools with MCP tools.
# ChatAgent.__init__ separates MCP tools into _local_mcp_tools during initialization,
# so we need to recombine them here to pass the complete tools list to the constructor.
# This makes sure MCP tools are preserved when cloning agents for handoff workflows.
all_tools = list(options.tools) if options.tools else []
if agent._local_mcp_tools:
all_tools.extend(agent._local_mcp_tools)
return ChatAgent(
chat_client=agent.chat_client,
instructions=options.instructions,
@@ -101,7 +109,7 @@ def _clone_chat_agent(agent: ChatAgent) -> ChatAgent:
store=options.store,
temperature=options.temperature,
tool_choice=options.tool_choice, # type: ignore[arg-type]
tools=list(options.tools) if options.tools else None,
tools=all_tools if all_tools else None,
top_p=options.top_p,
user=options.user,
additional_chat_options=dict(options.additional_properties),
@@ -3,6 +3,7 @@
from collections.abc import AsyncIterable, AsyncIterator
from dataclasses import dataclass
from typing import Any, cast
from unittest.mock import MagicMock
import pytest
@@ -10,6 +11,7 @@ from agent_framework import (
AgentRunResponse,
AgentRunResponseUpdate,
BaseAgent,
ChatAgent,
ChatMessage,
FunctionCallContent,
HandoffBuilder,
@@ -20,6 +22,8 @@ from agent_framework import (
WorkflowEvent,
WorkflowOutputEvent,
)
from agent_framework._mcp import MCPTool
from agent_framework._workflows._handoff import _clone_chat_agent
@dataclass
@@ -368,3 +372,32 @@ async def test_handoff_async_termination_condition() -> None:
user_messages = [msg for msg in final_conv_list if msg.role == Role.USER]
assert len(user_messages) == 2
assert termination_call_count > 0
async def test_clone_chat_agent_preserves_mcp_tools() -> None:
"""Test that _clone_chat_agent preserves MCP tools when cloning an agent."""
mock_chat_client = MagicMock()
mock_mcp_tool = MagicMock(spec=MCPTool)
mock_mcp_tool.name = "test_mcp_tool"
def sample_function() -> str:
return "test"
original_agent = ChatAgent(
chat_client=mock_chat_client,
name="TestAgent",
instructions="Test instructions",
tools=[mock_mcp_tool, sample_function],
)
assert hasattr(original_agent, "_local_mcp_tools")
assert len(original_agent._local_mcp_tools) == 1
assert original_agent._local_mcp_tools[0] == mock_mcp_tool
cloned_agent = _clone_chat_agent(original_agent)
assert hasattr(cloned_agent, "_local_mcp_tools")
assert len(cloned_agent._local_mcp_tools) == 1
assert cloned_agent._local_mcp_tools[0] == mock_mcp_tool
assert len(cloned_agent.chat_options.tools) == 1