@mcp-ts/sdk 1.6.1 → 2.0.0

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 (114) hide show
  1. package/README.md +12 -6
  2. package/dist/adapters/agui-adapter.d.mts +3 -3
  3. package/dist/adapters/agui-adapter.d.ts +3 -3
  4. package/dist/adapters/agui-adapter.js +4 -5
  5. package/dist/adapters/agui-adapter.js.map +1 -1
  6. package/dist/adapters/agui-adapter.mjs +4 -5
  7. package/dist/adapters/agui-adapter.mjs.map +1 -1
  8. package/dist/adapters/agui-middleware.d.mts +3 -3
  9. package/dist/adapters/agui-middleware.d.ts +3 -3
  10. package/dist/adapters/ai-adapter.d.mts +9 -3
  11. package/dist/adapters/ai-adapter.d.ts +9 -3
  12. package/dist/adapters/ai-adapter.js +20 -6
  13. package/dist/adapters/ai-adapter.js.map +1 -1
  14. package/dist/adapters/ai-adapter.mjs +20 -6
  15. package/dist/adapters/ai-adapter.mjs.map +1 -1
  16. package/dist/adapters/langchain-adapter.d.mts +3 -3
  17. package/dist/adapters/langchain-adapter.d.ts +3 -3
  18. package/dist/adapters/langchain-adapter.js +9 -6
  19. package/dist/adapters/langchain-adapter.js.map +1 -1
  20. package/dist/adapters/langchain-adapter.mjs +9 -6
  21. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  22. package/dist/adapters/mastra-adapter.d.mts +1 -1
  23. package/dist/adapters/mastra-adapter.d.ts +1 -1
  24. package/dist/adapters/mastra-adapter.js +5 -1
  25. package/dist/adapters/mastra-adapter.js.map +1 -1
  26. package/dist/adapters/mastra-adapter.mjs +5 -1
  27. package/dist/adapters/mastra-adapter.mjs.map +1 -1
  28. package/dist/bin/mcp-ts.js +7 -1
  29. package/dist/bin/mcp-ts.js.map +1 -1
  30. package/dist/bin/mcp-ts.mjs +7 -1
  31. package/dist/bin/mcp-ts.mjs.map +1 -1
  32. package/dist/client/index.d.mts +2 -2
  33. package/dist/client/index.d.ts +2 -2
  34. package/dist/client/index.js +9 -13
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/index.mjs +9 -13
  37. package/dist/client/index.mjs.map +1 -1
  38. package/dist/client/react.d.mts +7 -7
  39. package/dist/client/react.d.ts +7 -7
  40. package/dist/client/react.js +111 -63
  41. package/dist/client/react.js.map +1 -1
  42. package/dist/client/react.mjs +111 -63
  43. package/dist/client/react.mjs.map +1 -1
  44. package/dist/client/vue.d.mts +7 -7
  45. package/dist/client/vue.d.ts +7 -7
  46. package/dist/client/vue.js +14 -18
  47. package/dist/client/vue.js.map +1 -1
  48. package/dist/client/vue.mjs +14 -18
  49. package/dist/client/vue.mjs.map +1 -1
  50. package/dist/{index-DhA-OEAe.d.ts → index-C9gvpxy5.d.ts} +5 -5
  51. package/dist/{index-bFL4ZF2N.d.mts → index-eaH14_5u.d.mts} +5 -5
  52. package/dist/index.d.mts +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +616 -370
  55. package/dist/index.js.map +1 -1
  56. package/dist/index.mjs +615 -370
  57. package/dist/index.mjs.map +1 -1
  58. package/dist/{multi-session-client-CHE8QpVE.d.ts → multi-session-client-BYtguGJm.d.ts} +22 -22
  59. package/dist/{multi-session-client-CQsRbxYI.d.mts → multi-session-client-DYNe6az3.d.mts} +22 -22
  60. package/dist/server/index.d.mts +31 -34
  61. package/dist/server/index.d.ts +31 -34
  62. package/dist/server/index.js +531 -256
  63. package/dist/server/index.js.map +1 -1
  64. package/dist/server/index.mjs +530 -256
  65. package/dist/server/index.mjs.map +1 -1
  66. package/dist/shared/index.d.mts +5 -5
  67. package/dist/shared/index.d.ts +5 -5
  68. package/dist/shared/index.js +76 -101
  69. package/dist/shared/index.js.map +1 -1
  70. package/dist/shared/index.mjs +76 -101
  71. package/dist/shared/index.mjs.map +1 -1
  72. package/dist/{tool-router-Dh2804tM.d.ts → tool-router-Ddtybmr0.d.ts} +71 -73
  73. package/dist/{tool-router-BVaV1udm.d.mts → tool-router-Dnd6IOKC.d.mts} +71 -73
  74. package/dist/{types-rIuN1CQi.d.mts → types-BCAG20P6.d.mts} +4 -4
  75. package/dist/{types-rIuN1CQi.d.ts → types-BCAG20P6.d.ts} +4 -4
  76. package/dist/{utils-0qmYrqoa.d.mts → utils-DELRKQPU.d.mts} +1 -1
  77. package/dist/{utils-0qmYrqoa.d.ts → utils-DELRKQPU.d.ts} +1 -1
  78. package/migrations/neon/20260513010000_install_mcp_sessions.sql +69 -0
  79. package/migrations/neon/20260513020000_add_session_cleanup_cron.sql +35 -0
  80. package/{supabase/migrations → migrations/supabase}/20260330195700_install_mcp_sessions.sql +7 -9
  81. package/package.json +14 -5
  82. package/src/adapters/ai-adapter.ts +30 -1
  83. package/src/adapters/langchain-adapter.ts +6 -2
  84. package/src/adapters/mastra-adapter.ts +6 -2
  85. package/src/bin/mcp-ts.ts +8 -1
  86. package/src/client/core/app-host.ts +1 -1
  87. package/src/client/core/sse-client.ts +12 -14
  88. package/src/client/core/types.ts +1 -1
  89. package/src/client/react/oauth-popup.tsx +111 -51
  90. package/src/client/react/use-mcp-apps.tsx +1 -1
  91. package/src/client/react/use-mcp.ts +11 -11
  92. package/src/client/vue/use-mcp.ts +10 -10
  93. package/src/server/handlers/nextjs-handler.ts +18 -15
  94. package/src/server/handlers/sse-handler.ts +29 -29
  95. package/src/server/index.ts +1 -1
  96. package/src/server/mcp/multi-session-client.ts +17 -17
  97. package/src/server/mcp/oauth-client.ts +37 -37
  98. package/src/server/mcp/storage-oauth-provider.ts +17 -17
  99. package/src/server/storage/file-backend.ts +25 -25
  100. package/src/server/storage/index.ts +67 -10
  101. package/src/server/storage/memory-backend.ts +34 -34
  102. package/src/server/storage/neon-backend.ts +281 -0
  103. package/src/server/storage/redis-backend.ts +64 -64
  104. package/src/server/storage/sqlite-backend.ts +33 -33
  105. package/src/server/storage/supabase-backend.ts +23 -24
  106. package/src/server/storage/types.ts +18 -21
  107. package/src/shared/errors.ts +1 -1
  108. package/src/shared/index.ts +1 -2
  109. package/src/shared/meta-tools.ts +4 -6
  110. package/src/shared/schema-compressor.ts +2 -42
  111. package/src/shared/tool-index.ts +89 -84
  112. package/src/shared/tool-router.ts +0 -24
  113. package/src/shared/types.ts +4 -4
  114. /package/{supabase/migrations → migrations/supabase}/20260421010000_add_session_cleanup_cron.sql +0 -0
@@ -1,16 +1,16 @@
1
1
  import type { Redis } from 'ioredis';
2
- import { StorageBackend, SessionData } from './types.js';
2
+ import type { SessionStore, Session } from './types.js';
3
3
  import { SESSION_TTL_SECONDS } from '../../shared/constants.js';
4
4
  import { generateSessionId } from '../../shared/utils.js';
5
5
 
6
6
  /**
7
- * Redis implementation of StorageBackend
7
+ * Redis implementation of SessionStore
8
8
  */
9
- export class RedisStorageBackend implements StorageBackend {
9
+ export class RedisStorageBackend implements SessionStore {
10
10
  private readonly DEFAULT_TTL = SESSION_TTL_SECONDS;
11
11
  private readonly KEY_PREFIX = 'mcp:session:';
12
- private readonly IDENTITY_KEY_PREFIX = 'mcp:identity:';
13
- private readonly IDENTITY_KEY_SUFFIX = ':sessions';
12
+ private readonly USER_ID_KEY_PREFIX = 'mcp:userId:';
13
+ private readonly USER_ID_KEY_SUFFIX = ':sessions';
14
14
 
15
15
  constructor(private redis: Redis) { }
16
16
 
@@ -19,7 +19,7 @@ export class RedisStorageBackend implements StorageBackend {
19
19
  await this.redis.ping();
20
20
  console.log('[mcp-ts][Storage] Redis: ✓ Connected to server.');
21
21
  } catch (error: any) {
22
- throw new Error(`[RedisStorage] Failed to connect to Redis: ${error.message}`);
22
+ throw new Error(`[RedisStorageBackend] Failed to connect to Redis: ${error.message}`);
23
23
  }
24
24
  }
25
25
 
@@ -27,22 +27,22 @@ export class RedisStorageBackend implements StorageBackend {
27
27
  * Generates Redis key for a specific session
28
28
  * @private
29
29
  */
30
- private getSessionKey(identity: string, sessionId: string): string {
31
- return `${this.KEY_PREFIX}${identity}:${sessionId}`;
30
+ private getSessionKey(userId: string, sessionId: string): string {
31
+ return `${this.KEY_PREFIX}${userId}:${sessionId}`;
32
32
  }
33
33
 
34
34
  /**
35
- * Generates Redis key for tracking all sessions for an identity
35
+ * Generates Redis key for tracking all sessions for a user
36
36
  * @private
37
37
  */
38
- private getIdentityKey(identity: string): string {
39
- return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
38
+ private getUserIdKey(userId: string): string {
39
+ return `${this.USER_ID_KEY_PREFIX}${userId}${this.USER_ID_KEY_SUFFIX}`;
40
40
  }
41
41
 
42
- private parseIdentityFromKey(identityKey: string): string {
43
- return identityKey.slice(
44
- this.IDENTITY_KEY_PREFIX.length,
45
- identityKey.length - this.IDENTITY_KEY_SUFFIX.length
42
+ private parseUserIdFromKey(userIdKey: string): string {
43
+ return userIdKey.slice(
44
+ this.USER_ID_KEY_PREFIX.length,
45
+ userIdKey.length - this.USER_ID_KEY_SUFFIX.length
46
46
  );
47
47
  }
48
48
 
@@ -67,7 +67,7 @@ export class RedisStorageBackend implements StorageBackend {
67
67
  }
68
68
  } while (cursor !== '0');
69
69
  } catch (error) {
70
- console.warn('[RedisStorage] SCAN failed, falling back to KEYS:', error);
70
+ console.warn('[RedisStorageBackend] SCAN failed, falling back to KEYS:', error);
71
71
  return await this.redis.keys(pattern);
72
72
  }
73
73
 
@@ -78,12 +78,12 @@ export class RedisStorageBackend implements StorageBackend {
78
78
  return generateSessionId();
79
79
  }
80
80
 
81
- async createSession(session: SessionData, ttl?: number): Promise<void> {
82
- const { sessionId, identity } = session;
83
- if (!sessionId || !identity) throw new Error('identity and sessionId required');
81
+ async create(session: Session, ttl?: number): Promise<void> {
82
+ const { sessionId, userId } = session;
83
+ if (!sessionId || !userId) throw new Error('userId and sessionId required');
84
84
 
85
- const sessionKey = this.getSessionKey(identity, sessionId);
86
- const identityKey = this.getIdentityKey(identity);
85
+ const sessionKey = this.getSessionKey(userId, sessionId);
86
+ const userIdKey = this.getUserIdKey(userId);
87
87
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
88
88
 
89
89
  /** ioredis syntax: set(key, val, 'EX', ttl, 'NX') */
@@ -99,10 +99,10 @@ export class RedisStorageBackend implements StorageBackend {
99
99
  throw new Error(`Session ${sessionId} already exists`);
100
100
  }
101
101
 
102
- await this.redis.sadd(identityKey, sessionId);
102
+ await this.redis.sadd(userIdKey, sessionId);
103
103
  }
104
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
105
- const sessionKey = this.getSessionKey(identity, sessionId);
104
+ async update(userId: string, sessionId: string, data: Partial<Session>, ttl?: number): Promise<void> {
105
+ const sessionKey = this.getSessionKey(userId, sessionId);
106
106
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
107
107
 
108
108
  /** Lua script for atomic parsing, merging, and saving */
@@ -132,70 +132,70 @@ export class RedisStorageBackend implements StorageBackend {
132
132
  );
133
133
 
134
134
  if (result === 0) {
135
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
135
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
136
136
  }
137
137
  }
138
138
 
139
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
139
+ async get(userId: string, sessionId: string): Promise<Session | null> {
140
140
  try {
141
- const sessionKey = this.getSessionKey(identity, sessionId);
141
+ const sessionKey = this.getSessionKey(userId, sessionId);
142
142
  const sessionDataStr = await this.redis.get(sessionKey);
143
143
 
144
144
  if (!sessionDataStr) {
145
145
  return null;
146
146
  }
147
147
 
148
- const sessionData: SessionData = JSON.parse(sessionDataStr);
149
- return sessionData;
148
+ const Session: Session = JSON.parse(sessionDataStr);
149
+ return Session;
150
150
  } catch (error) {
151
- console.error('[RedisStorage] Failed to get session:', error);
151
+ console.error('[RedisStorageBackend] Failed to get session:', error);
152
152
  return null;
153
153
  }
154
154
  }
155
155
 
156
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
157
- const sessions = await this.getIdentitySessionsData(identity);
156
+ async listIds(userId: string): Promise<string[]> {
157
+ const sessions = await this.list(userId);
158
158
  return sessions.map((session) => session.sessionId);
159
159
  }
160
160
 
161
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
161
+ async list(userId: string): Promise<Session[]> {
162
162
  try {
163
- const identityKey = this.getIdentityKey(identity);
164
- const sessionIds = await this.redis.smembers(identityKey);
163
+ const userIdKey = this.getUserIdKey(userId);
164
+ const sessionIds = await this.redis.smembers(userIdKey);
165
165
  if (sessionIds.length === 0) return [];
166
166
 
167
167
  const results = await Promise.all(
168
168
  sessionIds.map(async (sessionId) => {
169
- const data = await this.redis.get(this.getSessionKey(identity, sessionId));
170
- return data ? (JSON.parse(data) as SessionData) : null;
169
+ const data = await this.redis.get(this.getSessionKey(userId, sessionId));
170
+ return data ? (JSON.parse(data) as Session) : null;
171
171
  })
172
172
  );
173
173
 
174
174
  const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
175
175
  if (staleSessionIds.length > 0) {
176
- await this.redis.srem(identityKey, ...staleSessionIds);
176
+ await this.redis.srem(userIdKey, ...staleSessionIds);
177
177
  }
178
178
 
179
- return results.filter((session): session is SessionData => session !== null);
179
+ return results.filter((session): session is Session => session !== null);
180
180
  } catch (error) {
181
- console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
181
+ console.error(`[RedisStorageBackend] Failed to get session data for ${userId}:`, error);
182
182
  return [];
183
183
  }
184
184
  }
185
185
 
186
- async removeSession(identity: string, sessionId: string): Promise<void> {
186
+ async delete(userId: string, sessionId: string): Promise<void> {
187
187
  try {
188
- const sessionKey = this.getSessionKey(identity, sessionId);
189
- const identityKey = this.getIdentityKey(identity);
188
+ const sessionKey = this.getSessionKey(userId, sessionId);
189
+ const userIdKey = this.getUserIdKey(userId);
190
190
 
191
- await this.redis.srem(identityKey, sessionId);
191
+ await this.redis.srem(userIdKey, sessionId);
192
192
  await this.redis.del(sessionKey);
193
193
  } catch (error) {
194
- console.error('[RedisStorage] Failed to remove session:', error);
194
+ console.error('[RedisStorageBackend] Failed to remove session:', error);
195
195
  }
196
196
  }
197
197
 
198
- async getAllSessionIds(): Promise<string[]> {
198
+ async listAllIds(): Promise<string[]> {
199
199
  try {
200
200
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
201
201
  const sessions = await Promise.all(
@@ -206,9 +206,9 @@ export class RedisStorageBackend implements StorageBackend {
206
206
  }
207
207
 
208
208
  try {
209
- return (JSON.parse(data) as SessionData).sessionId;
209
+ return (JSON.parse(data) as Session).sessionId;
210
210
  } catch (error) {
211
- console.error('[RedisStorage] Failed to parse session while listing all session IDs:', error);
211
+ console.error('[RedisStorageBackend] Failed to parse session while listing all session IDs:', error);
212
212
  return null;
213
213
  }
214
214
  })
@@ -216,7 +216,7 @@ export class RedisStorageBackend implements StorageBackend {
216
216
 
217
217
  return sessions.filter((sessionId): sessionId is string => sessionId !== null);
218
218
  } catch (error) {
219
- console.error('[RedisStorage] Failed to get all sessions:', error);
219
+ console.error('[RedisStorageBackend] Failed to get all sessions:', error);
220
220
  return [];
221
221
  }
222
222
  }
@@ -224,45 +224,45 @@ export class RedisStorageBackend implements StorageBackend {
224
224
  async clearAll(): Promise<void> {
225
225
  try {
226
226
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
227
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
228
- const allKeys = [...keys, ...identityKeys];
227
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
228
+ const allKeys = [...keys, ...userIdKeys];
229
229
  if (allKeys.length > 0) {
230
230
  await this.redis.del(...allKeys);
231
231
  }
232
232
  } catch (error) {
233
- console.error('[RedisStorage] Failed to clear sessions:', error);
233
+ console.error('[RedisStorageBackend] Failed to clear sessions:', error);
234
234
  }
235
235
  }
236
236
 
237
- async cleanupExpiredSessions(): Promise<void> {
237
+ async cleanupExpired(): Promise<void> {
238
238
  try {
239
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
239
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
240
240
 
241
- for (const identityKey of identityKeys) {
242
- const identity = this.parseIdentityFromKey(identityKey);
243
- const sessionIds = await this.redis.smembers(identityKey);
241
+ for (const userIdKey of userIdKeys) {
242
+ const userId = this.parseUserIdFromKey(userIdKey);
243
+ const sessionIds = await this.redis.smembers(userIdKey);
244
244
 
245
245
  if (sessionIds.length === 0) {
246
- await this.redis.del(identityKey);
246
+ await this.redis.del(userIdKey);
247
247
  continue;
248
248
  }
249
249
 
250
250
  const existenceChecks = await Promise.all(
251
- sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
251
+ sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(userId, sessionId)))
252
252
  );
253
253
 
254
254
  const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
255
255
  if (staleSessionIds.length > 0) {
256
- await this.redis.srem(identityKey, ...staleSessionIds);
256
+ await this.redis.srem(userIdKey, ...staleSessionIds);
257
257
  }
258
258
 
259
- const remainingCount = await this.redis.scard(identityKey);
259
+ const remainingCount = await this.redis.scard(userIdKey);
260
260
  if (remainingCount === 0) {
261
- await this.redis.del(identityKey);
261
+ await this.redis.del(userIdKey);
262
262
  }
263
263
  }
264
264
  } catch (error) {
265
- console.error('[RedisStorage] Failed to cleanup expired sessions:', error);
265
+ console.error('[RedisStorageBackend] Failed to cleanup expired sessions:', error);
266
266
  }
267
267
  }
268
268
 
@@ -270,7 +270,7 @@ export class RedisStorageBackend implements StorageBackend {
270
270
  try {
271
271
  await this.redis.quit();
272
272
  } catch (error) {
273
- console.error('[RedisStorage] Failed to disconnect:', error);
273
+ console.error('[RedisStorageBackend] Failed to disconnect:', error);
274
274
  }
275
275
  }
276
276
  }
@@ -1,5 +1,5 @@
1
1
  import type { Database } from 'better-sqlite3';
2
- import { StorageBackend, SessionData } from './types.js'; // Ensure .js extension
2
+ import type { SessionStore, Session } from './types.js';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import { generateSessionId } from '../../shared/utils.js';
@@ -9,7 +9,7 @@ export interface SqliteStorageOptions {
9
9
  table?: string;
10
10
  }
11
11
 
12
- export class SqliteStorage implements StorageBackend {
12
+ export class SqliteStorage implements SessionStore {
13
13
  private db: Database | null = null;
14
14
  private table: string;
15
15
  private initialized = false;
@@ -37,11 +37,11 @@ export class SqliteStorage implements StorageBackend {
37
37
  this.db.exec(`
38
38
  CREATE TABLE IF NOT EXISTS ${this.table} (
39
39
  sessionId TEXT PRIMARY KEY,
40
- identity TEXT NOT NULL,
40
+ userId TEXT NOT NULL,
41
41
  data TEXT NOT NULL,
42
42
  expiresAt INTEGER
43
43
  );
44
- CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
44
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_userId ON ${this.table}(userId);
45
45
  `);
46
46
 
47
47
  this.initialized = true;
@@ -66,21 +66,21 @@ export class SqliteStorage implements StorageBackend {
66
66
  return generateSessionId();
67
67
  }
68
68
 
69
- async createSession(session: SessionData, ttl?: number): Promise<void> {
69
+ async create(session: Session, ttl?: number): Promise<void> {
70
70
  this.ensureInitialized();
71
- const { sessionId, identity } = session;
71
+ const { sessionId, userId } = session;
72
72
 
73
- if (!sessionId || !identity) {
74
- throw new Error('identity and sessionId required');
73
+ if (!sessionId || !userId) {
74
+ throw new Error('userId and sessionId required');
75
75
  }
76
76
 
77
77
  const expiresAt = ttl ? Date.now() + ttl * 1000 : null;
78
78
 
79
79
  try {
80
80
  const stmt = this.db!.prepare(
81
- `INSERT INTO ${this.table} (sessionId, identity, data, expiresAt) VALUES (?, ?, ?, ?)`
81
+ `INSERT INTO ${this.table} (sessionId, userId, data, expiresAt) VALUES (?, ?, ?, ?)`
82
82
  );
83
- stmt.run(sessionId, identity, JSON.stringify(session), expiresAt);
83
+ stmt.run(sessionId, userId, JSON.stringify(session), expiresAt);
84
84
  } catch (error: any) {
85
85
  if (error.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') {
86
86
  throw new Error(`Session ${sessionId} already exists`);
@@ -89,70 +89,70 @@ export class SqliteStorage implements StorageBackend {
89
89
  }
90
90
  }
91
91
 
92
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
92
+ async update(userId: string, sessionId: string, data: Partial<Session>, ttl?: number): Promise<void> {
93
93
  this.ensureInitialized();
94
- if (!sessionId || !identity) {
95
- throw new Error('identity and sessionId required');
94
+ if (!sessionId || !userId) {
95
+ throw new Error('userId and sessionId required');
96
96
  }
97
97
 
98
- const currentSession = await this.getSession(identity, sessionId);
98
+ const currentSession = await this.get(userId, sessionId);
99
99
  if (!currentSession) {
100
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
100
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
101
101
  }
102
102
 
103
103
  const updatedSession = { ...currentSession, ...data };
104
104
  const expiresAt = ttl ? Date.now() + ttl * 1000 : null;
105
105
 
106
106
  const stmt = this.db!.prepare(
107
- `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND identity = ?`
107
+ `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND userId = ?`
108
108
  );
109
109
 
110
- stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, identity);
110
+ stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, userId);
111
111
  }
112
112
 
113
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
113
+ async get(userId: string, sessionId: string): Promise<Session | null> {
114
114
  this.ensureInitialized();
115
115
 
116
116
  const stmt = this.db!.prepare(
117
- `SELECT data FROM ${this.table} WHERE sessionId = ? AND identity = ?`
117
+ `SELECT data FROM ${this.table} WHERE sessionId = ? AND userId = ?`
118
118
  );
119
- const row = stmt.get(sessionId, identity) as { data: string } | undefined;
119
+ const row = stmt.get(sessionId, userId) as { data: string } | undefined;
120
120
 
121
121
  if (!row) return null;
122
- return JSON.parse(row.data) as SessionData;
122
+ return JSON.parse(row.data) as Session;
123
123
  }
124
124
 
125
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
125
+ async list(userId: string): Promise<Session[]> {
126
126
  this.ensureInitialized();
127
127
 
128
128
  const stmt = this.db!.prepare(
129
- `SELECT data FROM ${this.table} WHERE identity = ?`
129
+ `SELECT data FROM ${this.table} WHERE userId = ?`
130
130
  );
131
- const rows = stmt.all(identity) as { data: string }[];
131
+ const rows = stmt.all(userId) as { data: string }[];
132
132
 
133
- return rows.map(row => JSON.parse(row.data) as SessionData);
133
+ return rows.map(row => JSON.parse(row.data) as Session);
134
134
  }
135
135
 
136
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
136
+ async listIds(userId: string): Promise<string[]> {
137
137
  this.ensureInitialized();
138
138
 
139
139
  const stmt = this.db!.prepare(
140
- `SELECT sessionId FROM ${this.table} WHERE identity = ?`
140
+ `SELECT sessionId FROM ${this.table} WHERE userId = ?`
141
141
  );
142
- const rows = stmt.all(identity) as { sessionId: string }[];
142
+ const rows = stmt.all(userId) as { sessionId: string }[];
143
143
 
144
144
  return rows.map(row => row.sessionId);
145
145
  }
146
146
 
147
- async removeSession(identity: string, sessionId: string): Promise<void> {
147
+ async delete(userId: string, sessionId: string): Promise<void> {
148
148
  this.ensureInitialized();
149
149
  const stmt = this.db!.prepare(
150
- `DELETE FROM ${this.table} WHERE sessionId = ? AND identity = ?`
150
+ `DELETE FROM ${this.table} WHERE sessionId = ? AND userId = ?`
151
151
  );
152
- stmt.run(sessionId, identity);
152
+ stmt.run(sessionId, userId);
153
153
  }
154
154
 
155
- async getAllSessionIds(): Promise<string[]> {
155
+ async listAllIds(): Promise<string[]> {
156
156
  this.ensureInitialized();
157
157
  const stmt = this.db!.prepare(`SELECT sessionId FROM ${this.table}`);
158
158
  const rows = stmt.all() as { sessionId: string }[];
@@ -165,7 +165,7 @@ export class SqliteStorage implements StorageBackend {
165
165
  stmt.run();
166
166
  }
167
167
 
168
- async cleanupExpiredSessions(): Promise<void> {
168
+ async cleanupExpired(): Promise<void> {
169
169
  this.ensureInitialized();
170
170
  const now = Date.now();
171
171
  const stmt = this.db!.prepare(
@@ -1,10 +1,10 @@
1
1
  import type { SupabaseClient } from '@supabase/supabase-js';
2
- import { StorageBackend, SessionData } from './types.js';
2
+ import type { SessionStore, Session } from './types.js';
3
3
  import { SESSION_TTL_SECONDS } from '../../shared/constants.js';
4
4
  import { generateSessionId } from '../../shared/utils.js';
5
5
  import { encryptObject, decryptObject } from './crypto.js';
6
6
 
7
- export class SupabaseStorageBackend implements StorageBackend {
7
+ export class SupabaseStorageBackend implements SessionStore {
8
8
  private readonly DEFAULT_TTL = SESSION_TTL_SECONDS;
9
9
 
10
10
  constructor(private supabase: SupabaseClient) {}
@@ -34,7 +34,7 @@ export class SupabaseStorageBackend implements StorageBackend {
34
34
  return generateSessionId();
35
35
  }
36
36
 
37
- private mapRowToSessionData(row: any): SessionData {
37
+ private mapRowToSessionData(row: any): Session {
38
38
  return {
39
39
  sessionId: row.session_id,
40
40
  serverId: row.server_id,
@@ -43,7 +43,7 @@ export class SupabaseStorageBackend implements StorageBackend {
43
43
  transportType: row.transport_type,
44
44
  callbackUrl: row.callback_url,
45
45
  createdAt: new Date(row.created_at).getTime(),
46
- identity: row.identity,
46
+ userId: row.user_id,
47
47
  headers: decryptObject(row.headers),
48
48
  active: row.active,
49
49
  clientInformation: row.client_information,
@@ -53,9 +53,9 @@ export class SupabaseStorageBackend implements StorageBackend {
53
53
  };
54
54
  }
55
55
 
56
- async createSession(session: SessionData, ttl?: number): Promise<void> {
57
- const { sessionId, identity } = session;
58
- if (!sessionId || !identity) throw new Error('identity and sessionId required');
56
+ async create(session: Session, ttl?: number): Promise<void> {
57
+ const { sessionId, userId } = session;
58
+ if (!sessionId || !userId) throw new Error('userId and sessionId required');
59
59
 
60
60
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
61
61
  const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
@@ -64,14 +64,13 @@ export class SupabaseStorageBackend implements StorageBackend {
64
64
  .from('mcp_sessions')
65
65
  .insert({
66
66
  session_id: sessionId,
67
- user_id: identity, // Maps user_id to identity to support RLS using auth.uid()
67
+ user_id: userId, // Maps user_id to userId to support RLS using auth.uid()
68
68
  server_id: session.serverId,
69
69
  server_name: session.serverName,
70
70
  server_url: session.serverUrl,
71
71
  transport_type: session.transportType,
72
72
  callback_url: session.callbackUrl,
73
73
  created_at: new Date(session.createdAt || Date.now()).toISOString(),
74
- identity: identity,
75
74
  headers: encryptObject(session.headers),
76
75
  active: session.active ?? false,
77
76
  client_information: session.clientInformation,
@@ -90,7 +89,7 @@ export class SupabaseStorageBackend implements StorageBackend {
90
89
  }
91
90
  }
92
91
 
93
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
92
+ async update(userId: string, sessionId: string, data: Partial<Session>, ttl?: number): Promise<void> {
94
93
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
95
94
  const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
96
95
 
@@ -115,7 +114,7 @@ export class SupabaseStorageBackend implements StorageBackend {
115
114
  const { data: updatedRows, error } = await this.supabase
116
115
  .from('mcp_sessions')
117
116
  .update(updateData)
118
- .eq('identity', identity)
117
+ .eq('user_id', userId)
119
118
  .eq('session_id', sessionId)
120
119
  .select('id');
121
120
 
@@ -124,15 +123,15 @@ export class SupabaseStorageBackend implements StorageBackend {
124
123
  }
125
124
 
126
125
  if (!updatedRows || updatedRows.length === 0) {
127
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
126
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
128
127
  }
129
128
  }
130
129
 
131
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
130
+ async get(userId: string, sessionId: string): Promise<Session | null> {
132
131
  const { data, error } = await this.supabase
133
132
  .from('mcp_sessions')
134
133
  .select('*')
135
- .eq('identity', identity)
134
+ .eq('user_id', userId)
136
135
  .eq('session_id', sessionId)
137
136
  .maybeSingle();
138
137
 
@@ -146,25 +145,25 @@ export class SupabaseStorageBackend implements StorageBackend {
146
145
  return this.mapRowToSessionData(data);
147
146
  }
148
147
 
149
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
148
+ async list(userId: string): Promise<Session[]> {
150
149
  const { data, error } = await this.supabase
151
150
  .from('mcp_sessions')
152
151
  .select('*')
153
- .eq('identity', identity);
152
+ .eq('user_id', userId);
154
153
 
155
154
  if (error) {
156
- console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
155
+ console.error(`[SupabaseStorage] Failed to get session data for ${userId}:`, error);
157
156
  return [];
158
157
  }
159
158
 
160
159
  return data.map(row => this.mapRowToSessionData(row));
161
160
  }
162
161
 
163
- async removeSession(identity: string, sessionId: string): Promise<void> {
162
+ async delete(userId: string, sessionId: string): Promise<void> {
164
163
  const { error } = await this.supabase
165
164
  .from('mcp_sessions')
166
165
  .delete()
167
- .eq('identity', identity)
166
+ .eq('user_id', userId)
168
167
  .eq('session_id', sessionId);
169
168
 
170
169
  if (error) {
@@ -172,21 +171,21 @@ export class SupabaseStorageBackend implements StorageBackend {
172
171
  }
173
172
  }
174
173
 
175
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
174
+ async listIds(userId: string): Promise<string[]> {
176
175
  const { data, error } = await this.supabase
177
176
  .from('mcp_sessions')
178
177
  .select('session_id')
179
- .eq('identity', identity);
178
+ .eq('user_id', userId);
180
179
 
181
180
  if (error) {
182
- console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
181
+ console.error(`[SupabaseStorage] Failed to get sessions for ${userId}:`, error);
183
182
  return [];
184
183
  }
185
184
 
186
185
  return data.map(row => row.session_id);
187
186
  }
188
187
 
189
- async getAllSessionIds(): Promise<string[]> {
188
+ async listAllIds(): Promise<string[]> {
190
189
  const { data, error } = await this.supabase
191
190
  .from('mcp_sessions')
192
191
  .select('session_id');
@@ -211,7 +210,7 @@ export class SupabaseStorageBackend implements StorageBackend {
211
210
  }
212
211
  }
213
212
 
214
- async cleanupExpiredSessions(): Promise<void> {
213
+ async cleanupExpired(): Promise<void> {
215
214
  const { error } = await this.supabase
216
215
  .from('mcp_sessions')
217
216
  .delete()