@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.
@@ -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)');
@@ -2230,7 +2230,6 @@ var MCPClient = class {
2230
2230
  const response = await fetch(url, { ...init, signal });
2231
2231
  const hasSessionHeader = init?.headers && new Headers(init.headers).has("mcp-session-id");
2232
2232
  if (response.status === 404 && hasSessionHeader) {
2233
- this.client = null;
2234
2233
  throw new Error("MCP_SESSION_EXPIRED: Downstream session was not found on the server.");
2235
2234
  }
2236
2235
  return response;
@@ -2611,7 +2610,9 @@ var MCPClient = class {
2611
2610
  method: "tools/list",
2612
2611
  params: {}
2613
2612
  };
2614
- const result = await this.client.request(request, ListToolsResultSchema);
2613
+ const result = await this.withRetry(
2614
+ () => this.client.request(request, ListToolsResultSchema)
2615
+ );
2615
2616
  if (this.serverId) {
2616
2617
  this._onConnectionEvent.fire({
2617
2618
  type: "tools_discovered",
@@ -2651,7 +2652,9 @@ var MCPClient = class {
2651
2652
  }
2652
2653
  };
2653
2654
  try {
2654
- const result = await this.client.request(request, CallToolResultSchema);
2655
+ const result = await this.withRetry(
2656
+ () => this.client.request(request, CallToolResultSchema)
2657
+ );
2655
2658
  this._onObservabilityEvent.fire({
2656
2659
  type: "mcp:client:tool_call",
2657
2660
  level: "info",
@@ -2703,7 +2706,9 @@ var MCPClient = class {
2703
2706
  method: "prompts/list",
2704
2707
  params: {}
2705
2708
  };
2706
- const result = await this.client.request(request, ListPromptsResultSchema);
2709
+ const result = await this.withRetry(
2710
+ () => this.client.request(request, ListPromptsResultSchema)
2711
+ );
2707
2712
  this.emitStateChange("READY");
2708
2713
  this.emitProgress(`Discovered ${result.prompts.length} prompts`);
2709
2714
  return result;
@@ -2732,7 +2737,9 @@ var MCPClient = class {
2732
2737
  arguments: args
2733
2738
  }
2734
2739
  };
2735
- return await this.client.request(request, GetPromptResultSchema);
2740
+ return await this.withRetry(
2741
+ () => this.client.request(request, GetPromptResultSchema)
2742
+ );
2736
2743
  }
2737
2744
  /**
2738
2745
  * Lists all available resources from the connected MCP server
@@ -2749,7 +2756,9 @@ var MCPClient = class {
2749
2756
  method: "resources/list",
2750
2757
  params: {}
2751
2758
  };
2752
- const result = await this.client.request(request, ListResourcesResultSchema);
2759
+ const result = await this.withRetry(
2760
+ () => this.client.request(request, ListResourcesResultSchema)
2761
+ );
2753
2762
  this.emitStateChange("READY");
2754
2763
  this.emitProgress(`Discovered ${result.resources.length} resources`);
2755
2764
  return result;
@@ -2776,7 +2785,36 @@ var MCPClient = class {
2776
2785
  uri
2777
2786
  }
2778
2787
  };
2779
- return await this.client.request(request, ReadResourceResultSchema);
2788
+ return await this.withRetry(
2789
+ () => this.client.request(request, ReadResourceResultSchema)
2790
+ );
2791
+ }
2792
+ /**
2793
+ * Wraps an MCP request with automatic transport-session recovery.
2794
+ *
2795
+ * When the downstream MCP server rejects the request with a 404 indicating
2796
+ * the transport session has expired, this method tears down the stale SDK
2797
+ * client and transport, calls {@link reconnect} to negotiate a fresh session,
2798
+ * and retries the request once.
2799
+ *
2800
+ * Non-transient errors (network failures, auth errors, etc.) propagate as-is.
2801
+ */
2802
+ async withRetry(fn) {
2803
+ try {
2804
+ return await fn();
2805
+ } catch (error) {
2806
+ if (!(error instanceof Error && error.message.includes("MCP_SESSION_EXPIRED"))) throw error;
2807
+ if (this.client) {
2808
+ try {
2809
+ await this.client.close();
2810
+ } catch {
2811
+ }
2812
+ this.transport = null;
2813
+ this.client = null;
2814
+ }
2815
+ await this.reconnect();
2816
+ return await fn();
2817
+ }
2780
2818
  }
2781
2819
  /**
2782
2820
  * Reconnects to MCP server using existing OAuth provider from Redis
@@ -2789,6 +2827,12 @@ var MCPClient = class {
2789
2827
  if (!this.oauthProvider) {
2790
2828
  throw new Error("OAuth provider not initialized");
2791
2829
  }
2830
+ if (this.client) {
2831
+ try {
2832
+ await this.client.close();
2833
+ } catch {
2834
+ }
2835
+ }
2792
2836
  this.client = new Client(
2793
2837
  {
2794
2838
  name: MCP_CLIENT_NAME,
@@ -2984,6 +3028,18 @@ var MultiSessionClient = class {
2984
3028
  getClients() {
2985
3029
  return this.clients;
2986
3030
  }
3031
+ /**
3032
+ * Removes and disconnects a single session by ID.
3033
+ *
3034
+ * @returns `true` if the session was found and removed, `false` if not found.
3035
+ */
3036
+ async removeSession(sessionId) {
3037
+ const idx = this.clients.findIndex((c) => c.getSessionId() === sessionId);
3038
+ if (idx === -1) return false;
3039
+ const [client] = this.clients.splice(idx, 1);
3040
+ await client.disconnect();
3041
+ return true;
3042
+ }
2987
3043
  /**
2988
3044
  * Gracefully disconnects all active MCP clients and clears the internal list.
2989
3045
  *