@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
@@ -1666,12 +1666,12 @@ async function createStorage() {
1666
1666
  }
1667
1667
  if (type === "supabase") {
1668
1668
  const url = process.env.SUPABASE_URL;
1669
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1669
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1670
1670
  if (!url || !key) {
1671
- console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
1671
+ console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SECRET_KEY.');
1672
1672
  } else {
1673
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1674
- 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.');
1673
+ if (!process.env.SUPABASE_SECRET_KEY) {
1674
+ 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.');
1675
1675
  }
1676
1676
  try {
1677
1677
  const { createClient } = await import('@supabase/supabase-js');
@@ -1726,13 +1726,13 @@ async function createStorage() {
1726
1726
  console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
1727
1727
  return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
1728
1728
  }
1729
- if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
1729
+ if (process.env.SUPABASE_URL && (process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY)) {
1730
1730
  try {
1731
1731
  const { createClient } = await import('@supabase/supabase-js');
1732
1732
  const url = process.env.SUPABASE_URL;
1733
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1734
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1735
- 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.');
1733
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1734
+ if (!process.env.SUPABASE_SECRET_KEY) {
1735
+ 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.');
1736
1736
  }
1737
1737
  const client = createClient(url, key);
1738
1738
  console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
@@ -2079,7 +2079,7 @@ var MCPClient = class {
2079
2079
  * @param options - Client configuration options
2080
2080
  */
2081
2081
  constructor(options) {
2082
- __publicField(this, "client", null);
2082
+ __publicField(this, "client");
2083
2083
  __publicField(this, "oauthProvider", null);
2084
2084
  __publicField(this, "transport", null);
2085
2085
  __publicField(this, "userId");
@@ -2120,6 +2120,21 @@ var MCPClient = class {
2120
2120
  this.clientUri = options.clientUri;
2121
2121
  this.logoUri = options.logoUri;
2122
2122
  this.policyUri = options.policyUri;
2123
+ this.client = new Client(
2124
+ {
2125
+ name: MCP_CLIENT_NAME,
2126
+ version: MCP_CLIENT_VERSION
2127
+ },
2128
+ {
2129
+ capabilities: {
2130
+ extensions: {
2131
+ "io.modelcontextprotocol/ui": {
2132
+ mimeTypes: ["text/html+mcp"]
2133
+ }
2134
+ }
2135
+ }
2136
+ }
2137
+ );
2123
2138
  }
2124
2139
  /**
2125
2140
  * Emit a connection state change event
@@ -2230,7 +2245,6 @@ var MCPClient = class {
2230
2245
  const response = await fetch(url, { ...init, signal });
2231
2246
  const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
2232
2247
  if (response.status === 404 && hasSessionHeader) {
2233
- this.client = null;
2234
2248
  throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
2235
2249
  }
2236
2250
  return response;
@@ -2246,13 +2260,12 @@ var MCPClient = class {
2246
2260
  }
2247
2261
  }
2248
2262
  /**
2249
- * Initializes client components (client, transport, OAuth provider)
2250
- * Loads missing configuration from Redis session store if needed
2251
- * This method is idempotent and safe to call multiple times
2263
+ * Ensures session metadata and OAuth provider are loaded.
2264
+ * Does NOT create the SDK Client callers that need one create it themselves.
2252
2265
  * @private
2253
2266
  */
2254
- async initialize() {
2255
- if (this.client && this.oauthProvider) {
2267
+ async ensureSession() {
2268
+ if (this.oauthProvider) {
2256
2269
  return;
2257
2270
  }
2258
2271
  this.emitStateChange("INITIALIZING");
@@ -2295,23 +2308,6 @@ var MCPClient = class {
2295
2308
  }
2296
2309
  });
2297
2310
  }
2298
- if (!this.client) {
2299
- this.client = new Client(
2300
- {
2301
- name: MCP_CLIENT_NAME,
2302
- version: MCP_CLIENT_VERSION
2303
- },
2304
- {
2305
- capabilities: {
2306
- extensions: {
2307
- "io.modelcontextprotocol/ui": {
2308
- mimeTypes: ["text/html+mcp"]
2309
- }
2310
- }
2311
- }
2312
- }
2313
- );
2314
- }
2315
2311
  if (existingSession === null) {
2316
2312
  existingSession = await sessions.get(this.userId, this.sessionId);
2317
2313
  }
@@ -2428,17 +2424,16 @@ var MCPClient = class {
2428
2424
  * @throws {Error} When connection fails for other reasons
2429
2425
  */
2430
2426
  async connect() {
2431
- if (this.client?.transport) {
2427
+ if (this.client.transport) {
2432
2428
  this.transport = null;
2433
2429
  try {
2434
2430
  await this.client.close();
2435
2431
  } catch {
2436
2432
  }
2437
- this.client = null;
2438
2433
  }
2439
- await this.initialize();
2440
- if (!this.client || !this.oauthProvider) {
2441
- const error = "Client or OAuth provider not initialized";
2434
+ await this.ensureSession();
2435
+ if (!this.oauthProvider) {
2436
+ const error = "OAuth provider not initialized";
2442
2437
  this.emitError(error, "connection");
2443
2438
  this.emitStateChange("FAILED");
2444
2439
  throw new Error(error);
@@ -2505,7 +2500,7 @@ var MCPClient = class {
2505
2500
  async finishAuth(authCode, state) {
2506
2501
  this.emitStateChange("AUTHENTICATING");
2507
2502
  this.emitProgress("Exchanging authorization code for tokens...");
2508
- await this.initialize();
2503
+ await this.ensureSession();
2509
2504
  if (!this.oauthProvider) {
2510
2505
  const error = "OAuth provider not initialized";
2511
2506
  this.emitError(error, "auth");
@@ -2611,7 +2606,9 @@ var MCPClient = class {
2611
2606
  method: "tools/list",
2612
2607
  params: {}
2613
2608
  };
2614
- const result = await this.client.request(request, ListToolsResultSchema);
2609
+ const result = await this.withRetry(
2610
+ () => this.client.request(request, ListToolsResultSchema)
2611
+ );
2615
2612
  if (this.serverId) {
2616
2613
  this._onConnectionEvent.fire({
2617
2614
  type: "tools_discovered",
@@ -2651,7 +2648,9 @@ var MCPClient = class {
2651
2648
  }
2652
2649
  };
2653
2650
  try {
2654
- const result = await this.client.request(request, CallToolResultSchema);
2651
+ const result = await this.withRetry(
2652
+ () => this.client.request(request, CallToolResultSchema)
2653
+ );
2655
2654
  this._onObservabilityEvent.fire({
2656
2655
  type: "mcp:client:tool_call",
2657
2656
  level: "info",
@@ -2703,7 +2702,9 @@ var MCPClient = class {
2703
2702
  method: "prompts/list",
2704
2703
  params: {}
2705
2704
  };
2706
- const result = await this.client.request(request, ListPromptsResultSchema);
2705
+ const result = await this.withRetry(
2706
+ () => this.client.request(request, ListPromptsResultSchema)
2707
+ );
2707
2708
  this.emitStateChange("READY");
2708
2709
  this.emitProgress(`Discovered ${result.prompts.length} prompts`);
2709
2710
  return result;
@@ -2732,7 +2733,9 @@ var MCPClient = class {
2732
2733
  arguments: args
2733
2734
  }
2734
2735
  };
2735
- return await this.client.request(request, GetPromptResultSchema);
2736
+ return await this.withRetry(
2737
+ () => this.client.request(request, GetPromptResultSchema)
2738
+ );
2736
2739
  }
2737
2740
  /**
2738
2741
  * Lists all available resources from the connected MCP server
@@ -2749,7 +2752,9 @@ var MCPClient = class {
2749
2752
  method: "resources/list",
2750
2753
  params: {}
2751
2754
  };
2752
- const result = await this.client.request(request, ListResourcesResultSchema);
2755
+ const result = await this.withRetry(
2756
+ () => this.client.request(request, ListResourcesResultSchema)
2757
+ );
2753
2758
  this.emitStateChange("READY");
2754
2759
  this.emitProgress(`Discovered ${result.resources.length} resources`);
2755
2760
  return result;
@@ -2776,7 +2781,35 @@ var MCPClient = class {
2776
2781
  uri
2777
2782
  }
2778
2783
  };
2779
- return await this.client.request(request, ReadResourceResultSchema);
2784
+ return await this.withRetry(
2785
+ () => this.client.request(request, ReadResourceResultSchema)
2786
+ );
2787
+ }
2788
+ /**
2789
+ * Wraps an MCP request with automatic transport-session recovery.
2790
+ *
2791
+ * When the downstream MCP server rejects the request with a 404 indicating
2792
+ * the transport session has expired, this method tears down the stale SDK
2793
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
2794
+ * and retries the request once.
2795
+ *
2796
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
2797
+ */
2798
+ async withRetry(fn) {
2799
+ try {
2800
+ return await fn();
2801
+ } catch (error) {
2802
+ if (!(error instanceof Error && error.message.includes("MCP_SESSION_EXPIRED"))) throw error;
2803
+ if (this.client.transport) {
2804
+ try {
2805
+ await this.client.close();
2806
+ } catch {
2807
+ }
2808
+ this.transport = null;
2809
+ }
2810
+ await this.reconnect();
2811
+ return await fn();
2812
+ }
2780
2813
  }
2781
2814
  /**
2782
2815
  * Reconnects to MCP server using existing OAuth provider from Redis
@@ -2785,20 +2818,9 @@ var MCPClient = class {
2785
2818
  * @throws {Error} When OAuth provider is not initialized
2786
2819
  */
2787
2820
  async reconnect() {
2788
- await this.initialize();
2789
- if (!this.oauthProvider) {
2790
- throw new Error("OAuth provider not initialized");
2791
- }
2792
- this.client = new Client(
2793
- {
2794
- name: MCP_CLIENT_NAME,
2795
- version: MCP_CLIENT_VERSION
2796
- },
2797
- { capabilities: {} }
2798
- );
2799
- const tt = this.transportType || "streamable-http";
2800
- this.transport = this.getTransport(tt);
2801
- await this.client.connect(this.transport);
2821
+ await this.ensureSession();
2822
+ if (!this.oauthProvider) throw new Error("OAuth provider not initialized");
2823
+ await this.connect();
2802
2824
  }
2803
2825
  /**
2804
2826
  * Completely removes the session from Redis including all OAuth data
@@ -2806,7 +2828,7 @@ var MCPClient = class {
2806
2828
  */
2807
2829
  async clearSession() {
2808
2830
  try {
2809
- await this.initialize();
2831
+ await this.ensureSession();
2810
2832
  } catch (error) {
2811
2833
  console.warn("[MCPClient] Initialization failed during clearSession:", error);
2812
2834
  }
@@ -2821,7 +2843,7 @@ var MCPClient = class {
2821
2843
  * @returns True if connected, false otherwise
2822
2844
  */
2823
2845
  isConnected() {
2824
- return this.client !== null;
2846
+ return this.client.transport !== void 0;
2825
2847
  }
2826
2848
  /**
2827
2849
  * Disconnects from the MCP server and cleans up resources.
@@ -2839,10 +2861,12 @@ var MCPClient = class {
2839
2861
  } catch {
2840
2862
  }
2841
2863
  }
2842
- if (this.client) {
2843
- this.client.close();
2864
+ if (this.client.transport) {
2865
+ try {
2866
+ await this.client.close();
2867
+ } catch {
2868
+ }
2844
2869
  }
2845
- this.client = null;
2846
2870
  this.oauthProvider = null;
2847
2871
  this.transport = null;
2848
2872
  if (this.serverId) {
@@ -2984,6 +3008,18 @@ var MultiSessionClient = class {
2984
3008
  getClients() {
2985
3009
  return this.clients;
2986
3010
  }
3011
+ /**
3012
+ * Removes and disconnects a single session by ID.
3013
+ *
3014
+ * @returns `true` if the session was found and removed, `false` if not found.
3015
+ */
3016
+ async removeSession(sessionId) {
3017
+ const idx = this.clients.findIndex((c) => c.getSessionId() === sessionId);
3018
+ if (idx === -1) return false;
3019
+ const [client] = this.clients.splice(idx, 1);
3020
+ await client.disconnect();
3021
+ return true;
3022
+ }
2987
3023
  /**
2988
3024
  * Gracefully disconnects all active MCP clients and clears the internal list.
2989
3025
  *