@mcp-ts/sdk 2.4.0 → 2.4.1
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/README.md +58 -19
- package/dist/adapters/agui-adapter.d.mts +2 -2
- package/dist/adapters/agui-adapter.d.ts +2 -2
- package/dist/adapters/agui-middleware.d.mts +2 -2
- package/dist/adapters/agui-middleware.d.ts +2 -2
- package/dist/adapters/ai-adapter.d.mts +2 -2
- package/dist/adapters/ai-adapter.d.ts +2 -2
- package/dist/adapters/langchain-adapter.d.mts +2 -2
- package/dist/adapters/langchain-adapter.d.ts +2 -2
- 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 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/react.d.mts +4 -4
- package/dist/client/react.d.ts +4 -4
- package/dist/client/vue.d.mts +4 -4
- package/dist/client/vue.d.ts +4 -4
- package/dist/{events-CK3N--3g.d.mts → events-C4m7tK1U.d.mts} +0 -1
- package/dist/{events-CK3N--3g.d.ts → events-C4m7tK1U.d.ts} +0 -1
- package/dist/{index-CtXvKl8N.d.ts → index-Ch7ouNSa.d.ts} +1 -1
- package/dist/{index-ByIjEReo.d.mts → index-L5XoXgsb.d.mts} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +71 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +71 -20
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-CnvZEGPY.d.ts → multi-session-client-Bwm-efqg.d.ts} +28 -5
- package/dist/{multi-session-client-CIMUGF8S.d.mts → multi-session-client-k-9RvWvi.d.mts} +28 -5
- package/dist/server/index.d.mts +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +71 -20
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +71 -20
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/migrations/v2.3.4/neon/20260513010000_install_mcp_sessions.sql +69 -0
- package/migrations/v2.3.4/neon/20260513020000_add_session_cleanup_cron.sql +35 -0
- package/migrations/v2.3.4/supabase/20260330195700_install_mcp_sessions.sql +82 -0
- package/migrations/v2.3.4/supabase/20260421010000_add_session_cleanup_cron.sql +32 -0
- package/package.json +1 -1
- package/src/server/handlers/sse-handler.ts +7 -5
- package/src/server/mcp/multi-session-client.ts +46 -5
- package/src/server/mcp/oauth-client.ts +60 -10
- package/src/shared/events.ts +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-
|
|
1
|
+
import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-C4m7tK1U.js';
|
|
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';
|
|
@@ -282,10 +282,15 @@ declare class MCPClient {
|
|
|
282
282
|
*/
|
|
283
283
|
isConnected(): boolean;
|
|
284
284
|
/**
|
|
285
|
-
* Disconnects from the MCP server and cleans up resources
|
|
286
|
-
* Does not remove session from Redis
|
|
285
|
+
* Disconnects from the MCP server and cleans up resources.
|
|
286
|
+
* Does not remove session from Redis — use clearSession() for that.
|
|
287
|
+
*
|
|
288
|
+
* For Streamable HTTP sessions, sends an HTTP DELETE to the MCP endpoint
|
|
289
|
+
* before closing, as recommended by the MCP Streamable HTTP spec
|
|
290
|
+
* (section "Session Management", rule 5). This is best-effort — errors
|
|
291
|
+
* (e.g. server already restarted, 404/405 responses) are silently ignored.
|
|
287
292
|
*/
|
|
288
|
-
disconnect(
|
|
293
|
+
disconnect(): Promise<void>;
|
|
289
294
|
/**
|
|
290
295
|
* Dispose of all event emitters
|
|
291
296
|
* Call this when the client is no longer needed
|
|
@@ -387,6 +392,9 @@ declare class MultiSessionClient {
|
|
|
387
392
|
* Connects a single session, with built-in deduplication to prevent race conditions.
|
|
388
393
|
*
|
|
389
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.
|
|
390
398
|
* - If a connection attempt for this session is already in-flight (e.g. from a
|
|
391
399
|
* concurrent call), it joins the existing promise instead of starting a new one.
|
|
392
400
|
* This is the key concurrency lock — the `connectionPromises` map acts as a
|
|
@@ -418,6 +426,17 @@ declare class MultiSessionClient {
|
|
|
418
426
|
* re-fetching and re-connecting on every request.
|
|
419
427
|
*/
|
|
420
428
|
connect(): Promise<void>;
|
|
429
|
+
/**
|
|
430
|
+
* Drops all cached `MCPClient` instances and reconnects fresh from storage.
|
|
431
|
+
*
|
|
432
|
+
* Call this when downstream MCP servers have expired their transport sessions
|
|
433
|
+
* (e.g. after a remote server restart) and subsequent tool calls return
|
|
434
|
+
* "Session not found. Reconnect without session header." errors.
|
|
435
|
+
*
|
|
436
|
+
* OAuth tokens are preserved in the storage backend — no re-authentication
|
|
437
|
+
* is required. Only the in-memory transport sessions are cleared.
|
|
438
|
+
*/
|
|
439
|
+
reconnect(): Promise<void>;
|
|
421
440
|
/**
|
|
422
441
|
* Returns all currently connected `MCPClient` instances.
|
|
423
442
|
*
|
|
@@ -428,10 +447,14 @@ declare class MultiSessionClient {
|
|
|
428
447
|
/**
|
|
429
448
|
* Gracefully disconnects all active MCP clients and clears the internal client list.
|
|
430
449
|
*
|
|
450
|
+
* For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
|
|
451
|
+
* endpoint per the spec before closing locally. All disconnects run in
|
|
452
|
+
* parallel so shutdown is not serialised across many sessions.
|
|
453
|
+
*
|
|
431
454
|
* Call this during server shutdown or when a user logs out to free up
|
|
432
455
|
* underlying transport resources (SSE streams, HTTP connections, etc.).
|
|
433
456
|
*/
|
|
434
|
-
disconnect(): void
|
|
457
|
+
disconnect(): Promise<void>;
|
|
435
458
|
}
|
|
436
459
|
|
|
437
460
|
export { MCPClient as M, StorageOAuthClientProvider as S, MultiSessionClient as a };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-
|
|
1
|
+
import { b as Event, M as McpConnectionEvent, d as McpObservabilityEvent, c as McpConnectionState } from './events-C4m7tK1U.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';
|
|
@@ -282,10 +282,15 @@ declare class MCPClient {
|
|
|
282
282
|
*/
|
|
283
283
|
isConnected(): boolean;
|
|
284
284
|
/**
|
|
285
|
-
* Disconnects from the MCP server and cleans up resources
|
|
286
|
-
* Does not remove session from Redis
|
|
285
|
+
* Disconnects from the MCP server and cleans up resources.
|
|
286
|
+
* Does not remove session from Redis — use clearSession() for that.
|
|
287
|
+
*
|
|
288
|
+
* For Streamable HTTP sessions, sends an HTTP DELETE to the MCP endpoint
|
|
289
|
+
* before closing, as recommended by the MCP Streamable HTTP spec
|
|
290
|
+
* (section "Session Management", rule 5). This is best-effort — errors
|
|
291
|
+
* (e.g. server already restarted, 404/405 responses) are silently ignored.
|
|
287
292
|
*/
|
|
288
|
-
disconnect(
|
|
293
|
+
disconnect(): Promise<void>;
|
|
289
294
|
/**
|
|
290
295
|
* Dispose of all event emitters
|
|
291
296
|
* Call this when the client is no longer needed
|
|
@@ -387,6 +392,9 @@ declare class MultiSessionClient {
|
|
|
387
392
|
* Connects a single session, with built-in deduplication to prevent race conditions.
|
|
388
393
|
*
|
|
389
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.
|
|
390
398
|
* - If a connection attempt for this session is already in-flight (e.g. from a
|
|
391
399
|
* concurrent call), it joins the existing promise instead of starting a new one.
|
|
392
400
|
* This is the key concurrency lock — the `connectionPromises` map acts as a
|
|
@@ -418,6 +426,17 @@ declare class MultiSessionClient {
|
|
|
418
426
|
* re-fetching and re-connecting on every request.
|
|
419
427
|
*/
|
|
420
428
|
connect(): Promise<void>;
|
|
429
|
+
/**
|
|
430
|
+
* Drops all cached `MCPClient` instances and reconnects fresh from storage.
|
|
431
|
+
*
|
|
432
|
+
* Call this when downstream MCP servers have expired their transport sessions
|
|
433
|
+
* (e.g. after a remote server restart) and subsequent tool calls return
|
|
434
|
+
* "Session not found. Reconnect without session header." errors.
|
|
435
|
+
*
|
|
436
|
+
* OAuth tokens are preserved in the storage backend — no re-authentication
|
|
437
|
+
* is required. Only the in-memory transport sessions are cleared.
|
|
438
|
+
*/
|
|
439
|
+
reconnect(): Promise<void>;
|
|
421
440
|
/**
|
|
422
441
|
* Returns all currently connected `MCPClient` instances.
|
|
423
442
|
*
|
|
@@ -428,10 +447,14 @@ declare class MultiSessionClient {
|
|
|
428
447
|
/**
|
|
429
448
|
* Gracefully disconnects all active MCP clients and clears the internal client list.
|
|
430
449
|
*
|
|
450
|
+
* For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
|
|
451
|
+
* endpoint per the spec before closing locally. All disconnects run in
|
|
452
|
+
* parallel so shutdown is not serialised across many sessions.
|
|
453
|
+
*
|
|
431
454
|
* Call this during server shutdown or when a user logs out to free up
|
|
432
455
|
* underlying transport resources (SSE streams, HTTP connections, etc.).
|
|
433
456
|
*/
|
|
434
|
-
disconnect(): void
|
|
457
|
+
disconnect(): Promise<void>;
|
|
435
458
|
}
|
|
436
459
|
|
|
437
460
|
export { MCPClient as M, StorageOAuthClientProvider as S, MultiSessionClient as a };
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-
|
|
1
|
+
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-k-9RvWvi.mjs';
|
|
2
2
|
export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.mjs';
|
|
3
3
|
import { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
4
4
|
export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
5
|
-
import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-
|
|
6
|
-
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-
|
|
5
|
+
import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-C4m7tK1U.mjs';
|
|
6
|
+
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-C4m7tK1U.mjs';
|
|
7
7
|
import { r as McpRpcResponse, q as McpRpcRequest } from '../types-DCk_IF4L.mjs';
|
|
8
8
|
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, n as ListToolsResponse, T as ToolClient, u as ToolClientProvider, v as ToolInfo } from '../types-DCk_IF4L.mjs';
|
|
9
9
|
export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -249,7 +249,7 @@ declare class SSEConnectionManager {
|
|
|
249
249
|
/**
|
|
250
250
|
* Cleanup and close all connections
|
|
251
251
|
*/
|
|
252
|
-
dispose(): void
|
|
252
|
+
dispose(): Promise<void>;
|
|
253
253
|
}
|
|
254
254
|
/**
|
|
255
255
|
* Create an SSE endpoint handler compatible with Node.js HTTP frameworks.
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-
|
|
1
|
+
export { M as MCPClient, a as MultiSessionClient, S as StorageOAuthClientProvider } from '../multi-session-client-Bwm-efqg.js';
|
|
2
2
|
export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.js';
|
|
3
3
|
import { OAuthClientInformationMixed, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
4
4
|
export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
5
|
-
import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-
|
|
6
|
-
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-
|
|
5
|
+
import { M as McpConnectionEvent, d as McpObservabilityEvent } from '../events-C4m7tK1U.js';
|
|
6
|
+
export { D as Disposable, E as Emitter, b as Event, c as McpConnectionState } from '../events-C4m7tK1U.js';
|
|
7
7
|
import { r as McpRpcResponse, q as McpRpcRequest } from '../types-DCk_IF4L.js';
|
|
8
8
|
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, n as ListToolsResponse, T as ToolClient, u as ToolClientProvider, v as ToolInfo } from '../types-DCk_IF4L.js';
|
|
9
9
|
export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -249,7 +249,7 @@ declare class SSEConnectionManager {
|
|
|
249
249
|
/**
|
|
250
250
|
* Cleanup and close all connections
|
|
251
251
|
*/
|
|
252
|
-
dispose(): void
|
|
252
|
+
dispose(): Promise<void>;
|
|
253
253
|
}
|
|
254
254
|
/**
|
|
255
255
|
* Create an SSE endpoint handler compatible with Node.js HTTP frameworks.
|
package/dist/server/index.js
CHANGED
|
@@ -2288,7 +2288,7 @@ var MCPClient = class {
|
|
|
2288
2288
|
* Observation: SDK 1.24.0+ connections may hang indefinitely in some environments.
|
|
2289
2289
|
* This wrapper enforces a timeout and properly uses AbortController to unblock the request.
|
|
2290
2290
|
*/
|
|
2291
|
-
fetch: (url, init) => {
|
|
2291
|
+
fetch: async (url, init) => {
|
|
2292
2292
|
const timeout = 3e4;
|
|
2293
2293
|
const controller = new AbortController();
|
|
2294
2294
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
@@ -2296,7 +2296,17 @@ var MCPClient = class {
|
|
|
2296
2296
|
// @ts-ignore: AbortSignal.any is available in Node 20+
|
|
2297
2297
|
AbortSignal.any ? AbortSignal.any([init.signal, controller.signal]) : controller.signal
|
|
2298
2298
|
) : controller.signal;
|
|
2299
|
-
|
|
2299
|
+
try {
|
|
2300
|
+
const response = await fetch(url, { ...init, signal });
|
|
2301
|
+
const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
|
|
2302
|
+
if (response.status === 404 && hasSessionHeader) {
|
|
2303
|
+
this.client = null;
|
|
2304
|
+
throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
|
|
2305
|
+
}
|
|
2306
|
+
return response;
|
|
2307
|
+
} finally {
|
|
2308
|
+
clearTimeout(timeoutId);
|
|
2309
|
+
}
|
|
2300
2310
|
}
|
|
2301
2311
|
};
|
|
2302
2312
|
if (type === "sse") {
|
|
@@ -2488,6 +2498,14 @@ var MCPClient = class {
|
|
|
2488
2498
|
* @throws {Error} When connection fails for other reasons
|
|
2489
2499
|
*/
|
|
2490
2500
|
async connect() {
|
|
2501
|
+
if (this.client?.transport) {
|
|
2502
|
+
this.transport = null;
|
|
2503
|
+
try {
|
|
2504
|
+
await this.client.close();
|
|
2505
|
+
} catch {
|
|
2506
|
+
}
|
|
2507
|
+
this.client = null;
|
|
2508
|
+
}
|
|
2491
2509
|
await this.initialize();
|
|
2492
2510
|
if (!this.client || !this.oauthProvider) {
|
|
2493
2511
|
const error = "Client or OAuth provider not initialized";
|
|
@@ -2866,7 +2884,7 @@ var MCPClient = class {
|
|
|
2866
2884
|
await this.oauthProvider.invalidateCredentials("all");
|
|
2867
2885
|
}
|
|
2868
2886
|
await sessions.delete(this.userId, this.sessionId);
|
|
2869
|
-
this.disconnect();
|
|
2887
|
+
await this.disconnect();
|
|
2870
2888
|
}
|
|
2871
2889
|
/**
|
|
2872
2890
|
* Checks if the client is currently connected to an MCP server
|
|
@@ -2876,10 +2894,21 @@ var MCPClient = class {
|
|
|
2876
2894
|
return this.client !== null;
|
|
2877
2895
|
}
|
|
2878
2896
|
/**
|
|
2879
|
-
* Disconnects from the MCP server and cleans up resources
|
|
2880
|
-
* Does not remove session from Redis
|
|
2897
|
+
* Disconnects from the MCP server and cleans up resources.
|
|
2898
|
+
* Does not remove session from Redis — use clearSession() for that.
|
|
2899
|
+
*
|
|
2900
|
+
* For Streamable HTTP sessions, sends an HTTP DELETE to the MCP endpoint
|
|
2901
|
+
* before closing, as recommended by the MCP Streamable HTTP spec
|
|
2902
|
+
* (section "Session Management", rule 5). This is best-effort — errors
|
|
2903
|
+
* (e.g. server already restarted, 404/405 responses) are silently ignored.
|
|
2881
2904
|
*/
|
|
2882
|
-
disconnect(
|
|
2905
|
+
async disconnect() {
|
|
2906
|
+
if (this.transport instanceof streamableHttp_js.StreamableHTTPClientTransport) {
|
|
2907
|
+
try {
|
|
2908
|
+
await this.transport.terminateSession();
|
|
2909
|
+
} catch {
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2883
2912
|
if (this.client) {
|
|
2884
2913
|
this.client.close();
|
|
2885
2914
|
}
|
|
@@ -2891,7 +2920,6 @@ var MCPClient = class {
|
|
|
2891
2920
|
type: "disconnected",
|
|
2892
2921
|
sessionId: this.sessionId,
|
|
2893
2922
|
serverId: this.serverId,
|
|
2894
|
-
reason,
|
|
2895
2923
|
timestamp: Date.now()
|
|
2896
2924
|
});
|
|
2897
2925
|
this._onObservabilityEvent.fire({
|
|
@@ -2900,9 +2928,7 @@ var MCPClient = class {
|
|
|
2900
2928
|
message: `Disconnected from ${this.serverId}`,
|
|
2901
2929
|
sessionId: this.sessionId,
|
|
2902
2930
|
serverId: this.serverId,
|
|
2903
|
-
payload: {
|
|
2904
|
-
reason: reason || "unknown"
|
|
2905
|
-
},
|
|
2931
|
+
payload: {},
|
|
2906
2932
|
timestamp: Date.now(),
|
|
2907
2933
|
id: nanoid.nanoid()
|
|
2908
2934
|
});
|
|
@@ -3021,6 +3047,9 @@ var MultiSessionClient = class {
|
|
|
3021
3047
|
* Connects a single session, with built-in deduplication to prevent race conditions.
|
|
3022
3048
|
*
|
|
3023
3049
|
* - If a client for this session already exists and is connected, returns immediately.
|
|
3050
|
+
* - If the existing client entry is no longer connected (e.g. it was explicitly
|
|
3051
|
+
* disconnected), it is evicted so that `establishConnectionWithRetries` creates a
|
|
3052
|
+
* fresh transport — preventing "Client already connected" errors from the SDK.
|
|
3024
3053
|
* - If a connection attempt for this session is already in-flight (e.g. from a
|
|
3025
3054
|
* concurrent call), it joins the existing promise instead of starting a new one.
|
|
3026
3055
|
* This is the key concurrency lock — the `connectionPromises` map acts as a
|
|
@@ -3028,9 +3057,12 @@ var MultiSessionClient = class {
|
|
|
3028
3057
|
* - On completion (success or failure), the promise is cleaned up from the map.
|
|
3029
3058
|
*/
|
|
3030
3059
|
async connectSession(session) {
|
|
3031
|
-
const
|
|
3032
|
-
if (
|
|
3033
|
-
|
|
3060
|
+
const existing = this.clients.find((c) => c.getSessionId() === session.sessionId);
|
|
3061
|
+
if (existing) {
|
|
3062
|
+
if (existing.isConnected()) {
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
this.clients = this.clients.filter((c) => c !== existing);
|
|
3034
3066
|
}
|
|
3035
3067
|
if (this.connectionPromises.has(session.sessionId)) {
|
|
3036
3068
|
return this.connectionPromises.get(session.sessionId);
|
|
@@ -3105,8 +3137,24 @@ var MultiSessionClient = class {
|
|
|
3105
3137
|
*/
|
|
3106
3138
|
async connect() {
|
|
3107
3139
|
const sessions2 = await this.getActiveSessions();
|
|
3140
|
+
const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
|
|
3141
|
+
this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
|
|
3108
3142
|
await this.connectInBatches(sessions2);
|
|
3109
3143
|
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Drops all cached `MCPClient` instances and reconnects fresh from storage.
|
|
3146
|
+
*
|
|
3147
|
+
* Call this when downstream MCP servers have expired their transport sessions
|
|
3148
|
+
* (e.g. after a remote server restart) and subsequent tool calls return
|
|
3149
|
+
* "Session not found. Reconnect without session header." errors.
|
|
3150
|
+
*
|
|
3151
|
+
* OAuth tokens are preserved in the storage backend — no re-authentication
|
|
3152
|
+
* is required. Only the in-memory transport sessions are cleared.
|
|
3153
|
+
*/
|
|
3154
|
+
async reconnect() {
|
|
3155
|
+
await this.disconnect();
|
|
3156
|
+
await this.connect();
|
|
3157
|
+
}
|
|
3110
3158
|
/**
|
|
3111
3159
|
* Returns all currently connected `MCPClient` instances.
|
|
3112
3160
|
*
|
|
@@ -3119,11 +3167,15 @@ var MultiSessionClient = class {
|
|
|
3119
3167
|
/**
|
|
3120
3168
|
* Gracefully disconnects all active MCP clients and clears the internal client list.
|
|
3121
3169
|
*
|
|
3170
|
+
* For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
|
|
3171
|
+
* endpoint per the spec before closing locally. All disconnects run in
|
|
3172
|
+
* parallel so shutdown is not serialised across many sessions.
|
|
3173
|
+
*
|
|
3122
3174
|
* Call this during server shutdown or when a user logs out to free up
|
|
3123
3175
|
* underlying transport resources (SSE streams, HTTP connections, etc.).
|
|
3124
3176
|
*/
|
|
3125
|
-
disconnect() {
|
|
3126
|
-
this.clients.
|
|
3177
|
+
async disconnect() {
|
|
3178
|
+
await Promise.all(this.clients.map((client) => client.disconnect()));
|
|
3127
3179
|
this.clients = [];
|
|
3128
3180
|
}
|
|
3129
3181
|
};
|
|
@@ -3357,7 +3409,6 @@ var SSEConnectionManager = class {
|
|
|
3357
3409
|
const client = this.clients.get(sessionId);
|
|
3358
3410
|
if (client) {
|
|
3359
3411
|
await client.clearSession();
|
|
3360
|
-
client.disconnect();
|
|
3361
3412
|
this.clients.delete(sessionId);
|
|
3362
3413
|
} else {
|
|
3363
3414
|
await sessions.delete(this.userId, sessionId);
|
|
@@ -3566,14 +3617,14 @@ var SSEConnectionManager = class {
|
|
|
3566
3617
|
/**
|
|
3567
3618
|
* Cleanup and close all connections
|
|
3568
3619
|
*/
|
|
3569
|
-
dispose() {
|
|
3620
|
+
async dispose() {
|
|
3570
3621
|
this.isActive = false;
|
|
3571
3622
|
if (this.heartbeatTimer) {
|
|
3572
3623
|
clearInterval(this.heartbeatTimer);
|
|
3573
3624
|
}
|
|
3574
|
-
|
|
3575
|
-
client.disconnect()
|
|
3576
|
-
|
|
3625
|
+
await Promise.all(
|
|
3626
|
+
Array.from(this.clients.values()).map((client) => client.disconnect())
|
|
3627
|
+
);
|
|
3577
3628
|
this.clients.clear();
|
|
3578
3629
|
}
|
|
3579
3630
|
};
|