@mcp-ts/sdk 2.4.1 → 2.4.2

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 (42) 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/client/index.d.mts +2 -3
  12. package/dist/client/index.d.ts +2 -3
  13. package/dist/client/react.d.mts +4 -6
  14. package/dist/client/react.d.ts +4 -6
  15. package/dist/client/vue.d.mts +4 -6
  16. package/dist/client/vue.d.ts +4 -6
  17. package/dist/{index-Ch7ouNSa.d.ts → index-B8kJSrBJ.d.ts} +1 -2
  18. package/dist/{index-L5XoXgsb.d.mts → index-DiJsm_lK.d.mts} +1 -2
  19. package/dist/index.d.mts +5 -6
  20. package/dist/index.d.ts +5 -6
  21. package/dist/index.js +103 -95
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +103 -95
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/{multi-session-client-Bwm-efqg.d.ts → multi-session-client-BluyCPo9.d.ts} +201 -71
  26. package/dist/{multi-session-client-k-9RvWvi.d.mts → multi-session-client-CWs-AE78.d.mts} +201 -71
  27. package/dist/server/index.d.mts +5 -128
  28. package/dist/server/index.d.ts +5 -128
  29. package/dist/server/index.js +103 -95
  30. package/dist/server/index.js.map +1 -1
  31. package/dist/server/index.mjs +103 -95
  32. package/dist/server/index.mjs.map +1 -1
  33. package/dist/shared/index.d.mts +4 -5
  34. package/dist/shared/index.d.ts +4 -5
  35. package/dist/{tool-router-CuApsDiV.d.mts → tool-router-CbG4Tum6.d.mts} +1 -1
  36. package/dist/{tool-router-CZMrOG8J.d.ts → tool-router-ChIhPwgP.d.ts} +1 -1
  37. package/dist/{types-DCk_IF4L.d.mts → types-CjczQwNX.d.mts} +122 -1
  38. package/dist/{types-DCk_IF4L.d.ts → types-CjczQwNX.d.ts} +122 -1
  39. package/package.json +1 -1
  40. package/src/server/mcp/multi-session-client.ts +177 -132
  41. package/dist/events-C4m7tK1U.d.mts +0 -122
  42. package/dist/events-C4m7tK1U.d.ts +0 -122
@@ -2924,17 +2924,14 @@ var DEFAULT_RETRY_DELAY_MS = 1e3;
2924
2924
  var CONNECTION_BATCH_SIZE = 5;
2925
2925
  var MultiSessionClient = class {
2926
2926
  /**
2927
- * Creates a new MultiSessionClient for the given user userId.
2928
- *
2929
- * @param userId - A unique string identifying the user (e.g. user ID or email).
2930
- * @param options - Optional tuning for connection timeout, retry count, and retry delay.
2931
- * Falls back to sensible defaults if not provided.
2927
+ * @param userId - Unique identifier for the user (e.g. user ID or email).
2928
+ * @param options - Optional tuning and lifecycle hooks.
2932
2929
  */
2933
2930
  constructor(userId, options = {}) {
2934
2931
  __publicField(this, "clients", []);
2932
+ __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
2935
2933
  __publicField(this, "userId");
2936
2934
  __publicField(this, "options");
2937
- __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
2938
2935
  this.userId = userId;
2939
2936
  this.options = {
2940
2937
  timeout: DEFAULT_TIMEOUT_MS,
@@ -2943,28 +2940,85 @@ var MultiSessionClient = class {
2943
2940
  ...options
2944
2941
  };
2945
2942
  }
2943
+ // -----------------------------------------------------------------------
2944
+ // Public API
2945
+ // -----------------------------------------------------------------------
2946
2946
  /**
2947
- * Fetches all sessions for this userId from storage and returns only the
2948
- * ones that are ready to connect.
2947
+ * Fetches active sessions and establishes connections to all of them.
2949
2948
  *
2950
- * A session is considered connectable when:
2951
- * - It has a `serverId`, `serverUrl`, and `callbackUrl` (i.e. it was fully initialized)
2952
- * - Its status is `active`. Pending sessions are skipped here
2953
- * and let the OAuth flow complete separately before we try to reconnect them.
2949
+ * Call this once after creating the client. On long-running servers you
2950
+ * can cache the `MultiSessionClient` instance and call `connect()` on
2951
+ * each request already-connected sessions are skipped internally.
2954
2952
  */
2955
- async getActiveSessions() {
2956
- const sessionList = await sessions.list(this.userId);
2957
- const valid = sessionList.filter(
2953
+ async connect() {
2954
+ const sessions2 = await this.fetchActiveSessions();
2955
+ const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
2956
+ for (const client of this.clients) {
2957
+ if (!activeSessionIds.has(client.getSessionId())) {
2958
+ this.options.onSessionEvicted?.(client.getSessionId());
2959
+ }
2960
+ }
2961
+ this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
2962
+ await this.connectInBatches(sessions2);
2963
+ }
2964
+ /**
2965
+ * Drops all cached `MCPClient` instances and reconnects fresh from storage.
2966
+ *
2967
+ * Call this when downstream MCP servers have expired their transport sessions
2968
+ * (e.g. after a remote server restart) and subsequent tool calls return
2969
+ * "Session not found. Reconnect without session header." errors.
2970
+ *
2971
+ * OAuth tokens are preserved in the storage backend — no re-authentication
2972
+ * is required. Only the in-memory transport sessions are cleared.
2973
+ */
2974
+ async reconnect() {
2975
+ await this.disconnect();
2976
+ await this.connect();
2977
+ }
2978
+ /**
2979
+ * Returns all currently connected `MCPClient` instances.
2980
+ *
2981
+ * Use this to enumerate available tools across all connected servers,
2982
+ * or to route a tool call to the right client by `serverId`.
2983
+ */
2984
+ getClients() {
2985
+ return this.clients;
2986
+ }
2987
+ /**
2988
+ * Gracefully disconnects all active MCP clients and clears the internal list.
2989
+ *
2990
+ * For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
2991
+ * endpoint per the spec before closing locally. All disconnects run in
2992
+ * parallel so shutdown is not serialised across many sessions.
2993
+ *
2994
+ * Call this during server shutdown or when a user logs out to free up
2995
+ * underlying transport resources (SSE streams, HTTP connections, etc.).
2996
+ */
2997
+ async disconnect() {
2998
+ await Promise.all(this.clients.map((client) => client.disconnect()));
2999
+ this.clients = [];
3000
+ }
3001
+ // -----------------------------------------------------------------------
3002
+ // Internals
3003
+ // -----------------------------------------------------------------------
3004
+ /**
3005
+ * Resolves the list of sessions to connect.
3006
+ *
3007
+ * Uses the custom `sessionProvider` when provided, otherwise falls back
3008
+ * to querying the storage backend via `sessions.list(userId)`.
3009
+ */
3010
+ async fetchActiveSessions() {
3011
+ const sessionList = this.options.sessionProvider ? await this.options.sessionProvider() : await sessions.list(this.userId);
3012
+ return sessionList.filter(
2958
3013
  (s) => s.serverId && s.serverUrl && s.callbackUrl && s.status === "active"
2959
3014
  );
2960
- return valid;
2961
3015
  }
2962
3016
  /**
2963
- * Connects to a list of sessions in controlled batches of `CONNECTION_BATCH_SIZE`.
3017
+ * Connects a list of sessions in controlled batches.
2964
3018
  *
2965
- * Batching prevents overwhelming the event loop or external servers when a user
2966
- * has many active MCP sessions (e.g. 20+ servers). Within each batch, sessions
2967
- * are connected concurrently using `Promise.all` for speed.
3019
+ * Batching prevents overwhelming the event loop or external servers when
3020
+ * a user has many active MCP sessions. Within each batch, sessions are
3021
+ * connected concurrently using `Promise.all`.
2968
3022
  */
2969
3023
  async connectInBatches(sessions2) {
2970
3024
  for (let i = 0; i < sessions2.length; i += CONNECTION_BATCH_SIZE) {
@@ -2973,17 +3027,16 @@ var MultiSessionClient = class {
2973
3027
  }
2974
3028
  }
2975
3029
  /**
2976
- * Connects a single session, with built-in deduplication to prevent race conditions.
3030
+ * Connects a single session, with deduplication to prevent race conditions.
2977
3031
  *
2978
- * - If a client for this session already exists and is connected, returns immediately.
2979
- * - If the existing client entry is no longer connected (e.g. it was explicitly
2980
- * disconnected), it is evicted so that `establishConnectionWithRetries` creates a
2981
- * fresh transport preventing "Client already connected" errors from the SDK.
2982
- * - If a connection attempt for this session is already in-flight (e.g. from a
2983
- * concurrent call), it joins the existing promise instead of starting a new one.
2984
- * This is the key concurrency lock the `connectionPromises` map acts as a
2985
- * per-session mutex so we never spin up two physical connections for the same session.
2986
- * - On completion (success or failure), the promise is cleaned up from the map.
3032
+ * - If a client for this session already exists and is connected, returns
3033
+ * immediately.
3034
+ * - If the existing client entry is no longer connected (e.g. explicit
3035
+ * disconnect), it is evicted so a fresh transport is created.
3036
+ * - If a connection attempt for this session is already in-flight, the
3037
+ * existing promise is reused as a per-session mutex.
3038
+ * - On completion (success or failure), the promise is cleaned up from
3039
+ * the connectionPromises map.
2987
3040
  */
2988
3041
  async connectSession(session) {
2989
3042
  const existing = this.clients.find((c) => c.getSessionId() === session.sessionId);
@@ -2991,6 +3044,7 @@ var MultiSessionClient = class {
2991
3044
  if (existing.isConnected()) {
2992
3045
  return;
2993
3046
  }
3047
+ this.options.onSessionEvicted?.(existing.getSessionId());
2994
3048
  this.clients = this.clients.filter((c) => c !== existing);
2995
3049
  }
2996
3050
  if (this.connectionPromises.has(session.sessionId)) {
@@ -3005,22 +3059,19 @@ var MultiSessionClient = class {
3005
3059
  }
3006
3060
  }
3007
3061
  /**
3008
- * The core connection loop for a single session.
3062
+ * Core connection loop for a single session with retry logic.
3009
3063
  *
3010
- * Attempts to establish a physical MCP connection, retrying up to `maxRetries` times
3011
- * if the connection fails. Each attempt:
3012
- * 1. Creates a fresh `MCPClient` instance from the session data.
3013
- * 2. Races the connect call against a timeout promise — if the server doesn't respond
3014
- * within `timeoutMs`, the attempt is aborted and counted as a failure.
3015
- * 3. On success, replaces any stale client entry for this session in the `clients` array.
3064
+ * 1. Creates a fresh `MCPClient` from the session data.
3065
+ * 2. Races `client.connect()` against a timeout.
3066
+ * 3. On success, replaces any stale entry and fires `onSessionConnected`.
3016
3067
  * 4. On failure, waits `retryDelay` ms before the next attempt.
3017
3068
  *
3018
- * If all attempts are exhausted, logs an error and returns silently (does not throw),
3019
- * so a single bad server doesn't block the rest of the batch from connecting.
3069
+ * If all attempts are exhausted, logs an error and returns silently so
3070
+ * a single bad server doesn't block the rest of the batch.
3020
3071
  */
3021
3072
  async establishConnectionWithRetries(session) {
3022
- const maxRetries = this.options.maxRetries ?? DEFAULT_MAX_RETRIES;
3023
- const retryDelay = this.options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
3073
+ const maxRetries = this.options.maxRetries;
3074
+ const retryDelay = this.options.retryDelay;
3024
3075
  let lastError;
3025
3076
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
3026
3077
  try {
@@ -3034,10 +3085,13 @@ var MultiSessionClient = class {
3034
3085
  transportType: session.transportType,
3035
3086
  headers: session.headers
3036
3087
  });
3037
- const timeoutMs = this.options.timeout ?? DEFAULT_TIMEOUT_MS;
3088
+ const timeoutMs = this.options.timeout;
3038
3089
  let timeoutTimer;
3039
3090
  const timeoutPromise = new Promise((_, reject) => {
3040
- timeoutTimer = setTimeout(() => reject(new Error(`Connection timed out after ${timeoutMs}ms`)), timeoutMs);
3091
+ timeoutTimer = setTimeout(
3092
+ () => reject(new Error(`Connection timed out after ${timeoutMs}ms`)),
3093
+ timeoutMs
3094
+ );
3041
3095
  });
3042
3096
  try {
3043
3097
  await Promise.race([client.connect(), timeoutPromise]);
@@ -3046,6 +3100,7 @@ var MultiSessionClient = class {
3046
3100
  }
3047
3101
  this.clients = this.clients.filter((c) => c.getSessionId() !== session.sessionId);
3048
3102
  this.clients.push(client);
3103
+ this.options.onSessionConnected?.(session.sessionId, client);
3049
3104
  return;
3050
3105
  } catch (error) {
3051
3106
  lastError = error;
@@ -3054,58 +3109,11 @@ var MultiSessionClient = class {
3054
3109
  }
3055
3110
  }
3056
3111
  }
3057
- console.error(`[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`, lastError);
3058
- }
3059
- /**
3060
- * The main entry point. Fetches all active sessions for this userId from
3061
- * storage and establishes connections to all of them in batches.
3062
- *
3063
- * Call this once after creating the client. On traditional servers, you can
3064
- * cache the `MultiSessionClient` instance after calling `connect()` to avoid
3065
- * re-fetching and re-connecting on every request.
3066
- */
3067
- async connect() {
3068
- const sessions2 = await this.getActiveSessions();
3069
- const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
3070
- this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
3071
- await this.connectInBatches(sessions2);
3072
- }
3073
- /**
3074
- * Drops all cached `MCPClient` instances and reconnects fresh from storage.
3075
- *
3076
- * Call this when downstream MCP servers have expired their transport sessions
3077
- * (e.g. after a remote server restart) and subsequent tool calls return
3078
- * "Session not found. Reconnect without session header." errors.
3079
- *
3080
- * OAuth tokens are preserved in the storage backend — no re-authentication
3081
- * is required. Only the in-memory transport sessions are cleared.
3082
- */
3083
- async reconnect() {
3084
- await this.disconnect();
3085
- await this.connect();
3086
- }
3087
- /**
3088
- * Returns all currently connected `MCPClient` instances.
3089
- *
3090
- * Use this to enumerate available tools across all connected servers,
3091
- * or to route a tool call to the right client by `serverId`.
3092
- */
3093
- getClients() {
3094
- return this.clients;
3095
- }
3096
- /**
3097
- * Gracefully disconnects all active MCP clients and clears the internal client list.
3098
- *
3099
- * For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
3100
- * endpoint per the spec before closing locally. All disconnects run in
3101
- * parallel so shutdown is not serialised across many sessions.
3102
- *
3103
- * Call this during server shutdown or when a user logs out to free up
3104
- * underlying transport resources (SSE streams, HTTP connections, etc.).
3105
- */
3106
- async disconnect() {
3107
- await Promise.all(this.clients.map((client) => client.disconnect()));
3108
- this.clients = [];
3112
+ this.options.onSessionFailed?.(session.sessionId, lastError);
3113
+ console.error(
3114
+ `[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`,
3115
+ lastError
3116
+ );
3109
3117
  }
3110
3118
  };
3111
3119