@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.d.mts
CHANGED
|
@@ -1,135 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { c as SessionMutationListener, e as SessionStore } from '../multi-session-client-CWs-AE78.mjs';
|
|
2
|
+
export { M as MCPClient, a as MultiSessionClient, S as Session, b as SessionMutationEvent, d as SessionMutationType, f as StorageOAuthClientProvider } from '../multi-session-client-CWs-AE78.mjs';
|
|
2
3
|
export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.mjs';
|
|
3
|
-
import {
|
|
4
|
+
import { M as McpConnectionEvent, t as McpObservabilityEvent, x as McpRpcResponse, w as McpRpcRequest } from '../types-CjczQwNX.mjs';
|
|
5
|
+
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, k as Disposable, E as Emitter, m as Event, q as ListToolsResponse, s as McpConnectionState, T as ToolClient, A as ToolClientProvider, B as ToolInfo } from '../types-CjczQwNX.mjs';
|
|
4
6
|
export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
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
|
-
import { r as McpRpcResponse, q as McpRpcRequest } from '../types-DCk_IF4L.mjs';
|
|
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
7
|
export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
10
8
|
import '@modelcontextprotocol/sdk/client/auth.js';
|
|
11
9
|
|
|
12
|
-
interface OAuthState {
|
|
13
|
-
nonce: string;
|
|
14
|
-
sessionId: string;
|
|
15
|
-
serverId: string;
|
|
16
|
-
createdAt: number;
|
|
17
|
-
}
|
|
18
|
-
type SessionStatus = 'pending' | 'active';
|
|
19
|
-
interface Session {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
serverId?: string;
|
|
22
|
-
serverName?: string;
|
|
23
|
-
serverUrl: string;
|
|
24
|
-
transportType: 'sse' | 'streamable-http';
|
|
25
|
-
callbackUrl: string;
|
|
26
|
-
createdAt: number;
|
|
27
|
-
updatedAt?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Storage-owned expiration timestamp for pending/inactive sessions.
|
|
30
|
-
* Active sessions use updatedAt-based dormancy cleanup instead.
|
|
31
|
-
*/
|
|
32
|
-
expiresAt?: number | null;
|
|
33
|
-
userId: string;
|
|
34
|
-
headers?: Record<string, string>;
|
|
35
|
-
authUrl?: string | null;
|
|
36
|
-
/**
|
|
37
|
-
* Session status marker used for lifecycle cleanup:
|
|
38
|
-
* - pending: short-lived intermediate/auth-pending session state
|
|
39
|
-
* - active: restorable session after successful connection/auth completion
|
|
40
|
-
*/
|
|
41
|
-
status?: SessionStatus;
|
|
42
|
-
}
|
|
43
|
-
interface SessionCredentials {
|
|
44
|
-
sessionId: string;
|
|
45
|
-
userId: string;
|
|
46
|
-
clientInformation?: OAuthClientInformationMixed | null;
|
|
47
|
-
tokens?: OAuthTokens | null;
|
|
48
|
-
codeVerifier?: string | null;
|
|
49
|
-
clientId?: string | null;
|
|
50
|
-
oauthState?: OAuthState | null;
|
|
51
|
-
}
|
|
52
|
-
type SessionMutationType = 'create' | 'update' | 'delete';
|
|
53
|
-
interface SessionMutationEvent {
|
|
54
|
-
type: SessionMutationType;
|
|
55
|
-
userId: string;
|
|
56
|
-
sessionId: string;
|
|
57
|
-
timestamp: number;
|
|
58
|
-
session?: Session;
|
|
59
|
-
patch?: Partial<Session>;
|
|
60
|
-
}
|
|
61
|
-
type SessionMutationListener = (event: SessionMutationEvent) => void | Promise<void>;
|
|
62
|
-
/**
|
|
63
|
-
* Interface for MCP session stores.
|
|
64
|
-
*/
|
|
65
|
-
interface SessionStore {
|
|
66
|
-
/**
|
|
67
|
-
* Optional initialization (e.g., database connection)
|
|
68
|
-
*/
|
|
69
|
-
init?(): Promise<void>;
|
|
70
|
-
/**
|
|
71
|
-
* Generates a unique session ID
|
|
72
|
-
*/
|
|
73
|
-
generateSessionId(): string;
|
|
74
|
-
/**
|
|
75
|
-
* Creates a new session. Throws if session already exists.
|
|
76
|
-
* @param session - Session data to create
|
|
77
|
-
*/
|
|
78
|
-
create(session: Session): Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
* Updates an existing session with partial data. Throws if session does not exist.
|
|
81
|
-
* @param userId - User identifier
|
|
82
|
-
* @param sessionId - Session identifier
|
|
83
|
-
* @param data - Partial session data to update
|
|
84
|
-
*/
|
|
85
|
-
update(userId: string, sessionId: string, data: Partial<Session>): Promise<void>;
|
|
86
|
-
/**
|
|
87
|
-
* Patches runtime credentials for an existing session.
|
|
88
|
-
* These values are separated from connection metadata in durable SQL stores.
|
|
89
|
-
*/
|
|
90
|
-
patchCredentials(userId: string, sessionId: string, data: Partial<SessionCredentials>): Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Retrieves a session
|
|
93
|
-
*/
|
|
94
|
-
get(userId: string, sessionId: string): Promise<Session | null>;
|
|
95
|
-
/**
|
|
96
|
-
* Retrieves runtime credentials for a session.
|
|
97
|
-
*/
|
|
98
|
-
getCredentials(userId: string, sessionId: string): Promise<SessionCredentials | null>;
|
|
99
|
-
/**
|
|
100
|
-
* Clears runtime credentials without removing connection metadata.
|
|
101
|
-
*/
|
|
102
|
-
clearCredentials(userId: string, sessionId: string): Promise<void>;
|
|
103
|
-
/**
|
|
104
|
-
* Gets full session data for all sessions owned by a user
|
|
105
|
-
*/
|
|
106
|
-
list(userId: string): Promise<Session[]>;
|
|
107
|
-
/**
|
|
108
|
-
* Removes a session
|
|
109
|
-
*/
|
|
110
|
-
delete(userId: string, sessionId: string): Promise<void>;
|
|
111
|
-
/**
|
|
112
|
-
* Gets all session IDs owned by a user
|
|
113
|
-
*/
|
|
114
|
-
listIds(userId: string): Promise<string[]>;
|
|
115
|
-
/**
|
|
116
|
-
* Gets all session IDs across all users (Admin)
|
|
117
|
-
*/
|
|
118
|
-
listAllIds(): Promise<string[]>;
|
|
119
|
-
/**
|
|
120
|
-
* Clears all sessions (Admin)
|
|
121
|
-
*/
|
|
122
|
-
clearAll(): Promise<void>;
|
|
123
|
-
/**
|
|
124
|
-
* Clean up expired sessions
|
|
125
|
-
*/
|
|
126
|
-
cleanupExpired(): Promise<void>;
|
|
127
|
-
/**
|
|
128
|
-
* Disconnect from storage backend
|
|
129
|
-
*/
|
|
130
|
-
disconnect(): Promise<void>;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
10
|
declare function onSessionMutation(listener: SessionMutationListener): () => void;
|
|
134
11
|
/**
|
|
135
12
|
* Global session store instance
|
|
@@ -302,4 +179,4 @@ declare function createNextMcpHandler(options?: NextMcpHandlerOptions): {
|
|
|
302
179
|
POST: (request: Request) => Promise<Response>;
|
|
303
180
|
};
|
|
304
181
|
|
|
305
|
-
export { type ClientMetadata, McpConnectionEvent, McpObservabilityEvent, McpRpcRequest, McpRpcResponse, type NextMcpHandlerOptions, SSEConnectionManager, type SSEHandlerOptions,
|
|
182
|
+
export { type ClientMetadata, McpConnectionEvent, McpObservabilityEvent, McpRpcRequest, McpRpcResponse, type NextMcpHandlerOptions, SSEConnectionManager, type SSEHandlerOptions, SessionMutationListener, SessionStore, createNextMcpHandler, createSSEHandler, onSessionMutation, sessions };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,135 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { c as SessionMutationListener, e as SessionStore } from '../multi-session-client-BluyCPo9.js';
|
|
2
|
+
export { M as MCPClient, a as MultiSessionClient, S as Session, b as SessionMutationEvent, d as SessionMutationType, f as StorageOAuthClientProvider } from '../multi-session-client-BluyCPo9.js';
|
|
2
3
|
export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.js';
|
|
3
|
-
import {
|
|
4
|
+
import { M as McpConnectionEvent, t as McpObservabilityEvent, x as McpRpcResponse, w as McpRpcRequest } from '../types-CjczQwNX.js';
|
|
5
|
+
export { a as CallToolRequest, b as CallToolResponse, f as ConnectRequest, g as ConnectResponse, k as Disposable, E as Emitter, m as Event, q as ListToolsResponse, s as McpConnectionState, T as ToolClient, A as ToolClientProvider, B as ToolInfo } from '../types-CjczQwNX.js';
|
|
4
6
|
export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
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
|
-
import { r as McpRpcResponse, q as McpRpcRequest } from '../types-DCk_IF4L.js';
|
|
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
7
|
export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
10
8
|
import '@modelcontextprotocol/sdk/client/auth.js';
|
|
11
9
|
|
|
12
|
-
interface OAuthState {
|
|
13
|
-
nonce: string;
|
|
14
|
-
sessionId: string;
|
|
15
|
-
serverId: string;
|
|
16
|
-
createdAt: number;
|
|
17
|
-
}
|
|
18
|
-
type SessionStatus = 'pending' | 'active';
|
|
19
|
-
interface Session {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
serverId?: string;
|
|
22
|
-
serverName?: string;
|
|
23
|
-
serverUrl: string;
|
|
24
|
-
transportType: 'sse' | 'streamable-http';
|
|
25
|
-
callbackUrl: string;
|
|
26
|
-
createdAt: number;
|
|
27
|
-
updatedAt?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Storage-owned expiration timestamp for pending/inactive sessions.
|
|
30
|
-
* Active sessions use updatedAt-based dormancy cleanup instead.
|
|
31
|
-
*/
|
|
32
|
-
expiresAt?: number | null;
|
|
33
|
-
userId: string;
|
|
34
|
-
headers?: Record<string, string>;
|
|
35
|
-
authUrl?: string | null;
|
|
36
|
-
/**
|
|
37
|
-
* Session status marker used for lifecycle cleanup:
|
|
38
|
-
* - pending: short-lived intermediate/auth-pending session state
|
|
39
|
-
* - active: restorable session after successful connection/auth completion
|
|
40
|
-
*/
|
|
41
|
-
status?: SessionStatus;
|
|
42
|
-
}
|
|
43
|
-
interface SessionCredentials {
|
|
44
|
-
sessionId: string;
|
|
45
|
-
userId: string;
|
|
46
|
-
clientInformation?: OAuthClientInformationMixed | null;
|
|
47
|
-
tokens?: OAuthTokens | null;
|
|
48
|
-
codeVerifier?: string | null;
|
|
49
|
-
clientId?: string | null;
|
|
50
|
-
oauthState?: OAuthState | null;
|
|
51
|
-
}
|
|
52
|
-
type SessionMutationType = 'create' | 'update' | 'delete';
|
|
53
|
-
interface SessionMutationEvent {
|
|
54
|
-
type: SessionMutationType;
|
|
55
|
-
userId: string;
|
|
56
|
-
sessionId: string;
|
|
57
|
-
timestamp: number;
|
|
58
|
-
session?: Session;
|
|
59
|
-
patch?: Partial<Session>;
|
|
60
|
-
}
|
|
61
|
-
type SessionMutationListener = (event: SessionMutationEvent) => void | Promise<void>;
|
|
62
|
-
/**
|
|
63
|
-
* Interface for MCP session stores.
|
|
64
|
-
*/
|
|
65
|
-
interface SessionStore {
|
|
66
|
-
/**
|
|
67
|
-
* Optional initialization (e.g., database connection)
|
|
68
|
-
*/
|
|
69
|
-
init?(): Promise<void>;
|
|
70
|
-
/**
|
|
71
|
-
* Generates a unique session ID
|
|
72
|
-
*/
|
|
73
|
-
generateSessionId(): string;
|
|
74
|
-
/**
|
|
75
|
-
* Creates a new session. Throws if session already exists.
|
|
76
|
-
* @param session - Session data to create
|
|
77
|
-
*/
|
|
78
|
-
create(session: Session): Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
* Updates an existing session with partial data. Throws if session does not exist.
|
|
81
|
-
* @param userId - User identifier
|
|
82
|
-
* @param sessionId - Session identifier
|
|
83
|
-
* @param data - Partial session data to update
|
|
84
|
-
*/
|
|
85
|
-
update(userId: string, sessionId: string, data: Partial<Session>): Promise<void>;
|
|
86
|
-
/**
|
|
87
|
-
* Patches runtime credentials for an existing session.
|
|
88
|
-
* These values are separated from connection metadata in durable SQL stores.
|
|
89
|
-
*/
|
|
90
|
-
patchCredentials(userId: string, sessionId: string, data: Partial<SessionCredentials>): Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Retrieves a session
|
|
93
|
-
*/
|
|
94
|
-
get(userId: string, sessionId: string): Promise<Session | null>;
|
|
95
|
-
/**
|
|
96
|
-
* Retrieves runtime credentials for a session.
|
|
97
|
-
*/
|
|
98
|
-
getCredentials(userId: string, sessionId: string): Promise<SessionCredentials | null>;
|
|
99
|
-
/**
|
|
100
|
-
* Clears runtime credentials without removing connection metadata.
|
|
101
|
-
*/
|
|
102
|
-
clearCredentials(userId: string, sessionId: string): Promise<void>;
|
|
103
|
-
/**
|
|
104
|
-
* Gets full session data for all sessions owned by a user
|
|
105
|
-
*/
|
|
106
|
-
list(userId: string): Promise<Session[]>;
|
|
107
|
-
/**
|
|
108
|
-
* Removes a session
|
|
109
|
-
*/
|
|
110
|
-
delete(userId: string, sessionId: string): Promise<void>;
|
|
111
|
-
/**
|
|
112
|
-
* Gets all session IDs owned by a user
|
|
113
|
-
*/
|
|
114
|
-
listIds(userId: string): Promise<string[]>;
|
|
115
|
-
/**
|
|
116
|
-
* Gets all session IDs across all users (Admin)
|
|
117
|
-
*/
|
|
118
|
-
listAllIds(): Promise<string[]>;
|
|
119
|
-
/**
|
|
120
|
-
* Clears all sessions (Admin)
|
|
121
|
-
*/
|
|
122
|
-
clearAll(): Promise<void>;
|
|
123
|
-
/**
|
|
124
|
-
* Clean up expired sessions
|
|
125
|
-
*/
|
|
126
|
-
cleanupExpired(): Promise<void>;
|
|
127
|
-
/**
|
|
128
|
-
* Disconnect from storage backend
|
|
129
|
-
*/
|
|
130
|
-
disconnect(): Promise<void>;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
10
|
declare function onSessionMutation(listener: SessionMutationListener): () => void;
|
|
134
11
|
/**
|
|
135
12
|
* Global session store instance
|
|
@@ -302,4 +179,4 @@ declare function createNextMcpHandler(options?: NextMcpHandlerOptions): {
|
|
|
302
179
|
POST: (request: Request) => Promise<Response>;
|
|
303
180
|
};
|
|
304
181
|
|
|
305
|
-
export { type ClientMetadata, McpConnectionEvent, McpObservabilityEvent, McpRpcRequest, McpRpcResponse, type NextMcpHandlerOptions, SSEConnectionManager, type SSEHandlerOptions,
|
|
182
|
+
export { type ClientMetadata, McpConnectionEvent, McpObservabilityEvent, McpRpcRequest, McpRpcResponse, type NextMcpHandlerOptions, SSEConnectionManager, type SSEHandlerOptions, SessionMutationListener, SessionStore, createNextMcpHandler, createSSEHandler, onSessionMutation, sessions };
|
package/dist/server/index.js
CHANGED
|
@@ -2995,17 +2995,14 @@ var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
|
2995
2995
|
var CONNECTION_BATCH_SIZE = 5;
|
|
2996
2996
|
var MultiSessionClient = class {
|
|
2997
2997
|
/**
|
|
2998
|
-
*
|
|
2999
|
-
*
|
|
3000
|
-
* @param userId - A unique string identifying the user (e.g. user ID or email).
|
|
3001
|
-
* @param options - Optional tuning for connection timeout, retry count, and retry delay.
|
|
3002
|
-
* Falls back to sensible defaults if not provided.
|
|
2998
|
+
* @param userId - Unique identifier for the user (e.g. user ID or email).
|
|
2999
|
+
* @param options - Optional tuning and lifecycle hooks.
|
|
3003
3000
|
*/
|
|
3004
3001
|
constructor(userId, options = {}) {
|
|
3005
3002
|
__publicField(this, "clients", []);
|
|
3003
|
+
__publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
|
|
3006
3004
|
__publicField(this, "userId");
|
|
3007
3005
|
__publicField(this, "options");
|
|
3008
|
-
__publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
|
|
3009
3006
|
this.userId = userId;
|
|
3010
3007
|
this.options = {
|
|
3011
3008
|
timeout: DEFAULT_TIMEOUT_MS,
|
|
@@ -3014,28 +3011,85 @@ var MultiSessionClient = class {
|
|
|
3014
3011
|
...options
|
|
3015
3012
|
};
|
|
3016
3013
|
}
|
|
3014
|
+
// -----------------------------------------------------------------------
|
|
3015
|
+
// Public API
|
|
3016
|
+
// -----------------------------------------------------------------------
|
|
3017
3017
|
/**
|
|
3018
|
-
* Fetches
|
|
3019
|
-
* ones that are ready to connect.
|
|
3018
|
+
* Fetches active sessions and establishes connections to all of them.
|
|
3020
3019
|
*
|
|
3021
|
-
*
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
* and let the OAuth flow complete separately before we try to reconnect them.
|
|
3020
|
+
* Call this once after creating the client. On long-running servers you
|
|
3021
|
+
* can cache the `MultiSessionClient` instance and call `connect()` on
|
|
3022
|
+
* each request — already-connected sessions are skipped internally.
|
|
3025
3023
|
*/
|
|
3026
|
-
async
|
|
3027
|
-
const
|
|
3028
|
-
const
|
|
3024
|
+
async connect() {
|
|
3025
|
+
const sessions2 = await this.fetchActiveSessions();
|
|
3026
|
+
const activeSessionIds = new Set(sessions2.map((s) => s.sessionId));
|
|
3027
|
+
for (const client of this.clients) {
|
|
3028
|
+
if (!activeSessionIds.has(client.getSessionId())) {
|
|
3029
|
+
this.options.onSessionEvicted?.(client.getSessionId());
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
this.clients = this.clients.filter((c) => activeSessionIds.has(c.getSessionId()));
|
|
3033
|
+
await this.connectInBatches(sessions2);
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* Drops all cached `MCPClient` instances and reconnects fresh from storage.
|
|
3037
|
+
*
|
|
3038
|
+
* Call this when downstream MCP servers have expired their transport sessions
|
|
3039
|
+
* (e.g. after a remote server restart) and subsequent tool calls return
|
|
3040
|
+
* "Session not found. Reconnect without session header." errors.
|
|
3041
|
+
*
|
|
3042
|
+
* OAuth tokens are preserved in the storage backend — no re-authentication
|
|
3043
|
+
* is required. Only the in-memory transport sessions are cleared.
|
|
3044
|
+
*/
|
|
3045
|
+
async reconnect() {
|
|
3046
|
+
await this.disconnect();
|
|
3047
|
+
await this.connect();
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* Returns all currently connected `MCPClient` instances.
|
|
3051
|
+
*
|
|
3052
|
+
* Use this to enumerate available tools across all connected servers,
|
|
3053
|
+
* or to route a tool call to the right client by `serverId`.
|
|
3054
|
+
*/
|
|
3055
|
+
getClients() {
|
|
3056
|
+
return this.clients;
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Gracefully disconnects all active MCP clients and clears the internal list.
|
|
3060
|
+
*
|
|
3061
|
+
* For Streamable HTTP sessions, each client sends an HTTP DELETE to its MCP
|
|
3062
|
+
* endpoint per the spec before closing locally. All disconnects run in
|
|
3063
|
+
* parallel so shutdown is not serialised across many sessions.
|
|
3064
|
+
*
|
|
3065
|
+
* Call this during server shutdown or when a user logs out to free up
|
|
3066
|
+
* underlying transport resources (SSE streams, HTTP connections, etc.).
|
|
3067
|
+
*/
|
|
3068
|
+
async disconnect() {
|
|
3069
|
+
await Promise.all(this.clients.map((client) => client.disconnect()));
|
|
3070
|
+
this.clients = [];
|
|
3071
|
+
}
|
|
3072
|
+
// -----------------------------------------------------------------------
|
|
3073
|
+
// Internals
|
|
3074
|
+
// -----------------------------------------------------------------------
|
|
3075
|
+
/**
|
|
3076
|
+
* Resolves the list of sessions to connect.
|
|
3077
|
+
*
|
|
3078
|
+
* Uses the custom `sessionProvider` when provided, otherwise falls back
|
|
3079
|
+
* to querying the storage backend via `sessions.list(userId)`.
|
|
3080
|
+
*/
|
|
3081
|
+
async fetchActiveSessions() {
|
|
3082
|
+
const sessionList = this.options.sessionProvider ? await this.options.sessionProvider() : await sessions.list(this.userId);
|
|
3083
|
+
return sessionList.filter(
|
|
3029
3084
|
(s) => s.serverId && s.serverUrl && s.callbackUrl && s.status === "active"
|
|
3030
3085
|
);
|
|
3031
|
-
return valid;
|
|
3032
3086
|
}
|
|
3033
3087
|
/**
|
|
3034
|
-
* Connects
|
|
3088
|
+
* Connects a list of sessions in controlled batches.
|
|
3035
3089
|
*
|
|
3036
|
-
* Batching prevents overwhelming the event loop or external servers when
|
|
3037
|
-
* has many active MCP sessions
|
|
3038
|
-
*
|
|
3090
|
+
* Batching prevents overwhelming the event loop or external servers when
|
|
3091
|
+
* a user has many active MCP sessions. Within each batch, sessions are
|
|
3092
|
+
* connected concurrently using `Promise.all`.
|
|
3039
3093
|
*/
|
|
3040
3094
|
async connectInBatches(sessions2) {
|
|
3041
3095
|
for (let i = 0; i < sessions2.length; i += CONNECTION_BATCH_SIZE) {
|
|
@@ -3044,17 +3098,16 @@ var MultiSessionClient = class {
|
|
|
3044
3098
|
}
|
|
3045
3099
|
}
|
|
3046
3100
|
/**
|
|
3047
|
-
* Connects a single session, with
|
|
3101
|
+
* Connects a single session, with deduplication to prevent race conditions.
|
|
3048
3102
|
*
|
|
3049
|
-
* - If a client for this session already exists and is connected, returns
|
|
3050
|
-
*
|
|
3051
|
-
*
|
|
3052
|
-
*
|
|
3053
|
-
* - If a connection attempt for this session is already in-flight
|
|
3054
|
-
*
|
|
3055
|
-
*
|
|
3056
|
-
*
|
|
3057
|
-
* - On completion (success or failure), the promise is cleaned up from the map.
|
|
3103
|
+
* - If a client for this session already exists and is connected, returns
|
|
3104
|
+
* immediately.
|
|
3105
|
+
* - If the existing client entry is no longer connected (e.g. explicit
|
|
3106
|
+
* disconnect), it is evicted so a fresh transport is created.
|
|
3107
|
+
* - If a connection attempt for this session is already in-flight, the
|
|
3108
|
+
* existing promise is reused as a per-session mutex.
|
|
3109
|
+
* - On completion (success or failure), the promise is cleaned up from
|
|
3110
|
+
* the connectionPromises map.
|
|
3058
3111
|
*/
|
|
3059
3112
|
async connectSession(session) {
|
|
3060
3113
|
const existing = this.clients.find((c) => c.getSessionId() === session.sessionId);
|
|
@@ -3062,6 +3115,7 @@ var MultiSessionClient = class {
|
|
|
3062
3115
|
if (existing.isConnected()) {
|
|
3063
3116
|
return;
|
|
3064
3117
|
}
|
|
3118
|
+
this.options.onSessionEvicted?.(existing.getSessionId());
|
|
3065
3119
|
this.clients = this.clients.filter((c) => c !== existing);
|
|
3066
3120
|
}
|
|
3067
3121
|
if (this.connectionPromises.has(session.sessionId)) {
|
|
@@ -3076,22 +3130,19 @@ var MultiSessionClient = class {
|
|
|
3076
3130
|
}
|
|
3077
3131
|
}
|
|
3078
3132
|
/**
|
|
3079
|
-
*
|
|
3133
|
+
* Core connection loop for a single session with retry logic.
|
|
3080
3134
|
*
|
|
3081
|
-
*
|
|
3082
|
-
*
|
|
3083
|
-
*
|
|
3084
|
-
* 2. Races the connect call against a timeout promise — if the server doesn't respond
|
|
3085
|
-
* within `timeoutMs`, the attempt is aborted and counted as a failure.
|
|
3086
|
-
* 3. On success, replaces any stale client entry for this session in the `clients` array.
|
|
3135
|
+
* 1. Creates a fresh `MCPClient` from the session data.
|
|
3136
|
+
* 2. Races `client.connect()` against a timeout.
|
|
3137
|
+
* 3. On success, replaces any stale entry and fires `onSessionConnected`.
|
|
3087
3138
|
* 4. On failure, waits `retryDelay` ms before the next attempt.
|
|
3088
3139
|
*
|
|
3089
|
-
* If all attempts are exhausted, logs an error and returns silently
|
|
3090
|
-
*
|
|
3140
|
+
* If all attempts are exhausted, logs an error and returns silently so
|
|
3141
|
+
* a single bad server doesn't block the rest of the batch.
|
|
3091
3142
|
*/
|
|
3092
3143
|
async establishConnectionWithRetries(session) {
|
|
3093
|
-
const maxRetries = this.options.maxRetries
|
|
3094
|
-
const retryDelay = this.options.retryDelay
|
|
3144
|
+
const maxRetries = this.options.maxRetries;
|
|
3145
|
+
const retryDelay = this.options.retryDelay;
|
|
3095
3146
|
let lastError;
|
|
3096
3147
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3097
3148
|
try {
|
|
@@ -3105,10 +3156,13 @@ var MultiSessionClient = class {
|
|
|
3105
3156
|
transportType: session.transportType,
|
|
3106
3157
|
headers: session.headers
|
|
3107
3158
|
});
|
|
3108
|
-
const timeoutMs = this.options.timeout
|
|
3159
|
+
const timeoutMs = this.options.timeout;
|
|
3109
3160
|
let timeoutTimer;
|
|
3110
3161
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3111
|
-
timeoutTimer = setTimeout(
|
|
3162
|
+
timeoutTimer = setTimeout(
|
|
3163
|
+
() => reject(new Error(`Connection timed out after ${timeoutMs}ms`)),
|
|
3164
|
+
timeoutMs
|
|
3165
|
+
);
|
|
3112
3166
|
});
|
|
3113
3167
|
try {
|
|
3114
3168
|
await Promise.race([client.connect(), timeoutPromise]);
|
|
@@ -3117,6 +3171,7 @@ var MultiSessionClient = class {
|
|
|
3117
3171
|
}
|
|
3118
3172
|
this.clients = this.clients.filter((c) => c.getSessionId() !== session.sessionId);
|
|
3119
3173
|
this.clients.push(client);
|
|
3174
|
+
this.options.onSessionConnected?.(session.sessionId, client);
|
|
3120
3175
|
return;
|
|
3121
3176
|
} catch (error) {
|
|
3122
3177
|
lastError = error;
|
|
@@ -3125,58 +3180,11 @@ var MultiSessionClient = class {
|
|
|
3125
3180
|
}
|
|
3126
3181
|
}
|
|
3127
3182
|
}
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
*
|
|
3134
|
-
* Call this once after creating the client. On traditional servers, you can
|
|
3135
|
-
* cache the `MultiSessionClient` instance after calling `connect()` to avoid
|
|
3136
|
-
* re-fetching and re-connecting on every request.
|
|
3137
|
-
*/
|
|
3138
|
-
async connect() {
|
|
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()));
|
|
3142
|
-
await this.connectInBatches(sessions2);
|
|
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
|
-
}
|
|
3158
|
-
/**
|
|
3159
|
-
* Returns all currently connected `MCPClient` instances.
|
|
3160
|
-
*
|
|
3161
|
-
* Use this to enumerate available tools across all connected servers,
|
|
3162
|
-
* or to route a tool call to the right client by `serverId`.
|
|
3163
|
-
*/
|
|
3164
|
-
getClients() {
|
|
3165
|
-
return this.clients;
|
|
3166
|
-
}
|
|
3167
|
-
/**
|
|
3168
|
-
* Gracefully disconnects all active MCP clients and clears the internal client list.
|
|
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
|
-
*
|
|
3174
|
-
* Call this during server shutdown or when a user logs out to free up
|
|
3175
|
-
* underlying transport resources (SSE streams, HTTP connections, etc.).
|
|
3176
|
-
*/
|
|
3177
|
-
async disconnect() {
|
|
3178
|
-
await Promise.all(this.clients.map((client) => client.disconnect()));
|
|
3179
|
-
this.clients = [];
|
|
3183
|
+
this.options.onSessionFailed?.(session.sessionId, lastError);
|
|
3184
|
+
console.error(
|
|
3185
|
+
`[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`,
|
|
3186
|
+
lastError
|
|
3187
|
+
);
|
|
3180
3188
|
}
|
|
3181
3189
|
};
|
|
3182
3190
|
|