From 6517b1a764b0e41cc9f125f53a1c240aecc55cf8 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Tue, 7 Jul 2026 11:58:11 -0700 Subject: [PATCH] fix(mcp): carry preview length warnings for OpenAPI specs and the form alias Per review: the create-server preview request now includes the alias the user typed so warnings measure the prefix the runtime will actually apply, and the OpenAPI spec preview branch returns the same warnings array as the MCP branch since registered OpenAPI tools get the server prefix too --- .../mcp_server/rest_endpoints.py | 32 +++++++++-------- .../mcp_server/test_rest_endpoints.py | 34 +++++++++++++++++++ .../src/hooks/useTestMCPConnection.tsx | 3 ++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index 1481a027f4b..27a01ee3c30 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -1294,9 +1294,25 @@ if MCP_AVAILABLE: new_mcp_server_request = _inherit_credentials_from_existing_server(new_mcp_server_request) + def _preview_warnings(tool_names: List[str]) -> List[str]: + # 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). + if is_short_mcp_tool_prefix_enabled() and not new_mcp_server_request.server_id: + return [] + return tool_name_length_warnings( + tool_names, + get_server_prefix(new_mcp_server_request), + MCP_MAX_TOOL_NAME_LENGTH, + ) + # For OpenAPI spec servers, generate tools from the spec directly if new_mcp_server_request.spec_path: - return await _preview_openapi_tools(new_mcp_server_request.spec_path) + openapi_preview = await _preview_openapi_tools(new_mcp_server_request.spec_path) + return { + **openapi_preview, + "warnings": _preview_warnings([tool["name"] for tool in openapi_preview.get("tools") or []]), + } from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import ( MCPRequestHandler, @@ -1326,23 +1342,11 @@ 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] - # 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, "error": None, "message": "Successfully retrieved tools", - "warnings": warnings, + "warnings": _preview_warnings([tool.name for tool in list_tools_result]), } return await _execute_with_mcp_client( 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 4d8248e6782..5243e457e0b 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 @@ -688,6 +688,40 @@ class TestTestToolsList: assert result["warnings"] == [] + async def test_openapi_preview_flags_tool_names_exceeding_provider_limit(self, monkeypatch): + """The OpenAPI spec preview must carry the same length warnings as the MCP + preview, since registered OpenAPI tools get the server prefix too.""" + from litellm.constants import MCP_MAX_TOOL_NAME_LENGTH + from litellm.proxy._types import LitellmUserRoles + + alias = "network_config_audit" + fitting = "t" * (MCP_MAX_TOOL_NAME_LENGTH - len(alias) - 1) + too_long = "t" * (MCP_MAX_TOOL_NAME_LENGTH - len(alias)) + + async def fake_preview(spec_path): + return { + "tools": [{"name": fitting}, {"name": too_long}], + "error": None, + "message": "Found 2 tools from OpenAPI spec", + } + + monkeypatch.setattr(rest_endpoints, "_preview_openapi_tools", fake_preview, raising=False) + + result = await rest_endpoints.test_tools_list( + _build_request(), + NewMCPServerRequest( + server_name="example", + alias=alias, + spec_path="/tmp/spec.json", + auth_type=MCPAuth.none, + ), + user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN), + ) + + assert len(result["tools"]) == 2 + assert len(result["warnings"]) == 1 + assert f"{alias}-{too_long}" in result["warnings"][0] + class TestListToolsRestAPI: pytestmark = pytest.mark.asyncio diff --git a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx index 38e9679a850..96a65f63c2d 100644 --- a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx +++ b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx @@ -5,6 +5,7 @@ import { AUTH_TYPE, OAUTH_FLOW, TRANSPORT } from "@/components/mcp_tools/types"; interface MCPServerConfig { server_id?: string; server_name?: string; + alias?: string; url?: string; spec_path?: string; transport?: string; @@ -140,6 +141,7 @@ export const useTestMCPConnection = ({ const mcpServerConfig: MCPServerConfig = { server_id: formValues.server_id || "", server_name: formValues.server_name || "", + alias: formValues.alias, url: formValues.url, spec_path: formValues.spec_path, transport: effectiveTransport, @@ -214,6 +216,7 @@ export const useTestMCPConnection = ({ formValues.spec_path, formValues.transport, formValues.auth_type, + formValues.alias, accessToken, enabled, oauthAccessToken,