Skip to content

fix(mcp): delegate PKCE bypass for internal MCP servers - #27977

Merged
Sameerlite merged 3 commits into
litellm_internal_stagingfrom
litellm_mcp_internal_delegate_pkce
May 15, 2026
Merged

fix(mcp): delegate PKCE bypass for internal MCP servers#27977
Sameerlite merged 3 commits into
litellm_internal_stagingfrom
litellm_mcp_internal_delegate_pkce

Conversation

@Sameerlite

@Sameerlite Sameerlite commented May 15, 2026

Copy link
Copy Markdown
Contributor

Removes available_on_public_internet checks from delegate-auth-to-upstream flows so internal (available_on_public_internet: false) oauth2 interactive servers get the same anonymous PKCE bypass as public servers.

Touched paths:

  • user_api_key_auth_mcp._target_servers_delegate_auth_to_upstream
  • MCPServerManager.get_allowed_mcp_servers anonymous delegate allow-list
  • _mcp_oauth_user_api_key_auth for browser /authorize (and related) without a LiteLLM session

M2M (client_credentials) exclusion unchanged.

Tests: TestMCPDelegateAuthToUpstream::test_delegate_bypass_for_internal_server, test_get_allowed_servers_includes_internal_delegate, TestTemporaryMCPSessionEndpoints::test_mcp_oauth_user_api_key_auth_internal_delegate_bypasses.
image
image

Remove available_on_public_internet gating from delegate-auth-to-upstream
paths so oauth2 + delegate_auth_to_upstream interactive servers behave
the same when marked internal. Keeps M2M exclusion. Updates tests.
@codecov

codecov Bot commented May 15, 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 May 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR expands the anonymous PKCE bypass for OAuth2 delegate-to-upstream MCP servers to also cover available_on_public_internet: false (internal-only) servers, which were previously blocked at three separate guards. Operator visibility is improved via a load-time warning log and a new dashboard Alert when this configuration is detected.

  • Removes available_on_public_internet gating from _target_servers_delegate_auth_to_upstream (tool-call auth), get_allowed_mcp_servers (anonymous allow-list), and _mcp_oauth_user_api_key_auth (PKCE /authorize + /token flows), meaning internal delegate servers now bypass LiteLLM auth exactly like public ones.
  • Adds _warn_internal_delegate_pkce_if_applicable emitted at config and DB load time, and a matching Alert in MCPPermissionManagement.tsx, to surface the configuration to operators.
  • Tests for all three guards are inverted to assert bypass (no 401) for internal servers; new TestInternalDelegatePkceWarningLog tests validate the warning log and its M2M exclusion.

Confidence Score: 4/5

The change deliberately widens anonymous access to internal MCP servers; correctness depends entirely on upstream IdP and network controls being properly enforced.

Three auth guards that previously blocked internal-only servers from the anonymous PKCE path are removed simultaneously. Any operator who previously combined available_on_public_internet: false with delegate_auth_to_upstream: true as a LiteLLM-level authentication gate for internal servers will have that gate silently removed after upgrade, with no opt-in or opt-out flag. The M2M exclusion and the authorization_code-only bypass on /token are preserved correctly, and the new tests accurately reflect the intended post-change behaviour.

user_api_key_auth_mcp.py and mcp_management_endpoints.py contain the removed auth guards and warrant the most scrutiny; mcp_server_manager.py removes the third guard in get_allowed_mcp_servers.

Important Files Changed

Filename Overview
litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py Removes the available_on_public_internet guard from _target_servers_delegate_auth_to_upstream, allowing internal-only MCP servers to bypass LiteLLM auth for tool-call requests when delegate_auth_to_upstream is True
litellm/proxy/_experimental/mcp_server/mcp_server_manager.py Removes available_on_public_internet guard from get_allowed_mcp_servers anonymous allow-list, and adds _warn_internal_delegate_pkce_if_applicable warning log emitted at config and DB load time for affected servers
litellm/proxy/management_endpoints/mcp_management_endpoints.py Removes available_on_public_internet guard from _mcp_oauth_user_api_key_auth, allowing internal delegate servers to bypass LiteLLM auth for /authorize and authorization_code /token PKCE flows
tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py Tests inverted to assert bypass (no 401) for internal delegate servers; test_get_allowed_servers_includes_internal_delegate now expects internal servers in the allow-list
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py New TestInternalDelegatePkceWarningLog class adds good coverage for the warning log emitted at load time; M2M exclusion and public-server non-emission cases are tested
tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py Test renamed and assertions inverted to confirm bypass; expected_auth variable is still constructed but never referenced in any assertion (dead code noted in previous review comment)
ui/litellm-dashboard/src/components/mcp_tools/MCPPermissionManagement.tsx Adds a dashboard Alert warning when oauth2 + delegate_auth_to_upstream + available_on_public_internet=false are all set; uses antd Alert per style guide
CLAUDE.md Documents the new available_on_public_internet=false + delegate_auth_to_upstream=true semantics in the MCP OAuth section

Reviews (2): Last reviewed commit: "fix(mcp): dedupe load_servers_from_confi..." | Re-trigger Greptile

@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptile this is an intended design, will it be okay if we raise a warning on the UI if user tries this combination?

@greptile-apps

greptile-apps Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

A UI warning is a reasonable compromise for an intentional design. A few thoughts on making it effective:

  1. UI warning alone is a soft guard — it can be dismissed or missed. I'd suggest pairing it with a server-side logger.warning when the config is loaded (e.g., in MCPServerManager when a server is registered with available_on_public_internet=False + delegate_auth_to_upstream=True), so the intent is surfaced in logs too, not just the UI.

  2. Warning content — the message should be explicit that anonymous PKCE callers will bypass LiteLLM auth and be forwarded directly to the upstream OAuth2 provider. Something like:

    "This MCP server is configured as internal-only but delegates auth to upstream. Anonymous users will be able to reach the upstream OAuth2 /authorize flow without a LiteLLM session. Ensure your upstream provider enforces access controls."

  3. Docs — worth a note in the MCP server config docs clarifying the interaction between available_on_public_internet: false and delegate_auth_to_upstream: true so operators understand this is expected, not a misconfiguration.

With those in place, the PR looks acceptable from a security standpoint — the M2M exclusion remains intact and the bypass is scoped to the PKCE interactive flow only.

Log verbose_logger.warning when loading oauth2 interactive servers with
available_on_public_internet=false and delegate_auth_to_upstream=true
(config + DB). Dashboard Alert for the same combo. CLAUDE note for
operators. Tests for log and M2M skip.
@Sameerlite

Copy link
Copy Markdown
Contributor Author

@greptile re review

Removes accidental duplicate alias/mcp_aliases and get_server_prefix
logic (fixes PLR0915 and avoids resetting alias after mapping).
@Sameerlite
Sameerlite requested a review from mateo-berri May 15, 2026 10:27

@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

nonblocking nit: tests/test_litellm/proxy/management_endpoints/test_mcp_management_endpoints.py:1707 constructs expected_auth and feeds it to the _user_api_key_auth_builder mock, but then asserts auth_builder_mock.assert_not_called() and only isinstance(result, UserAPIKeyAuth). The mock return value is never consumed. Other sibling tests (1608/1644/1693) do assert result is expected_auth. Either add the same identity assert here or drop expected_auth + patched return value

@Sameerlite
Sameerlite merged commit 106b2f2 into litellm_internal_staging May 15, 2026
116 checks passed
@Sameerlite
Sameerlite deleted the litellm_mcp_internal_delegate_pkce branch May 15, 2026 17:27
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…legate_pkce

fix(mcp): delegate PKCE bypass for internal MCP servers
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.

2 participants