Merge pull request #17908 from BerriAI/litellm_fix-mcp-tool-name-prefix

Litellm fix mcp tool name prefix
This commit is contained in:
YutaSaito
2025-12-13 08:23:47 +09:00
committed by GitHub
2 changed files with 131 additions and 1 deletions
@@ -494,9 +494,21 @@ class LiteLLM_Proxy_MCP_Handler:
server_name = tool_server_map[tool_name]
# Remove the server name prefix if the tool name includes it.
sanitized_tool_name = tool_name
unprefixed_name, prefixed_server_name = split_server_prefix_from_name(
tool_name
)
if (
prefixed_server_name
and prefixed_server_name == server_name
and unprefixed_name
):
sanitized_tool_name = unprefixed_name
result = await global_mcp_server_manager.call_tool(
server_name=server_name,
name=tool_name,
name=sanitized_tool_name,
arguments=parsed_arguments,
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
@@ -626,6 +638,9 @@ class LiteLLM_Proxy_MCP_Handler:
function_calls: List[Dict[str, Any]] = []
for output_item in response.output:
if not isinstance(output_item, dict) and hasattr(output_item, "model_dump"):
output_item = output_item.model_dump()
if isinstance(output_item, dict):
if output_item.get("type") == "function_call":
call_id = output_item.get("call_id") or output_item.get("id")
@@ -1,9 +1,34 @@
import sys
import types
from unittest.mock import AsyncMock
import pytest
from litellm.responses.mcp.litellm_proxy_mcp_handler import (
LiteLLM_Proxy_MCP_Handler,
)
from litellm.types.utils import ModelResponse
from litellm.types.responses.main import OutputFunctionToolCall
class _DummyMCPResult:
def __init__(self):
self.content = []
def _setup_mcp_call_environment(monkeypatch: pytest.MonkeyPatch) -> AsyncMock:
"""Patch MCP globals so _execute_tool_calls can run in tests."""
proxy_module = types.SimpleNamespace(proxy_logging_obj=object())
monkeypatch.setitem(sys.modules, "litellm.proxy.proxy_server", proxy_module)
fake_manager = types.SimpleNamespace(
call_tool=AsyncMock(return_value=_DummyMCPResult())
)
monkeypatch.setattr(
"litellm.proxy._experimental.mcp_server.mcp_server_manager.global_mcp_server_manager",
fake_manager,
)
return fake_manager.call_tool
def test_deduplicate_mcp_tools_single_allowed_server():
@@ -142,3 +167,93 @@ def test_transform_mcp_tools_to_openai_uses_chat_format(monkeypatch):
assert resp_tools == [{"responses": True}]
assert captured["chat"] == ["tool"]
assert captured["responses"] == ["tool"]
def test_create_follow_up_input_handles_response_function_tool_call():
response = types.SimpleNamespace(
output=[
OutputFunctionToolCall(
id="id",
type="function_call",
call_id="call-1",
name="foo",
arguments="{}",
status="completed",
)
]
)
follow_up = LiteLLM_Proxy_MCP_Handler._create_follow_up_input(
response=response,
tool_results=[],
original_input=None,
)
assert follow_up == [
{
"type": "function_call",
"call_id": "call-1",
"name": "foo",
"arguments": "{}",
}
]
@pytest.mark.asyncio
async def test_execute_tool_calls_strips_server_prefix(monkeypatch):
call_tool_mock = _setup_mcp_call_environment(monkeypatch)
tool_name = "deepwiki-read_wiki_structure"
tool_calls = [
{
"id": "call-1",
"function": {"name": tool_name, "arguments": "{}"},
}
]
await LiteLLM_Proxy_MCP_Handler._execute_tool_calls(
tool_server_map={tool_name: "deepwiki"},
tool_calls=tool_calls,
user_api_key_auth=None,
)
assert call_tool_mock.await_args.kwargs["name"] == "read_wiki_structure"
@pytest.mark.asyncio
async def test_execute_tool_calls_keeps_tool_name_without_prefix(monkeypatch):
call_tool_mock = _setup_mcp_call_environment(monkeypatch)
tool_name = "read_wiki_structure"
tool_calls = [
{
"id": "call-2",
"function": {"name": tool_name, "arguments": "{}"},
}
]
await LiteLLM_Proxy_MCP_Handler._execute_tool_calls(
tool_server_map={tool_name: "deepwiki"},
tool_calls=tool_calls,
user_api_key_auth=None,
)
assert call_tool_mock.await_args.kwargs["name"] == tool_name
@pytest.mark.asyncio
async def test_execute_tool_calls_keeps_tool_name_when_equal_to_server(monkeypatch):
call_tool_mock = _setup_mcp_call_environment(monkeypatch)
tool_name = "echo"
tool_calls = [
{
"id": "call-3",
"function": {"name": tool_name, "arguments": "{}"},
}
]
await LiteLLM_Proxy_MCP_Handler._execute_tool_calls(
tool_server_map={tool_name: "echo"},
tool_calls=tool_calls,
user_api_key_auth=None,
)
assert call_tool_mock.await_args.kwargs["name"] == tool_name