fix(proxy): handle client-side unique-ID suffixes in MCP semantic tool filter#26533
fix(proxy): handle client-side unique-ID suffixes in MCP semantic tool filter#26533Prithvi1994 wants to merge 3 commits into
Conversation
…l filter MCP clients like LibreChat append a unique-ID suffix to tool names (e.g. `<canonical>_<uid>`) to avoid naming collisions across multiple connected MCP servers. The existing `_name_matches_canonical` method only handled the prefix case (`<alias><sep><canonical>`) introduced in BerriAI#26117. The symmetric suffix case fell through, causing the filter to drop all tools and forward `tools: []` with `tool_choice: auto`, which strict upstream providers reject with a 400 error. Add a suffix-matching branch that recognises `<canonical><sep><uid>` patterns. The remainder after the canonical must be a single `<sep><unique_id>` segment with no further MCP_TOOL_PREFIX_SEPARATOR, preventing `svc-search-extra_tool` from falsely matching canonical `svc-search`. Fixes BerriAI#26507
|
|
Low: No security issues foundThis PR extends the MCP tool name matching logic to handle client-side unique-ID suffixes (e.g. Status: 0 open Posted by Veria AI · 2026-04-26T00:35:42.387Z |
Greptile SummaryThis PR fixes Confidence Score: 5/5Safe to merge — purely additive logic with comprehensive test coverage and no existing tests modified. No P0 or P1 findings. The suffix-match guard correctly checks both No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py | Adds a symmetric suffix-match branch to _name_matches_canonical; guard checks both _ and - in the remainder to prevent false positives. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_semantic_tool_filter.py | Adds 7 focused unit tests for suffix matching; no network calls, all mocked — satisfies the no-real-network-calls rule for this test folder. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["_name_matches_canonical(client_name, canonical)"] --> B{client_name == canonical?}
B -- Yes --> R1[return True]
B -- No --> C{MCP_TOOL_PREFIX_SEPARATOR in canonical?}
C -- No --> R2[return False]
C -- Yes --> D{len check passes?}
D -- No --> R3[return False]
D -- Yes --> E{client_name endswith canonical?}
E -- Yes --> F{separator in _ or -?}
F -- Yes --> R4[return True - prefix match]
F -- No --> G
E -- No --> G{client_name startswith canonical?}
G -- No --> R5[return False]
G -- Yes --> H{remainder starts with _ or -?}
H -- No --> R6[return False]
H -- Yes --> I{rest has no _ or - chars?}
I -- Yes --> R7[return True - suffix match]
I -- No --> R8[return False]
Reviews (2): Last reviewed commit: "fix(proxy): reject suffix remainders con..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…st configured one Greptile review on BerriAI#26533: the guard `MCP_TOOL_PREFIX_SEPARATOR not in rest` only checked for the configured separator (default `-`). When the suffix separator is `-` and the remainder is `extra_tool`, the guard passes because `extra_tool` contains no `-` — but `_` is also a valid MCP separator. Fix: check for both `_` and `-` in the remainder, since a unique-ID segment should contain no separator at all.
- anomalyco/opencode#24465: ecosystem table row (merge-as-is) - openai/codex#19575: cloud-executor first milestone (merge-after-nits) - BerriAI/litellm#26533: MCP semantic filter suffix-match (merge-after-nits)
VANDRANKI
left a comment
There was a problem hiding this comment.
LGTM. The fix is correct and the anchoring is careful.
The key safety invariant — checking that the remainder after the canonical prefix contains no MCP_TOOL_PREFIX_SEPARATOR — correctly rejects svc-search-extra_tool as a match for canonical svc-search. Without that guard, any tool that happens to start with the same characters would collide. The test for this case (test_suffix_does_not_match_another_namespaced_tool) is essential and is there.
The gate on MCP_TOOL_PREFIX_SEPARATOR in canonical is also correctly preserved from the existing prefix logic — unprefixed names stay out of both paths.
Note for maintainers: PR #26858 also targets this issue but its branch includes 140 changed files (many unrelated to the MCP fix). This PR is the focused, reviewable fix.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Problem
MCP clients like LibreChat append a unique-ID suffix to tool names (e.g.
fc_web_search-firecrawl_scrape_a1b2c3d4) to avoid naming collisions across multiple connected MCP servers. The_name_matches_canonicalmethod inSemanticMCPToolFilteronly handled the prefix case (<alias><sep><canonical>) introduced in #26117. The symmetric suffix case (<canonical><sep><uid>) fell through all existing checks, causing the filter to drop every tool and forwardtools: []withtool_choice: auto, which strict upstream providers reject with a 400 error.Concrete example from the issue:
fc_web_search-firecrawl_scrapetools[].namefrom LibreChatfc_web_search-firecrawl_scrape_a1b2c3d4fc_web_search-firecrawl_scrape_name_matches_canonicalresultFalse—endswith(canonical)isFalsebecause the name ends in_a1b2c3d4_get_tools_by_namesresult[]Root Cause
_name_matches_canonicalonly checked ifclient_name.endswith(canonical)(prefix case). It never checked ifclient_name.startswith(canonical)followed by a separator (suffix case).Solution
Add a symmetric suffix-matching branch to
_name_matches_canonical:client_name.startswith(canonical), check that the remainder is a single<sep><unique_id>segmentMCP_TOOL_PREFIX_SEPARATOR, preventingsvc-search-extra_toolfrom falsely matching canonicalsvc-search_and-are accepted as separators (same as the prefix case)MCP_TOOL_PREFIX_SEPARATOR in canonicalguard still applies — unprefixed canonicals won't trigger suffix matchingTesting
Added 7 new test cases to
TestGetToolsByNames:test_client_suffix_with_underscore_separator— LibreChat patterntest_client_suffix_with_dash_separator— dash varianttest_suffix_does_not_match_another_namespaced_tool— prevents false positivestest_suffix_without_separator_in_canonical_does_not_match— safety guardtest_exact_match_preferred_over_suffixed— exact match winstest_prefix_and_suffix_both_match_same_canonical— both patterns resolvetest_name_matches_canonical_suffix_static— direct static-method testsAll existing prefix-match tests continue to pass unchanged.
Does not break
ifbranch after the existing prefix check_get_tools_by_names,filter_tools, or any other methodMCP_TOOL_PREFIX_SEPARATOR not in rest) is strictly more conservative than the prefix-match guard — it rejects remainders that look like another namespaced toolFixes #26507