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

(cherry picked from commit 7cc660866a)
This commit is contained in:
tin-berri 2026-07-07 21:42:08 -07:00 • committed by Yuneng Jiang
parent 2d35ea5d07
commit 8c8e4d0251
No known key found for this signature in database
2 changed files with 24 additions and 2 deletions

View file

@ -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(<CreateMCPServer {...defaultProps} isModalVisible={false} />);
expect(oauthHook.reset).not.toHaveBeenCalled();
// A real open -> closed transition must still reset (the #30000 leak fix).
rerender(<CreateMCPServer {...defaultProps} isModalVisible={true} />);
rerender(<CreateMCPServer {...defaultProps} isModalVisible={false} />);
expect(oauthHook.reset).toHaveBeenCalled();
});
});
describe("when stdio transport is selected", () => {

View file

@ -603,9 +603,15 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
// 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);