Skip to content

fix(mcp): alias/display-name tool routing, REST filters, BYOK auth - #32320

Merged
Sameerlite merged 10 commits into
litellm_internal_stagingfrom
litellm_fix-mcp-alias-tool-routing
Jul 7, 2026
Merged

fix(mcp): alias/display-name tool routing, REST filters, BYOK auth#32320
Sameerlite merged 10 commits into
litellm_internal_stagingfrom
litellm_fix-mcp-alias-tool-routing

Conversation

@Sameerlite

@Sameerlite Sameerlite commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Summary

Fixes six MCP bugs found while testing servers whose alias differs from server_name and whose tools carry admin-configured display name overrides:

  • Bug 8: Alias ≠ server_name breaks MCP tool invocation with "Unknown tool" error. The Responses API auto-execution path stripped a tool's prefix by string-comparing it against the wrong name field; it now resolves the actual server and strips using all of its known prefix forms (alias, server_name, server_id).
  • Bug 9: /mcp-rest/tools/list ignores filter query parameters. mcp_server_name and toolset_name are now real query parameters that scope the response the same way POST /mcp/<server_name> and POST /toolset/<toolset_name>/mcp already do.
  • Bug 10: Tool Display Name field accepts invalid characters. A display name replaces the tool name sent to the LLM provider, so a value with spaces or special characters saved successfully but failed every subsequent Bedrock tool call. Values are now validated against Bedrock's [a-zA-Z0-9_-]+ pattern both server-side (create/update payload) and in the Admin UI (inline error + save-blocking guard).
  • Bug 11: Tool Display Name overrides break invocation in Responses API auto-execution. The display name shown in tools/list was forwarded as-is to the upstream MCP server instead of being reverse-mapped back to the original tool name; it now resolves through the same lookup the direct /mcp/<server_name> endpoint already used.
  • Bug 12: Tool override UI never loads saved display names/descriptions and always shows originals. The server table API now returns the saved tool_name_to_display_name/tool_name_to_description overrides, and the edit form normalizes them (dict or legacy JSON-string shape) back into form state.
  • Bug 13: BYOK credentials are not injected in Playground, leading to 401. Playground/Responses API routes MCP execution through call_tool, which skipped the BYOK credential lookup and never set the OpenAPI auth ContextVar; it now resolves and injects the stored per-user credential on that path too.

Test plan

  • Backend: tests/test_litellm/proxy/_experimental/mcp_server/ (full directory) and tests/test_litellm/responses/mcp/ pass, aside from pre-existing unrelated failures (missing optional semantic_router dependency, one order-dependent env-var test)
  • Frontend: npx vitest run src/components/mcp_tools/ passes (160/160)
  • New regression tests added for each backend fix: alias-mismatch prefix stripping, display-name reverse mapping, mcp_server_name/toolset_name REST filters, tool display name validation, BYOK credential injection, tool override persistence
  • Manual verification against a live proxy (Bedrock model + a real MCP server with alias ≠ server_name and a display name override) — see reproduction steps below

Screenshots / Proof of Fix

Not captured in this environment (no live proxy/Bedrock credentials available here). To verify manually:

  1. Run the proxy: python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload
  2. Register an MCP server with alias != server_name, add a tool display name override (Admin UI → MCP Servers → server → Settings → Tool Configuration → Flat List)
  3. Playground → Responses endpoint type → Bedrock model → enable the server → send a prompt that triggers the renamed tool
  4. Confirm the tool call succeeds (Bug 8/9/11/13) and that an invalid display name (e.g. with a space) is rejected on save (Bug 10) and reloads correctly after a page refresh (Bug 12)

Type

🐛 Bug Fix
✅ Test


Note

Medium Risk
Touches authentication (BYOK credential resolution, header forwarding) and tool-name resolution on hot paths (Responses auto-exec, call_tool); behavior changes are scoped but mistakes could cause wrong upstream tools or auth failures.

Overview
Fixes MCP tool routing and auth when alias differs from server_name, when admins set tool display name overrides, and when Playground/Responses call tools without going through execute_mcp_tool.

Responses auto-execution now resolves the server from the namespaced tool, reverse-maps display names to upstream tool names, and strips prefixes via strip_known_server_prefix (alias/server_name aware) instead of comparing only server_name.

GET /mcp-rest/tools/list accepts mcp_server_name and toolset_name query params (toolset scope via _apply_toolset_scope); tool_name_to_display_name / tool_name_to_description are included in the server table API and normalized in the edit UI.

Display names are validated against Bedrock’s [a-zA-Z0-9_-]+ on create/update (backend + dashboard inline/save guards).

OpenAPI-backed MCP call_tool resolves BYOK credentials (_resolve_byok_mcp_auth_header), formats auth for OpenAPI (ApiKey/Basic/Bearer), and forwards configured extra_headers through request context vars for direct handler calls.

Reviewed by Cursor Bugbot for commit 106ea4a. Bugbot is set up for automated code reviews on this repo. Configure here.

Sameerlite and others added 5 commits July 7, 2026 10:53
…ing match

When an MCP server's alias differs from its server_name, tool names are
listed with the alias prefix but _execute_tool_calls compared that prefix
against the server_name stored in tool_server_map. The mismatch silently
skipped prefix stripping, forwarding the fully-prefixed tool name upstream
and causing "Unknown tool" failures. Resolve the actual MCPServer object
and strip using its known prefix forms (alias, server_name, server_id)
instead.
Return saved tool display/description overrides from the server table API
so the edit UI reloads them, resolve display names before prefix stripping
on tool calls, and honor mcp_server_name and toolset_name filters on the
REST tools list endpoint.

Co-authored-by: Cursor <cursoragent@cursor.com>
Playground and Responses API route MCP execution through call_tool, which
skipped BYOK lookup and never set the OpenAPI auth ContextVar, so upstream
calls went out unauthenticated despite a stored user credential.

Co-authored-by: Cursor <cursoragent@cursor.com>
…erse mapping

Regression tests for _execute_tool_calls: an MCP server whose alias differs
from its server_name must still have its tool-name prefix stripped correctly,
and a tool called by its configured display name must resolve back to the
original tool name before dispatch.
…tern

A display name replaces the tool name sent to the LLM provider, so a value
with spaces or other special characters saves successfully but fails every
subsequent Bedrock tool call. Validate tool_name_to_display_name server-side
(create/update payload) against Bedrock's [a-zA-Z0-9_-]+ constraint, and add
matching inline validation plus a save-blocking guard in the Admin UI's
create and edit MCP server forms.
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes six MCP bugs related to alias/display-name tool routing, REST filter query parameters, and BYOK authentication injection. The changes span both the Python proxy backend and the React dashboard frontend.

  • Backend (Python): Fixes alias-mismatch prefix stripping in Responses API auto-execution via strip_known_server_prefix (Bug 8), adds mcp_server_name/toolset_name query params to GET /mcp-rest/tools/list (Bug 9), validates tool display names against Bedrock's [a-zA-Z0-9_-]+ pattern on create/update (Bug 10), reverse-maps display names to original tool names before upstream dispatch (Bug 11), exposes tool_name_to_display_name/tool_name_to_description in the server table API (Bug 12), and injects BYOK credentials via context vars in the call_tool path (Bug 13).
  • Frontend (TypeScript/React): Adds inline display-name validation in MCPToolConfiguration, a save-blocking guard in MCPServerEdit and CreateMCPServer, and a normalizeToolOverrideMap utility to handle legacy JSON-string overrides from the DB.

Confidence Score: 5/5

Safe to merge — all six bug fixes are narrowly scoped to their target code paths, and each is covered by new mock-only regression tests that pass.

The alias/display-name routing fix in _execute_tool_calls correctly chains get_mcp_server_by_name → _resolve_display_name_to_original → strip_known_server_prefix, with the three helpers forming a consistent prefix-aware pipeline. The BYOK injection uses context vars with a try/finally reset, avoiding leakage across concurrent requests. The toolset_name DB lookup in _resolve_toolset_scope uses the cached variant and is guarded by get_prisma_client_or_throw. Frontend save-blocking guards replicate the backend [a-zA-Z0-9_-]+ pattern faithfully. No existing test assertions were weakened — the display-name test value changed from an invalid name to a valid one to keep the save-path test meaningful under the new validation rule.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/_experimental/mcp_server/mcp_server_manager.py Adds _resolve_byok_mcp_auth_header, _format_byok_openapi_auth_header, _openapi_forwarded_extra_headers helpers; injects BYOK credentials and forwarded headers via context vars into OpenAPI tool handlers; surfaces tool_name_to_display_name/tool_name_to_description in the server table API. All paths are well-guarded.
litellm/proxy/_experimental/mcp_server/rest_endpoints.py Adds mcp_server_name and toolset_name query params to GET /tools/list; introduces _resolve_toolset_scope (cached DB lookup) and _as_query_str helpers; re-raises 404s from toolset resolution; condition-gates the tool-search branch only when no server/toolset filter is active.
litellm/proxy/_experimental/mcp_server/utils.py Adds TOOL_DISPLAY_NAME_PATTERN regex and validate_tool_display_names (Bedrock-safe name check); calls it from validate_and_normalize_mcp_server_payload on create/update. FastAPI import is deferred inside the function (proxy/ folder, so allowed).
litellm/responses/mcp/litellm_proxy_mcp_handler.py Replaces naive split_server_prefix_from_name comparison with get_mcp_server_by_name lookup + _resolve_display_name_to_original + strip_known_server_prefix; correctly handles alias ≠ server_name and display name overrides in the Responses API auto-execution path.
ui/litellm-dashboard/src/components/mcp_tools/utils.tsx Adds TOOL_DISPLAY_NAME_PATTERN regex constant, validateToolDisplayName antd-validator, and normalizeToolOverrideMap (handles dict or legacy JSON-string from DB). All exported and tested.
ui/litellm-dashboard/src/components/mcp_tools/mcp_server_edit.tsx Adds save-blocking guard for invalid display names; normalizes loaded overrides via normalizeToolOverrideMap on mount to fix Bug 12 (overrides never loading).
ui/litellm-dashboard/src/components/mcp_tools/mcp_tool_configuration.tsx Refactors ToolRow from arrow-function to named function to support isDisplayNameInvalid state; shows inline error text and antd status="error" on the display name input.
ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx Adds pre-save display name validation guard in handleCreate, mirroring the guard in MCPServerEdit.
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py Only formatting changes (line-length normalization); no logic changes, no test weakening.
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py Adds test_build_mcp_server_table_preserves_tool_overrides regression test for Bug 12 backend fix; no existing tests modified.
tests/test_litellm/proxy/_experimental/mcp_server/test_rest_endpoints.py Adds four new tests for mcp_server_name/toolset_name query params and 404 behavior; all mock-only, no real network calls.
tests/test_litellm/proxy/_experimental/mcp_server/test_utils.py New file; tests validate_tool_display_names (valid/invalid patterns) and validate_and_normalize_mcp_server_payload with tool display name payloads.
tests/test_litellm/responses/mcp/test_litellm_proxy_mcp_handler.py Adds get_mcp_server_by_name mock to existing fixture (required by new code path), and two new regression tests for alias prefix stripping (Bug 8) and display name reverse-mapping (Bug 11).

Reviews (6): Last reviewed commit: "fix: correct mcp alias routing regressio..." | Re-trigger Greptile

Comment thread litellm/proxy/_experimental/mcp_server/rest_endpoints.py Outdated
Comment thread litellm/proxy/_experimental/mcp_server/mcp_server_manager.py Outdated
No logic changes; satisfies the format checks flagged on PR #32320.
@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptileai

Extract toolset-scope resolution and query-param normalization out of
list_tool_rest_api into helpers to bring it back under the C901 complexity
budget (was 18, now within the 15 threshold).

Add the missing get_mcp_server_by_name stub to the streaming iterator test's
mock manager; the alias-fallback resolution added for tool-name-prefix
stripping calls it unconditionally when _get_mcp_server_from_tool_name misses.
@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptileai

…tch gap

_format_byok_openapi_auth_header, _openapi_forwarded_extra_headers, and
_resolve_byok_mcp_auth_header were only exercised indirectly via a mocked
call_tool test, leaving their branches (auth-type formatting, header
forwarding/stripping, missing-credential 401) uncovered.
@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptileai

@Sameerlite

Copy link
Copy Markdown
Contributor Author

bugbot run

_resolve_byok_mcp_auth_header can raise a 401 when no credential is stored.
Resolving it after during_hook_task was already queued meant a hook's
side effects (audit logging, rate-limit bookkeeping) could run and record
success for a tool call that then fails on the missing credential.
@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptileai

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Global mapping breaks display reverse
    • Responses auto-execution now prefers the server from tool_server_map before falling back to global tool-name lookup, with a collision regression test.
  • ✅ Fixed: Toolset 404 returns HTTP 200
    • The REST tool list endpoint now re-raises 404 HTTPExceptions so missing toolsets return a real 404.
  • ✅ Fixed: REST filters skipped for tool search
    • The virtual tool-search catalog shortcut now only applies to unscoped list requests, allowing server and toolset filters to return real scoped tools.

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 106ea4a. Configure here.

Comment thread litellm/responses/mcp/litellm_proxy_mcp_handler.py
Comment thread litellm/proxy/_experimental/mcp_server/rest_endpoints.py
Comment thread litellm/proxy/_experimental/mcp_server/rest_endpoints.py
@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 all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ Sameerlite
❌ cursoragent
You have signed the CLA already but the status is still pending? Let us recheck it.

@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri mateo-berri 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; thanks!

@Sameerlite
Sameerlite merged commit a78dc69 into litellm_internal_staging Jul 7, 2026
124 checks passed
@Sameerlite
Sameerlite deleted the litellm_fix-mcp-alias-tool-routing branch July 7, 2026 15:20
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.

4 participants