@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.
Files changed (82) hide show
  1. package/README.md +113 -30
  2. package/dist/adapters/agui-adapter.d.mts +3 -3
  3. package/dist/adapters/agui-adapter.d.ts +3 -3
  4. package/dist/adapters/agui-middleware.d.mts +3 -3
  5. package/dist/adapters/agui-middleware.d.ts +3 -3
  6. package/dist/adapters/ai-adapter.d.mts +3 -3
  7. package/dist/adapters/ai-adapter.d.ts +3 -3
  8. package/dist/adapters/langchain-adapter.d.mts +3 -3
  9. package/dist/adapters/langchain-adapter.d.ts +3 -3
  10. package/dist/adapters/mastra-adapter.d.mts +1 -1
  11. package/dist/adapters/mastra-adapter.d.ts +1 -1
  12. package/dist/client/index.d.mts +2 -2
  13. package/dist/client/index.d.ts +2 -2
  14. package/dist/client/index.js +2 -2
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/client/index.mjs +2 -2
  17. package/dist/client/index.mjs.map +1 -1
  18. package/dist/client/react.d.mts +9 -8
  19. package/dist/client/react.d.ts +9 -8
  20. package/dist/client/react.js +66 -26
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs +67 -27
  23. package/dist/client/react.mjs.map +1 -1
  24. package/dist/client/vue.d.mts +6 -5
  25. package/dist/client/vue.d.ts +6 -5
  26. package/dist/client/vue.js +27 -17
  27. package/dist/client/vue.js.map +1 -1
  28. package/dist/client/vue.mjs +27 -17
  29. package/dist/client/vue.mjs.map +1 -1
  30. package/dist/{index-Cfjsme-a.d.mts → index-ByIjEReo.d.mts} +2 -2
  31. package/dist/{index-CmjMd2ac.d.ts → index-CtXvKl8N.d.ts} +2 -2
  32. package/dist/index.d.mts +5 -5
  33. package/dist/index.d.ts +5 -5
  34. package/dist/index.js +728 -306
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +722 -304
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/{multi-session-client-C7hGqzgM.d.mts → multi-session-client-CIMUGF8S.d.mts} +15 -33
  39. package/dist/{multi-session-client-C_kPHpD5.d.ts → multi-session-client-CnvZEGPY.d.ts} +15 -33
  40. package/dist/server/index.d.mts +45 -17
  41. package/dist/server/index.d.ts +45 -17
  42. package/dist/server/index.js +720 -300
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +716 -299
  45. package/dist/server/index.mjs.map +1 -1
  46. package/dist/shared/index.d.mts +9 -8
  47. package/dist/shared/index.d.ts +9 -8
  48. package/dist/shared/index.js +7 -5
  49. package/dist/shared/index.js.map +1 -1
  50. package/dist/shared/index.mjs +5 -4
  51. package/dist/shared/index.mjs.map +1 -1
  52. package/dist/{tool-router-BMzhoNYt.d.ts → tool-router-CZMrOG8J.d.ts} +1 -1
  53. package/dist/{tool-router-BhHsvBfP.d.mts → tool-router-CuApsDiV.d.mts} +1 -1
  54. package/dist/{types-CxFaaZrM.d.mts → types-DCk_IF4L.d.mts} +5 -3
  55. package/dist/{types-CxFaaZrM.d.ts → types-DCk_IF4L.d.ts} +5 -3
  56. package/migrations/neon/20260513010000_install_mcp_sessions.sql +40 -3
  57. package/migrations/neon/20260513020000_add_session_cleanup_cron.sql +4 -4
  58. package/migrations/supabase/20260330195700_install_mcp_sessions.sql +67 -3
  59. package/migrations/supabase/20260421010000_add_session_cleanup_cron.sql +6 -6
  60. package/package.json +13 -3
  61. package/src/client/core/sse-client.ts +2 -2
  62. package/src/client/react/index.ts +1 -1
  63. package/src/client/react/oauth-popup.tsx +34 -13
  64. package/src/client/react/use-mcp.ts +19 -45
  65. package/src/client/utils/session-state.ts +43 -0
  66. package/src/client/vue/use-mcp.ts +18 -47
  67. package/src/server/handlers/sse-handler.ts +9 -4
  68. package/src/server/mcp/multi-session-client.ts +2 -6
  69. package/src/server/mcp/oauth-client.ts +73 -101
  70. package/src/server/mcp/storage-oauth-provider.ts +79 -50
  71. package/src/server/storage/file-backend.ts +74 -18
  72. package/src/server/storage/index.ts +2 -4
  73. package/src/server/storage/memory-backend.ts +49 -13
  74. package/src/server/storage/neon-backend.ts +201 -68
  75. package/src/server/storage/redis-backend.ts +81 -23
  76. package/src/server/storage/session-lifecycle.ts +78 -0
  77. package/src/server/storage/sqlite-backend.ts +89 -15
  78. package/src/server/storage/supabase-backend.ts +188 -63
  79. package/src/server/storage/types.ts +49 -16
  80. package/src/shared/constants.ts +5 -6
  81. package/src/shared/types.ts +5 -3
  82. package/src/shared/utils.ts +26 -0
@@ -1,9 +1,9 @@
1
- import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-Cfjsme-a.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-Cfjsme-a.mjs';
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-CxFaaZrM.mjs';
6
- export { q as McpRpcRequest, r as McpRpcResponse } from '../types-CxFaaZrM.mjs';
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: (sessionId: string, code: string) => Promise<FinishAuthResult>;
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(sessionId, code)` to complete code exchange
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: (sessionId: string, code: string) => Promise<unknown>): void;
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(sessionId, code)`
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;
@@ -1,9 +1,9 @@
1
- import { c as SSEClient, e as AppHostOptions, f as AppHostClient, a as AppHost } from '../index-CmjMd2ac.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-CmjMd2ac.js';
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-CxFaaZrM.js';
6
- export { q as McpRpcRequest, r as McpRpcResponse } from '../types-CxFaaZrM.js';
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: (sessionId: string, code: string) => Promise<FinishAuthResult>;
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(sessionId, code)` to complete code exchange
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: (sessionId: string, code: string) => Promise<unknown>): void;
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(sessionId, code)`
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;
@@ -50,8 +50,8 @@ var SSEClient = class {
50
50
  async getSession(sessionId) {
51
51
  return this.sendRequest("getSession", { sessionId });
52
52
  }
53
- async finishAuth(sessionId, code) {
54
- return this.sendRequest("finishAuth", { sessionId, code });
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 = getVisibleState(event.state, existing.state, event.previousState);
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: getVisibleState(event.state, void 0, event.previousState),
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.active === false ? "AUTHENTICATING" : "VALIDATING",
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.active === false) {
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 (sessionId, code) => {
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(sessionId, code);
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 targetSessionId = typeof event.data.sessionId === "string" ? event.data.sessionId : "";
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(targetSession.sessionId, code);
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: targetSession.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 popupWindow = pendingPopupsRef.current.get(connection.sessionId) || null;
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: connection.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: connection.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
- if (event.data.sessionId !== sessionId) {
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);