@mcp-ts/sdk 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/react.d.mts +81 -2
- package/dist/client/react.d.ts +81 -2
- package/dist/client/react.js +316 -3
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +312 -4
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.js +11 -2
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +11 -2
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.js +55 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +55 -11
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +55 -11
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +55 -11
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client/react/index.ts +14 -0
- package/src/client/react/oauth-popup.tsx +446 -0
- package/src/client/react/use-mcp.ts +36 -3
- package/src/client/vue/use-mcp.ts +38 -3
- package/src/server/handlers/sse-handler.ts +39 -0
- package/src/server/mcp/oauth-client.ts +32 -14
- package/supabase/migrations/20260421010000_add_session_cleanup_cron.sql +32 -0
package/dist/client/react.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { c as McpConnectionState, M as McpConnectionEvent } from '../events-CK3N
|
|
|
4
4
|
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, d as McpObservabilityEvent } from '../events-CK3N--3g.mjs';
|
|
5
5
|
import { v as ToolInfo, k as FinishAuthResult, n as ListToolsRpcResult, L as ListPromptsResult, l as ListResourcesResult } from '../types-CfCoIsWI.mjs';
|
|
6
6
|
export { p as McpRpcRequest, q as McpRpcResponse } from '../types-CfCoIsWI.mjs';
|
|
7
|
-
import React$1 from 'react';
|
|
7
|
+
import React$1, { ReactNode, CSSProperties } from 'react';
|
|
8
8
|
import '@modelcontextprotocol/ext-apps/app-bridge';
|
|
9
9
|
import '@modelcontextprotocol/sdk/types.js';
|
|
10
10
|
|
|
@@ -170,6 +170,85 @@ interface McpClient$1 {
|
|
|
170
170
|
*/
|
|
171
171
|
declare function useMcp(options: UseMcpOptions): McpClient$1;
|
|
172
172
|
|
|
173
|
+
interface OAuthPopupConnectionLike {
|
|
174
|
+
sessionId: string;
|
|
175
|
+
state: string;
|
|
176
|
+
error?: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Optional helpers for popup-based OAuth UX.
|
|
180
|
+
*
|
|
181
|
+
* These utilities sit on top of the core MCP auth primitives:
|
|
182
|
+
* - `useMcp({ onRedirect })` to decide how auth navigation happens
|
|
183
|
+
* - `finishAuth(sessionId, code)` to complete code exchange
|
|
184
|
+
*
|
|
185
|
+
* Consumers are free to:
|
|
186
|
+
* - use these helpers as-is for a turnkey popup flow
|
|
187
|
+
* - build their own popup UI/message bridge
|
|
188
|
+
* - skip popups entirely and handle auth in a normal callback page
|
|
189
|
+
*/
|
|
190
|
+
interface OAuthPopupRedirectOptions {
|
|
191
|
+
width?: number;
|
|
192
|
+
height?: number;
|
|
193
|
+
windowName?: string;
|
|
194
|
+
features?: string[];
|
|
195
|
+
onBlocked?: (url: string) => void;
|
|
196
|
+
}
|
|
197
|
+
interface McpOAuthCallbackContentProps {
|
|
198
|
+
code?: string | null;
|
|
199
|
+
sessionId?: string | null;
|
|
200
|
+
title?: ReactNode;
|
|
201
|
+
initialStatus?: string;
|
|
202
|
+
loadingFallback?: ReactNode;
|
|
203
|
+
rootStyle?: CSSProperties;
|
|
204
|
+
cardStyle?: CSSProperties;
|
|
205
|
+
titleStyle?: CSSProperties;
|
|
206
|
+
messageStyle?: CSSProperties;
|
|
207
|
+
renderContainer?: (content: ReactNode) => ReactNode;
|
|
208
|
+
debugPhase?: 'loading' | 'success' | 'error';
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Opens a centered popup window for OAuth.
|
|
212
|
+
*
|
|
213
|
+
* Convenience only: callers can replace this entirely with their own popup,
|
|
214
|
+
* modal, redirect, or router-based navigation strategy.
|
|
215
|
+
*/
|
|
216
|
+
declare function openCenteredPopup(url: string, options?: OAuthPopupRedirectOptions): Window | null;
|
|
217
|
+
/**
|
|
218
|
+
* Creates an `onRedirect` handler suitable for `useMcp({ onRedirect })`.
|
|
219
|
+
*
|
|
220
|
+
* This is the simplest popup entry point, but it is intentionally optional.
|
|
221
|
+
* Applications can provide any redirect handler they want, including full-page
|
|
222
|
+
* navigation or a completely custom popup implementation.
|
|
223
|
+
*/
|
|
224
|
+
declare function createOAuthPopupRedirectHandler(options?: OAuthPopupRedirectOptions): (url: string) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Handles opener-side popup coordination for OAuth code exchange.
|
|
227
|
+
*
|
|
228
|
+
* Use this when you want popup auth but do not want to reimplement the
|
|
229
|
+
* postMessage wiring between the main app window and the popup callback page.
|
|
230
|
+
*
|
|
231
|
+
* If you are not using a popup flow, you do not need this hook.
|
|
232
|
+
*/
|
|
233
|
+
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (sessionId: string, code: string) => Promise<unknown>): void;
|
|
234
|
+
/**
|
|
235
|
+
* Default popup callback UI for popup-based OAuth flows.
|
|
236
|
+
*
|
|
237
|
+
* This component reads the OAuth `code` and `state/sessionId`, notifies the
|
|
238
|
+
* opener window, waits for success/failure, and closes the popup on success.
|
|
239
|
+
*
|
|
240
|
+
* It is intentionally optional: apps can replace it with their own callback
|
|
241
|
+
* page UI or skip popup auth entirely and call `finishAuth(sessionId, code)`
|
|
242
|
+
* from any callback route they control.
|
|
243
|
+
*/
|
|
244
|
+
declare function McpOAuthCallbackContent({ code, sessionId, title, initialStatus, loadingFallback, rootStyle, cardStyle, titleStyle, messageStyle, renderContainer, debugPhase, }: McpOAuthCallbackContentProps): JSX.Element;
|
|
245
|
+
/**
|
|
246
|
+
* Tiny fallback component for Suspense-wrapped callback pages.
|
|
247
|
+
*/
|
|
248
|
+
declare function McpOAuthCallbackFallback({ children, }: {
|
|
249
|
+
children?: ReactNode;
|
|
250
|
+
}): JSX.Element;
|
|
251
|
+
|
|
173
252
|
/**
|
|
174
253
|
* Hook to host an MCP App in a React component
|
|
175
254
|
*
|
|
@@ -256,4 +335,4 @@ declare function useMcpApps(mcpClient: McpClient | null): {
|
|
|
256
335
|
};
|
|
257
336
|
declare function getMcpAppMetadata(mcpClient: McpClient | null, toolName: string, input?: Record<string, unknown> | null): McpAppMetadata | undefined;
|
|
258
337
|
|
|
259
|
-
export { AppHost, type McpAppMetadata, McpAppRenderer, type McpAppRendererHandle, type McpAppRendererProps, type McpClient$1 as McpClient, type McpConnection, McpConnectionEvent, McpConnectionState, SSEClient, ToolInfo, type UseMcpOptions, getMcpAppMetadata, useAppHost, useMcp, useMcpApps };
|
|
338
|
+
export { AppHost, type McpAppMetadata, McpAppRenderer, type McpAppRendererHandle, type McpAppRendererProps, type McpClient$1 as McpClient, type McpConnection, McpConnectionEvent, McpConnectionState, McpOAuthCallbackContent, type McpOAuthCallbackContentProps, McpOAuthCallbackFallback, type OAuthPopupConnectionLike, type OAuthPopupRedirectOptions, SSEClient, ToolInfo, type UseMcpOptions, createOAuthPopupRedirectHandler, getMcpAppMetadata, openCenteredPopup, useAppHost, useMcp, useMcpApps, useMcpOAuthPopup };
|
package/dist/client/react.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { c as McpConnectionState, M as McpConnectionEvent } from '../events-CK3N
|
|
|
4
4
|
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, d as McpObservabilityEvent } from '../events-CK3N--3g.js';
|
|
5
5
|
import { v as ToolInfo, k as FinishAuthResult, n as ListToolsRpcResult, L as ListPromptsResult, l as ListResourcesResult } from '../types-CfCoIsWI.js';
|
|
6
6
|
export { p as McpRpcRequest, q as McpRpcResponse } from '../types-CfCoIsWI.js';
|
|
7
|
-
import React$1 from 'react';
|
|
7
|
+
import React$1, { ReactNode, CSSProperties } from 'react';
|
|
8
8
|
import '@modelcontextprotocol/ext-apps/app-bridge';
|
|
9
9
|
import '@modelcontextprotocol/sdk/types.js';
|
|
10
10
|
|
|
@@ -170,6 +170,85 @@ interface McpClient$1 {
|
|
|
170
170
|
*/
|
|
171
171
|
declare function useMcp(options: UseMcpOptions): McpClient$1;
|
|
172
172
|
|
|
173
|
+
interface OAuthPopupConnectionLike {
|
|
174
|
+
sessionId: string;
|
|
175
|
+
state: string;
|
|
176
|
+
error?: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Optional helpers for popup-based OAuth UX.
|
|
180
|
+
*
|
|
181
|
+
* These utilities sit on top of the core MCP auth primitives:
|
|
182
|
+
* - `useMcp({ onRedirect })` to decide how auth navigation happens
|
|
183
|
+
* - `finishAuth(sessionId, code)` to complete code exchange
|
|
184
|
+
*
|
|
185
|
+
* Consumers are free to:
|
|
186
|
+
* - use these helpers as-is for a turnkey popup flow
|
|
187
|
+
* - build their own popup UI/message bridge
|
|
188
|
+
* - skip popups entirely and handle auth in a normal callback page
|
|
189
|
+
*/
|
|
190
|
+
interface OAuthPopupRedirectOptions {
|
|
191
|
+
width?: number;
|
|
192
|
+
height?: number;
|
|
193
|
+
windowName?: string;
|
|
194
|
+
features?: string[];
|
|
195
|
+
onBlocked?: (url: string) => void;
|
|
196
|
+
}
|
|
197
|
+
interface McpOAuthCallbackContentProps {
|
|
198
|
+
code?: string | null;
|
|
199
|
+
sessionId?: string | null;
|
|
200
|
+
title?: ReactNode;
|
|
201
|
+
initialStatus?: string;
|
|
202
|
+
loadingFallback?: ReactNode;
|
|
203
|
+
rootStyle?: CSSProperties;
|
|
204
|
+
cardStyle?: CSSProperties;
|
|
205
|
+
titleStyle?: CSSProperties;
|
|
206
|
+
messageStyle?: CSSProperties;
|
|
207
|
+
renderContainer?: (content: ReactNode) => ReactNode;
|
|
208
|
+
debugPhase?: 'loading' | 'success' | 'error';
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Opens a centered popup window for OAuth.
|
|
212
|
+
*
|
|
213
|
+
* Convenience only: callers can replace this entirely with their own popup,
|
|
214
|
+
* modal, redirect, or router-based navigation strategy.
|
|
215
|
+
*/
|
|
216
|
+
declare function openCenteredPopup(url: string, options?: OAuthPopupRedirectOptions): Window | null;
|
|
217
|
+
/**
|
|
218
|
+
* Creates an `onRedirect` handler suitable for `useMcp({ onRedirect })`.
|
|
219
|
+
*
|
|
220
|
+
* This is the simplest popup entry point, but it is intentionally optional.
|
|
221
|
+
* Applications can provide any redirect handler they want, including full-page
|
|
222
|
+
* navigation or a completely custom popup implementation.
|
|
223
|
+
*/
|
|
224
|
+
declare function createOAuthPopupRedirectHandler(options?: OAuthPopupRedirectOptions): (url: string) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Handles opener-side popup coordination for OAuth code exchange.
|
|
227
|
+
*
|
|
228
|
+
* Use this when you want popup auth but do not want to reimplement the
|
|
229
|
+
* postMessage wiring between the main app window and the popup callback page.
|
|
230
|
+
*
|
|
231
|
+
* If you are not using a popup flow, you do not need this hook.
|
|
232
|
+
*/
|
|
233
|
+
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (sessionId: string, code: string) => Promise<unknown>): void;
|
|
234
|
+
/**
|
|
235
|
+
* Default popup callback UI for popup-based OAuth flows.
|
|
236
|
+
*
|
|
237
|
+
* This component reads the OAuth `code` and `state/sessionId`, notifies the
|
|
238
|
+
* opener window, waits for success/failure, and closes the popup on success.
|
|
239
|
+
*
|
|
240
|
+
* It is intentionally optional: apps can replace it with their own callback
|
|
241
|
+
* page UI or skip popup auth entirely and call `finishAuth(sessionId, code)`
|
|
242
|
+
* from any callback route they control.
|
|
243
|
+
*/
|
|
244
|
+
declare function McpOAuthCallbackContent({ code, sessionId, title, initialStatus, loadingFallback, rootStyle, cardStyle, titleStyle, messageStyle, renderContainer, debugPhase, }: McpOAuthCallbackContentProps): JSX.Element;
|
|
245
|
+
/**
|
|
246
|
+
* Tiny fallback component for Suspense-wrapped callback pages.
|
|
247
|
+
*/
|
|
248
|
+
declare function McpOAuthCallbackFallback({ children, }: {
|
|
249
|
+
children?: ReactNode;
|
|
250
|
+
}): JSX.Element;
|
|
251
|
+
|
|
173
252
|
/**
|
|
174
253
|
* Hook to host an MCP App in a React component
|
|
175
254
|
*
|
|
@@ -256,4 +335,4 @@ declare function useMcpApps(mcpClient: McpClient | null): {
|
|
|
256
335
|
};
|
|
257
336
|
declare function getMcpAppMetadata(mcpClient: McpClient | null, toolName: string, input?: Record<string, unknown> | null): McpAppMetadata | undefined;
|
|
258
337
|
|
|
259
|
-
export { AppHost, type McpAppMetadata, McpAppRenderer, type McpAppRendererHandle, type McpAppRendererProps, type McpClient$1 as McpClient, type McpConnection, McpConnectionEvent, McpConnectionState, SSEClient, ToolInfo, type UseMcpOptions, getMcpAppMetadata, useAppHost, useMcp, useMcpApps };
|
|
338
|
+
export { AppHost, type McpAppMetadata, McpAppRenderer, type McpAppRendererHandle, type McpAppRendererProps, type McpClient$1 as McpClient, type McpConnection, McpConnectionEvent, McpConnectionState, McpOAuthCallbackContent, type McpOAuthCallbackContentProps, McpOAuthCallbackFallback, type OAuthPopupConnectionLike, type OAuthPopupRedirectOptions, SSEClient, ToolInfo, type UseMcpOptions, createOAuthPopupRedirectHandler, getMcpAppMetadata, openCenteredPopup, useAppHost, useMcp, useMcpApps, useMcpOAuthPopup };
|
package/dist/client/react.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var nanoid = require('nanoid');
|
|
5
|
-
var appBridge = require('@modelcontextprotocol/ext-apps/app-bridge');
|
|
6
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var appBridge = require('@modelcontextprotocol/ext-apps/app-bridge');
|
|
7
7
|
|
|
8
8
|
var __defProp = Object.defineProperty;
|
|
9
9
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -315,12 +315,19 @@ function useMcp(options) {
|
|
|
315
315
|
const updateConnectionsFromEvent = react.useCallback((event) => {
|
|
316
316
|
if (!isMountedRef.current) return;
|
|
317
317
|
const isTransientReconnectState = (state) => state === "INITIALIZING" || state === "VALIDATING" || state === "RECONNECTING" || state === "CONNECTING" || state === "CONNECTED" || state === "DISCOVERING";
|
|
318
|
+
const getVisibleState = (incomingState, existingState, previousState) => {
|
|
319
|
+
if (incomingState === "INITIALIZING" && (existingState === "AUTHENTICATING" || existingState === "AUTHENTICATED" || previousState === "AUTHENTICATING" || previousState === "AUTHENTICATED")) {
|
|
320
|
+
return existingState === "AUTHENTICATED" || previousState === "AUTHENTICATED" ? "AUTHENTICATED" : "AUTHENTICATING";
|
|
321
|
+
}
|
|
322
|
+
return incomingState;
|
|
323
|
+
};
|
|
318
324
|
setConnections((prev) => {
|
|
319
325
|
switch (event.type) {
|
|
320
326
|
case "state_changed": {
|
|
321
327
|
const existing = prev.find((c) => c.sessionId === event.sessionId);
|
|
322
328
|
if (existing) {
|
|
323
|
-
const
|
|
329
|
+
const normalizedState = getVisibleState(event.state, existing.state, event.previousState);
|
|
330
|
+
const nextState = existing.state === "READY" && isTransientReconnectState(normalizedState) ? existing.state : normalizedState;
|
|
324
331
|
return prev.map(
|
|
325
332
|
(c) => c.sessionId === event.sessionId ? {
|
|
326
333
|
...c,
|
|
@@ -340,7 +347,9 @@ function useMcp(options) {
|
|
|
340
347
|
serverId: event.serverId,
|
|
341
348
|
serverName: event.serverName,
|
|
342
349
|
serverUrl: event.serverUrl,
|
|
343
|
-
|
|
350
|
+
// New connections do not have prior local state, so we normalize
|
|
351
|
+
// only against the server-reported previous state.
|
|
352
|
+
state: getVisibleState(event.state, void 0, event.previousState),
|
|
344
353
|
createdAt: event.createdAt ? new Date(event.createdAt) : void 0,
|
|
345
354
|
tools: []
|
|
346
355
|
}
|
|
@@ -593,6 +602,305 @@ function useMcp(options) {
|
|
|
593
602
|
]
|
|
594
603
|
);
|
|
595
604
|
}
|
|
605
|
+
var AUTH_CODE_MESSAGE = "MCP_AUTH_CODE";
|
|
606
|
+
var AUTH_RESULT_MESSAGE = "MCP_AUTH_RESULT";
|
|
607
|
+
function postPopupResult(popupWindow, result) {
|
|
608
|
+
popupWindow?.postMessage(
|
|
609
|
+
{
|
|
610
|
+
type: AUTH_RESULT_MESSAGE,
|
|
611
|
+
...result
|
|
612
|
+
},
|
|
613
|
+
window.location.origin
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
function openCenteredPopup(url, options = {}) {
|
|
617
|
+
const {
|
|
618
|
+
width = 600,
|
|
619
|
+
height = 700,
|
|
620
|
+
windowName = "mcp-auth-popup",
|
|
621
|
+
features = [],
|
|
622
|
+
onBlocked
|
|
623
|
+
} = options;
|
|
624
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
625
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
626
|
+
const featureList = [
|
|
627
|
+
`width=${width}`,
|
|
628
|
+
`height=${height}`,
|
|
629
|
+
`left=${left}`,
|
|
630
|
+
`top=${top}`,
|
|
631
|
+
"resizable=yes",
|
|
632
|
+
"scrollbars=yes",
|
|
633
|
+
"status=yes",
|
|
634
|
+
...features
|
|
635
|
+
].join(",");
|
|
636
|
+
const popup = window.open(url, windowName, featureList);
|
|
637
|
+
if (!popup) {
|
|
638
|
+
onBlocked?.(url);
|
|
639
|
+
}
|
|
640
|
+
return popup;
|
|
641
|
+
}
|
|
642
|
+
function createOAuthPopupRedirectHandler(options = {}) {
|
|
643
|
+
return (url) => {
|
|
644
|
+
openCenteredPopup(url, {
|
|
645
|
+
...options,
|
|
646
|
+
onBlocked: options.onBlocked ?? ((blockedUrl) => {
|
|
647
|
+
window.alert("Popup blocked! Allow popups for this site to complete authentication.");
|
|
648
|
+
window.location.href = blockedUrl;
|
|
649
|
+
})
|
|
650
|
+
});
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function useMcpOAuthPopup(connections, finishAuth) {
|
|
654
|
+
const pendingPopupsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
655
|
+
react.useEffect(() => {
|
|
656
|
+
const handleMessage = async (event) => {
|
|
657
|
+
if (event.origin !== window.location.origin) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (event.data?.type !== AUTH_CODE_MESSAGE || !event.data.code) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const popupWindow = event.source && "postMessage" in event.source ? event.source : null;
|
|
664
|
+
const targetSessionId = typeof event.data.sessionId === "string" ? event.data.sessionId : "";
|
|
665
|
+
if (!targetSessionId) {
|
|
666
|
+
postPopupResult(popupWindow, {
|
|
667
|
+
success: false,
|
|
668
|
+
error: "Missing OAuth session identifier"
|
|
669
|
+
});
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const targetSession = connections.find((connection) => connection.sessionId === targetSessionId);
|
|
673
|
+
if (!targetSession) {
|
|
674
|
+
postPopupResult(popupWindow, {
|
|
675
|
+
sessionId: targetSessionId,
|
|
676
|
+
success: false,
|
|
677
|
+
error: "OAuth session not found in the current client state"
|
|
678
|
+
});
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (popupWindow) {
|
|
682
|
+
pendingPopupsRef.current.set(targetSession.sessionId, popupWindow);
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
await finishAuth(targetSession.sessionId, event.data.code);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
pendingPopupsRef.current.delete(targetSession.sessionId);
|
|
688
|
+
postPopupResult(popupWindow, {
|
|
689
|
+
sessionId: targetSession.sessionId,
|
|
690
|
+
success: false,
|
|
691
|
+
error: error instanceof Error ? error.message : "Failed to finish auth"
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
window.addEventListener("message", handleMessage);
|
|
696
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
697
|
+
}, [connections, finishAuth]);
|
|
698
|
+
react.useEffect(() => {
|
|
699
|
+
for (const connection of connections) {
|
|
700
|
+
const popupWindow = pendingPopupsRef.current.get(connection.sessionId);
|
|
701
|
+
if (!popupWindow) {
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (connection.state === "AUTHENTICATED") {
|
|
705
|
+
postPopupResult(popupWindow, {
|
|
706
|
+
sessionId: connection.sessionId,
|
|
707
|
+
success: true
|
|
708
|
+
});
|
|
709
|
+
pendingPopupsRef.current.delete(connection.sessionId);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (connection.state === "FAILED") {
|
|
713
|
+
postPopupResult(popupWindow, {
|
|
714
|
+
sessionId: connection.sessionId,
|
|
715
|
+
success: false,
|
|
716
|
+
error: connection.error || "Failed to complete authorization"
|
|
717
|
+
});
|
|
718
|
+
pendingPopupsRef.current.delete(connection.sessionId);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}, [connections]);
|
|
722
|
+
}
|
|
723
|
+
function McpOAuthCallbackContent({
|
|
724
|
+
code,
|
|
725
|
+
sessionId,
|
|
726
|
+
title = "Verifying Authorization",
|
|
727
|
+
initialStatus = "Completing your authorization...",
|
|
728
|
+
loadingFallback = "Loading...",
|
|
729
|
+
rootStyle,
|
|
730
|
+
cardStyle,
|
|
731
|
+
titleStyle,
|
|
732
|
+
messageStyle,
|
|
733
|
+
renderContainer,
|
|
734
|
+
debugPhase
|
|
735
|
+
}) {
|
|
736
|
+
const [phase, setPhase] = react.useState(debugPhase || "loading");
|
|
737
|
+
const [errorMessage, setErrorMessage] = react.useState("");
|
|
738
|
+
const openerMissing = typeof window !== "undefined" ? !window.opener : false;
|
|
739
|
+
const missingCode = !code;
|
|
740
|
+
const missingSessionId = !sessionId;
|
|
741
|
+
const blockingError = openerMissing ? "Error: No opener window found. This window should be opened from the app." : missingCode ? "Error: No authorization code received." : missingSessionId ? "Error: No OAuth state received." : null;
|
|
742
|
+
react.useEffect(() => {
|
|
743
|
+
if (debugPhase) {
|
|
744
|
+
setPhase(debugPhase);
|
|
745
|
+
if (debugPhase === "error") setErrorMessage("Test error message representing a real failure.");
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
if (blockingError) {
|
|
749
|
+
setPhase("error");
|
|
750
|
+
setErrorMessage(blockingError);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
let closed = false;
|
|
754
|
+
const handleResult = (event) => {
|
|
755
|
+
if (event.origin !== window.location.origin) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
if (event.data?.type !== AUTH_RESULT_MESSAGE) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (event.data.sessionId !== sessionId) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
if (event.data.success) {
|
|
765
|
+
setPhase("success");
|
|
766
|
+
window.removeEventListener("message", handleResult);
|
|
767
|
+
closed = true;
|
|
768
|
+
window.setTimeout(() => window.close(), 1200);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const message = typeof event.data.error === "string" && event.data.error.length > 0 ? event.data.error : "Failed to complete authorization.";
|
|
772
|
+
setPhase("error");
|
|
773
|
+
setErrorMessage(message);
|
|
774
|
+
};
|
|
775
|
+
window.addEventListener("message", handleResult);
|
|
776
|
+
try {
|
|
777
|
+
window.opener.postMessage(
|
|
778
|
+
{ type: AUTH_CODE_MESSAGE, code, sessionId },
|
|
779
|
+
window.location.origin
|
|
780
|
+
);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
console.error("Failed to communicate with opener:", error);
|
|
783
|
+
window.setTimeout(() => {
|
|
784
|
+
setPhase("error");
|
|
785
|
+
setErrorMessage("Error: Could not communicate with main window.");
|
|
786
|
+
}, 0);
|
|
787
|
+
}
|
|
788
|
+
return () => {
|
|
789
|
+
if (!closed) {
|
|
790
|
+
window.removeEventListener("message", handleResult);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
}, [blockingError, code, sessionId, debugPhase]);
|
|
794
|
+
const loadingBubbles = /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "8px", justifyContent: "center", height: "12px", alignItems: "center" }, children: [0, 150, 300].map((delay) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
795
|
+
"span",
|
|
796
|
+
{
|
|
797
|
+
style: {
|
|
798
|
+
width: "8px",
|
|
799
|
+
height: "8px",
|
|
800
|
+
borderRadius: "50%",
|
|
801
|
+
backgroundColor: "currentColor",
|
|
802
|
+
opacity: 0.3,
|
|
803
|
+
animation: `mcp-pulse 1.2s ease-in-out infinite`,
|
|
804
|
+
animationDelay: `${delay}ms`
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
delay
|
|
808
|
+
)) });
|
|
809
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
810
|
+
"div",
|
|
811
|
+
{
|
|
812
|
+
style: {
|
|
813
|
+
display: "flex",
|
|
814
|
+
justifyContent: "center",
|
|
815
|
+
alignItems: "center",
|
|
816
|
+
minHeight: "100vh",
|
|
817
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
818
|
+
flexDirection: "column",
|
|
819
|
+
backgroundColor: "#fafafa",
|
|
820
|
+
color: "#18181b",
|
|
821
|
+
boxSizing: "border-box",
|
|
822
|
+
padding: "1.5rem",
|
|
823
|
+
...rootStyle
|
|
824
|
+
},
|
|
825
|
+
children: [
|
|
826
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
827
|
+
@keyframes mcp-pulse { 0%, 100% { transform: scale(0.8); opacity: 0.4; } 50% { transform: scale(1.2); opacity: 1; } }
|
|
828
|
+
@keyframes mcp-fade-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
|
829
|
+
` }),
|
|
830
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
831
|
+
"div",
|
|
832
|
+
{
|
|
833
|
+
style: {
|
|
834
|
+
backgroundColor: "#fff",
|
|
835
|
+
borderRadius: "20px",
|
|
836
|
+
boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.05)",
|
|
837
|
+
width: "100%",
|
|
838
|
+
maxWidth: "400px",
|
|
839
|
+
overflow: "hidden",
|
|
840
|
+
border: "1px solid #f4f4f5",
|
|
841
|
+
display: "flex",
|
|
842
|
+
flexDirection: "column",
|
|
843
|
+
...cardStyle
|
|
844
|
+
},
|
|
845
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "3rem 2rem", textAlign: "center", animation: "mcp-fade-up 0.4s ease-out" }, children: [
|
|
846
|
+
phase === "loading" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem", alignItems: "center" }, children: [
|
|
847
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", height: "48px", width: "48px", background: "#f8fafc", borderRadius: "12px", border: "1px solid #f1f5f9", color: "#64748b" }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) }) }),
|
|
848
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
849
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.125rem", fontWeight: 600, ...titleStyle }, children: title }),
|
|
850
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: "0.9rem", color: "#71717a", lineHeight: 1.5, ...messageStyle }, children: initialStatus })
|
|
851
|
+
] }),
|
|
852
|
+
loadingBubbles
|
|
853
|
+
] }),
|
|
854
|
+
phase === "success" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem", alignItems: "center" }, children: [
|
|
855
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { style: { color: "#10b981" }, width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
856
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
857
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 12l3 3 5-5" })
|
|
858
|
+
] }),
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 600, ...titleStyle }, children: "Connected" }),
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: "0.9rem", color: "#71717a", lineHeight: 1.5, ...messageStyle }, children: "Authorization complete. This window will close automatically." })
|
|
861
|
+
] }),
|
|
862
|
+
phase === "error" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem", alignItems: "center" }, children: [
|
|
863
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { style: { color: "#ef4444" }, width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
864
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
865
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 9l-6 6M9 9l6 6" })
|
|
866
|
+
] }),
|
|
867
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 600, ...titleStyle }, children: "Connection Failed" }),
|
|
868
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: "0.9rem", color: "#ef4444", fontWeight: 500, ...messageStyle }, children: errorMessage }),
|
|
869
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
870
|
+
"button",
|
|
871
|
+
{
|
|
872
|
+
onClick: () => window.close(),
|
|
873
|
+
style: {
|
|
874
|
+
marginTop: "0.5rem",
|
|
875
|
+
padding: "0.625rem 1.25rem",
|
|
876
|
+
border: "none",
|
|
877
|
+
borderRadius: "8px",
|
|
878
|
+
backgroundColor: "#fef2f2",
|
|
879
|
+
color: "#ef4444",
|
|
880
|
+
cursor: "pointer",
|
|
881
|
+
fontWeight: 600,
|
|
882
|
+
fontSize: "0.875rem"
|
|
883
|
+
},
|
|
884
|
+
children: "Close Window"
|
|
885
|
+
}
|
|
886
|
+
)
|
|
887
|
+
] })
|
|
888
|
+
] })
|
|
889
|
+
}
|
|
890
|
+
)
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
if (renderContainer) {
|
|
895
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderContainer(content) });
|
|
896
|
+
}
|
|
897
|
+
return content;
|
|
898
|
+
}
|
|
899
|
+
function McpOAuthCallbackFallback({
|
|
900
|
+
children = "Loading..."
|
|
901
|
+
}) {
|
|
902
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children || "Loading..." });
|
|
903
|
+
}
|
|
596
904
|
|
|
597
905
|
// src/client/core/constants.ts
|
|
598
906
|
var SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
|
|
@@ -1373,12 +1681,17 @@ exports.APP_HOST_DEFAULTS = APP_HOST_DEFAULTS;
|
|
|
1373
1681
|
exports.AppHost = AppHost;
|
|
1374
1682
|
exports.DEFAULT_MCP_APP_CSP = DEFAULT_MCP_APP_CSP;
|
|
1375
1683
|
exports.McpAppRenderer = McpAppRenderer;
|
|
1684
|
+
exports.McpOAuthCallbackContent = McpOAuthCallbackContent;
|
|
1685
|
+
exports.McpOAuthCallbackFallback = McpOAuthCallbackFallback;
|
|
1376
1686
|
exports.SANDBOX_PROXY_READY_METHOD = SANDBOX_PROXY_READY_METHOD;
|
|
1377
1687
|
exports.SANDBOX_RESOURCE_READY_METHOD = SANDBOX_RESOURCE_READY_METHOD;
|
|
1378
1688
|
exports.SSEClient = SSEClient;
|
|
1689
|
+
exports.createOAuthPopupRedirectHandler = createOAuthPopupRedirectHandler;
|
|
1379
1690
|
exports.getMcpAppMetadata = getMcpAppMetadata;
|
|
1691
|
+
exports.openCenteredPopup = openCenteredPopup;
|
|
1380
1692
|
exports.useAppHost = useAppHost;
|
|
1381
1693
|
exports.useMcp = useMcp;
|
|
1382
1694
|
exports.useMcpApps = useMcpApps;
|
|
1695
|
+
exports.useMcpOAuthPopup = useMcpOAuthPopup;
|
|
1383
1696
|
//# sourceMappingURL=react.js.map
|
|
1384
1697
|
//# sourceMappingURL=react.js.map
|