diff --git a/README.md b/README.md index 1b871b0..b247654 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,7 @@ function useMcp(options: UseMcpOptions): UseMcpResult | `autoReconnect` | `boolean \| number` | Auto reconnect if an established connection is lost, with delay in ms (default: 3000) | | `transportType` | `'auto' \| 'http' \| 'sse'` | Transport type preference: 'auto' (HTTP with SSE fallback), 'http' (HTTP only), 'sse' (SSE only) (default: 'auto') | | `preventAutoAuth` | `boolean` | Prevent automatic authentication popup on initial connection (default: false) | +| `onPopupWindow` | `(url: string, features: string, window: Window \| null) => void` | Callback invoked just after the authentication popup window is opened | #### Return Value diff --git a/src/auth/browser-provider.ts b/src/auth/browser-provider.ts index 7b0e7b1..04c708c 100644 --- a/src/auth/browser-provider.ts +++ b/src/auth/browser-provider.ts @@ -15,6 +15,7 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider { readonly clientName: string readonly clientUri: string readonly callbackUrl: string + readonly onPopupWindow: ((url: string, features: string, window: Window | null) => void) | undefined constructor( serverUrl: string, @@ -23,6 +24,7 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider { clientName?: string clientUri?: string callbackUrl?: string + onPopupWindow?: (url: string, features: string, window: Window | null) => void } = {}, ) { this.serverUrl = serverUrl @@ -34,6 +36,7 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider { options.callbackUrl || (typeof window !== 'undefined' ? new URL('/oauth/callback', window.location.origin).toString() : '/oauth/callback'), ) + this.onPopupWindow = options.onPopupWindow } // --- SDK Interface Methods --- @@ -169,6 +172,11 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider { try { const popup = window.open(sanitizedAuthUrl, `mcp_auth_${this.serverUrlHash}`, popupFeatures) + // If a callback is provided, invoke it after opening the popup + if (this.onPopupWindow) { + this.onPopupWindow(sanitizedAuthUrl, popupFeatures, popup) + } + if (!popup || popup.closed || typeof popup.closed === 'undefined') { console.warn( `[${this.storageKeyPrefix}] Popup likely blocked by browser. Manual navigation might be required using the stored URL.`, diff --git a/src/react/types.ts b/src/react/types.ts index 61934ea..b80df54 100644 --- a/src/react/types.ts +++ b/src/react/types.ts @@ -30,6 +30,12 @@ export type UseMcpOptions = { transportType?: 'auto' | 'http' | 'sse' /** Prevent automatic authentication popup on initial connection (default: false) */ preventAutoAuth?: boolean + /** + * Callback function that is invoked just before the authentication popup window is opened. + * @param url The URL that will be opened in the popup. + * @param features The features string for the popup window. + */ + onPopupWindow?: (url: string, features: string, window: Window | null) => void } export type UseMcpResult = { diff --git a/src/react/useMcp.ts b/src/react/useMcp.ts index f95b4f2..c7239f1 100644 --- a/src/react/useMcp.ts +++ b/src/react/useMcp.ts @@ -47,6 +47,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { autoReconnect = DEFAULT_RECONNECT_DELAY, transportType = 'auto', preventAutoAuth = false, + onPopupWindow, } = options const [state, setState] = useState('discovering') @@ -176,6 +177,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { clientName, clientUri, callbackUrl, + onPopupWindow, }) addLog('debug', 'BrowserOAuthClientProvider initialized in connect.') } @@ -779,6 +781,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { clientName, clientUri, callbackUrl, + onPopupWindow, }) addLog('debug', 'BrowserOAuthClientProvider initialized/updated on mount/option change.') }