From 8c8e4d02511383fbc003c342642168c680d52915 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Tue, 7 Jul 2026 21:42:08 -0700 Subject: [PATCH] fix(ui/mcp): do not reset in-flight OAuth resume when create modal mounts closed (#32416) (cherry picked from commit 7cc660866aea077508246d95e3f77cb8b940d212) --- .../mcp_tools/create_mcp_server.test.tsx | 16 ++++++++++++++++ .../components/mcp_tools/create_mcp_server.tsx | 10 ++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx index 1245bcee3fa..672644b685c 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.test.tsx @@ -937,6 +937,22 @@ describe("CreateMCPServer", () => { const reopenedUrlInput = screen.getByPlaceholderText("https://your-mcp-server.com") as HTMLInputElement; expect(reopenedUrlInput.value).toBe(""); }); + + it("does not reset an in-flight OAuth resume when mounted with the modal closed (post-redirect restore)", () => { + // After the "Authorize & Fetch Token" redirect the page reloads and this + // component mounts with isModalVisible=false while useMcpOAuthFlow is still + // exchanging the authorization code. Calling reset() during that mount bumps + // the hook's reset version and the fetched token is silently discarded, so + // the user sees no Connection Status / Tool Configuration and must authorize + // again after saving. + const { rerender } = render(); + expect(oauthHook.reset).not.toHaveBeenCalled(); + + // A real open -> closed transition must still reset (the #30000 leak fix). + rerender(); + rerender(); + expect(oauthHook.reset).toHaveBeenCalled(); + }); }); describe("when stdio transport is selected", () => { diff --git a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx index 05a0696674a..725a3f1534f 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/create_mcp_server.tsx @@ -603,9 +603,15 @@ const CreateMCPServer: React.FC = ({ // Clear form, tools, and OAuth state when the modal closes so a previous server's // authorization, credentials, or tool list never bleed into the next "Add New MCP // Server" session, including when a parent dismisses the modal without routing - // through handleCancel or handleCreate. + // through handleCancel or handleCreate. Only a real open -> closed transition may + // trigger this: on the post-OAuth-redirect remount the modal starts closed while + // resumeOAuthFlow's token exchange is in flight, and resetting then discards the + // fetched token. + const wasModalVisibleRef = React.useRef(isModalVisible); React.useEffect(() => { - if (!isModalVisible) { + const wasVisible = wasModalVisibleRef.current; + wasModalVisibleRef.current = isModalVisible; + if (!isModalVisible && wasVisible) { form.resetFields(); setFormValues({}); setOauthAccessToken(null);