@mcp-ts/sdk 2.4.2 → 2.4.4

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 (56) hide show
  1. package/dist/adapters/agui-adapter.d.mts +3 -3
  2. package/dist/adapters/agui-adapter.d.ts +3 -3
  3. package/dist/adapters/agui-middleware.d.mts +3 -3
  4. package/dist/adapters/agui-middleware.d.ts +3 -3
  5. package/dist/adapters/ai-adapter.d.mts +3 -3
  6. package/dist/adapters/ai-adapter.d.ts +3 -3
  7. package/dist/adapters/langchain-adapter.d.mts +3 -3
  8. package/dist/adapters/langchain-adapter.d.ts +3 -3
  9. package/dist/adapters/mastra-adapter.d.mts +2 -2
  10. package/dist/adapters/mastra-adapter.d.ts +2 -2
  11. package/dist/bin/mcp-ts.js +5 -5
  12. package/dist/bin/mcp-ts.js.map +1 -1
  13. package/dist/bin/mcp-ts.mjs +5 -5
  14. package/dist/bin/mcp-ts.mjs.map +1 -1
  15. package/dist/client/index.d.mts +2 -2
  16. package/dist/client/index.d.ts +2 -2
  17. package/dist/client/react.d.mts +4 -5
  18. package/dist/client/react.d.ts +4 -5
  19. package/dist/client/react.js.map +1 -1
  20. package/dist/client/react.mjs.map +1 -1
  21. package/dist/client/vue.d.mts +4 -5
  22. package/dist/client/vue.d.ts +4 -5
  23. package/dist/client/vue.js.map +1 -1
  24. package/dist/client/vue.mjs.map +1 -1
  25. package/dist/{index-B8kJSrBJ.d.ts → index-CZk2gu2E.d.ts} +1 -1
  26. package/dist/{index-DiJsm_lK.d.mts → index-CkM3p0eE.d.mts} +1 -1
  27. package/dist/index.d.mts +4 -4
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +99 -63
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +99 -63
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/{multi-session-client-CWs-AE78.d.mts → multi-session-client-ChQrBZhF.d.mts} +21 -5
  34. package/dist/{multi-session-client-BluyCPo9.d.ts → multi-session-client-QOOPdEVp.d.ts} +21 -5
  35. package/dist/server/index.d.mts +4 -4
  36. package/dist/server/index.d.ts +4 -4
  37. package/dist/server/index.js +99 -63
  38. package/dist/server/index.js.map +1 -1
  39. package/dist/server/index.mjs +99 -63
  40. package/dist/server/index.mjs.map +1 -1
  41. package/dist/shared/index.d.mts +4 -4
  42. package/dist/shared/index.d.ts +4 -4
  43. package/dist/shared/index.js.map +1 -1
  44. package/dist/shared/index.mjs.map +1 -1
  45. package/dist/{tool-router-CbG4Tum6.d.mts → tool-router-CGs3IDOJ.d.mts} +1 -1
  46. package/dist/{tool-router-ChIhPwgP.d.ts → tool-router-DQ-HrD7W.d.ts} +1 -1
  47. package/dist/{types-CjczQwNX.d.mts → types-Bf-7GOLW.d.mts} +0 -2
  48. package/dist/{types-CjczQwNX.d.ts → types-Bf-7GOLW.d.ts} +0 -2
  49. package/package.json +1 -1
  50. package/src/bin/mcp-ts.ts +5 -5
  51. package/src/client/react/use-mcp.ts +0 -1
  52. package/src/client/vue/use-mcp.ts +0 -1
  53. package/src/server/mcp/multi-session-client.ts +13 -0
  54. package/src/server/mcp/oauth-client.ts +82 -75
  55. package/src/server/storage/index.ts +8 -8
  56. package/src/shared/events.ts +0 -2
@@ -1,4 +1,4 @@
1
- import { m as Event, M as McpConnectionEvent, t as McpObservabilityEvent, s as McpConnectionState, A as ToolClientProvider } from './types-CjczQwNX.mjs';
1
+ import { m as Event, M as McpConnectionEvent, t as McpObservabilityEvent, s as McpConnectionState, A as ToolClientProvider } from './types-Bf-7GOLW.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';
@@ -184,12 +184,11 @@ declare class MCPClient {
184
184
  */
185
185
  private getTransport;
186
186
  /**
187
- * Initializes client components (client, transport, OAuth provider)
188
- * Loads missing configuration from Redis session store if needed
189
- * This method is idempotent and safe to call multiple times
187
+ * Ensures session metadata and OAuth provider are loaded.
188
+ * Does NOT create the SDK Client callers that need one create it themselves.
190
189
  * @private
191
190
  */
192
- private initialize;
191
+ private ensureSession;
193
192
  /**
194
193
  * Saves current session state to the session store
195
194
  * Creates new session if it doesn't exist, updates if it does
@@ -264,6 +263,17 @@ declare class MCPClient {
264
263
  * @throws {Error} When client is not connected
265
264
  */
266
265
  readResource(uri: string): Promise<ReadResourceResult>;
266
+ /**
267
+ * Wraps an MCP request with automatic transport-session recovery.
268
+ *
269
+ * When the downstream MCP server rejects the request with a 404 indicating
270
+ * the transport session has expired, this method tears down the stale SDK
271
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
272
+ * and retries the request once.
273
+ *
274
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
275
+ */
276
+ private withRetry;
267
277
  /**
268
278
  * Reconnects to MCP server using existing OAuth provider from Redis
269
279
  * Used for session restoration in serverless environments
@@ -534,6 +544,12 @@ declare class MultiSessionClient implements ToolClientProvider {
534
544
  * or to route a tool call to the right client by `serverId`.
535
545
  */
536
546
  getClients(): MCPClient[];
547
+ /**
548
+ * Removes and disconnects a single session by ID.
549
+ *
550
+ * @returns `true` if the session was found and removed, `false` if not found.
551
+ */
552
+ removeSession(sessionId: string): Promise<boolean>;
537
553
  /**
538
554
  * Gracefully disconnects all active MCP clients and clears the internal list.
539
555
  *
@@ -1,4 +1,4 @@
1
- import { m as Event, M as McpConnectionEvent, t as McpObservabilityEvent, s as McpConnectionState, A as ToolClientProvider } from './types-CjczQwNX.js';
1
+ import { m as Event, M as McpConnectionEvent, t as McpObservabilityEvent, s as McpConnectionState, A as ToolClientProvider } from './types-Bf-7GOLW.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';
@@ -184,12 +184,11 @@ declare class MCPClient {
184
184
  */
185
185
  private getTransport;
186
186
  /**
187
- * Initializes client components (client, transport, OAuth provider)
188
- * Loads missing configuration from Redis session store if needed
189
- * This method is idempotent and safe to call multiple times
187
+ * Ensures session metadata and OAuth provider are loaded.
188
+ * Does NOT create the SDK Client callers that need one create it themselves.
190
189
  * @private
191
190
  */
192
- private initialize;
191
+ private ensureSession;
193
192
  /**
194
193
  * Saves current session state to the session store
195
194
  * Creates new session if it doesn't exist, updates if it does
@@ -264,6 +263,17 @@ declare class MCPClient {
264
263
  * @throws {Error} When client is not connected
265
264
  */
266
265
  readResource(uri: string): Promise<ReadResourceResult>;
266
+ /**
267
+ * Wraps an MCP request with automatic transport-session recovery.
268
+ *
269
+ * When the downstream MCP server rejects the request with a 404 indicating
270
+ * the transport session has expired, this method tears down the stale SDK
271
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
272
+ * and retries the request once.
273
+ *
274
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
275
+ */
276
+ private withRetry;
267
277
  /**
268
278
  * Reconnects to MCP server using existing OAuth provider from Redis
269
279
  * Used for session restoration in serverless environments
@@ -534,6 +544,12 @@ declare class MultiSessionClient implements ToolClientProvider {
534
544
  * or to route a tool call to the right client by `serverId`.
535
545
  */
536
546
  getClients(): MCPClient[];
547
+ /**
548
+ * Removes and disconnects a single session by ID.
549
+ *
550
+ * @returns `true` if the session was found and removed, `false` if not found.
551
+ */
552
+ removeSession(sessionId: string): Promise<boolean>;
537
553
  /**
538
554
  * Gracefully disconnects all active MCP clients and clears the internal list.
539
555
  *
@@ -1,8 +1,8 @@
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';
1
+ import { c as SessionMutationListener, e as SessionStore } from '../multi-session-client-ChQrBZhF.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-ChQrBZhF.mjs';
3
3
  export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.mjs';
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
+ import { M as McpConnectionEvent, t as McpObservabilityEvent, x as McpRpcResponse, w as McpRpcRequest } from '../types-Bf-7GOLW.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-Bf-7GOLW.mjs';
6
6
  export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
7
7
  export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
8
8
  import '@modelcontextprotocol/sdk/client/auth.js';
@@ -1,8 +1,8 @@
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';
1
+ import { c as SessionMutationListener, e as SessionStore } from '../multi-session-client-QOOPdEVp.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-QOOPdEVp.js';
3
3
  export { U as UnauthorizedError, s as sanitizeServerLabel } from '../utils-DELRKQPU.js';
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
+ import { M as McpConnectionEvent, t as McpObservabilityEvent, x as McpRpcResponse, w as McpRpcRequest } from '../types-Bf-7GOLW.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-Bf-7GOLW.js';
6
6
  export { OAuthClientInformation, OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
7
7
  export { CallToolResult, ListToolsResult, Tool } from '@modelcontextprotocol/sdk/types.js';
8
8
  import '@modelcontextprotocol/sdk/client/auth.js';
@@ -1734,12 +1734,12 @@ async function createStorage() {
1734
1734
  }
1735
1735
  if (type === "supabase") {
1736
1736
  const url = process.env.SUPABASE_URL;
1737
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1737
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1738
1738
  if (!url || !key) {
1739
- console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
1739
+ console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SECRET_KEY.');
1740
1740
  } else {
1741
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1742
- console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
1741
+ if (!process.env.SUPABASE_SECRET_KEY) {
1742
+ console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SECRET_KEY" is recommended.');
1743
1743
  }
1744
1744
  try {
1745
1745
  const { createClient } = await import('@supabase/supabase-js');
@@ -1794,13 +1794,13 @@ async function createStorage() {
1794
1794
  console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
1795
1795
  return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
1796
1796
  }
1797
- if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
1797
+ if (process.env.SUPABASE_URL && (process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY)) {
1798
1798
  try {
1799
1799
  const { createClient } = await import('@supabase/supabase-js');
1800
1800
  const url = process.env.SUPABASE_URL;
1801
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1802
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1803
- console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
1801
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1802
+ if (!process.env.SUPABASE_SECRET_KEY) {
1803
+ console.warn('[mcp-ts][Storage] \u26A0\uFE0F Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SECRET_KEY" is recommended.');
1804
1804
  }
1805
1805
  const client = createClient(url, key);
1806
1806
  console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
@@ -2149,7 +2149,7 @@ var MCPClient = class {
2149
2149
  * @param options - Client configuration options
2150
2150
  */
2151
2151
  constructor(options) {
2152
- __publicField(this, "client", null);
2152
+ __publicField(this, "client");
2153
2153
  __publicField(this, "oauthProvider", null);
2154
2154
  __publicField(this, "transport", null);
2155
2155
  __publicField(this, "userId");
@@ -2190,6 +2190,21 @@ var MCPClient = class {
2190
2190
  this.clientUri = options.clientUri;
2191
2191
  this.logoUri = options.logoUri;
2192
2192
  this.policyUri = options.policyUri;
2193
+ this.client = new index_js.Client(
2194
+ {
2195
+ name: MCP_CLIENT_NAME,
2196
+ version: MCP_CLIENT_VERSION
2197
+ },
2198
+ {
2199
+ capabilities: {
2200
+ extensions: {
2201
+ "io.modelcontextprotocol/ui": {
2202
+ mimeTypes: ["text/html+mcp"]
2203
+ }
2204
+ }
2205
+ }
2206
+ }
2207
+ );
2193
2208
  }
2194
2209
  /**
2195
2210
  * Emit a connection state change event
@@ -2300,7 +2315,6 @@ var MCPClient = class {
2300
2315
  const response = await fetch(url, { ...init, signal });
2301
2316
  const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
2302
2317
  if (response.status === 404 && hasSessionHeader) {
2303
- this.client = null;
2304
2318
  throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
2305
2319
  }
2306
2320
  return response;
@@ -2316,13 +2330,12 @@ var MCPClient = class {
2316
2330
  }
2317
2331
  }
2318
2332
  /**
2319
- * Initializes client components (client, transport, OAuth provider)
2320
- * Loads missing configuration from Redis session store if needed
2321
- * This method is idempotent and safe to call multiple times
2333
+ * Ensures session metadata and OAuth provider are loaded.
2334
+ * Does NOT create the SDK Client callers that need one create it themselves.
2322
2335
  * @private
2323
2336
  */
2324
- async initialize() {
2325
- if (this.client && this.oauthProvider) {
2337
+ async ensureSession() {
2338
+ if (this.oauthProvider) {
2326
2339
  return;
2327
2340
  }
2328
2341
  this.emitStateChange("INITIALIZING");
@@ -2365,23 +2378,6 @@ var MCPClient = class {
2365
2378
  }
2366
2379
  });
2367
2380
  }
2368
- if (!this.client) {
2369
- this.client = new index_js.Client(
2370
- {
2371
- name: MCP_CLIENT_NAME,
2372
- version: MCP_CLIENT_VERSION
2373
- },
2374
- {
2375
- capabilities: {
2376
- extensions: {
2377
- "io.modelcontextprotocol/ui": {
2378
- mimeTypes: ["text/html+mcp"]
2379
- }
2380
- }
2381
- }
2382
- }
2383
- );
2384
- }
2385
2381
  if (existingSession === null) {
2386
2382
  existingSession = await sessions.get(this.userId, this.sessionId);
2387
2383
  }
@@ -2498,17 +2494,16 @@ var MCPClient = class {
2498
2494
  * @throws {Error} When connection fails for other reasons
2499
2495
  */
2500
2496
  async connect() {
2501
- if (this.client?.transport) {
2497
+ if (this.client.transport) {
2502
2498
  this.transport = null;
2503
2499
  try {
2504
2500
  await this.client.close();
2505
2501
  } catch {
2506
2502
  }
2507
- this.client = null;
2508
2503
  }
2509
- await this.initialize();
2510
- if (!this.client || !this.oauthProvider) {
2511
- const error = "Client or OAuth provider not initialized";
2504
+ await this.ensureSession();
2505
+ if (!this.oauthProvider) {
2506
+ const error = "OAuth provider not initialized";
2512
2507
  this.emitError(error, "connection");
2513
2508
  this.emitStateChange("FAILED");
2514
2509
  throw new Error(error);
@@ -2575,7 +2570,7 @@ var MCPClient = class {
2575
2570
  async finishAuth(authCode, state) {
2576
2571
  this.emitStateChange("AUTHENTICATING");
2577
2572
  this.emitProgress("Exchanging authorization code for tokens...");
2578
- await this.initialize();
2573
+ await this.ensureSession();
2579
2574
  if (!this.oauthProvider) {
2580
2575
  const error = "OAuth provider not initialized";
2581
2576
  this.emitError(error, "auth");
@@ -2681,7 +2676,9 @@ var MCPClient = class {
2681
2676
  method: "tools/list",
2682
2677
  params: {}
2683
2678
  };
2684
- const result = await this.client.request(request, types_js.ListToolsResultSchema);
2679
+ const result = await this.withRetry(
2680
+ () => this.client.request(request, types_js.ListToolsResultSchema)
2681
+ );
2685
2682
  if (this.serverId) {
2686
2683
  this._onConnectionEvent.fire({
2687
2684
  type: "tools_discovered",
@@ -2721,7 +2718,9 @@ var MCPClient = class {
2721
2718
  }
2722
2719
  };
2723
2720
  try {
2724
- const result = await this.client.request(request, types_js.CallToolResultSchema);
2721
+ const result = await this.withRetry(
2722
+ () => this.client.request(request, types_js.CallToolResultSchema)
2723
+ );
2725
2724
  this._onObservabilityEvent.fire({
2726
2725
  type: "mcp:client:tool_call",
2727
2726
  level: "info",
@@ -2773,7 +2772,9 @@ var MCPClient = class {
2773
2772
  method: "prompts/list",
2774
2773
  params: {}
2775
2774
  };
2776
- const result = await this.client.request(request, types_js.ListPromptsResultSchema);
2775
+ const result = await this.withRetry(
2776
+ () => this.client.request(request, types_js.ListPromptsResultSchema)
2777
+ );
2777
2778
  this.emitStateChange("READY");
2778
2779
  this.emitProgress(`Discovered ${result.prompts.length} prompts`);
2779
2780
  return result;
@@ -2802,7 +2803,9 @@ var MCPClient = class {
2802
2803
  arguments: args
2803
2804
  }
2804
2805
  };
2805
- return await this.client.request(request, types_js.GetPromptResultSchema);
2806
+ return await this.withRetry(
2807
+ () => this.client.request(request, types_js.GetPromptResultSchema)
2808
+ );
2806
2809
  }
2807
2810
  /**
2808
2811
  * Lists all available resources from the connected MCP server
@@ -2819,7 +2822,9 @@ var MCPClient = class {
2819
2822
  method: "resources/list",
2820
2823
  params: {}
2821
2824
  };
2822
- const result = await this.client.request(request, types_js.ListResourcesResultSchema);
2825
+ const result = await this.withRetry(
2826
+ () => this.client.request(request, types_js.ListResourcesResultSchema)
2827
+ );
2823
2828
  this.emitStateChange("READY");
2824
2829
  this.emitProgress(`Discovered ${result.resources.length} resources`);
2825
2830
  return result;
@@ -2846,7 +2851,35 @@ var MCPClient = class {
2846
2851
  uri
2847
2852
  }
2848
2853
  };
2849
- return await this.client.request(request, types_js.ReadResourceResultSchema);
2854
+ return await this.withRetry(
2855
+ () => this.client.request(request, types_js.ReadResourceResultSchema)
2856
+ );
2857
+ }
2858
+ /**
2859
+ * Wraps an MCP request with automatic transport-session recovery.
2860
+ *
2861
+ * When the downstream MCP server rejects the request with a 404 indicating
2862
+ * the transport session has expired, this method tears down the stale SDK
2863
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
2864
+ * and retries the request once.
2865
+ *
2866
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
2867
+ */
2868
+ async withRetry(fn) {
2869
+ try {
2870
+ return await fn();
2871
+ } catch (error) {
2872
+ if (!(error instanceof Error && error.message.includes("MCP_SESSION_EXPIRED"))) throw error;
2873
+ if (this.client.transport) {
2874
+ try {
2875
+ await this.client.close();
2876
+ } catch {
2877
+ }
2878
+ this.transport = null;
2879
+ }
2880
+ await this.reconnect();
2881
+ return await fn();
2882
+ }
2850
2883
  }
2851
2884
  /**
2852
2885
  * Reconnects to MCP server using existing OAuth provider from Redis
@@ -2855,20 +2888,9 @@ var MCPClient = class {
2855
2888
  * @throws {Error} When OAuth provider is not initialized
2856
2889
  */
2857
2890
  async reconnect() {
2858
- await this.initialize();
2859
- if (!this.oauthProvider) {
2860
- throw new Error("OAuth provider not initialized");
2861
- }
2862
- this.client = new index_js.Client(
2863
- {
2864
- name: MCP_CLIENT_NAME,
2865
- version: MCP_CLIENT_VERSION
2866
- },
2867
- { capabilities: {} }
2868
- );
2869
- const tt = this.transportType || "streamable-http";
2870
- this.transport = this.getTransport(tt);
2871
- await this.client.connect(this.transport);
2891
+ await this.ensureSession();
2892
+ if (!this.oauthProvider) throw new Error("OAuth provider not initialized");
2893
+ await this.connect();
2872
2894
  }
2873
2895
  /**
2874
2896
  * Completely removes the session from Redis including all OAuth data
@@ -2876,7 +2898,7 @@ var MCPClient = class {
2876
2898
  */
2877
2899
  async clearSession() {
2878
2900
  try {
2879
- await this.initialize();
2901
+ await this.ensureSession();
2880
2902
  } catch (error) {
2881
2903
  console.warn("[MCPClient] Initialization failed during clearSession:", error);
2882
2904
  }
@@ -2891,7 +2913,7 @@ var MCPClient = class {
2891
2913
  * @returns True if connected, false otherwise
2892
2914
  */
2893
2915
  isConnected() {
2894
- return this.client !== null;
2916
+ return this.client.transport !== void 0;
2895
2917
  }
2896
2918
  /**
2897
2919
  * Disconnects from the MCP server and cleans up resources.
@@ -2909,10 +2931,12 @@ var MCPClient = class {
2909
2931
  } catch {
2910
2932
  }
2911
2933
  }
2912
- if (this.client) {
2913
- this.client.close();
2934
+ if (this.client.transport) {
2935
+ try {
2936
+ await this.client.close();
2937
+ } catch {
2938
+ }
2914
2939
  }
2915
- this.client = null;
2916
2940
  this.oauthProvider = null;
2917
2941
  this.transport = null;
2918
2942
  if (this.serverId) {
@@ -3055,6 +3079,18 @@ var MultiSessionClient = class {
3055
3079
  getClients() {
3056
3080
  return this.clients;
3057
3081
  }
3082
+ /**
3083
+ * Removes and disconnects a single session by ID.
3084
+ *
3085
+ * @returns `true` if the session was found and removed, `false` if not found.
3086
+ */
3087
+ async removeSession(sessionId) {
3088
+ const idx = this.clients.findIndex((c) => c.getSessionId() === sessionId);
3089
+ if (idx === -1) return false;
3090
+ const [client] = this.clients.splice(idx, 1);
3091
+ await client.disconnect();
3092
+ return true;
3093
+ }
3058
3094
  /**
3059
3095
  * Gracefully disconnects all active MCP clients and clears the internal list.
3060
3096
  *