From c093858a678ecb991560c04627690bd773223cd9 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Tue, 7 Jul 2026 09:28:23 -0700 Subject: [PATCH] fix(mcp): address review on tool name length exclusion Skip speculative preview warnings in short-prefix mode when the payload has no server_id (the 3 char prefix derives from the id assigned at create time) and label the exclusion warning with the alias when it differs from the server name so the prefix in the dropped tool name is traceable. --- .../mcp_server/mcp_server_manager.py | 7 ++- .../mcp_server/rest_endpoints.py | 16 +++++-- .../mcp_server/test_rest_endpoints.py | 44 +++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 0f92485f0b0..53905c7b7a7 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -3198,12 +3198,17 @@ class MCPServerManager: kept, dropped = split_tools_by_name_length(tools, MCP_MAX_TOOL_NAME_LENGTH) if dropped: dropped_names = ", ".join(f"{tool.name} ({len(tool.name)} chars)" for tool in dropped) + server_label = ( + server.name + if not server.alias or server.alias == server.name + else f"{server.name} (alias {server.alias})" + ) verbose_logger.warning( "MCP server %s has %d tool(s) whose name exceeds %d characters, which providers such as " "AWS Bedrock, OpenAI, and Gemini reject. Excluding them from tool listings: %s. " "Use a shorter server alias, rename the tools on the MCP server, or set " "LITELLM_MCP_MAX_TOOL_NAME_LENGTH to change the limit.", - server.name, + server_label, len(dropped), MCP_MAX_TOOL_NAME_LENGTH, dropped_names, diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index 581a2f5dc72..955a418c208 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -26,6 +26,7 @@ from litellm.constants import MCP_MAX_TOOL_NAME_LENGTH from litellm.proxy._experimental.mcp_server.utils import ( MCPMissingUserEnvVarsError, get_server_prefix, + is_short_mcp_tool_prefix_enabled, merge_mcp_headers, tool_name_length_warnings, ) @@ -1311,10 +1312,17 @@ if MCP_AVAILABLE: list_tools_response = await client.run_with_session(_list_tools_session_operation) list_tools_result: List[MCPTool] = list_tools_response.tools model_dumped_tools: List[dict] = [tool.model_dump() for tool in list_tools_result] - warnings = tool_name_length_warnings( - [tool.name for tool in list_tools_result], - get_server_prefix(new_mcp_server_request), - MCP_MAX_TOOL_NAME_LENGTH, + # In short-prefix mode the 3-char prefix derives from the server_id + # assigned at create time, so without one the final length is + # unknowable; skip speculative warnings (runtime exclusion still warns). + warnings = ( + [] + if is_short_mcp_tool_prefix_enabled() and not new_mcp_server_request.server_id + else tool_name_length_warnings( + [tool.name for tool in list_tools_result], + get_server_prefix(new_mcp_server_request), + MCP_MAX_TOOL_NAME_LENGTH, + ) ) return { "tools": model_dumped_tools, diff --git a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py index 1e609dbc882..a9cddc676a1 100644 --- a/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py +++ b/tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py @@ -644,6 +644,50 @@ class TestTestToolsList: assert len(result["warnings"]) == 1 assert f"{alias}-{too_long}" in result["warnings"][0] + async def test_no_speculative_warnings_in_short_prefix_mode(self, monkeypatch): + """In short-prefix mode without a server_id the 3-char runtime prefix is not + knowable yet, so the preview must not warn based on the full alias.""" + from types import SimpleNamespace + + from mcp.types import Tool as MCPTool + + from litellm.constants import MCP_MAX_TOOL_NAME_LENGTH + from litellm.proxy._types import LitellmUserRoles + + alias = "network_config_audit" + fits_short_prefix_only = "t" * (MCP_MAX_TOOL_NAME_LENGTH - len(alias)) + + class FakeClient: + async def run_with_session(self, operation): + return SimpleNamespace(tools=[MCPTool(name=fits_short_prefix_only, inputSchema={})]) + + async def fake_execute( + request, + operation, + mcp_auth_header=None, + oauth2_headers=None, + raw_headers=None, + ): + return await operation(FakeClient()) + + monkeypatch.setattr( + rest_endpoints, "_execute_with_mcp_client", fake_execute, raising=False + ) + monkeypatch.setenv("LITELLM_USE_SHORT_MCP_TOOL_PREFIX", "1") + + result = await rest_endpoints.test_tools_list( + _build_request(), + NewMCPServerRequest( + server_name="example", + alias=alias, + url="https://example.com", + auth_type=MCPAuth.none, + ), + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert result["warnings"] == [] + class TestListToolsRestAPI: pytestmark = pytest.mark.asyncio