@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
package/dist/index.mjs CHANGED
@@ -1670,12 +1670,12 @@ async function createStorage() {
1670
1670
  }
1671
1671
  if (type === "supabase") {
1672
1672
  const url = process.env.SUPABASE_URL;
1673
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1673
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1674
1674
  if (!url || !key) {
1675
- console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
1675
+ console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SECRET_KEY.');
1676
1676
  } else {
1677
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1678
- 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.');
1677
+ if (!process.env.SUPABASE_SECRET_KEY) {
1678
+ 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.');
1679
1679
  }
1680
1680
  try {
1681
1681
  const { createClient } = await import('@supabase/supabase-js');
@@ -1730,13 +1730,13 @@ async function createStorage() {
1730
1730
  console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
1731
1731
  return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
1732
1732
  }
1733
- if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
1733
+ if (process.env.SUPABASE_URL && (process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY)) {
1734
1734
  try {
1735
1735
  const { createClient } = await import('@supabase/supabase-js');
1736
1736
  const url = process.env.SUPABASE_URL;
1737
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
1738
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
1739
- 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.');
1737
+ const key = process.env.SUPABASE_SECRET_KEY || process.env.SUPABASE_ANON_KEY;
1738
+ if (!process.env.SUPABASE_SECRET_KEY) {
1739
+ 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.');
1740
1740
  }
1741
1741
  const client = createClient(url, key);
1742
1742
  console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
@@ -2151,7 +2151,7 @@ var MCPClient = class {
2151
2151
  * @param options - Client configuration options
2152
2152
  */
2153
2153
  constructor(options) {
2154
- __publicField(this, "client", null);
2154
+ __publicField(this, "client");
2155
2155
  __publicField(this, "oauthProvider", null);
2156
2156
  __publicField(this, "transport", null);
2157
2157
  __publicField(this, "userId");
@@ -2192,6 +2192,21 @@ var MCPClient = class {
2192
2192
  this.clientUri = options.clientUri;
2193
2193
  this.logoUri = options.logoUri;
2194
2194
  this.policyUri = options.policyUri;
2195
+ this.client = new Client(
2196
+ {
2197
+ name: MCP_CLIENT_NAME,
2198
+ version: MCP_CLIENT_VERSION
2199
+ },
2200
+ {
2201
+ capabilities: {
2202
+ extensions: {
2203
+ "io.modelcontextprotocol/ui": {
2204
+ mimeTypes: ["text/html+mcp"]
2205
+ }
2206
+ }
2207
+ }
2208
+ }
2209
+ );
2195
2210
  }
2196
2211
  /**
2197
2212
  * Emit a connection state change event
@@ -2302,7 +2317,6 @@ var MCPClient = class {
2302
2317
  const response = await fetch(url, { ...init, signal });
2303
2318
  const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
2304
2319
  if (response.status === 404 && hasSessionHeader) {
2305
- this.client = null;
2306
2320
  throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
2307
2321
  }
2308
2322
  return response;
@@ -2318,13 +2332,12 @@ var MCPClient = class {
2318
2332
  }
2319
2333
  }
2320
2334
  /**
2321
- * Initializes client components (client, transport, OAuth provider)
2322
- * Loads missing configuration from Redis session store if needed
2323
- * This method is idempotent and safe to call multiple times
2335
+ * Ensures session metadata and OAuth provider are loaded.
2336
+ * Does NOT create the SDK Client callers that need one create it themselves.
2324
2337
  * @private
2325
2338
  */
2326
- async initialize() {
2327
- if (this.client && this.oauthProvider) {
2339
+ async ensureSession() {
2340
+ if (this.oauthProvider) {
2328
2341
  return;
2329
2342
  }
2330
2343
  this.emitStateChange("INITIALIZING");
@@ -2367,23 +2380,6 @@ var MCPClient = class {
2367
2380
  }
2368
2381
  });
2369
2382
  }
2370
- if (!this.client) {
2371
- this.client = new Client(
2372
- {
2373
- name: MCP_CLIENT_NAME,
2374
- version: MCP_CLIENT_VERSION
2375
- },
2376
- {
2377
- capabilities: {
2378
- extensions: {
2379
- "io.modelcontextprotocol/ui": {
2380
- mimeTypes: ["text/html+mcp"]
2381
- }
2382
- }
2383
- }
2384
- }
2385
- );
2386
- }
2387
2383
  if (existingSession === null) {
2388
2384
  existingSession = await sessions.get(this.userId, this.sessionId);
2389
2385
  }
@@ -2500,17 +2496,16 @@ var MCPClient = class {
2500
2496
  * @throws {Error} When connection fails for other reasons
2501
2497
  */
2502
2498
  async connect() {
2503
- if (this.client?.transport) {
2499
+ if (this.client.transport) {
2504
2500
  this.transport = null;
2505
2501
  try {
2506
2502
  await this.client.close();
2507
2503
  } catch {
2508
2504
  }
2509
- this.client = null;
2510
2505
  }
2511
- await this.initialize();
2512
- if (!this.client || !this.oauthProvider) {
2513
- const error = "Client or OAuth provider not initialized";
2506
+ await this.ensureSession();
2507
+ if (!this.oauthProvider) {
2508
+ const error = "OAuth provider not initialized";
2514
2509
  this.emitError(error, "connection");
2515
2510
  this.emitStateChange("FAILED");
2516
2511
  throw new Error(error);
@@ -2577,7 +2572,7 @@ var MCPClient = class {
2577
2572
  async finishAuth(authCode, state) {
2578
2573
  this.emitStateChange("AUTHENTICATING");
2579
2574
  this.emitProgress("Exchanging authorization code for tokens...");
2580
- await this.initialize();
2575
+ await this.ensureSession();
2581
2576
  if (!this.oauthProvider) {
2582
2577
  const error = "OAuth provider not initialized";
2583
2578
  this.emitError(error, "auth");
@@ -2683,7 +2678,9 @@ var MCPClient = class {
2683
2678
  method: "tools/list",
2684
2679
  params: {}
2685
2680
  };
2686
- const result = await this.client.request(request, ListToolsResultSchema);
2681
+ const result = await this.withRetry(
2682
+ () => this.client.request(request, ListToolsResultSchema)
2683
+ );
2687
2684
  if (this.serverId) {
2688
2685
  this._onConnectionEvent.fire({
2689
2686
  type: "tools_discovered",
@@ -2723,7 +2720,9 @@ var MCPClient = class {
2723
2720
  }
2724
2721
  };
2725
2722
  try {
2726
- const result = await this.client.request(request, CallToolResultSchema);
2723
+ const result = await this.withRetry(
2724
+ () => this.client.request(request, CallToolResultSchema)
2725
+ );
2727
2726
  this._onObservabilityEvent.fire({
2728
2727
  type: "mcp:client:tool_call",
2729
2728
  level: "info",
@@ -2775,7 +2774,9 @@ var MCPClient = class {
2775
2774
  method: "prompts/list",
2776
2775
  params: {}
2777
2776
  };
2778
- const result = await this.client.request(request, ListPromptsResultSchema);
2777
+ const result = await this.withRetry(
2778
+ () => this.client.request(request, ListPromptsResultSchema)
2779
+ );
2779
2780
  this.emitStateChange("READY");
2780
2781
  this.emitProgress(`Discovered ${result.prompts.length} prompts`);
2781
2782
  return result;
@@ -2804,7 +2805,9 @@ var MCPClient = class {
2804
2805
  arguments: args
2805
2806
  }
2806
2807
  };
2807
- return await this.client.request(request, GetPromptResultSchema);
2808
+ return await this.withRetry(
2809
+ () => this.client.request(request, GetPromptResultSchema)
2810
+ );
2808
2811
  }
2809
2812
  /**
2810
2813
  * Lists all available resources from the connected MCP server
@@ -2821,7 +2824,9 @@ var MCPClient = class {
2821
2824
  method: "resources/list",
2822
2825
  params: {}
2823
2826
  };
2824
- const result = await this.client.request(request, ListResourcesResultSchema);
2827
+ const result = await this.withRetry(
2828
+ () => this.client.request(request, ListResourcesResultSchema)
2829
+ );
2825
2830
  this.emitStateChange("READY");
2826
2831
  this.emitProgress(`Discovered ${result.resources.length} resources`);
2827
2832
  return result;
@@ -2848,7 +2853,35 @@ var MCPClient = class {
2848
2853
  uri
2849
2854
  }
2850
2855
  };
2851
- return await this.client.request(request, ReadResourceResultSchema);
2856
+ return await this.withRetry(
2857
+ () => this.client.request(request, ReadResourceResultSchema)
2858
+ );
2859
+ }
2860
+ /**
2861
+ * Wraps an MCP request with automatic transport-session recovery.
2862
+ *
2863
+ * When the downstream MCP server rejects the request with a 404 indicating
2864
+ * the transport session has expired, this method tears down the stale SDK
2865
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
2866
+ * and retries the request once.
2867
+ *
2868
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
2869
+ */
2870
+ async withRetry(fn) {
2871
+ try {
2872
+ return await fn();
2873
+ } catch (error) {
2874
+ if (!(error instanceof Error && error.message.includes("MCP_SESSION_EXPIRED"))) throw error;
2875
+ if (this.client.transport) {
2876
+ try {
2877
+ await this.client.close();
2878
+ } catch {
2879
+ }
2880
+ this.transport = null;
2881
+ }
2882
+ await this.reconnect();
2883
+ return await fn();
2884
+ }
2852
2885
  }
2853
2886
  /**
2854
2887
  * Reconnects to MCP server using existing OAuth provider from Redis
@@ -2857,20 +2890,9 @@ var MCPClient = class {
2857
2890
  * @throws {Error} When OAuth provider is not initialized
2858
2891
  */
2859
2892
  async reconnect() {
2860
- await this.initialize();
2861
- if (!this.oauthProvider) {
2862
- throw new Error("OAuth provider not initialized");
2863
- }
2864
- this.client = new Client(
2865
- {
2866
- name: MCP_CLIENT_NAME,
2867
- version: MCP_CLIENT_VERSION
2868
- },
2869
- { capabilities: {} }
2870
- );
2871
- const tt = this.transportType || "streamable-http";
2872
- this.transport = this.getTransport(tt);
2873
- await this.client.connect(this.transport);
2893
+ await this.ensureSession();
2894
+ if (!this.oauthProvider) throw new Error("OAuth provider not initialized");
2895
+ await this.connect();
2874
2896
  }
2875
2897
  /**
2876
2898
  * Completely removes the session from Redis including all OAuth data
@@ -2878,7 +2900,7 @@ var MCPClient = class {
2878
2900
  */
2879
2901
  async clearSession() {
2880
2902
  try {
2881
- await this.initialize();
2903
+ await this.ensureSession();
2882
2904
  } catch (error) {
2883
2905
  console.warn("[MCPClient] Initialization failed during clearSession:", error);
2884
2906
  }
@@ -2893,7 +2915,7 @@ var MCPClient = class {
2893
2915
  * @returns True if connected, false otherwise
2894
2916
  */
2895
2917
  isConnected() {
2896
- return this.client !== null;
2918
+ return this.client.transport !== void 0;
2897
2919
  }
2898
2920
  /**
2899
2921
  * Disconnects from the MCP server and cleans up resources.
@@ -2911,10 +2933,12 @@ var MCPClient = class {
2911
2933
  } catch {
2912
2934
  }
2913
2935
  }
2914
- if (this.client) {
2915
- this.client.close();
2936
+ if (this.client.transport) {
2937
+ try {
2938
+ await this.client.close();
2939
+ } catch {
2940
+ }
2916
2941
  }
2917
- this.client = null;
2918
2942
  this.oauthProvider = null;
2919
2943
  this.transport = null;
2920
2944
  if (this.serverId) {
@@ -3056,6 +3080,18 @@ var MultiSessionClient = class {
3056
3080
  getClients() {
3057
3081
  return this.clients;
3058
3082
  }
3083
+ /**
3084
+ * Removes and disconnects a single session by ID.
3085
+ *
3086
+ * @returns `true` if the session was found and removed, `false` if not found.
3087
+ */
3088
+ async removeSession(sessionId) {
3089
+ const idx = this.clients.findIndex((c) => c.getSessionId() === sessionId);
3090
+ if (idx === -1) return false;
3091
+ const [client] = this.clients.splice(idx, 1);
3092
+ await client.disconnect();
3093
+ return true;
3094
+ }
3059
3095
  /**
3060
3096
  * Gracefully disconnects all active MCP clients and clears the internal list.
3061
3097
  *