@mcp-ts/sdk 2.3.3 → 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 +729 -397
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +724 -396
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/{multi-session-client-DYNe6az3.d.mts → multi-session-client-CIMUGF8S.d.mts} +15 -41
  39. package/dist/{multi-session-client-BYtguGJm.d.ts → multi-session-client-CnvZEGPY.d.ts} +15 -41
  40. package/dist/server/index.d.mts +45 -17
  41. package/dist/server/index.d.ts +45 -17
  42. package/dist/server/index.js +721 -391
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +718 -391
  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 -220
  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
@@ -4,9 +4,6 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
4
4
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
5
5
  import {
6
6
  UnauthorizedError as SDKUnauthorizedError,
7
- refreshAuthorization,
8
- discoverOAuthProtectedResourceMetadata,
9
- discoverAuthorizationServerMetadata,
10
7
  } from '@modelcontextprotocol/sdk/client/auth.js';
11
8
  import {
12
9
  ListToolsRequest,
@@ -28,17 +25,14 @@ import {
28
25
  ReadResourceResult,
29
26
  ReadResourceResultSchema,
30
27
  } from '@modelcontextprotocol/sdk/types.js';
31
- import type { OAuthTokens, OAuthClientInformationFull } from '@modelcontextprotocol/sdk/shared/auth.js';
32
28
  import { StorageOAuthClientProvider, type AgentsOAuthProvider } from './storage-oauth-provider.js';
33
- import { sanitizeServerLabel } from '../../shared/utils.js';
34
29
  import { Emitter, type McpConnectionEvent, type McpObservabilityEvent, type McpConnectionState } from '../../shared/events.js';
35
30
  import { UnauthorizedError } from '../../shared/errors.js';
36
31
  import { sessions } from '../storage/index.js';
32
+ import type { Session, SessionStatus } from '../storage/types.js';
37
33
  import {
38
34
  MCP_CLIENT_NAME,
39
35
  MCP_CLIENT_VERSION,
40
- SESSION_TTL_SECONDS,
41
- STATE_EXPIRATION_MS,
42
36
  } from '../../shared/constants.js';
43
37
 
44
38
  /**
@@ -60,20 +54,6 @@ interface McpAppClientCapabilities extends Omit<ClientCapabilities, 'extensions'
60
54
  };
61
55
  }
62
56
 
63
- function isInvalidRefreshTokenError(error: unknown): boolean {
64
- if (!(error instanceof Error)) {
65
- return false;
66
- }
67
-
68
- const text = `${error.name} ${error.message}`.toLowerCase();
69
- return (
70
- text.includes('invalidgrant') ||
71
- text.includes('invalid_grant') ||
72
- text.includes('invalid refresh token') ||
73
- /refresh\s+token\s+(?:is\s+)?(?:invalid|expired|revoked)/i.test(text)
74
- );
75
- }
76
-
77
57
  export interface MCPOAuthClientOptions {
78
58
  serverUrl?: string;
79
59
  serverName?: string;
@@ -296,23 +276,25 @@ export class MCPClient {
296
276
  this.emitStateChange('INITIALIZING');
297
277
  this.emitProgress('Loading session configuration...');
298
278
 
279
+ let existingSession: Session | null = null;
280
+
299
281
  if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
300
- const sessionData = await sessions.get(this.userId, this.sessionId);
301
- if (!sessionData) {
282
+ existingSession = await sessions.get(this.userId, this.sessionId);
283
+ if (!existingSession) {
302
284
  throw new Error(`Session not found: ${this.sessionId}`);
303
285
  }
304
286
 
305
- this.serverUrl = this.serverUrl || sessionData.serverUrl;
306
- this.callbackUrl = this.callbackUrl || sessionData.callbackUrl;
287
+ this.serverUrl = this.serverUrl || existingSession.serverUrl;
288
+ this.callbackUrl = this.callbackUrl || existingSession.callbackUrl;
307
289
  /**
308
290
  * Do NOT load transportType from session if not explicitly provided.
309
291
  * We want to re-negotiate (try streamable -> sse) on new connections if in "Auto" mode.
310
292
  * this.transportType = this.transportType || sessionData.transportType;
311
293
  */
312
- this.serverName = this.serverName || sessionData.serverName;
313
- this.serverId = this.serverId || sessionData.serverId || 'unknown';
314
- this.headers = this.headers || sessionData.headers;
315
- this.createdAt = sessionData.createdAt;
294
+ this.serverName = this.serverName || existingSession.serverName;
295
+ this.serverId = this.serverId || existingSession.serverId || 'unknown';
296
+ this.headers = this.headers || existingSession.headers;
297
+ this.createdAt = existingSession.createdAt;
316
298
  }
317
299
 
318
300
  if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
@@ -363,10 +345,13 @@ export class MCPClient {
363
345
  // Create session in the session store if it doesn't exist yet
364
346
  // This is needed BEFORE OAuth flow starts because the OAuth provider
365
347
  // will call saveCodeVerifier() which requires the session to exist
366
- const existingSession = await sessions.get(this.userId, this.sessionId);
348
+ if (existingSession === null) {
349
+ existingSession = await sessions.get(this.userId, this.sessionId);
350
+ }
367
351
  if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
368
352
  this.createdAt = Date.now();
369
- console.log(`[MCPClient] Creating initial session ${this.sessionId} for OAuth flow`);
353
+ const updatedAt = this.createdAt;
354
+ console.log(`[MCPClient] Creating pending session ${this.sessionId} for connection setup`);
370
355
  await sessions.create({
371
356
  sessionId: this.sessionId,
372
357
  userId: this.userId,
@@ -377,21 +362,21 @@ export class MCPClient {
377
362
  transportType: this.transportType || 'streamable-http',
378
363
  headers: this.headers,
379
364
  createdAt: this.createdAt,
380
- active: false,
381
- }, Math.floor(STATE_EXPIRATION_MS / 1000)); // Short TTL until connection succeeds
365
+ updatedAt,
366
+ status: 'pending',
367
+ });
382
368
  }
383
369
  }
384
370
 
385
371
  /**
386
372
  * Saves current session state to the session store
387
373
  * Creates new session if it doesn't exist, updates if it does
388
- * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
389
- * @param active - Session status marker used to avoid unnecessary TTL rewrites
374
+ * @param status - Session lifecycle status used by storage cleanup
390
375
  * @private
391
376
  */
392
377
  private async saveSession(
393
- ttl: number = SESSION_TTL_SECONDS,
394
- active: boolean = true
378
+ status: SessionStatus = 'active',
379
+ existingSession?: Session | null
395
380
  ): Promise<void> {
396
381
  if (!this.sessionId || !this.serverId || !this.serverUrl || !this.callbackUrl) {
397
382
  return;
@@ -407,15 +392,33 @@ export class MCPClient {
407
392
  transportType: (this.transportType || 'streamable-http') as TransportType,
408
393
  headers: this.headers,
409
394
  createdAt: this.createdAt || Date.now(),
410
- active,
395
+ updatedAt: Date.now(),
396
+ status,
411
397
  };
398
+ if (status === 'active') {
399
+ (sessionData as typeof sessionData & { authUrl: null }).authUrl = null;
400
+ }
412
401
 
413
402
  // Try to update first, create if doesn't exist
414
- const existingSession = await sessions.get(this.userId, this.sessionId);
403
+ if (existingSession === undefined) {
404
+ existingSession = await sessions.get(this.userId, this.sessionId);
405
+ }
415
406
  if (existingSession) {
416
- await sessions.update(this.userId, this.sessionId, sessionData, ttl);
407
+ await sessions.update(this.userId, this.sessionId, sessionData);
417
408
  } else {
418
- await sessions.create(sessionData, ttl);
409
+ await sessions.create(sessionData);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Removes transient setup/auth sessions without masking the original error.
415
+ * @private
416
+ */
417
+ private async deleteTransientSession(): Promise<void> {
418
+ try {
419
+ await sessions.delete(this.userId, this.sessionId);
420
+ } catch {
421
+ // Best effort only: preserve the original connection/auth error.
419
422
  }
420
423
  }
421
424
 
@@ -504,9 +507,6 @@ export class MCPClient {
504
507
  }
505
508
 
506
509
  try {
507
- this.emitProgress('Validating OAuth tokens...');
508
- await this.getValidTokens();
509
-
510
510
  this.emitStateChange('CONNECTING');
511
511
 
512
512
  /** Use the tryConnect loop to handle transport fallbacks */
@@ -520,8 +520,8 @@ export class MCPClient {
520
520
 
521
521
  // Refresh session metadata on every successful connect so active sessions
522
522
  // record ongoing usage and don't look dormant to session cleanup jobs.
523
- console.log(`[MCPClient] Saving session ${this.sessionId} with 12hr TTL (connect success)`);
524
- await this.saveSession(SESSION_TTL_SECONDS, true);
523
+ console.log(`[MCPClient] Saving active session ${this.sessionId} (connect success)`);
524
+ await this.saveSession('active');
525
525
  } catch (error) {
526
526
  /** Handle Authentication Errors */
527
527
  if (
@@ -550,23 +550,16 @@ export class MCPClient {
550
550
  this.emitError(message, 'auth');
551
551
  this.emitStateChange('FAILED');
552
552
 
553
- // Proactive Cleanup: This session has reached a terminal failure state.
554
- // We remove it now to ensure the database remains lean, bypassing the
555
- // automated lifecycle sweep.
556
- try {
557
- await sessions.delete(this.userId, this.sessionId);
558
- } catch {
559
- // Non-blocking: Proactive cleanup failures are suppressed to prioritize
560
- // the original error context.
561
- }
553
+ // Remove terminal setup failures immediately. Active sessions are not
554
+ // deleted here because this branch only runs before OAuth is available.
555
+ await this.deleteTransientSession();
562
556
 
563
557
  throw new Error(message);
564
558
  }
565
559
 
566
560
  this.emitStateChange('AUTHENTICATING');
567
- // Save session with 10min TTL for OAuth pending state
568
- console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
569
- await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1000), false);
561
+ console.log(`[MCPClient] Saving pending OAuth session ${this.sessionId}`);
562
+ await this.saveSession('pending');
570
563
 
571
564
  if (this.serverId) {
572
565
  this._onConnectionEvent.fire({
@@ -590,16 +583,15 @@ export class MCPClient {
590
583
  this.emitError(errorMessage, 'connection');
591
584
  this.emitStateChange('FAILED');
592
585
 
593
- // Terminal Handshake Failure: only purge transient sessions. Active
594
- // sessions may still hold valid credentials for a later reconnect.
586
+ // Remove transient sessions that failed before becoming restorable.
587
+ // Existing active sessions may still hold usable credentials for reconnect.
595
588
  try {
596
589
  const existingSession = await sessions.get(this.userId, this.sessionId);
597
- if (!existingSession || existingSession.active !== true) {
590
+ if (!existingSession || existingSession.status !== 'active') {
598
591
  await sessions.delete(this.userId, this.sessionId);
599
592
  }
600
593
  } catch {
601
- // Non-blocking: Cleanup is performed on a best-effort basis and should
602
- // not interfere with the primary error propagation.
594
+ // Best effort only: preserve the original connection error.
603
595
  }
604
596
 
605
597
  throw error;
@@ -614,7 +606,7 @@ export class MCPClient {
614
606
  */
615
607
 
616
608
  // TODO: needs to be optimized
617
- async finishAuth(authCode: string): Promise<void> {
609
+ async finishAuth(authCode: string, state?: string): Promise<void> {
618
610
  this.emitStateChange('AUTHENTICATING');
619
611
  this.emitProgress('Exchanging authorization code for tokens...');
620
612
 
@@ -627,6 +619,18 @@ export class MCPClient {
627
619
  throw new Error(error);
628
620
  }
629
621
 
622
+ if (state) {
623
+ const stateCheck = await this.oauthProvider.checkState(state);
624
+ if (!stateCheck.valid) {
625
+ const error = stateCheck.error || 'Invalid OAuth state';
626
+ this.emitError(error, 'auth');
627
+ this.emitStateChange('FAILED');
628
+ throw new Error(error);
629
+ }
630
+
631
+ await this.oauthProvider.consumeState(state);
632
+ }
633
+
630
634
  /**
631
635
  * Determine which transports to try for finishing auth
632
636
  * If transportType is set, use only that. Otherwise try streamable_http then sse.
@@ -687,9 +691,8 @@ export class MCPClient {
687
691
  this.transportType = currentType;
688
692
 
689
693
  this.emitStateChange('CONNECTED');
690
- // Update session with 12hr TTL after successful OAuth
691
- console.log(`[MCPClient] Updating session ${this.sessionId} to 12hr TTL (OAuth complete)`);
692
- await this.saveSession(SESSION_TTL_SECONDS, true);
694
+ console.log(`[MCPClient] Saving active session ${this.sessionId} (OAuth complete)`);
695
+ await this.saveSession('active');
693
696
 
694
697
  return; // Success, exit function
695
698
 
@@ -710,6 +713,7 @@ export class MCPClient {
710
713
  const msg = error instanceof Error ? error.message : 'Authentication failed';
711
714
  this.emitError(msg, 'auth');
712
715
  this.emitStateChange('FAILED');
716
+ await this.deleteTransientSession();
713
717
  throw error;
714
718
  }
715
719
 
@@ -717,6 +721,7 @@ export class MCPClient {
717
721
  const msg = error instanceof Error ? error.message : 'Authentication failed';
718
722
  this.emitError(msg, 'auth');
719
723
  this.emitStateChange('FAILED');
724
+ await this.deleteTransientSession();
720
725
  throw error;
721
726
  }
722
727
 
@@ -729,6 +734,7 @@ export class MCPClient {
729
734
  const errorMessage = lastError instanceof Error ? lastError.message : 'Authentication failed';
730
735
  this.emitError(errorMessage, 'auth');
731
736
  this.emitStateChange('FAILED');
737
+ await this.deleteTransientSession();
732
738
  throw lastError;
733
739
  }
734
740
  }
@@ -947,79 +953,6 @@ export class MCPClient {
947
953
  return await this.client.request(request, ReadResourceResultSchema);
948
954
  }
949
955
 
950
- /**
951
- * Refreshes the OAuth access token using the refresh token
952
- * Discovers OAuth metadata from server and exchanges refresh token for new access token
953
- * @returns True if refresh was successful, false otherwise
954
- */
955
- async refreshToken(): Promise<boolean> {
956
- await this.initialize();
957
-
958
- if (!this.oauthProvider) {
959
- return false;
960
- }
961
-
962
- const tokens = await this.oauthProvider.tokens();
963
- if (!tokens || !tokens.refresh_token) {
964
- return false;
965
- }
966
-
967
- const clientInformation = await this.oauthProvider.clientInformation();
968
- if (!clientInformation) {
969
- return false;
970
- }
971
-
972
- try {
973
- const resourceMetadata = await discoverOAuthProtectedResourceMetadata(this.serverUrl!);
974
- const authServerUrl = resourceMetadata?.authorization_servers?.[0] || this.serverUrl!;
975
- const authMetadata = await discoverAuthorizationServerMetadata(authServerUrl);
976
-
977
- const newTokens = await refreshAuthorization(authServerUrl, {
978
- metadata: authMetadata,
979
- clientInformation,
980
- refreshToken: tokens.refresh_token,
981
- });
982
-
983
- await this.oauthProvider.saveTokens(newTokens);
984
- return true;
985
- } catch (error) {
986
- console.error('[OAuth] Token refresh failed:', error);
987
- if (isInvalidRefreshTokenError(error)) {
988
- try {
989
- await this.oauthProvider.invalidateCredentials?.('tokens');
990
- this.emitProgress('OAuth refresh token is invalid; requesting reauthorization...');
991
- } catch (invalidateError) {
992
- console.warn('[OAuth] Failed to invalidate stale refresh token credentials:', invalidateError);
993
- }
994
- }
995
- return false;
996
- }
997
- }
998
-
999
- /**
1000
- * Ensures OAuth tokens are valid, refreshing them if expired
1001
- * Called automatically by connect() - rarely needs to be called manually
1002
- * @returns True if valid tokens are available, false otherwise
1003
- */
1004
- async getValidTokens(): Promise<boolean> {
1005
- await this.initialize();
1006
-
1007
- if (!this.oauthProvider) {
1008
- return false;
1009
- }
1010
-
1011
- const tokens = await this.oauthProvider.tokens();
1012
- if (!tokens) {
1013
- return false;
1014
- }
1015
-
1016
- if (this.oauthProvider.isTokenExpired()) {
1017
- return await this.refreshToken();
1018
- }
1019
-
1020
- return true;
1021
- }
1022
-
1023
956
  /**
1024
957
  * Reconnects to MCP server using existing OAuth provider from Redis
1025
958
  * Used for session restoration in serverless environments
@@ -1174,84 +1107,4 @@ export class MCPClient {
1174
1107
  return this.sessionId;
1175
1108
  }
1176
1109
 
1177
- /**
1178
- * Gets MCP server configuration for all active user sessions
1179
- * Loads sessions from Redis, validates OAuth tokens, refreshes if expired
1180
- * Returns ready-to-use configuration with valid auth headers
1181
- * @param userId - User ID to fetch sessions for
1182
- * @returns Object keyed by sanitized server labels containing transport, url, headers, etc.
1183
- * @static
1184
- */
1185
- static async getMcpServerConfig(userId: string): Promise<Record<string, any>> {
1186
- const mcpConfig: Record<string, any> = {};
1187
- const sessionList = await sessions.list(userId);
1188
-
1189
- await Promise.all(
1190
- sessionList.map(async (sessionData) => {
1191
- const { sessionId } = sessionData;
1192
-
1193
- try {
1194
- // Validate session - remove if missing required fields
1195
- if (
1196
- !sessionData.serverId ||
1197
- !sessionData.transportType ||
1198
- !sessionData.serverUrl ||
1199
- !sessionData.callbackUrl
1200
- ) {
1201
- await sessions.delete(userId, sessionId);
1202
- return;
1203
- }
1204
-
1205
- // Get OAuth headers if session requires authentication
1206
- let headers: Record<string, string> | undefined;
1207
- try {
1208
- // Inject existing session data to avoid redundant session store reads in initialize()
1209
- const client = new MCPClient({
1210
- userId,
1211
- sessionId,
1212
- serverId: sessionData.serverId,
1213
- serverUrl: sessionData.serverUrl,
1214
- callbackUrl: sessionData.callbackUrl,
1215
- serverName: sessionData.serverName,
1216
- transportType: sessionData.transportType,
1217
- headers: sessionData.headers,
1218
- });
1219
-
1220
- await client.initialize();
1221
-
1222
- const hasValidTokens = await client.getValidTokens();
1223
- if (hasValidTokens && client.oauthProvider) {
1224
- const tokens = await client.oauthProvider.tokens();
1225
- if (tokens?.access_token) {
1226
- headers = { Authorization: `Bearer ${tokens.access_token}` };
1227
- }
1228
- }
1229
- } catch (error) {
1230
- console.warn(`[MCP] Failed to get OAuth tokens for ${sessionId}:`, error);
1231
- }
1232
-
1233
- // Build server config
1234
- const label = sanitizeServerLabel(
1235
- sessionData.serverName || sessionData.serverId || 'server'
1236
- );
1237
-
1238
- mcpConfig[label] = {
1239
- transport: sessionData.transportType,
1240
- url: sessionData.serverUrl,
1241
- ...(sessionData.serverName && {
1242
- serverName: sessionData.serverName,
1243
- serverLabel: label,
1244
- }),
1245
- ...(headers && { headers }),
1246
- };
1247
- } catch (error) {
1248
- await sessions.delete(userId, sessionId);
1249
- console.warn(`[MCP] Failed to process session ${sessionId}:`, error);
1250
- }
1251
- })
1252
- );
1253
-
1254
- return mcpConfig;
1255
- }
1256
-
1257
1110
  }