@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.
- package/dist/adapters/agui-adapter.d.mts +3 -4
- package/dist/adapters/agui-adapter.d.ts +3 -4
- package/dist/adapters/agui-middleware.d.mts +3 -4
- package/dist/adapters/agui-middleware.d.ts +3 -4
- package/dist/adapters/ai-adapter.d.mts +3 -4
- package/dist/adapters/ai-adapter.d.ts +3 -4
- package/dist/adapters/langchain-adapter.d.mts +3 -4
- package/dist/adapters/langchain-adapter.d.ts +3 -4
- package/dist/adapters/mastra-adapter.d.mts +2 -2
- package/dist/adapters/mastra-adapter.d.ts +2 -2
- package/dist/client/index.d.mts +2 -3
- package/dist/client/index.d.ts +2 -3
- package/dist/client/react.d.mts +4 -6
- package/dist/client/react.d.ts +4 -6
- package/dist/client/vue.d.mts +4 -6
- package/dist/client/vue.d.ts +4 -6
- package/dist/{index-Ch7ouNSa.d.ts → index-B8kJSrBJ.d.ts} +1 -2
- package/dist/{index-L5XoXgsb.d.mts → index-DiJsm_lK.d.mts} +1 -2
- package/dist/index.d.mts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.js +103 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -95
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-Bwm-efqg.d.ts → multi-session-client-BluyCPo9.d.ts} +201 -71
- package/dist/{multi-session-client-k-9RvWvi.d.mts → multi-session-client-CWs-AE78.d.mts} +201 -71
- package/dist/server/index.d.mts +5 -128
- package/dist/server/index.d.ts +5 -128
- package/dist/server/index.js +103 -95
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +103 -95
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +4 -5
- package/dist/shared/index.d.ts +4 -5
- package/dist/{tool-router-CuApsDiV.d.mts → tool-router-CbG4Tum6.d.mts} +1 -1
- package/dist/{tool-router-CZMrOG8J.d.ts → tool-router-ChIhPwgP.d.ts} +1 -1
- package/dist/{types-DCk_IF4L.d.mts → types-CjczQwNX.d.mts} +122 -1
- package/dist/{types-DCk_IF4L.d.ts → types-CjczQwNX.d.ts} +122 -1
- package/package.json +1 -1
- package/src/server/mcp/multi-session-client.ts +177 -132
- package/dist/events-C4m7tK1U.d.mts +0 -122
- package/dist/events-C4m7tK1U.d.ts +0 -122
package/dist/server/index.mjs
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
2948
|
-
* ones that are ready to connect.
|
|
2947
|
+
* Fetches active sessions and establishes connections to all of them.
|
|
2949
2948
|
*
|
|
2950
|
-
*
|
|
2951
|
-
*
|
|
2952
|
-
*
|
|
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
|
|
2956
|
-
const
|
|
2957
|
-
const
|
|
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
|
|
3017
|
+
* Connects a list of sessions in controlled batches.
|
|
2964
3018
|
*
|
|
2965
|
-
* Batching prevents overwhelming the event loop or external servers when
|
|
2966
|
-
* has many active MCP sessions
|
|
2967
|
-
*
|
|
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
|
|
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
|
|
2979
|
-
*
|
|
2980
|
-
*
|
|
2981
|
-
*
|
|
2982
|
-
* - If a connection attempt for this session is already in-flight
|
|
2983
|
-
*
|
|
2984
|
-
*
|
|
2985
|
-
*
|
|
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
|
-
*
|
|
3062
|
+
* Core connection loop for a single session with retry logic.
|
|
3009
3063
|
*
|
|
3010
|
-
*
|
|
3011
|
-
*
|
|
3012
|
-
*
|
|
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
|
|
3019
|
-
*
|
|
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
|
|
3023
|
-
const retryDelay = this.options.retryDelay
|
|
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
|
|
3088
|
+
const timeoutMs = this.options.timeout;
|
|
3038
3089
|
let timeoutTimer;
|
|
3039
3090
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3040
|
-
timeoutTimer = setTimeout(
|
|
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
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
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
|
|