fix(mcp): fail closed on missing upstream credentials - #41364
Conversation
|
@cursor review Please review the current commit for missing-credential enforcement, caller-header compatibility, and any actionable regressions before readiness is declared |
Greptile SummaryThis PR hardens MCP outbound credential handling while preserving established credential precedence and supported authentication modes.
Confidence Score: 5/5The current tip appears safe to merge with no remaining actionable review concerns. No new changes were made since the previous review, both previous findings are resolved, and the raw-Authorization finding was explicitly withdrawn after confirming that complete raw values remain supported. Important Files Changed
Reviews (7): Last reviewed commit: "fix(mcp): reject bare schemes in raw aut..." | Re-trigger Greptile |
|
@greptileai Please review the current commit for credential validation, preserved caller-header behavior, and actionable regressions before this PR is considered ready |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@greptileai @cursor review @veria-ai Please re-review commit 258176d for the Basic-separator and rendered-scheme repairs, including existing caller-header compatibility controls |
|
@greptileai @cursor review @veria-ai Please review fdb8e35 for credential validation, unchanged OpenAPI header precedence, OAuth compatibility, and resolution of prior findings |
|
@greptileai @cursor review @veria-ai Please review 6c517bf for the API-key Authorization correction, supported credential compatibility, and resolution of prior findings |
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
|
@veria-ai Please reassess the raw Authorization finding against the existing opaque-value contract and measured compatibility regression described in the inline reply |
|
@greptileai @veria-ai Please review 70ef8b2, including the adopted raw Authorization correction, meaningful compatibility controls, and confirmation that all prior concerns are resolved |
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 70ef8b2. Configure here.
|
@greptileai Please reassess against the approved fail-closed requirement: reject known bare schemes, preserve complete raw credentials, and avoid introducing an opt-in bypass |
|
@greptileai Please refresh the current-tip review summary to reflect your withdrawn raw-authorization finding and confirm any remaining actionable concerns |
|
@mateo-berri This corrects broken OBO fallback and missing-credential handling. Configurations relying on that behavior now fail; a brief release note should suffice |

TLDR
Problem this solves:
How it solves it:
Bearer Beareror a bareBasicfail validationUser Flow
Before: selecting OBO can still send an unauthenticated tool request
After: selecting OBO requires authentication before upstream discovery or execution
Relevant issues
Affected release
Linear ticket
Resolves LIT-4501
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@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).
Screenshots / Proof of Fix
Two DB-backed proxies, one per commit, each one process started with
--num_workers 2andproxy_config_reload_interval_seconds: 5so both workers pick up registry changes from the database (SYNC_WAIT=8below waits 8s after every registry write). Before is the merge base 9cd7873 on 127.0.0.1:25002 with database litellm_lit7914_before, After is this PR's head 70ef8b2 on 127.0.0.1:45138 with database litellm_lit7914_after. The upstream is a local FastMCP echo server per leg (127.0.0.1:51521 for Before, 127.0.0.1:23920 for After) whose one addition is a middleware that appends the auth headers of every request it receives to a JSONL file, the same thing the upstream owner would read off their access log. The admin key drives the management routes and is also the caller on the MCP protocol probe, so the incomplete OBO case carries no user subject to exchange and lands on the 401 branch. The author's earlier run at this head covered the other branch with a Keycloak user JWT: incomplete OBO returned 500token_exchange requires client_id and client_secretwith zero upstream requests where the merge base returned 200Each case registers the server with
auth_type: none, lists once so the tool is known, updates the server to the auth under test, waits for both workers to reload, then lists, callsechotwice over/mcp-rest, and finally lists and calls through the gateway's/mcpendpoint with the mcp SDK client. After every step the echo server's log is diffed to show what actually reached upstreamProxy config for both legs
Upstream echo MCP server,
python echo_mcp_server.py PORT LOG(mcp 1.28.1)MCP protocol client used at the end of each case,
python mcp_client_probe.py BASE_URL ALIAS(mcp 1.28.1)Commands (zsh, with
PORT,ECHO_PORT,ECHO_LOG,SYNC_WAIT=8andLITELLM_MASTER_KEYset per leg)Before (9cd7873)
After (70ef8b2)
Observations from the run
Type
🐛 Bug Fix
Caveats (if any)
Severe
Medium
Final Attestation
Note
High Risk
Changes authentication and credential resolution for MCP upstream calls; misconfigured protected servers now error before connect, and OBO routing behavior shifts so incomplete configs no longer fall back to unauthenticated v1 paths.
Overview
Fail-closed upstream auth for MCP so misconfigured or empty credentials cannot reach the upstream on discovery, native MCP, or OpenAPI tool calls.
Declared oauth2_token_exchange (OBO) servers always map to the v2 token-exchange resolver (including incomplete
client_secretand BYOK), instead of deferring to v1 and allowing static-token fallback or anonymous connects. Static auth modes (api_key, bearer, basic, token, rawauthorization) are checked via newvalidate_static_credential/prepare_mcp_client: native clients preview egress headers withMCPClient.prepare_request_auth()before connect; OpenAPI tools validate merged headers right before HTTP. Placeholder values (bare schemes, duplicated scheme tokens, invalid Basic) are rejected with HTTP errors and no upstream request.discovery_auth_fingerprintnow hashes the same preview path used for validation.Reviewed by Cursor Bugbot for commit 70ef8b2. Bugbot is set up for automated code reviews on this repo. Configure here.