Skip to content

fix(proxy): handle client-side unique-ID suffixes in MCP semantic tool filter#26533

Open
Prithvi1994 wants to merge 3 commits into
BerriAI:mainfrom
Prithvi1994:fix/mcp-suffix-tool-filter
Open

fix(proxy): handle client-side unique-ID suffixes in MCP semantic tool filter#26533
Prithvi1994 wants to merge 3 commits into
BerriAI:mainfrom
Prithvi1994:fix/mcp-suffix-tool-filter

Conversation

@Prithvi1994

Copy link
Copy Markdown

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_canonical method in SemanticMCPToolFilter only 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 forward tools: [] with tool_choice: auto, which strict upstream providers reject with a 400 error.

Concrete example from the issue:

form value
MCP registry canonical (proxy side) fc_web_search-firecrawl_scrape
Incoming tools[].name from LibreChat fc_web_search-firecrawl_scrape_a1b2c3d4
Router top-k match returns fc_web_search-firecrawl_scrape
_name_matches_canonical result Falseendswith(canonical) is False because the name ends in _a1b2c3d4
_get_tools_by_names result []

Root Cause

_name_matches_canonical only checked if client_name.endswith(canonical) (prefix case). It never checked if client_name.startswith(canonical) followed by a separator (suffix case).

Solution

Add a symmetric suffix-matching branch to _name_matches_canonical:

  • If client_name.startswith(canonical), check that the remainder is a single <sep><unique_id> segment
  • The remainder after the separator must contain no MCP_TOOL_PREFIX_SEPARATOR, preventing svc-search-extra_tool from falsely matching canonical svc-search
  • Both _ and - are accepted as separators (same as the prefix case)
  • The existing MCP_TOOL_PREFIX_SEPARATOR in canonical guard still applies — unprefixed canonicals won't trigger suffix matching

Testing

Added 7 new test cases to TestGetToolsByNames:

  • test_client_suffix_with_underscore_separator — LibreChat pattern
  • test_client_suffix_with_dash_separator — dash variant
  • test_suffix_does_not_match_another_namespaced_tool — prevents false positives
  • test_suffix_without_separator_in_canonical_does_not_match — safety guard
  • test_exact_match_preferred_over_suffixed — exact match wins
  • test_prefix_and_suffix_both_match_same_canonical — both patterns resolve
  • test_name_matches_canonical_suffix_static — direct static-method tests

All existing prefix-match tests continue to pass unchanged.

Does not break

  • The fix is purely additive — a new if branch after the existing prefix check
  • No changes to _get_tools_by_names, filter_tools, or any other method
  • The suffix-match guard (MCP_TOOL_PREFIX_SEPARATOR not in rest) is strictly more conservative than the prefix-match guard — it rejects remainders that look like another namespaced tool

Fixes #26507

…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
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed-hq

codspeed-hq Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing Prithvi1994:fix/mcp-suffix-tool-filter (c343371) with main (6ff668c)

Open in CodSpeed

@veria-ai

veria-ai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Low: No security issues found

This PR extends the MCP tool name matching logic to handle client-side unique-ID suffixes (e.g. <canonical>_a1b2c3d4). The change is in a semantic tool filter that narrows which already-available tools are included in the LLM context window — it does not control tool authorization or execution. The suffix matching is appropriately gated: the canonical must contain the MCP prefix separator, and the suffix portion must be purely alphanumeric (no _ or -), which limits false-positive matches.


Status: 0 open
Risk: 2/10

Posted by Veria AI · 2026-04-26T00:35:42.387Z

@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes _name_matches_canonical in SemanticMCPToolFilter to handle the LibreChat/suffix pattern (<canonical><sep><uid>), which previously caused the filter to return an empty tool list and trigger 400 errors from strict upstream providers. The fix is purely additive — a new if branch checks startswith(canonical) with a guard that rejects remainders containing either _ or -, preventing namespaced tool names from spuriously matching.

Confidence Score: 5/5

Safe 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 _ and - in the remainder (addressing the concern raised in the previous review thread). All 7 new tests are pure unit tests with no network calls, consistent with the repository rule for this test folder.

No files require special attention.

Important Files Changed

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]
Loading

Reviews (2): Last reviewed commit: "fix(proxy): reject suffix remainders con..." | Re-trigger Greptile

Comment thread litellm/proxy/_experimental/mcp_server/semantic_tool_filter.py
@codecov

codecov Bot commented Apr 25, 2026

Copy link
Copy Markdown

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.
Bojun-Vvibe added a commit to Bojun-Vvibe/oss-contributions that referenced this pull request Apr 26, 2026
- 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 VANDRANKI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants