@mcp-ts/sdk 2.4.2 → 2.4.3

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/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)');
@@ -2302,7 +2302,6 @@ var MCPClient = class {
2302
2302
  const response = await fetch(url, { ...init, signal });
2303
2303
  const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
2304
2304
  if (response.status === 404 && hasSessionHeader) {
2305
- this.client = null;
2306
2305
  throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
2307
2306
  }
2308
2307
  return response;
@@ -2683,7 +2682,9 @@ var MCPClient = class {
2683
2682
  method: "tools/list",
2684
2683
  params: {}
2685
2684
  };
2686
- const result = await this.client.request(request, ListToolsResultSchema);
2685
+ const result = await this.withRetry(
2686
+ () => this.client.request(request, ListToolsResultSchema)
2687
+ );
2687
2688
  if (this.serverId) {
2688
2689
  this._onConnectionEvent.fire({
2689
2690
  type: "tools_discovered",
@@ -2723,7 +2724,9 @@ var MCPClient = class {
2723
2724
  }
2724
2725
  };
2725
2726
  try {
2726
- const result = await this.client.request(request, CallToolResultSchema);
2727
+ const result = await this.withRetry(
2728
+ () => this.client.request(request, CallToolResultSchema)
2729
+ );
2727
2730
  this._onObservabilityEvent.fire({
2728
2731
  type: "mcp:client:tool_call",
2729
2732
  level: "info",
@@ -2775,7 +2778,9 @@ var MCPClient = class {
2775
2778
  method: "prompts/list",
2776
2779
  params: {}
2777
2780
  };
2778
- const result = await this.client.request(request, ListPromptsResultSchema);
2781
+ const result = await this.withRetry(
2782
+ () => this.client.request(request, ListPromptsResultSchema)
2783
+ );
2779
2784
  this.emitStateChange("READY");
2780
2785
  this.emitProgress(`Discovered ${result.prompts.length} prompts`);
2781
2786
  return result;
@@ -2804,7 +2809,9 @@ var MCPClient = class {
2804
2809
  arguments: args
2805
2810
  }
2806
2811
  };
2807
- return await this.client.request(request, GetPromptResultSchema);
2812
+ return await this.withRetry(
2813
+ () => this.client.request(request, GetPromptResultSchema)
2814
+ );
2808
2815
  }
2809
2816
  /**
2810
2817
  * Lists all available resources from the connected MCP server
@@ -2821,7 +2828,9 @@ var MCPClient = class {
2821
2828
  method: "resources/list",
2822
2829
  params: {}
2823
2830
  };
2824
- const result = await this.client.request(request, ListResourcesResultSchema);
2831
+ const result = await this.withRetry(
2832
+ () => this.client.request(request, ListResourcesResultSchema)
2833
+ );
2825
2834
  this.emitStateChange("READY");
2826
2835
  this.emitProgress(`Discovered ${result.resources.length} resources`);
2827
2836
  return result;
@@ -2848,7 +2857,36 @@ var MCPClient = class {
2848
2857
  uri
2849
2858
  }
2850
2859
  };
2851
- return await this.client.request(request, ReadResourceResultSchema);
2860
+ return await this.withRetry(
2861
+ () => this.client.request(request, ReadResourceResultSchema)
2862
+ );
2863
+ }
2864
+ /**
2865
+ * Wraps an MCP request with automatic transport-session recovery.
2866
+ *
2867
+ * When the downstream MCP server rejects the request with a 404 indicating
2868
+ * the transport session has expired, this method tears down the stale SDK
2869
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
2870
+ * and retries the request once.
2871
+ *
2872
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
2873
+ */
2874
+ async withRetry(fn) {
2875
+ try {
2876
+ return await fn();
2877
+ } catch (error) {
2878
+ if (!(error instanceof Error && error.message.includes("MCP_SESSION_EXPIRED"))) throw error;
2879
+ if (this.client) {
2880
+ try {
2881
+ await this.client.close();
2882
+ } catch {
2883
+ }
2884
+ this.transport = null;
2885
+ this.client = null;
2886
+ }
2887
+ await this.reconnect();
2888
+ return await fn();
2889
+ }
2852
2890
  }
2853
2891
  /**
2854
2892
  * Reconnects to MCP server using existing OAuth provider from Redis
@@ -2861,6 +2899,12 @@ var MCPClient = class {
2861
2899
  if (!this.oauthProvider) {
2862
2900
  throw new Error("OAuth provider not initialized");
2863
2901
  }
2902
+ if (this.client) {
2903
+ try {
2904
+ await this.client.close();
2905
+ } catch {
2906
+ }
2907
+ }
2864
2908
  this.client = new Client(
2865
2909
  {
2866
2910
  name: MCP_CLIENT_NAME,
@@ -3056,6 +3100,18 @@ var MultiSessionClient = class {
3056
3100
  getClients() {
3057
3101
  return this.clients;
3058
3102
  }
3103
+ /**
3104
+ * Removes and disconnects a single session by ID.
3105
+ *
3106
+ * @returns `true` if the session was found and removed, `false` if not found.
3107
+ */
3108
+ async removeSession(sessionId) {
3109
+ const idx = this.clients.findIndex((c) => c.getSessionId() === sessionId);
3110
+ if (idx === -1) return false;
3111
+ const [client] = this.clients.splice(idx, 1);
3112
+ await client.disconnect();
3113
+ return true;
3114
+ }
3059
3115
  /**
3060
3116
  * Gracefully disconnects all active MCP clients and clears the internal list.
3061
3117
  *