@mcp-ts/sdk 2.4.1 → 2.4.3

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 (49) hide show
  1. package/dist/adapters/agui-adapter.d.mts +3 -4
  2. package/dist/adapters/agui-adapter.d.ts +3 -4
  3. package/dist/adapters/agui-middleware.d.mts +3 -4
  4. package/dist/adapters/agui-middleware.d.ts +3 -4
  5. package/dist/adapters/ai-adapter.d.mts +3 -4
  6. package/dist/adapters/ai-adapter.d.ts +3 -4
  7. package/dist/adapters/langchain-adapter.d.mts +3 -4
  8. package/dist/adapters/langchain-adapter.d.ts +3 -4
  9. package/dist/adapters/mastra-adapter.d.mts +2 -2
  10. package/dist/adapters/mastra-adapter.d.ts +2 -2
  11. package/dist/bin/mcp-ts.js +5 -5
  12. package/dist/bin/mcp-ts.js.map +1 -1
  13. package/dist/bin/mcp-ts.mjs +5 -5
  14. package/dist/bin/mcp-ts.mjs.map +1 -1
  15. package/dist/client/index.d.mts +2 -3
  16. package/dist/client/index.d.ts +2 -3
  17. package/dist/client/react.d.mts +4 -6
  18. package/dist/client/react.d.ts +4 -6
  19. package/dist/client/vue.d.mts +4 -6
  20. package/dist/client/vue.d.ts +4 -6
  21. package/dist/{index-Ch7ouNSa.d.ts → index-B8kJSrBJ.d.ts} +1 -2
  22. package/dist/{index-L5XoXgsb.d.mts → index-DiJsm_lK.d.mts} +1 -2
  23. package/dist/index.d.mts +5 -6
  24. package/dist/index.d.ts +5 -6
  25. package/dist/index.js +174 -110
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +174 -110
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/{multi-session-client-k-9RvWvi.d.mts → multi-session-client-B6hsYO8V.d.mts} +218 -71
  30. package/dist/{multi-session-client-Bwm-efqg.d.ts → multi-session-client-DOBvtaQV.d.ts} +218 -71
  31. package/dist/server/index.d.mts +5 -128
  32. package/dist/server/index.d.ts +5 -128
  33. package/dist/server/index.js +174 -110
  34. package/dist/server/index.js.map +1 -1
  35. package/dist/server/index.mjs +174 -110
  36. package/dist/server/index.mjs.map +1 -1
  37. package/dist/shared/index.d.mts +4 -5
  38. package/dist/shared/index.d.ts +4 -5
  39. package/dist/{tool-router-CuApsDiV.d.mts → tool-router-CbG4Tum6.d.mts} +1 -1
  40. package/dist/{tool-router-CZMrOG8J.d.ts → tool-router-ChIhPwgP.d.ts} +1 -1
  41. package/dist/{types-DCk_IF4L.d.mts → types-CjczQwNX.d.mts} +122 -1
  42. package/dist/{types-DCk_IF4L.d.ts → types-CjczQwNX.d.ts} +122 -1
  43. package/package.json +1 -1
  44. package/src/bin/mcp-ts.ts +5 -5
  45. package/src/server/mcp/multi-session-client.ts +190 -132
  46. package/src/server/mcp/oauth-client.ts +49 -7
  47. package/src/server/storage/index.ts +8 -8
  48. package/dist/events-C4m7tK1U.d.mts +0 -122
  49. package/dist/events-C4m7tK1U.d.ts +0 -122
@@ -1,4 +1,4 @@
1
- import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-C4m7tK1U.mjs';
1
+ import { m as Event, M as McpConnectionEvent, t as McpObservabilityEvent, s as McpConnectionState, A as ToolClientProvider } from './types-CjczQwNX.mjs';
2
2
  import { ListToolsResult, CallToolResult, ListPromptsResult, GetPromptResult, ListResourcesResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
4
4
  import { OAuthClientMetadata, OAuthClientInformationMixed, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
@@ -264,6 +264,17 @@ declare class MCPClient {
264
264
  * @throws {Error} When client is not connected
265
265
  */
266
266
  readResource(uri: string): Promise<ReadResourceResult>;
267
+ /**
268
+ * Wraps an MCP request with automatic transport-session recovery.
269
+ *
270
+ * When the downstream MCP server rejects the request with a 404 indicating
271
+ * the transport session has expired, this method tears down the stale SDK
272
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
273
+ * and retries the request once.
274
+ *
275
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
276
+ */
277
+ private withRetry;
267
278
  /**
268
279
  * Reconnects to MCP server using existing OAuth provider from Redis
269
280
  * Used for session restoration in serverless environments
@@ -328,102 +339,192 @@ declare class MCPClient {
328
339
  getSessionId(): string;
329
340
  }
330
341
 
342
+ interface OAuthState {
343
+ nonce: string;
344
+ sessionId: string;
345
+ serverId: string;
346
+ createdAt: number;
347
+ }
348
+ type SessionStatus = 'pending' | 'active';
349
+ interface Session {
350
+ sessionId: string;
351
+ serverId?: string;
352
+ serverName?: string;
353
+ serverUrl: string;
354
+ transportType: 'sse' | 'streamable-http';
355
+ callbackUrl: string;
356
+ createdAt: number;
357
+ updatedAt?: number;
358
+ /**
359
+ * Storage-owned expiration timestamp for pending/inactive sessions.
360
+ * Active sessions use updatedAt-based dormancy cleanup instead.
361
+ */
362
+ expiresAt?: number | null;
363
+ userId: string;
364
+ headers?: Record<string, string>;
365
+ authUrl?: string | null;
366
+ /**
367
+ * Session status marker used for lifecycle cleanup:
368
+ * - pending: short-lived intermediate/auth-pending session state
369
+ * - active: restorable session after successful connection/auth completion
370
+ */
371
+ status?: SessionStatus;
372
+ }
373
+ interface SessionCredentials {
374
+ sessionId: string;
375
+ userId: string;
376
+ clientInformation?: OAuthClientInformationMixed | null;
377
+ tokens?: OAuthTokens | null;
378
+ codeVerifier?: string | null;
379
+ clientId?: string | null;
380
+ oauthState?: OAuthState | null;
381
+ }
382
+ type SessionMutationType = 'create' | 'update' | 'delete';
383
+ interface SessionMutationEvent {
384
+ type: SessionMutationType;
385
+ userId: string;
386
+ sessionId: string;
387
+ timestamp: number;
388
+ session?: Session;
389
+ patch?: Partial<Session>;
390
+ }
391
+ type SessionMutationListener = (event: SessionMutationEvent) => void | Promise<void>;
331
392
  /**
332
- * Manages multiple MCP connections for a single user.
333
- * Allows aggregating tools from all connected servers.
393
+ * Interface for MCP session stores.
334
394
  */
395
+ interface SessionStore {
396
+ /**
397
+ * Optional initialization (e.g., database connection)
398
+ */
399
+ init?(): Promise<void>;
400
+ /**
401
+ * Generates a unique session ID
402
+ */
403
+ generateSessionId(): string;
404
+ /**
405
+ * Creates a new session. Throws if session already exists.
406
+ * @param session - Session data to create
407
+ */
408
+ create(session: Session): Promise<void>;
409
+ /**
410
+ * Updates an existing session with partial data. Throws if session does not exist.
411
+ * @param userId - User identifier
412
+ * @param sessionId - Session identifier
413
+ * @param data - Partial session data to update
414
+ */
415
+ update(userId: string, sessionId: string, data: Partial<Session>): Promise<void>;
416
+ /**
417
+ * Patches runtime credentials for an existing session.
418
+ * These values are separated from connection metadata in durable SQL stores.
419
+ */
420
+ patchCredentials(userId: string, sessionId: string, data: Partial<SessionCredentials>): Promise<void>;
421
+ /**
422
+ * Retrieves a session
423
+ */
424
+ get(userId: string, sessionId: string): Promise<Session | null>;
425
+ /**
426
+ * Retrieves runtime credentials for a session.
427
+ */
428
+ getCredentials(userId: string, sessionId: string): Promise<SessionCredentials | null>;
429
+ /**
430
+ * Clears runtime credentials without removing connection metadata.
431
+ */
432
+ clearCredentials(userId: string, sessionId: string): Promise<void>;
433
+ /**
434
+ * Gets full session data for all sessions owned by a user
435
+ */
436
+ list(userId: string): Promise<Session[]>;
437
+ /**
438
+ * Removes a session
439
+ */
440
+ delete(userId: string, sessionId: string): Promise<void>;
441
+ /**
442
+ * Gets all session IDs owned by a user
443
+ */
444
+ listIds(userId: string): Promise<string[]>;
445
+ /**
446
+ * Gets all session IDs across all users (Admin)
447
+ */
448
+ listAllIds(): Promise<string[]>;
449
+ /**
450
+ * Clears all sessions (Admin)
451
+ */
452
+ clearAll(): Promise<void>;
453
+ /**
454
+ * Clean up expired sessions
455
+ */
456
+ cleanupExpired(): Promise<void>;
457
+ /**
458
+ * Disconnect from storage backend
459
+ */
460
+ disconnect(): Promise<void>;
461
+ }
462
+
335
463
  interface MultiSessionOptions {
336
464
  /**
337
- * Connection timeout in milliseconds
465
+ * Connection timeout in milliseconds.
338
466
  * @default 15000
339
467
  */
340
468
  timeout?: number;
341
469
  /**
342
- * Maximum number of retry attempts
470
+ * Maximum number of retry attempts per session.
343
471
  * @default 2
344
472
  */
345
473
  maxRetries?: number;
346
474
  /**
347
- * Delay between retries in milliseconds
475
+ * Delay between retry attempts in milliseconds.
348
476
  * @default 1000
349
477
  */
350
478
  retryDelay?: number;
351
- }
352
- /**
353
- * Manages multiple MCP client connections for a single user.
354
- *
355
- * On a traditional long-running server, you can cache this instance per user
356
- * so the connections stay alive between requests. On serverless, a new instance
357
- * will be created per invocation, but the underlying session data is always
358
- * read from the storage backend so nothing is lost between calls.
359
- */
360
- declare class MultiSessionClient {
361
- private clients;
362
- private userId;
363
- private options;
364
479
  /**
365
- * Creates a new MultiSessionClient for the given user userId.
480
+ * Custom session provider. When provided, `connect()` calls this
481
+ * instead of querying the storage backend. Useful when sessions are
482
+ * synced externally (e.g. via Supabase Realtime, WebSocket, or an
483
+ * in-memory cache).
366
484
  *
367
- * @param userId - A unique string identifying the user (e.g. user ID or email).
368
- * @param options - Optional tuning for connection timeout, retry count, and retry delay.
369
- * Falls back to sensible defaults if not provided.
485
+ * Default: reads from the storage backend via `sessions.list(userId)`.
370
486
  */
371
- constructor(userId: string, options?: MultiSessionOptions);
487
+ sessionProvider?: () => Promise<Session[]>;
372
488
  /**
373
- * Fetches all sessions for this userId from storage and returns only the
374
- * ones that are ready to connect.
375
- *
376
- * A session is considered connectable when:
377
- * - It has a `serverId`, `serverUrl`, and `callbackUrl` (i.e. it was fully initialized)
378
- * - Its status is `active`. Pending sessions are skipped here
379
- * and let the OAuth flow complete separately before we try to reconnect them.
489
+ * Called after a session is successfully connected.
380
490
  */
381
- private getActiveSessions;
491
+ onSessionConnected?: (sessionId: string, client: MCPClient) => void;
382
492
  /**
383
- * Connects to a list of sessions in controlled batches of `CONNECTION_BATCH_SIZE`.
384
- *
385
- * Batching prevents overwhelming the event loop or external servers when a user
386
- * has many active MCP sessions (e.g. 20+ servers). Within each batch, sessions
387
- * are connected concurrently using `Promise.all` for speed.
493
+ * Called when a session is evicted from the in-memory client list
494
+ * because it no longer exists in the active sessions list.
388
495
  */
389
- private connectInBatches;
390
- private connectionPromises;
496
+ onSessionEvicted?: (sessionId: string) => void;
391
497
  /**
392
- * Connects a single session, with built-in deduplication to prevent race conditions.
393
- *
394
- * - If a client for this session already exists and is connected, returns immediately.
395
- * - If the existing client entry is no longer connected (e.g. it was explicitly
396
- * disconnected), it is evicted so that `establishConnectionWithRetries` creates a
397
- * fresh transport — preventing "Client already connected" errors from the SDK.
398
- * - If a connection attempt for this session is already in-flight (e.g. from a
399
- * concurrent call), it joins the existing promise instead of starting a new one.
400
- * This is the key concurrency lock — the `connectionPromises` map acts as a
401
- * per-session mutex so we never spin up two physical connections for the same session.
402
- * - On completion (success or failure), the promise is cleaned up from the map.
498
+ * Called when all retry attempts for a session have been exhausted.
403
499
  */
404
- private connectSession;
500
+ onSessionFailed?: (sessionId: string, error: unknown) => void;
501
+ }
502
+ /**
503
+ * Manages multiple MCP client connections for a single user.
504
+ *
505
+ * Cached instances stay alive between requests on long-running servers.
506
+ * On serverless, the underlying session data persists in storage — the
507
+ * client rehydrates from `sessionProvider` (or the default storage backend)
508
+ * each time `connect()` is called.
509
+ *
510
+ * @implements {ToolClientProvider}
511
+ */
512
+ declare class MultiSessionClient implements ToolClientProvider {
513
+ private clients;
514
+ private connectionPromises;
515
+ private userId;
516
+ private options;
405
517
  /**
406
- * The core connection loop for a single session.
407
- *
408
- * Attempts to establish a physical MCP connection, retrying up to `maxRetries` times
409
- * if the connection fails. Each attempt:
410
- * 1. Creates a fresh `MCPClient` instance from the session data.
411
- * 2. Races the connect call against a timeout promise — if the server doesn't respond
412
- * within `timeoutMs`, the attempt is aborted and counted as a failure.
413
- * 3. On success, replaces any stale client entry for this session in the `clients` array.
414
- * 4. On failure, waits `retryDelay` ms before the next attempt.
415
- *
416
- * If all attempts are exhausted, logs an error and returns silently (does not throw),
417
- * so a single bad server doesn't block the rest of the batch from connecting.
518
+ * @param userId - Unique identifier for the user (e.g. user ID or email).
519
+ * @param options - Optional tuning and lifecycle hooks.
418
520
  */
419
- private establishConnectionWithRetries;
521
+ constructor(userId: string, options?: MultiSessionOptions);
420
522
  /**
421
- * The main entry point. Fetches all active sessions for this userId from
422
- * storage and establishes connections to all of them in batches.
523
+ * Fetches active sessions and establishes connections to all of them.
423
524
  *
424
- * Call this once after creating the client. On traditional servers, you can
425
- * cache the `MultiSessionClient` instance after calling `connect()` to avoid
426
- * re-fetching and re-connecting on every request.
525
+ * Call this once after creating the client. On long-running servers you
526
+ * can cache the `MultiSessionClient` instance and call `connect()` on
527
+ * each request — already-connected sessions are skipped internally.
427
528
  */
428
529
  connect(): Promise<void>;
429
530
  /**
@@ -445,7 +546,13 @@ declare class MultiSessionClient {
445
546
  */
446
547
  getClients(): MCPClient[];
447
548
  /**
448
- * Gracefully disconnects all active MCP clients and clears the internal client list.
549
+ * Removes and disconnects a single session by ID.
550
+ *
551
+ * @returns `true` if the session was found and removed, `false` if not found.
552
+ */
553
+ removeSession(sessionId: string): Promise<boolean>;
554
+ /**
555
+ * Gracefully disconnects all active MCP clients and clears the internal list.
449
556
  *
450
557
  * For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
451
558
  * endpoint per the spec before closing locally. All disconnects run in
@@ -455,6 +562,46 @@ declare class MultiSessionClient {
455
562
  * underlying transport resources (SSE streams, HTTP connections, etc.).
456
563
  */
457
564
  disconnect(): Promise<void>;
565
+ /**
566
+ * Resolves the list of sessions to connect.
567
+ *
568
+ * Uses the custom `sessionProvider` when provided, otherwise falls back
569
+ * to querying the storage backend via `sessions.list(userId)`.
570
+ */
571
+ private fetchActiveSessions;
572
+ /**
573
+ * Connects a list of sessions in controlled batches.
574
+ *
575
+ * Batching prevents overwhelming the event loop or external servers when
576
+ * a user has many active MCP sessions. Within each batch, sessions are
577
+ * connected concurrently using `Promise.all`.
578
+ */
579
+ private connectInBatches;
580
+ /**
581
+ * Connects a single session, with deduplication to prevent race conditions.
582
+ *
583
+ * - If a client for this session already exists and is connected, returns
584
+ * immediately.
585
+ * - If the existing client entry is no longer connected (e.g. explicit
586
+ * disconnect), it is evicted so a fresh transport is created.
587
+ * - If a connection attempt for this session is already in-flight, the
588
+ * existing promise is reused as a per-session mutex.
589
+ * - On completion (success or failure), the promise is cleaned up from
590
+ * the connectionPromises map.
591
+ */
592
+ private connectSession;
593
+ /**
594
+ * Core connection loop for a single session with retry logic.
595
+ *
596
+ * 1. Creates a fresh `MCPClient` from the session data.
597
+ * 2. Races `client.connect()` against a timeout.
598
+ * 3. On success, replaces any stale entry and fires `onSessionConnected`.
599
+ * 4. On failure, waits `retryDelay` ms before the next attempt.
600
+ *
601
+ * If all attempts are exhausted, logs an error and returns silently so
602
+ * a single bad server doesn't block the rest of the batch.
603
+ */
604
+ private establishConnectionWithRetries;
458
605
  }
459
606
 
460
- export { MCPClient as M, StorageOAuthClientProvider as S, MultiSessionClient as a };
607
+ export { MCPClient as M, type Session as S, MultiSessionClient as a, type SessionMutationEvent as b, type SessionMutationListener as c, type SessionMutationType as d, type SessionStore as e, StorageOAuthClientProvider as f };