@mcp-ts/sdk 1.3.5 → 1.3.6

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.
@@ -141,6 +141,14 @@ var RedisStorageBackend = class {
141
141
  __publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
142
142
  __publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
143
143
  }
144
+ async init() {
145
+ try {
146
+ await this.redis.ping();
147
+ console.log("[mcp-ts][Storage] Redis: \u2713 Connected to server.");
148
+ } catch (error) {
149
+ throw new Error(`[RedisStorage] Failed to connect to Redis: ${error.message}`);
150
+ }
151
+ }
144
152
  /**
145
153
  * Generates Redis key for a specific session
146
154
  * @private
@@ -366,6 +374,9 @@ var MemoryStorageBackend = class {
366
374
  // Map<identity, Set<sessionId>>
367
375
  __publicField(this, "identitySessions", /* @__PURE__ */ new Map());
368
376
  }
377
+ async init() {
378
+ console.log("[mcp-ts][Storage] Memory: \u2713 internal memory store active.");
379
+ }
369
380
  getSessionKey(identity, sessionId) {
370
381
  return `${identity}:${sessionId}`;
371
382
  }
@@ -485,6 +496,7 @@ var FileStorageBackend = class {
485
496
  }
486
497
  }
487
498
  this.initialized = true;
499
+ console.log(`[mcp-ts][Storage] File: \u2713 storage directory at ${path.dirname(this.filePath)} verified.`);
488
500
  }
489
501
  async ensureInitialized() {
490
502
  if (!this.initialized) await this.init();
@@ -589,6 +601,7 @@ var SqliteStorage = class {
589
601
  CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
590
602
  `);
591
603
  this.initialized = true;
604
+ console.log(`[mcp-ts][Storage] SQLite: \u2713 database at ${this.dbPath} verified.`);
592
605
  } catch (error) {
593
606
  if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("better-sqlite3")) {
594
607
  throw new Error(
@@ -704,6 +717,157 @@ var SqliteStorage = class {
704
717
  }
705
718
  };
706
719
 
720
+ // src/server/storage/supabase-backend.ts
721
+ var SupabaseStorageBackend = class {
722
+ constructor(supabase) {
723
+ this.supabase = supabase;
724
+ __publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
725
+ }
726
+ async init() {
727
+ const { error } = await this.supabase.from("mcp_sessions").select("session_id").limit(0);
728
+ if (error) {
729
+ if (error.code === "42P01") {
730
+ throw new Error(
731
+ '[SupabaseStorage] Table "mcp_sessions" not found in your database. Please run "npx mcp-ts supabase-init" in your project to set up the required table and RLS policies.'
732
+ );
733
+ }
734
+ throw new Error(`[SupabaseStorage] Initialization check failed: ${error.message}`);
735
+ }
736
+ console.log('[mcp-ts][Storage] Supabase: \u2713 "mcp_sessions" table verified.');
737
+ }
738
+ generateSessionId() {
739
+ return crypto.randomUUID();
740
+ }
741
+ mapRowToSessionData(row) {
742
+ return {
743
+ sessionId: row.session_id,
744
+ serverId: row.server_id,
745
+ serverName: row.server_name,
746
+ serverUrl: row.server_url,
747
+ transportType: row.transport_type,
748
+ callbackUrl: row.callback_url,
749
+ createdAt: new Date(row.created_at).getTime(),
750
+ identity: row.identity,
751
+ headers: row.headers,
752
+ active: row.active,
753
+ clientInformation: row.client_information,
754
+ tokens: row.tokens,
755
+ codeVerifier: row.code_verifier,
756
+ clientId: row.client_id
757
+ };
758
+ }
759
+ async createSession(session, ttl) {
760
+ const { sessionId, identity } = session;
761
+ if (!sessionId || !identity) throw new Error("identity and sessionId required");
762
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
763
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
764
+ const { error } = await this.supabase.from("mcp_sessions").insert({
765
+ session_id: sessionId,
766
+ user_id: identity,
767
+ // Maps user_id to identity to support RLS using auth.uid()
768
+ server_id: session.serverId,
769
+ server_name: session.serverName,
770
+ server_url: session.serverUrl,
771
+ transport_type: session.transportType,
772
+ callback_url: session.callbackUrl,
773
+ created_at: new Date(session.createdAt || Date.now()).toISOString(),
774
+ identity,
775
+ headers: session.headers,
776
+ active: session.active ?? false,
777
+ client_information: session.clientInformation,
778
+ tokens: session.tokens,
779
+ code_verifier: session.codeVerifier,
780
+ client_id: session.clientId,
781
+ expires_at: expiresAt
782
+ });
783
+ if (error) {
784
+ if (error.code === "23505") {
785
+ throw new Error(`Session ${sessionId} already exists`);
786
+ }
787
+ throw new Error(`Failed to create session in Supabase: ${error.message}`);
788
+ }
789
+ }
790
+ async updateSession(identity, sessionId, data, ttl) {
791
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
792
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
793
+ const updateData = {
794
+ expires_at: expiresAt,
795
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
796
+ };
797
+ if ("serverId" in data) updateData.server_id = data.serverId;
798
+ if ("serverName" in data) updateData.server_name = data.serverName;
799
+ if ("serverUrl" in data) updateData.server_url = data.serverUrl;
800
+ if ("transportType" in data) updateData.transport_type = data.transportType;
801
+ if ("callbackUrl" in data) updateData.callback_url = data.callbackUrl;
802
+ if ("active" in data) updateData.active = data.active;
803
+ if ("headers" in data) updateData.headers = data.headers;
804
+ if ("clientInformation" in data) updateData.client_information = data.clientInformation;
805
+ if ("tokens" in data) updateData.tokens = data.tokens;
806
+ if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
807
+ if ("clientId" in data) updateData.client_id = data.clientId;
808
+ const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
809
+ if (error) {
810
+ throw new Error(`Failed to update session: ${error.message}`);
811
+ }
812
+ if (!updatedRows || updatedRows.length === 0) {
813
+ throw new Error(`Session ${sessionId} not found for identity ${identity}`);
814
+ }
815
+ }
816
+ async getSession(identity, sessionId) {
817
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity).eq("session_id", sessionId).maybeSingle();
818
+ if (error) {
819
+ console.error("[SupabaseStorage] Failed to get session:", error);
820
+ return null;
821
+ }
822
+ if (!data) return null;
823
+ return this.mapRowToSessionData(data);
824
+ }
825
+ async getIdentitySessionsData(identity) {
826
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity);
827
+ if (error) {
828
+ console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
829
+ return [];
830
+ }
831
+ return data.map((row) => this.mapRowToSessionData(row));
832
+ }
833
+ async removeSession(identity, sessionId) {
834
+ const { error } = await this.supabase.from("mcp_sessions").delete().eq("identity", identity).eq("session_id", sessionId);
835
+ if (error) {
836
+ console.error("[SupabaseStorage] Failed to remove session:", error);
837
+ }
838
+ }
839
+ async getIdentityMcpSessions(identity) {
840
+ const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("identity", identity);
841
+ if (error) {
842
+ console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
843
+ return [];
844
+ }
845
+ return data.map((row) => row.session_id);
846
+ }
847
+ async getAllSessionIds() {
848
+ const { data, error } = await this.supabase.from("mcp_sessions").select("session_id");
849
+ if (error) {
850
+ console.error("[SupabaseStorage] Failed to get all sessions:", error);
851
+ return [];
852
+ }
853
+ return data.map((row) => row.session_id);
854
+ }
855
+ async clearAll() {
856
+ const { error } = await this.supabase.from("mcp_sessions").delete().neq("session_id", "");
857
+ if (error) {
858
+ console.error("[SupabaseStorage] Failed to clear sessions:", error);
859
+ }
860
+ }
861
+ async cleanupExpiredSessions() {
862
+ const { error } = await this.supabase.from("mcp_sessions").delete().lt("expires_at", (/* @__PURE__ */ new Date()).toISOString());
863
+ if (error) {
864
+ console.error("[SupabaseStorage] Failed to cleanup expired sessions:", error);
865
+ }
866
+ }
867
+ async disconnect() {
868
+ }
869
+ };
870
+
707
871
  // src/server/storage/index.ts
708
872
  var storageInstance = null;
709
873
  var storagePromise = null;
@@ -722,53 +886,85 @@ async function createStorage() {
722
886
  try {
723
887
  const { getRedis: getRedis2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
724
888
  const redis2 = await getRedis2();
725
- console.log("[Storage] Using Redis storage (Explicit)");
726
- return new RedisStorageBackend(redis2);
889
+ console.log('[mcp-ts][Storage] Explicit selection: "redis"');
890
+ return await initializeStorage(new RedisStorageBackend(redis2));
727
891
  } catch (error) {
728
- console.error("[Storage] Failed to initialize Redis:", error.message);
729
- console.log("[Storage] Falling back to In-Memory storage");
730
- return new MemoryStorageBackend();
892
+ console.error("[mcp-ts][Storage] Failed to initialize Redis:", error.message);
893
+ console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
894
+ return await initializeStorage(new MemoryStorageBackend());
731
895
  }
732
896
  }
733
897
  if (type === "file") {
734
898
  const filePath = process.env.MCP_TS_STORAGE_FILE;
735
- if (!filePath) {
736
- console.warn('[Storage] MCP_TS_STORAGE_TYPE is "file" but MCP_TS_STORAGE_FILE is missing');
737
- }
738
- console.log(`[Storage] Using File storage (${filePath}) (Explicit)`);
899
+ console.log(`[mcp-ts][Storage] Explicit selection: "file" (${filePath || "default"})`);
739
900
  return await initializeStorage(new FileStorageBackend({ path: filePath }));
740
901
  }
741
902
  if (type === "sqlite") {
742
903
  const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
743
- console.log(`[Storage] Using SQLite storage (${dbPath || "default"}) (Explicit)`);
904
+ console.log(`[mcp-ts][Storage] Explicit selection: "sqlite" (${dbPath || "default"})`);
744
905
  return await initializeStorage(new SqliteStorage({ path: dbPath }));
745
906
  }
907
+ if (type === "supabase") {
908
+ const url = process.env.SUPABASE_URL;
909
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
910
+ if (!url || !key) {
911
+ console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
912
+ } else {
913
+ if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
914
+ 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.');
915
+ }
916
+ try {
917
+ const { createClient } = await import('@supabase/supabase-js');
918
+ const client = createClient(url, key);
919
+ console.log('[mcp-ts][Storage] Explicit selection: "supabase"');
920
+ return await initializeStorage(new SupabaseStorageBackend(client));
921
+ } catch (error) {
922
+ console.error("[mcp-ts][Storage] Failed to initialize Supabase:", error.message);
923
+ console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
924
+ return await initializeStorage(new MemoryStorageBackend());
925
+ }
926
+ }
927
+ }
746
928
  if (type === "memory") {
747
- console.log("[Storage] Using In-Memory storage (Explicit)");
748
- return new MemoryStorageBackend();
929
+ console.log('[mcp-ts][Storage] Explicit selection: "memory"');
930
+ return await initializeStorage(new MemoryStorageBackend());
749
931
  }
750
932
  if (process.env.REDIS_URL) {
751
933
  try {
752
934
  const { getRedis: getRedis2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
753
935
  const redis2 = await getRedis2();
754
- console.log("[Storage] Auto-detected REDIS_URL. Using Redis storage.");
755
- return new RedisStorageBackend(redis2);
936
+ console.log('[mcp-ts][Storage] Auto-detection: "redis" (via REDIS_URL)');
937
+ return await initializeStorage(new RedisStorageBackend(redis2));
756
938
  } catch (error) {
757
- console.error("[Storage] Redis auto-detection failed:", error.message);
758
- console.log("[Storage] Falling back to In-Memory storage");
759
- return new MemoryStorageBackend();
939
+ console.error("[mcp-ts][Storage] Redis auto-detection failed:", error.message);
940
+ console.log("[mcp-ts][Storage] Falling back to next available backend");
760
941
  }
761
942
  }
762
943
  if (process.env.MCP_TS_STORAGE_FILE) {
763
- console.log(`[Storage] Auto-detected MCP_TS_STORAGE_FILE. Using File storage (${process.env.MCP_TS_STORAGE_FILE}).`);
944
+ console.log(`[mcp-ts][Storage] Auto-detection: "file" (${process.env.MCP_TS_STORAGE_FILE})`);
764
945
  return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
765
946
  }
766
947
  if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
767
- console.log(`[Storage] Auto-detected MCP_TS_STORAGE_SQLITE_PATH. Using SQLite storage (${process.env.MCP_TS_STORAGE_SQLITE_PATH}).`);
948
+ console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
768
949
  return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
769
950
  }
770
- console.log("[Storage] No storage configured. Using In-Memory storage (Default).");
771
- return new MemoryStorageBackend();
951
+ if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
952
+ try {
953
+ const { createClient } = await import('@supabase/supabase-js');
954
+ const url = process.env.SUPABASE_URL;
955
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
956
+ if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
957
+ 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.');
958
+ }
959
+ const client = createClient(url, key);
960
+ console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
961
+ return await initializeStorage(new SupabaseStorageBackend(client));
962
+ } catch (error) {
963
+ console.error("[mcp-ts][Storage] Supabase auto-detection failed:", error.message);
964
+ }
965
+ }
966
+ console.log('[mcp-ts][Storage] Defaulting to: "memory"');
967
+ return await initializeStorage(new MemoryStorageBackend());
772
968
  }
773
969
  async function getStorage() {
774
970
  if (storageInstance) {