@mcp-ts/sdk 2.3.4 → 2.4.0
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/README.md +113 -30
- package/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/mastra-adapter.d.mts +1 -1
- package/dist/adapters/mastra-adapter.d.ts +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +9 -8
- package/dist/client/react.d.ts +9 -8
- package/dist/client/react.js +66 -26
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +67 -27
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +6 -5
- package/dist/client/vue.d.ts +6 -5
- package/dist/client/vue.js +27 -17
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +27 -17
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{index-Cfjsme-a.d.mts → index-ByIjEReo.d.mts} +2 -2
- package/dist/{index-CmjMd2ac.d.ts → index-CtXvKl8N.d.ts} +2 -2
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +728 -306
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +722 -304
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-C7hGqzgM.d.mts → multi-session-client-CIMUGF8S.d.mts} +15 -33
- package/dist/{multi-session-client-C_kPHpD5.d.ts → multi-session-client-CnvZEGPY.d.ts} +15 -33
- package/dist/server/index.d.mts +45 -17
- package/dist/server/index.d.ts +45 -17
- package/dist/server/index.js +720 -300
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +716 -299
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +9 -8
- package/dist/shared/index.d.ts +9 -8
- package/dist/shared/index.js +7 -5
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +5 -4
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{tool-router-BMzhoNYt.d.ts → tool-router-CZMrOG8J.d.ts} +1 -1
- package/dist/{tool-router-BhHsvBfP.d.mts → tool-router-CuApsDiV.d.mts} +1 -1
- package/dist/{types-CxFaaZrM.d.mts → types-DCk_IF4L.d.mts} +5 -3
- package/dist/{types-CxFaaZrM.d.ts → types-DCk_IF4L.d.ts} +5 -3
- package/migrations/neon/20260513010000_install_mcp_sessions.sql +40 -3
- package/migrations/neon/20260513020000_add_session_cleanup_cron.sql +4 -4
- package/migrations/supabase/20260330195700_install_mcp_sessions.sql +67 -3
- package/migrations/supabase/20260421010000_add_session_cleanup_cron.sql +6 -6
- package/package.json +13 -3
- package/src/client/core/sse-client.ts +2 -2
- package/src/client/react/index.ts +1 -1
- package/src/client/react/oauth-popup.tsx +34 -13
- package/src/client/react/use-mcp.ts +19 -45
- package/src/client/utils/session-state.ts +43 -0
- package/src/client/vue/use-mcp.ts +18 -47
- package/src/server/handlers/sse-handler.ts +9 -4
- package/src/server/mcp/multi-session-client.ts +2 -6
- package/src/server/mcp/oauth-client.ts +73 -101
- package/src/server/mcp/storage-oauth-provider.ts +79 -50
- package/src/server/storage/file-backend.ts +74 -18
- package/src/server/storage/index.ts +2 -4
- package/src/server/storage/memory-backend.ts +49 -13
- package/src/server/storage/neon-backend.ts +201 -68
- package/src/server/storage/redis-backend.ts +81 -23
- package/src/server/storage/session-lifecycle.ts +78 -0
- package/src/server/storage/sqlite-backend.ts +89 -15
- package/src/server/storage/supabase-backend.ts +188 -63
- package/src/server/storage/types.ts +49 -16
- package/src/shared/constants.ts +5 -6
- package/src/shared/types.ts +5 -3
- package/src/shared/utils.ts +26 -0
package/dist/client/react.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-
|
|
2
|
-
export { A as APP_HOST_DEFAULTS, D as DEFAULT_MCP_APP_CSP, S as SANDBOX_PROXY_READY_METHOD, b as SANDBOX_RESOURCE_READY_METHOD, d as SSEClientOptions } from '../index-
|
|
1
|
+
import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-ByIjEReo.mjs';
|
|
2
|
+
export { A as APP_HOST_DEFAULTS, D as DEFAULT_MCP_APP_CSP, S as SANDBOX_PROXY_READY_METHOD, b as SANDBOX_RESOURCE_READY_METHOD, d as SSEClientOptions } from '../index-ByIjEReo.mjs';
|
|
3
3
|
import { c as McpConnectionState, M as McpConnectionEvent } from '../events-CK3N--3g.mjs';
|
|
4
4
|
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, d as McpObservabilityEvent } from '../events-CK3N--3g.mjs';
|
|
5
|
-
import { v as ToolInfo, k as FinishAuthResult, o as ListToolsRpcResult, L as ListPromptsResult, m as ListResourcesResult } from '../types-
|
|
6
|
-
export { q as McpRpcRequest, r as McpRpcResponse } from '../types-
|
|
5
|
+
import { v as ToolInfo, k as FinishAuthResult, o as ListToolsRpcResult, L as ListPromptsResult, m as ListResourcesResult } from '../types-DCk_IF4L.mjs';
|
|
6
|
+
export { q as McpRpcRequest, r as McpRpcResponse } from '../types-DCk_IF4L.mjs';
|
|
7
7
|
import React$1, { ReactNode, CSSProperties } from 'react';
|
|
8
8
|
import '@modelcontextprotocol/ext-apps/app-bridge';
|
|
9
9
|
import '@modelcontextprotocol/sdk/types.js';
|
|
@@ -72,6 +72,7 @@ interface McpConnection {
|
|
|
72
72
|
authUrl?: string;
|
|
73
73
|
error?: string;
|
|
74
74
|
createdAt?: Date;
|
|
75
|
+
updatedAt?: Date;
|
|
75
76
|
}
|
|
76
77
|
interface McpClient$1 {
|
|
77
78
|
/**
|
|
@@ -141,7 +142,7 @@ interface McpClient$1 {
|
|
|
141
142
|
/**
|
|
142
143
|
* Complete OAuth authorization
|
|
143
144
|
*/
|
|
144
|
-
finishAuth: (
|
|
145
|
+
finishAuth: (state: string, code: string) => Promise<FinishAuthResult>;
|
|
145
146
|
/**
|
|
146
147
|
* Explicitly resume OAuth flow for an existing session
|
|
147
148
|
*/
|
|
@@ -190,7 +191,7 @@ interface OAuthPopupConnectionLike {
|
|
|
190
191
|
*
|
|
191
192
|
* These utilities sit on top of the core MCP auth primitives:
|
|
192
193
|
* - `useMcp({ onRedirect })` to decide how auth navigation happens
|
|
193
|
-
* - `finishAuth(
|
|
194
|
+
* - `finishAuth(state, code)` to complete code exchange
|
|
194
195
|
*
|
|
195
196
|
* Consumers are free to:
|
|
196
197
|
* - use these helpers as-is for a turnkey popup flow
|
|
@@ -240,7 +241,7 @@ declare function createOAuthPopupRedirectHandler(options?: OAuthPopupRedirectOpt
|
|
|
240
241
|
*
|
|
241
242
|
* If you are not using a popup flow, you do not need this hook.
|
|
242
243
|
*/
|
|
243
|
-
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (
|
|
244
|
+
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (state: string, code: string) => Promise<unknown>): void;
|
|
244
245
|
/**
|
|
245
246
|
* Default popup callback UI for popup-based OAuth flows.
|
|
246
247
|
*
|
|
@@ -248,7 +249,7 @@ declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
248
249
|
* opener window, waits for success/failure, and closes the popup on success.
|
|
249
250
|
*
|
|
250
251
|
* It is intentionally optional: apps can replace it with their own callback
|
|
251
|
-
* page UI or skip popup auth entirely and call `finishAuth(
|
|
252
|
+
* page UI or skip popup auth entirely and call `finishAuth(state, code)`
|
|
252
253
|
* from any callback route they control.
|
|
253
254
|
*/
|
|
254
255
|
declare function McpOAuthCallbackContent({ code, sessionId, title, initialStatus, loadingFallback, rootStyle, cardStyle, titleStyle, messageStyle, renderContainer, debugPhase, }: McpOAuthCallbackContentProps): JSX.Element;
|
package/dist/client/react.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-
|
|
2
|
-
export { A as APP_HOST_DEFAULTS, D as DEFAULT_MCP_APP_CSP, S as SANDBOX_PROXY_READY_METHOD, b as SANDBOX_RESOURCE_READY_METHOD, d as SSEClientOptions } from '../index-
|
|
1
|
+
import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-CtXvKl8N.js';
|
|
2
|
+
export { A as APP_HOST_DEFAULTS, D as DEFAULT_MCP_APP_CSP, S as SANDBOX_PROXY_READY_METHOD, b as SANDBOX_RESOURCE_READY_METHOD, d as SSEClientOptions } from '../index-CtXvKl8N.js';
|
|
3
3
|
import { c as McpConnectionState, M as McpConnectionEvent } from '../events-CK3N--3g.js';
|
|
4
4
|
export { D as Disposable, a as DisposableStore, E as Emitter, b as Event, d as McpObservabilityEvent } from '../events-CK3N--3g.js';
|
|
5
|
-
import { v as ToolInfo, k as FinishAuthResult, o as ListToolsRpcResult, L as ListPromptsResult, m as ListResourcesResult } from '../types-
|
|
6
|
-
export { q as McpRpcRequest, r as McpRpcResponse } from '../types-
|
|
5
|
+
import { v as ToolInfo, k as FinishAuthResult, o as ListToolsRpcResult, L as ListPromptsResult, m as ListResourcesResult } from '../types-DCk_IF4L.js';
|
|
6
|
+
export { q as McpRpcRequest, r as McpRpcResponse } from '../types-DCk_IF4L.js';
|
|
7
7
|
import React$1, { ReactNode, CSSProperties } from 'react';
|
|
8
8
|
import '@modelcontextprotocol/ext-apps/app-bridge';
|
|
9
9
|
import '@modelcontextprotocol/sdk/types.js';
|
|
@@ -72,6 +72,7 @@ interface McpConnection {
|
|
|
72
72
|
authUrl?: string;
|
|
73
73
|
error?: string;
|
|
74
74
|
createdAt?: Date;
|
|
75
|
+
updatedAt?: Date;
|
|
75
76
|
}
|
|
76
77
|
interface McpClient$1 {
|
|
77
78
|
/**
|
|
@@ -141,7 +142,7 @@ interface McpClient$1 {
|
|
|
141
142
|
/**
|
|
142
143
|
* Complete OAuth authorization
|
|
143
144
|
*/
|
|
144
|
-
finishAuth: (
|
|
145
|
+
finishAuth: (state: string, code: string) => Promise<FinishAuthResult>;
|
|
145
146
|
/**
|
|
146
147
|
* Explicitly resume OAuth flow for an existing session
|
|
147
148
|
*/
|
|
@@ -190,7 +191,7 @@ interface OAuthPopupConnectionLike {
|
|
|
190
191
|
*
|
|
191
192
|
* These utilities sit on top of the core MCP auth primitives:
|
|
192
193
|
* - `useMcp({ onRedirect })` to decide how auth navigation happens
|
|
193
|
-
* - `finishAuth(
|
|
194
|
+
* - `finishAuth(state, code)` to complete code exchange
|
|
194
195
|
*
|
|
195
196
|
* Consumers are free to:
|
|
196
197
|
* - use these helpers as-is for a turnkey popup flow
|
|
@@ -240,7 +241,7 @@ declare function createOAuthPopupRedirectHandler(options?: OAuthPopupRedirectOpt
|
|
|
240
241
|
*
|
|
241
242
|
* If you are not using a popup flow, you do not need this hook.
|
|
242
243
|
*/
|
|
243
|
-
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (
|
|
244
|
+
declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(connections: TConnection[], finishAuth: (state: string, code: string) => Promise<unknown>): void;
|
|
244
245
|
/**
|
|
245
246
|
* Default popup callback UI for popup-based OAuth flows.
|
|
246
247
|
*
|
|
@@ -248,7 +249,7 @@ declare function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
248
249
|
* opener window, waits for success/failure, and closes the popup on success.
|
|
249
250
|
*
|
|
250
251
|
* It is intentionally optional: apps can replace it with their own callback
|
|
251
|
-
* page UI or skip popup auth entirely and call `finishAuth(
|
|
252
|
+
* page UI or skip popup auth entirely and call `finishAuth(state, code)`
|
|
252
253
|
* from any callback route they control.
|
|
253
254
|
*/
|
|
254
255
|
declare function McpOAuthCallbackContent({ code, sessionId, title, initialStatus, loadingFallback, rootStyle, cardStyle, titleStyle, messageStyle, renderContainer, debugPhase, }: McpOAuthCallbackContentProps): JSX.Element;
|
package/dist/client/react.js
CHANGED
|
@@ -50,8 +50,8 @@ var SSEClient = class {
|
|
|
50
50
|
async getSession(sessionId) {
|
|
51
51
|
return this.sendRequest("getSession", { sessionId });
|
|
52
52
|
}
|
|
53
|
-
async finishAuth(
|
|
54
|
-
return this.sendRequest("finishAuth", {
|
|
53
|
+
async finishAuth(state, code) {
|
|
54
|
+
return this.sendRequest("finishAuth", { state, code });
|
|
55
55
|
}
|
|
56
56
|
async listPrompts(sessionId) {
|
|
57
57
|
return this.sendRequest("listPrompts", { sessionId });
|
|
@@ -253,6 +253,20 @@ var SSEClient = class {
|
|
|
253
253
|
}
|
|
254
254
|
};
|
|
255
255
|
|
|
256
|
+
// src/client/utils/session-state.ts
|
|
257
|
+
function getInitialConnectionState(status) {
|
|
258
|
+
return status === "active" ? "VALIDATING" : "AUTHENTICATING";
|
|
259
|
+
}
|
|
260
|
+
function isTransientReconnectState(state) {
|
|
261
|
+
return state === "INITIALIZING" || state === "VALIDATING" || state === "RECONNECTING" || state === "CONNECTING" || state === "CONNECTED" || state === "DISCOVERING";
|
|
262
|
+
}
|
|
263
|
+
function getVisibleConnectionState(incomingState, existingState, previousState) {
|
|
264
|
+
if (incomingState === "INITIALIZING" && (existingState === "AUTHENTICATING" || existingState === "AUTHENTICATED" || previousState === "AUTHENTICATING" || previousState === "AUTHENTICATED")) {
|
|
265
|
+
return existingState === "AUTHENTICATED" || previousState === "AUTHENTICATED" ? "AUTHENTICATED" : "AUTHENTICATING";
|
|
266
|
+
}
|
|
267
|
+
return incomingState;
|
|
268
|
+
}
|
|
269
|
+
|
|
256
270
|
// src/client/react/use-mcp.ts
|
|
257
271
|
function useMcp(options) {
|
|
258
272
|
const {
|
|
@@ -310,26 +324,21 @@ function useMcp(options) {
|
|
|
310
324
|
}, [url, userId, authToken, autoConnect, autoInitialize]);
|
|
311
325
|
const updateConnectionsFromEvent = react.useCallback((event) => {
|
|
312
326
|
if (!isMountedRef.current) return;
|
|
313
|
-
const isTransientReconnectState = (state) => state === "INITIALIZING" || state === "VALIDATING" || state === "RECONNECTING" || state === "CONNECTING" || state === "CONNECTED" || state === "DISCOVERING";
|
|
314
|
-
const getVisibleState = (incomingState, existingState, previousState) => {
|
|
315
|
-
if (incomingState === "INITIALIZING" && (existingState === "AUTHENTICATING" || existingState === "AUTHENTICATED" || previousState === "AUTHENTICATING" || previousState === "AUTHENTICATED")) {
|
|
316
|
-
return existingState === "AUTHENTICATED" || previousState === "AUTHENTICATED" ? "AUTHENTICATED" : "AUTHENTICATING";
|
|
317
|
-
}
|
|
318
|
-
return incomingState;
|
|
319
|
-
};
|
|
320
327
|
setConnections((prev) => {
|
|
321
328
|
switch (event.type) {
|
|
322
329
|
case "state_changed": {
|
|
323
330
|
const existing = prev.find((c) => c.sessionId === event.sessionId);
|
|
324
331
|
if (existing) {
|
|
325
|
-
const normalizedState =
|
|
332
|
+
const normalizedState = getVisibleConnectionState(event.state, existing.state, event.previousState);
|
|
326
333
|
const nextState = existing.state === "READY" && isTransientReconnectState(normalizedState) ? existing.state : normalizedState;
|
|
334
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
327
335
|
return prev.map(
|
|
328
336
|
(c) => c.sessionId === event.sessionId ? {
|
|
329
337
|
...c,
|
|
330
338
|
state: nextState,
|
|
331
339
|
// update createdAt if present in event, otherwise keep existing
|
|
332
|
-
createdAt: event.createdAt ? new Date(event.createdAt) : c.createdAt
|
|
340
|
+
createdAt: event.createdAt ? new Date(event.createdAt) : c.createdAt,
|
|
341
|
+
updatedAt
|
|
333
342
|
} : c
|
|
334
343
|
);
|
|
335
344
|
} else {
|
|
@@ -345,8 +354,9 @@ function useMcp(options) {
|
|
|
345
354
|
serverUrl: event.serverUrl,
|
|
346
355
|
// New connections do not have prior local state, so we normalize
|
|
347
356
|
// only against the server-reported previous state.
|
|
348
|
-
state:
|
|
357
|
+
state: getVisibleConnectionState(event.state, void 0, event.previousState),
|
|
349
358
|
createdAt: event.createdAt ? new Date(event.createdAt) : void 0,
|
|
359
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
350
360
|
tools: []
|
|
351
361
|
}
|
|
352
362
|
];
|
|
@@ -357,7 +367,7 @@ function useMcp(options) {
|
|
|
357
367
|
clientRef.current.preloadToolUiResources(event.sessionId, event.tools);
|
|
358
368
|
}
|
|
359
369
|
return prev.map(
|
|
360
|
-
(c) => c.sessionId === event.sessionId ? { ...c, tools: event.tools, state: "READY" } : c
|
|
370
|
+
(c) => c.sessionId === event.sessionId ? { ...c, tools: event.tools, state: "READY", updatedAt: /* @__PURE__ */ new Date() } : c
|
|
361
371
|
);
|
|
362
372
|
}
|
|
363
373
|
case "auth_required": {
|
|
@@ -412,8 +422,9 @@ function useMcp(options) {
|
|
|
412
422
|
serverName: s.serverName ?? "Unknown Server",
|
|
413
423
|
serverUrl: s.serverUrl,
|
|
414
424
|
transport: s.transport,
|
|
415
|
-
state: s.
|
|
425
|
+
state: getInitialConnectionState(s.status),
|
|
416
426
|
createdAt: new Date(s.createdAt),
|
|
427
|
+
updatedAt: new Date(s.updatedAt ?? s.createdAt),
|
|
417
428
|
tools: []
|
|
418
429
|
}))
|
|
419
430
|
);
|
|
@@ -422,7 +433,7 @@ function useMcp(options) {
|
|
|
422
433
|
sessions.map(async (session) => {
|
|
423
434
|
if (clientRef.current) {
|
|
424
435
|
try {
|
|
425
|
-
if (session.
|
|
436
|
+
if (session.status !== "active") {
|
|
426
437
|
return;
|
|
427
438
|
}
|
|
428
439
|
suppressAuthRedirectSessionsRef.current.add(session.sessionId);
|
|
@@ -493,11 +504,11 @@ function useMcp(options) {
|
|
|
493
504
|
const disconnectSSE = react.useCallback(() => {
|
|
494
505
|
clientRef.current?.disconnect();
|
|
495
506
|
}, []);
|
|
496
|
-
const finishAuth = react.useCallback(async (
|
|
507
|
+
const finishAuth = react.useCallback(async (state, code) => {
|
|
497
508
|
if (!clientRef.current) {
|
|
498
509
|
throw new Error("SSE client not initialized");
|
|
499
510
|
}
|
|
500
|
-
return await clientRef.current.finishAuth(
|
|
511
|
+
return await clientRef.current.finishAuth(state, code);
|
|
501
512
|
}, []);
|
|
502
513
|
const resumeAuth = react.useCallback(async (sessionId) => {
|
|
503
514
|
if (!clientRef.current) {
|
|
@@ -621,6 +632,27 @@ function useMcp(options) {
|
|
|
621
632
|
]
|
|
622
633
|
);
|
|
623
634
|
}
|
|
635
|
+
var OAUTH_STATE_SEPARATOR = ".";
|
|
636
|
+
nanoid.customAlphabet(
|
|
637
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
638
|
+
1
|
|
639
|
+
);
|
|
640
|
+
nanoid.customAlphabet(
|
|
641
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
642
|
+
11
|
|
643
|
+
);
|
|
644
|
+
function parseOAuthState(state) {
|
|
645
|
+
const separatorIndex = state.indexOf(OAUTH_STATE_SEPARATOR);
|
|
646
|
+
if (separatorIndex <= 0 || separatorIndex === state.length - 1) {
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
const nonce = state.slice(0, separatorIndex);
|
|
650
|
+
const sessionId = state.slice(separatorIndex + 1);
|
|
651
|
+
if (!nonce || !sessionId || sessionId.includes(OAUTH_STATE_SEPARATOR)) {
|
|
652
|
+
return void 0;
|
|
653
|
+
}
|
|
654
|
+
return { nonce, sessionId };
|
|
655
|
+
}
|
|
624
656
|
var AUTH_CODE_MESSAGE = "MCP_AUTH_CODE";
|
|
625
657
|
var AUTH_RESULT_MESSAGE = "MCP_AUTH_RESULT";
|
|
626
658
|
var AUTH_CHANNEL_NAME = "mcp-auth-channel";
|
|
@@ -699,9 +731,10 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
699
731
|
return;
|
|
700
732
|
}
|
|
701
733
|
const popupWindow = event.source && "postMessage" in event.source ? event.source : null;
|
|
702
|
-
const
|
|
734
|
+
const rawState = typeof event.data.state === "string" ? event.data.state : typeof event.data.sessionId === "string" ? event.data.sessionId : "";
|
|
735
|
+
const targetSessionId = rawState ? parseOAuthState(rawState)?.sessionId || rawState : "";
|
|
703
736
|
if (popupWindow && targetSessionId) {
|
|
704
|
-
pendingPopupsRef.current.set(targetSessionId, popupWindow);
|
|
737
|
+
pendingPopupsRef.current.set(targetSessionId, { popupWindow, state: rawState });
|
|
705
738
|
}
|
|
706
739
|
if (!targetSessionId) {
|
|
707
740
|
if (popupWindow) {
|
|
@@ -717,6 +750,7 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
717
750
|
if (popupWindow) {
|
|
718
751
|
postPopupResult(popupWindow, {
|
|
719
752
|
sessionId: targetSessionId,
|
|
753
|
+
state: rawState,
|
|
720
754
|
success: false,
|
|
721
755
|
error: "OAuth session not found in the current client state"
|
|
722
756
|
});
|
|
@@ -729,13 +763,14 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
729
763
|
}
|
|
730
764
|
processingCodesRef.current.add(codeKey);
|
|
731
765
|
try {
|
|
732
|
-
await finishAuth(
|
|
766
|
+
await finishAuth(rawState, code);
|
|
733
767
|
} catch (error) {
|
|
734
768
|
processingCodesRef.current.delete(codeKey);
|
|
735
769
|
pendingPopupsRef.current.delete(targetSession.sessionId);
|
|
736
770
|
if (popupWindow) {
|
|
737
771
|
postPopupResult(popupWindow, {
|
|
738
|
-
sessionId:
|
|
772
|
+
sessionId: rawState,
|
|
773
|
+
state: rawState,
|
|
739
774
|
success: false,
|
|
740
775
|
error: error instanceof Error ? error.message : "Failed to finish auth"
|
|
741
776
|
});
|
|
@@ -758,10 +793,13 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
758
793
|
}, [connections, finishAuth]);
|
|
759
794
|
react.useEffect(() => {
|
|
760
795
|
for (const connection of connections) {
|
|
761
|
-
const
|
|
796
|
+
const pendingPopup = pendingPopupsRef.current.get(connection.sessionId);
|
|
797
|
+
const popupWindow = pendingPopup?.popupWindow || null;
|
|
798
|
+
const resultState = pendingPopup?.state || connection.sessionId;
|
|
762
799
|
if (connection.state === "AUTHENTICATED" || connection.state === "READY" || connection.state === "CONNECTED") {
|
|
763
800
|
postPopupResult(popupWindow, {
|
|
764
|
-
sessionId:
|
|
801
|
+
sessionId: resultState,
|
|
802
|
+
state: resultState,
|
|
765
803
|
success: true
|
|
766
804
|
});
|
|
767
805
|
for (const codeKey of processingCodesRef.current) {
|
|
@@ -774,7 +812,8 @@ function useMcpOAuthPopup(connections, finishAuth) {
|
|
|
774
812
|
}
|
|
775
813
|
if (connection.state === "FAILED") {
|
|
776
814
|
postPopupResult(popupWindow, {
|
|
777
|
-
sessionId:
|
|
815
|
+
sessionId: resultState,
|
|
816
|
+
state: resultState,
|
|
778
817
|
success: false,
|
|
779
818
|
error: connection.error || "Failed to complete authorization"
|
|
780
819
|
});
|
|
@@ -826,7 +865,8 @@ function McpOAuthCallbackContent({
|
|
|
826
865
|
if (event.data?.type !== AUTH_RESULT_MESSAGE) {
|
|
827
866
|
return;
|
|
828
867
|
}
|
|
829
|
-
|
|
868
|
+
const resultState = typeof event.data.state === "string" ? event.data.state : typeof event.data.sessionId === "string" ? event.data.sessionId : "";
|
|
869
|
+
if (resultState !== sessionId) {
|
|
830
870
|
return;
|
|
831
871
|
}
|
|
832
872
|
if (event.data.success) {
|
|
@@ -843,7 +883,7 @@ function McpOAuthCallbackContent({
|
|
|
843
883
|
};
|
|
844
884
|
window.addEventListener("message", handleResult);
|
|
845
885
|
channel?.addEventListener("message", handleResult);
|
|
846
|
-
const payload = { type: AUTH_CODE_MESSAGE, code, sessionId };
|
|
886
|
+
const payload = { type: AUTH_CODE_MESSAGE, code, state: sessionId, sessionId };
|
|
847
887
|
if (window.opener) {
|
|
848
888
|
try {
|
|
849
889
|
window.opener.postMessage(payload, window.location.origin);
|