fix(mcp): exclude tools whose prefixed name exceeds the 64 char provider limit - #32319
fix(mcp): exclude tools whose prefixed name exceeds the 64 char provider limit#32319tin-berri wants to merge 5 commits into
Conversation
Greptile SummaryThis PR fixes a real LLM request failure by filtering out MCP tools whose prefixed (
Confidence Score: 5/5Safe to merge; the change is well-scoped to MCP tool listing paths and is gated by a configurable constant that can be disabled at runtime. The exclusion logic is applied at a single seam (_get_tools_from_server) with a clearly-named opt-out flag for admin paths, the 400 intercept in call_tool normalizes both prefixed and unprefixed inputs correctly before measuring, and every code path added here is covered by dedicated mocked unit tests. No correctness issues were found. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/constants.py | Adds MCP_MAX_TOOL_NAME_LENGTH constant (default 64), consistent with existing pattern of reading env vars at module import time. |
| litellm/proxy/_experimental/mcp_server/utils.py | Adds three pure helpers: split_tools_by_name_length, tool_name_length_disabled_reason, and tool_name_length_warnings. All are correctly implemented with the inclusive boundary check and ≤0 disables the feature. |
| litellm/proxy/_experimental/mcp_server/mcp_server_manager.py | Adds drop_overlong_names parameter to _get_tools_from_server, _drop_tools_exceeding_name_length helper, and intercepts call_tool with a 400 for disabled tools. Both prefixed and unprefixed call_tool inputs are normalized correctly before the length check. |
| litellm/proxy/_experimental/mcp_server/rest_endpoints.py | Adds disabled/disabled_reason to tool response objects for admin UI, and preview warnings to the test_tools_list endpoint; _execute_with_mcp_client passes through the operation result dict unchanged so warnings flow correctly. |
| litellm/proxy/_experimental/mcp_server/server.py | Adds optional disabled and disabled_reason fields to ListMCPToolsRestAPIResponseObject with safe defaults (False/None), preserving backward compatibility. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py | New TestToolNameLengthExclusion class tests the full exclusion path end-to-end with mocked clients; verifies both the drop and the 400 rejection in call_tool with both prefixed and unprefixed names. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py | Tests add-time preview warnings, short-prefix mode suppression, OpenAPI path warnings, and disabled annotation in the admin listing. All use mocked network calls; no real requests. |
| ui/litellm-dashboard/src/components/mcp_tools/mcp_tools.tsx | Renders disabled tools grayed out with a Disabled pill (Ant Design Tooltip wrapping a span) showing the disabled_reason; correctly checks tool.disabled before applying the selected-tool styling. |
| ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx | Adds toolsWarnings state, correctly initializes to [] on error/clear, adds alias to dependency array so the preview refetches on alias change. |
Reviews (8): Last reviewed commit: "fix(mcp): carry preview length warnings ..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
1 similar comment
ca303e2 to
e1cd771
Compare
Merging this PR will degrade performance by 11.42%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
|
bugbot run |
|
@greptileai rereview |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit fbdbebd. Configure here.
…der limit Providers such as AWS Bedrock, OpenAI, and Gemini reject tool names longer than 64 characters, and the MCP gateway prefixes every upstream tool name with the server alias, so long prefixed names flowed into LLM requests and failed with a provider 400. Tool listings now exclude names over the limit with an actionable warning, and the add-time tools preview flags them. Excluded tools stay callable by name; LITELLM_MCP_MAX_TOOL_NAME_LENGTH overrides or disables the limit.
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.
Per review of the exclusion contract: the admin UI tool listing now keeps tools whose prefixed name exceeds the provider limit visible, flagged disabled with the reason (grayed row and tooltip in the dashboard), while LLM-facing listings keep excluding them. Direct calls to a disabled tool are rejected at the shared call_tool seam with a clean 400 tool_name_too_long error on every surface (REST, MCP JSON-RPC, Responses API) instead of being forwarded upstream. The add-time preview warnings now render in the create-server form
…ng tests The MagicMock servers in tests/mcp_tests leaked auto-generated short_prefix and server_name mocks into the prefix normalization added for the disabled tool annotation, and the call assertion was missing the new drop_overlong_names kwarg
…m 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
fbdbebd to
6517b1a
Compare
|
CI note: every check passes except CodSpeed, which flags test_completion_simple_message at about -10%. That benchmark exercises litellm.completion with a mock response; tracing sys.modules after running it shows none of the four modules this PR changes (mcp_server_manager, rest_endpoints, server, utils under litellm/proxy/_experimental/mcp_server) are ever imported on that path. The only touched file that loads is litellm/constants.py, which gains a single module-level int(os.getenv(...)) read, and imports happen before the measured benchmark loop. CodSpeed's own report also carries a 'different runtime environments detected' accuracy warning for this comparison, the check was re-enabled on the base branch earlier today (#32340), and other unrelated open PRs are currently failing it as well. The regression is an artifact of the cross-environment baseline; acknowledging it on the CodSpeed dashboard is the remaining step |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6517b1a. Configure here.
mateo-berri
left a comment
There was a problem hiding this comment.
Why can't they just use the shortened mapping for the mcp server name? Also, not all clients reject tools over 64 chars right?
Relevant issues
Linear ticket
Resolves LIT-4216
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Behavior
The limit is
MCP_MAX_TOOL_NAME_LENGTH(default 64, from the Bedrock/OpenAI/Gemini tool name caps), measured against the final listed name, meaning the<alias>-<tool>form LiteLLM sends to LLMs. Override with theLITELLM_MCP_MAX_TOOL_NAME_LENGTHenvironment variable; 0 or negative disables the feature entirelyPOST /mcp-rest/test/tools/list, rendered in the create-server form) returns a warning for every tool whose prefixed name will exceed the limit, before anything is persisted. Server creation is not rejectedGET /mcp-rest/tools/list, the server's Tools tab), over-limit tools stay visible but are flaggeddisabledwith adisabled_reason; the dashboard renders them grayed out with a Disabled pill and a tooltip carrying the reason, so they do not look like they failed to loadtools/list,GET /v1/mcp/tools, tool search, Responses API MCP handler), over-limit tools are excluded entirely so the tool schema sent to providers never contains a name they would reject; each exclusion logs a warning naming the server, the tools, and the remediationPOST /mcp-rest/tools/call, MCP JSON-RPCtools/call, Responses API) is intercepted before reaching the upstream and rejected with a clean 400tool_name_too_longerror carrying the same reason, instead of surfacing a provider validation crashScreenshots / Proof of Fix
Live proxy on
localhost:1337backed by Postgres, with a real remote MCP server (https://mcp.deepwiki.com/mcp) registered under a 45 character alias so the three prefixed tool names land at 58, 64, and 65 characters, and real AWS Bedrock (Claude Haiku 4.5) completionsBefore the fix
Live capture on the pre-PR commit (
5e73994441, proxy onlocalhost:4000): the 65 character name is listed by/v1/mcp/tools, the admin listing carries nodisabledfield, and no exclusion warning is loggedThe 65 character name is listed, so passing the listed tools to a Bedrock model fails with the exact error from the report
The add-time preview gives no indication either; its response keys are only
['error', 'message', 'tools']After the fix (same server row, same commands, proxy restarted on this branch)
$ curl -s http://localhost:1337/v1/mcp/tools -H "Authorization: Bearer $LITELLM_MASTER_KEY" 64 network_function_inventory_config_audit_agent-read_wiki_contents 58 network_function_inventory_config_audit_agent-ask_questionThe 65 character tool is excluded (64 stays, the limit is inclusive) and the proxy logs why
Live capture on the PR commit (
3407a2d983, same server row): the 65 character tool is gone from/v1/mcp/tools, the admin listing flags itdisabled: Truewith the reason, and the exclusion warning is loggedThe add-time preview (
POST /mcp-rest/test/tools/list, what the UI calls when adding a server) now flags itThe same chat completion built from the now-listed tools succeeds against real Bedrock
$ curl -s -X POST http://localhost:1337/v1/chat/completions ... -d '{..., "tools": [<the 2 tools now returned by /v1/mcp/tools>]}' model: bedrock-invoke-haiku-4-5 tool_call: network_function_inventory_config_audit_agent-read_wiki_contentsThe admin UI tool listing keeps the disabled tool visible with the reason, so it does not look like the tool failed to load
In the dashboard Tools tab the row renders grayed out with a red Disabled pill whose tooltip carries the reason. Before the fix the same tool is indistinguishable from the others
After the fix it is grayed out with a red Disabled pill, and hovering the pill shows the reason
A direct call to the disabled tool is rejected with a clean LiteLLM error instead of being forwarded
Type
🐛 Bug Fix
Changes
The MCP gateway prefixes every upstream tool name with the server alias (
<alias>-<tool>) but nothing anywhere in the stack enforced the 64 character tool name ceiling that AWS Bedrock, OpenAI, and Gemini apply (the only length check is the MCP SDK's 128 character SEP-986 probe), so long prefixed names flowed into LLM requests and failed with a provider 400 at request timelitellm/constants.pyaddsMCP_MAX_TOOL_NAME_LENGTH(default 64, override or disable viaLITELLM_MCP_MAX_TOOL_NAME_LENGTH, zero or negative disables)litellm/proxy/_experimental/mcp_server/utils.pyadds two pure helpers next toadd_server_prefix_to_name:split_tools_by_name_lengthandtool_name_length_warningsmcp_server_manager.pyapplies the exclusion at both return points of_get_tools_from_server, so every LLM-facing listing surface is covered by the one seam (MCP JSON-RPCtools/list,GET /v1/mcp/tools, tool search, the Responses API MCP handler, and the OpenAPI-spec branch), logging an actionable warning naming the server, the disabled tools, and the override. The admin UI single-server listing opts out viadrop_overlong_names=Falseso those tools stay visible, and_create_tool_response_objectsmarks themdisabledwith adisabled_reason.call_tool, the seam every tool-call surface funnels through (REST, MCP JSON-RPC, Responses API, OpenAPI), rejects calls to a disabled tool with a 400tool_name_too_longHTTPException that each surface converts to a clean client errorrest_endpoints.pyadds awarningslist to thePOST /mcp-rest/test/tools/listresponse (the endpoint the UI calls when adding a server) computed from the would-be prefixed names, which flags the problem at add time before the server is savedTests:
_get_tools_from_serverdrops a 65 character prefixed name and keeps a 64 character one (fails without the fix), the unprefixed path measures the raw name, the preview endpoint returns the warning (fails without the fix), and helper edge cases pin the inclusive boundary plus the disable switch. Notetests/.../test_mcp_env_vars.pyshows a pre-existing test-isolation flake when the wholemcp_servertest directory runs in one process; it reproduces identically onlitellm_internal_stagingwith this change stashed and is unrelatedFollow-up commits address review: preview warnings are skipped in short-prefix mode when the payload has no server_id (the 3 char prefix derives from the id assigned at create time, so alias-based lengths would be false positives), and the exclusion warning labels the server with its alias when that differs from the name. The
LITELLM_MCP_MAX_TOOL_NAME_LENGTHenvironment variable is documented in the environment variables reference (litellm-docs)The final commit changes the runtime contract per customer feedback: silently hiding the tools from the UI made them look like they failed to load, and leaving them callable meant manual or routed calls still failed downstream. The dashboard Tools tab now shows disabled tools grayed out with a Disabled pill and tooltip (new
disabled/disabled_reasonfields onListMCPToolsRestAPIResponseObject), the create-server form renders the preview warnings in the connection status panel, and direct calls are intercepted at the sharedcall_toolseam with a clean 400 instead of reaching the upstream. Renaming the tool or shortening the alias re-enables everything automatically since nothing is persistedLink to Devin session: https://app.devin.ai/sessions/f03da2725ec94d28b3facf766871b102
Note
Medium Risk
Changes which tools appear in LLM-facing MCP aggregations and rejects some
call_toolrequests that previously reached upstream; behavior is configurable via env and covered by tests, but mis-tuned limits or long aliases could hide tools unexpectedly.Overview
Adds
MCP_MAX_TOOL_NAME_LENGTH(default 64, envLITELLM_MCP_MAX_TOOL_NAME_LENGTH; ≤0 disables) and gates MCP tools on the final prefixed name (alias-tool) so Bedrock/OpenAI/Gemini no longer reject whole requests.LLM-facing listings (
_get_tools_from_serverwith defaultdrop_overlong_names=True) drop over-limit tools and log a warning.call_toolreturns HTTP 400tool_name_too_longbefore any upstream call.Admin / preview paths keep those tools visible: REST tool list sets
disabled/disabled_reason; add-server test tools list returns awarningsarray (skipped in short-prefix mode whenserver_idis unknown). Dashboard shows preview warnings and grayed-out tools with a Disabled tooltip.New helpers in
utils.py:split_tools_by_name_length,tool_name_length_disabled_reason,tool_name_length_warnings.Reviewed by Cursor Bugbot for commit 6517b1a. Bugbot is set up for automated code reviews on this repo. Configure here.