Skip to content

fix(ui/mcp): do not reset in-flight OAuth resume when create modal mounts closed - #32416

Merged
tin-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_mcp_create_oauth_resume_reset_fix
Jul 8, 2026
Merged

fix(ui/mcp): do not reset in-flight OAuth resume when create modal mounts closed#32416
tin-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_mcp_create_oauth_resume_reset_fix

Conversation

@tin-berri

@tin-berri tin-berri commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Regression introduced by #30000

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to 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

UI-only fix, so the proof is the on-create OAuth flow in the Admin UI. Steps to reproduce and verify:

  1. Run the proxy (python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug) and the dashboard dev server (npm run dev in ui/litellm-dashboard)
  2. Go to MCP Servers, click "Add New MCP Server"
  3. Fill in Transport = Streamable HTTP, an OAuth MCP server URL (e.g. https://mcp.linear.app/mcp), Authentication = OAuth, flow = Interactive (PKCE)
  4. Click "Authorize & Fetch Token", approve on the upstream consent page, and get redirected back to the UI
  5. Before this fix: the modal reopens with the fields restored, but the "Token fetched" confirmation never appears, Connection Status stays on "Complete required fields to test connection", Tool Configuration stays empty, and after clicking "Add MCP Server" the new server still requires clicking Authorize again
  6. With this fix: after the redirect back, "Token fetched. Expires in N seconds" appears, Connection Status lists the server's tools, Tool Configuration is populated, and after "Add MCP Server" the per-user token is persisted so no second authorization is needed

The pairs below capture the create-server modal in the state it lands in after the OAuth redirect returns, before at the merge-base 4b0ac8b352 (the buggy cleanup effect) and after at this branch's head 8543e6b8fa. Each is rendered from the dashboard dev server driving the real CreateMCPServer with the redirect remount replayed while resumeOAuthFlow's token exchange is in flight; only the token exchange and the tools listing are stubbed, so the sole variable between the two columns is this PR's cleanup guard

The OAuth section is where the regression first shows: before, the redirect returns but the mount-time resetOAuthFlow() discards the exchanged token so the "Token fetched" line never renders; after, the token survives the remount and the confirmation appears

OAuth token confirmation on the create form

Because the discarded token leaves oauthAccessToken null, Connection Status cannot test the server and stays on its "complete required fields" placeholder before; after, it lists the server as connected

Connection Status after the redirect

Tool Configuration is driven by the same connection, so it stays empty before and populates with the server's tools ready to allowlist after

Tool Configuration after the redirect

Type

🐛 Bug Fix

Changes

The create-server modal has a cleanup effect that resets form, tools, and OAuth state whenever isModalVisible is false, added in #30000 so a previous server's token cannot bleed into the next add-server session. That effect also runs on the initial mount, which breaks the "Authorize & Fetch Token" flow: after the OAuth redirect returns, the page reloads and CreateMCPServer mounts with the modal still closed (the restore effect reopens it a moment later) while useMcpOAuthFlow.resumeOAuthFlow is already exchanging the authorization code. The mount-time resetOAuthFlow() bumps the hook's reset version, so when the exchange resolves the hook discards the token by design (if (resetVersion !== resetVersionRef.current) return). The user sees restored form fields but no token, no Connection Status, no Tool Configuration, and the saved server never receives the per-user credential, forcing a second authorization from the server card

The fix guards the cleanup so it only fires on a real open to closed transition, tracked with a ref of the previous visibility. A genuine dismissal (cancel, successful create, or a parent flipping the prop) still resets everything, which keeps the #30000 leak fix intact; the post-redirect mount, where the modal starts closed, no longer clobbers the in-flight resume

Regression test: mounting with the modal closed must not call the OAuth hook's reset(), while an open to closed transition still must. The test fails on the previous code (reset was called on mount) and passes with the guard. The hook-side half of the contract, reset discarding an in-flight exchange, was already pinned by useMcpOAuthFlow.test.tsx

Link to Devin session: https://app.devin.ai/sessions/f03da2725ec94d28b3facf766871b102

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes a regression where CreateMCPServer mounting with isModalVisible=false (the normal state after an OAuth redirect page reload) would immediately call resetOAuthFlow(), causing the hook to discard the in-flight token exchange and leaving the user with no token, empty Connection Status, and empty Tool Configuration.

  • Introduces a wasModalVisibleRef initialized to the prop's starting value, and gates the cleanup effect on !isModalVisible && wasVisible so it only fires on a genuine open→closed transition — the mount-time run (where wasVisible === isModalVisible === false) is now a no-op.
  • Adds a regression test that asserts reset is not called on mount-with-closed and is still called after a real true→false transition, covering both sides of the guard without weakening the existing #30000 leak-prevention contract.

Confidence Score: 5/5

Safe to merge — the change is a two-line guard around an existing cleanup block and is covered by a targeted regression test.

The fix is minimal and correct: initializing the ref to the prop's initial value means a mount-with-closed never satisfies wasVisible && !isModalVisible, while every real open→closed transition still does. Both cases are exercised by the new test. No existing test assertions are weakened, and the change is entirely isolated to the cleanup effect.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx Guards the cleanup effect with a previous-visibility ref so it only fires on a real open→closed transition, preventing the post-OAuth-redirect mount from resetting an in-flight token exchange
ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx Adds a focused regression test that verifies (a) mounting with isModalVisible=false does not call reset and (b) a real open→closed transition still does, covering both sides of the guard

Reviews (1): Last reviewed commit: "fix(ui/mcp): do not reset in-flight OAut..." | Re-trigger Greptile

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 13.37%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 29 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
test_completion_simple_message 4.7 ms 4.2 ms +13.37%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing litellm_mcp_create_oauth_resume_reset_fix (8543e6b) with litellm_internal_staging (4b0ac8b)

Open in CodSpeed

@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!

@tin-berri
tin-berri merged commit 7cc6608 into litellm_internal_staging Jul 8, 2026
126 checks passed
@tin-berri
tin-berri deleted the litellm_mcp_create_oauth_resume_reset_fix branch July 8, 2026 04:42
yuneng-berri added a commit that referenced this pull request Jul 8, 2026
yuneng-berri added a commit that referenced this pull request Jul 8, 2026
chore(ui): rebuild dashboard bundle for patch-1.92.0rc1 (#32416)
stvnksslr pushed a commit to stvnksslr/litellm that referenced this pull request Jul 14, 2026
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