From 2c4f4c9bcd52a9319bd5c7762562d182da18c687 Mon Sep 17 00:00:00 2001 From: Govinda Vashishtha <57435703+govindavashishtha@users.noreply.github.com> Date: Tue, 22 Sep 2026 15:36:45 +0530 Subject: [PATCH] fix: let MCP OAuth popup show result before closing The opener was closing the popup as soon as the callback broadcast, so the success screen never stayed visible. Close only the channel on result and leave self-close to PostMcpOauthScreen. Co-authored-by: Cursor --- .changeset/mcp-oauth-popup-close-delay.md | 5 +++++ packages/trueforge-ui/src/hooks/useMcpAuth.ts | 15 ++++++++++----- .../trueforge-ui/test/hooks/useMcpAuth.test.ts | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 .changeset/mcp-oauth-popup-close-delay.md diff --git a/.changeset/mcp-oauth-popup-close-delay.md b/.changeset/mcp-oauth-popup-close-delay.md new file mode 100644 index 000000000..7fdcc46ad --- /dev/null +++ b/.changeset/mcp-oauth-popup-close-delay.md @@ -0,0 +1,5 @@ +--- +"@truefoundry/trueforge-ui": patch +--- + +Stop the MCP OAuth opener from closing the popup as soon as the callback broadcasts, so the success/failure screen can show before the popup closes itself. diff --git a/packages/trueforge-ui/src/hooks/useMcpAuth.ts b/packages/trueforge-ui/src/hooks/useMcpAuth.ts index a7d52c735..5839a1d4c 100644 --- a/packages/trueforge-ui/src/hooks/useMcpAuth.ts +++ b/packages/trueforge-ui/src/hooks/useMcpAuth.ts @@ -104,16 +104,21 @@ export const useMCPAuth = ({ callbackPath }: UseMCPAuthOptions = {}) => { }) => { const channel = new BroadcastChannel(MCP_AUTH_POPUP_CHANNEL); let popup: Window | null = null; - const cleanup = () => { + // Close the channel on result; leave the popup open so the callback screen can show + // success/failure and close itself. + const cleanupChannel = () => { channel.close(); + popupCleanupRef.current.delete(cleanupWithPopup); + }; + const cleanupWithPopup = () => { + cleanupChannel(); popup?.close(); - popupCleanupRef.current.delete(cleanup); }; channel.onmessage = (event: MessageEvent) => { if (!isPopupMessage(event.data) || event.data.popupUid !== popupUid) return; const { isSuccess } = event.data; - cleanup(); + cleanupChannel(); if (!isSuccess) { if (activeAttemptsRef.current.delete(attempt)) callback(false); return; @@ -122,11 +127,11 @@ export const useMCPAuth = ({ callbackPath }: UseMCPAuthOptions = {}) => { activeAttemptsRef.current.delete(attempt); }); }; - popupCleanupRef.current.add(cleanup); + popupCleanupRef.current.add(cleanupWithPopup); popup = window.open(authorizationEndpoint, '_blank', 'popup=true'); if (!popup) { - cleanup(); + cleanupWithPopup(); throw new Error('Popup blocked. Please allow pop-ups to authorize the MCP server.'); } diff --git a/packages/trueforge-ui/test/hooks/useMcpAuth.test.ts b/packages/trueforge-ui/test/hooks/useMcpAuth.test.ts index cd1e89386..c8fde6e37 100644 --- a/packages/trueforge-ui/test/hooks/useMcpAuth.test.ts +++ b/packages/trueforge-ui/test/hooks/useMcpAuth.test.ts @@ -215,7 +215,7 @@ describe('useMCPAuth', () => { expect(callback).toHaveBeenCalledOnce(); expect(callback).toHaveBeenCalledWith(true); expect(channel.close).toHaveBeenCalledOnce(); - expect(close).toHaveBeenCalledOnce(); + expect(close).not.toHaveBeenCalled(); expect(showError).not.toHaveBeenCalled(); });