@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
package/dist/index.mjs CHANGED
@@ -2996,17 +2996,14 @@ var DEFAULT_RETRY_DELAY_MS = 1e3;
2996
2996
  var CONNECTION_BATCH_SIZE = 5;
2997
2997
  var MultiSessionClient = class {
2998
2998
  /**
2999
- * Creates a new MultiSessionClient for the given user userId.
3000
- *
3001
- * @param userId - A unique string identifying the user (e.g. user ID or email).
3002
- * @param options - Optional tuning for connection timeout, retry count, and retry delay.
3003
- * Falls back to sensible defaults if not provided.
2999
+ * @param userId - Unique identifier for the user (e.g. user ID or email).
3000
+ * @param options - Optional tuning and lifecycle hooks.
3004
3001
  */
3005
3002
  constructor(userId, options = {}) {
3006
3003
  __publicField(this, "clients", []);
3004
+ __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
3007
3005
  __publicField(this, "userId");
3008
3006
  __publicField(this, "options");
3009
- __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
3010
3007
  this.userId = userId;
3011
3008
  this.options = {
3012
3009
  timeout: DEFAULT_TIMEOUT_MS,
@@ -3015,28 +3012,85 @@ var MultiSessionClient = class {
3015
3012
  ...options
3016
3013
  };
3017
3014
  }
3015
+ // -----------------------------------------------------------------------
3016
+ // Public API
3017
+ // -----------------------------------------------------------------------
3018
3018
  /**
3019
- * Fetches all sessions for this userId from storage and returns only the
3020
- * ones that are ready to connect.
3019
+ * Fetches active sessions and establishes connections to all of them.
3021
3020
  *
3022
- * A session is considered connectable when:
3023
- * - It has a `serverId`, `serverUrl`, and `callbackUrl` (i.e. it was fully initialized)
3024
- * - Its status is `active`. Pending sessions are skipped here
3025
- * and let the OAuth flow complete separately before we try to reconnect them.
3021
+ * Call this once after creating the client. On long-running servers you
3022
+ * can cache the `MultiSessionClient` instance and call `connect()` on
3023
+ * each request already-connected sessions are skipped internally.
3026
3024
  */
3027
- async getActiveSessions() {
3028
- const sessionList = await sessions.list(this.userId);
3029
- const valid = sessionList.filter(
3025
+ async connect() {
3026
+ const sessions2 = await this.fetchActiveSessions();
3027
+ const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
3028
+ for (const client of this.clients) {
3029
+ if (!activeSessionIds.has(client.getSessionId())) {
3030
+ this.options.onSessionEvicted?.(client.getSessionId());
3031
+ }
3032
+ }
3033
+ this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
3034
+ await this.connectInBatches(sessions2);
3035
+ }
3036
+ /**
3037
+ * Drops all cached `MCPClient` instances and reconnects fresh from storage.
3038
+ *
3039
+ * Call this when downstream MCP servers have expired their transport sessions
3040
+ * (e.g. after a remote server restart) and subsequent tool calls return
3041
+ * "Session not found. Reconnect without session header." errors.
3042
+ *
3043
+ * OAuth tokens are preserved in the storage backend — no re-authentication
3044
+ * is required. Only the in-memory transport sessions are cleared.
3045
+ */
3046
+ async reconnect() {
3047
+ await this.disconnect();
3048
+ await this.connect();
3049
+ }
3050
+ /**
3051
+ * Returns all currently connected `MCPClient` instances.
3052
+ *
3053
+ * Use this to enumerate available tools across all connected servers,
3054
+ * or to route a tool call to the right client by `serverId`.
3055
+ */
3056
+ getClients() {
3057
+ return this.clients;
3058
+ }
3059
+ /**
3060
+ * Gracefully disconnects all active MCP clients and clears the internal list.
3061
+ *
3062
+ * For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
3063
+ * endpoint per the spec before closing locally. All disconnects run in
3064
+ * parallel so shutdown is not serialised across many sessions.
3065
+ *
3066
+ * Call this during server shutdown or when a user logs out to free up
3067
+ * underlying transport resources (SSE streams, HTTP connections, etc.).
3068
+ */
3069
+ async disconnect() {
3070
+ await Promise.all(this.clients.map((client) => client.disconnect()));
3071
+ this.clients = [];
3072
+ }
3073
+ // -----------------------------------------------------------------------
3074
+ // Internals
3075
+ // -----------------------------------------------------------------------
3076
+ /**
3077
+ * Resolves the list of sessions to connect.
3078
+ *
3079
+ * Uses the custom `sessionProvider` when provided, otherwise falls back
3080
+ * to querying the storage backend via `sessions.list(userId)`.
3081
+ */
3082
+ async fetchActiveSessions() {
3083
+ const sessionList = this.options.sessionProvider ? await this.options.sessionProvider() : await sessions.list(this.userId);
3084
+ return sessionList.filter(
3030
3085
  (s) => s.serverId && s.serverUrl && s.callbackUrl && s.status === "active"
3031
3086
  );
3032
- return valid;
3033
3087
  }
3034
3088
  /**
3035
- * Connects to a list of sessions in controlled batches of `CONNECTION_BATCH_SIZE`.
3089
+ * Connects a list of sessions in controlled batches.
3036
3090
  *
3037
- * Batching prevents overwhelming the event loop or external servers when a user
3038
- * has many active MCP sessions (e.g. 20+ servers). Within each batch, sessions
3039
- * are connected concurrently using `Promise.all` for speed.
3091
+ * Batching prevents overwhelming the event loop or external servers when
3092
+ * a user has many active MCP sessions. Within each batch, sessions are
3093
+ * connected concurrently using `Promise.all`.
3040
3094
  */
3041
3095
  async connectInBatches(sessions2) {
3042
3096
  for (let i = 0; i < sessions2.length; i += CONNECTION_BATCH_SIZE) {
@@ -3045,17 +3099,16 @@ var MultiSessionClient = class {
3045
3099
  }
3046
3100
  }
3047
3101
  /**
3048
- * Connects a single session, with built-in deduplication to prevent race conditions.
3102
+ * Connects a single session, with deduplication to prevent race conditions.
3049
3103
  *
3050
- * - If a client for this session already exists and is connected, returns immediately.
3051
- * - If the existing client entry is no longer connected (e.g. it was explicitly
3052
- * disconnected), it is evicted so that `establishConnectionWithRetries` creates a
3053
- * fresh transport preventing "Client already connected" errors from the SDK.
3054
- * - If a connection attempt for this session is already in-flight (e.g. from a
3055
- * concurrent call), it joins the existing promise instead of starting a new one.
3056
- * This is the key concurrency lock the `connectionPromises` map acts as a
3057
- * per-session mutex so we never spin up two physical connections for the same session.
3058
- * - On completion (success or failure), the promise is cleaned up from the map.
3104
+ * - If a client for this session already exists and is connected, returns
3105
+ * immediately.
3106
+ * - If the existing client entry is no longer connected (e.g. explicit
3107
+ * disconnect), it is evicted so a fresh transport is created.
3108
+ * - If a connection attempt for this session is already in-flight, the
3109
+ * existing promise is reused as a per-session mutex.
3110
+ * - On completion (success or failure), the promise is cleaned up from
3111
+ * the connectionPromises map.
3059
3112
  */
3060
3113
  async connectSession(session) {
3061
3114
  const existing = this.clients.find((c) => c.getSessionId() === session.sessionId);
@@ -3063,6 +3116,7 @@ var MultiSessionClient = class {
3063
3116
  if (existing.isConnected()) {
3064
3117
  return;
3065
3118
  }
3119
+ this.options.onSessionEvicted?.(existing.getSessionId());
3066
3120
  this.clients = this.clients.filter((c) => c !== existing);
3067
3121
  }
3068
3122
  if (this.connectionPromises.has(session.sessionId)) {
@@ -3077,22 +3131,19 @@ var MultiSessionClient = class {
3077
3131
  }
3078
3132
  }
3079
3133
  /**
3080
- * The core connection loop for a single session.
3134
+ * Core connection loop for a single session with retry logic.
3081
3135
  *
3082
- * Attempts to establish a physical MCP connection, retrying up to `maxRetries` times
3083
- * if the connection fails. Each attempt:
3084
- * 1. Creates a fresh `MCPClient` instance from the session data.
3085
- * 2. Races the connect call against a timeout promise — if the server doesn't respond
3086
- * within `timeoutMs`, the attempt is aborted and counted as a failure.
3087
- * 3. On success, replaces any stale client entry for this session in the `clients` array.
3136
+ * 1. Creates a fresh `MCPClient` from the session data.
3137
+ * 2. Races `client.connect()` against a timeout.
3138
+ * 3. On success, replaces any stale entry and fires `onSessionConnected`.
3088
3139
  * 4. On failure, waits `retryDelay` ms before the next attempt.
3089
3140
  *
3090
- * If all attempts are exhausted, logs an error and returns silently (does not throw),
3091
- * so a single bad server doesn't block the rest of the batch from connecting.
3141
+ * If all attempts are exhausted, logs an error and returns silently so
3142
+ * a single bad server doesn't block the rest of the batch.
3092
3143
  */
3093
3144
  async establishConnectionWithRetries(session) {
3094
- const maxRetries = this.options.maxRetries ?? DEFAULT_MAX_RETRIES;
3095
- const retryDelay = this.options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
3145
+ const maxRetries = this.options.maxRetries;
3146
+ const retryDelay = this.options.retryDelay;
3096
3147
  let lastError;
3097
3148
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
3098
3149
  try {
@@ -3106,10 +3157,13 @@ var MultiSessionClient = class {
3106
3157
  transportType: session.transportType,
3107
3158
  headers: session.headers
3108
3159
  });
3109
- const timeoutMs = this.options.timeout ?? DEFAULT_TIMEOUT_MS;
3160
+ const timeoutMs = this.options.timeout;
3110
3161
  let timeoutTimer;
3111
3162
  const timeoutPromise = new Promise((_, reject) => {
3112
- timeoutTimer = setTimeout(() => reject(new Error(`Connection timed out after ${timeoutMs}ms`)), timeoutMs);
3163
+ timeoutTimer = setTimeout(
3164
+ () => reject(new Error(`Connection timed out after ${timeoutMs}ms`)),
3165
+ timeoutMs
3166
+ );
3113
3167
  });
3114
3168
  try {
3115
3169
  await Promise.race([client.connect(), timeoutPromise]);
@@ -3118,6 +3172,7 @@ var MultiSessionClient = class {
3118
3172
  }
3119
3173
  this.clients = this.clients.filter((c) => c.getSessionId() !== session.sessionId);
3120
3174
  this.clients.push(client);
3175
+ this.options.onSessionConnected?.(session.sessionId, client);
3121
3176
  return;
3122
3177
  } catch (error) {
3123
3178
  lastError = error;
@@ -3126,58 +3181,11 @@ var MultiSessionClient = class {
3126
3181
  }
3127
3182
  }
3128
3183
  }
3129
- console.error(`[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`, lastError);
3130
- }
3131
- /**
3132
- * The main entry point. Fetches all active sessions for this userId from
3133
- * storage and establishes connections to all of them in batches.
3134
- *
3135
- * Call this once after creating the client. On traditional servers, you can
3136
- * cache the `MultiSessionClient` instance after calling `connect()` to avoid
3137
- * re-fetching and re-connecting on every request.
3138
- */
3139
- async connect() {
3140
- const sessions2 = await this.getActiveSessions();
3141
- const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
3142
- this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
3143
- await this.connectInBatches(sessions2);
3144
- }
3145
- /**
3146
- * Drops all cached `MCPClient` instances and reconnects fresh from storage.
3147
- *
3148
- * Call this when downstream MCP servers have expired their transport sessions
3149
- * (e.g. after a remote server restart) and subsequent tool calls return
3150
- * "Session not found. Reconnect without session header." errors.
3151
- *
3152
- * OAuth tokens are preserved in the storage backend — no re-authentication
3153
- * is required. Only the in-memory transport sessions are cleared.
3154
- */
3155
- async reconnect() {
3156
- await this.disconnect();
3157
- await this.connect();
3158
- }
3159
- /**
3160
- * Returns all currently connected `MCPClient` instances.
3161
- *
3162
- * Use this to enumerate available tools across all connected servers,
3163
- * or to route a tool call to the right client by `serverId`.
3164
- */
3165
- getClients() {
3166
- return this.clients;
3167
- }
3168
- /**
3169
- * Gracefully disconnects all active MCP clients and clears the internal client list.
3170
- *
3171
- * For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
3172
- * endpoint per the spec before closing locally. All disconnects run in
3173
- * parallel so shutdown is not serialised across many sessions.
3174
- *
3175
- * Call this during server shutdown or when a user logs out to free up
3176
- * underlying transport resources (SSE streams, HTTP connections, etc.).
3177
- */
3178
- async disconnect() {
3179
- await Promise.all(this.clients.map((client) => client.disconnect()));
3180
- this.clients = [];
3184
+ this.options.onSessionFailed?.(session.sessionId, lastError);
3185
+ console.error(
3186
+ `[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`,
3187
+ lastError
3188
+ );
3181
3189
  }
3182
3190
  };
3183
3191