mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-23 08:24:15 +00:00
Merge pull request #17908 from BerriAI/litellm_fix-mcp-tool-name-prefix
Litellm fix mcp tool name prefix
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user