@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
@@ -149,35 +149,35 @@ var RedisStorageBackend = class {
149
149
  this.redis = redis2;
150
150
  __publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
151
151
  __publicField(this, "KEY_PREFIX", "mcp:session:");
152
- __publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
153
- __publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
152
+ __publicField(this, "USER_ID_KEY_PREFIX", "mcp:userId:");
153
+ __publicField(this, "USER_ID_KEY_SUFFIX", ":sessions");
154
154
  }
155
155
  async init() {
156
156
  try {
157
157
  await this.redis.ping();
158
158
  console.log("[mcp-ts][Storage] Redis: \u2713 Connected to server.");
159
159
  } catch (error) {
160
- throw new Error(`[RedisStorage] Failed to connect to Redis: ${error.message}`);
160
+ throw new Error(`[RedisStorageBackend] Failed to connect to Redis: ${error.message}`);
161
161
  }
162
162
  }
163
163
  /**
164
164
  * Generates Redis key for a specific session
165
165
  * @private
166
166
  */
167
- getSessionKey(identity, sessionId) {
168
- return `${this.KEY_PREFIX}${identity}:${sessionId}`;
167
+ getSessionKey(userId, sessionId) {
168
+ return `${this.KEY_PREFIX}${userId}:${sessionId}`;
169
169
  }
170
170
  /**
171
- * Generates Redis key for tracking all sessions for an identity
171
+ * Generates Redis key for tracking all sessions for a user
172
172
  * @private
173
173
  */
174
- getIdentityKey(identity) {
175
- return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
174
+ getUserIdKey(userId) {
175
+ return `${this.USER_ID_KEY_PREFIX}${userId}${this.USER_ID_KEY_SUFFIX}`;
176
176
  }
177
- parseIdentityFromKey(identityKey) {
178
- return identityKey.slice(
179
- this.IDENTITY_KEY_PREFIX.length,
180
- identityKey.length - this.IDENTITY_KEY_SUFFIX.length
177
+ parseUserIdFromKey(userIdKey) {
178
+ return userIdKey.slice(
179
+ this.USER_ID_KEY_PREFIX.length,
180
+ userIdKey.length - this.USER_ID_KEY_SUFFIX.length
181
181
  );
182
182
  }
183
183
  async scanKeys(pattern) {
@@ -196,7 +196,7 @@ var RedisStorageBackend = class {
196
196
  }
197
197
  } while (cursor !== "0");
198
198
  } catch (error) {
199
- console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
199
+ console.warn("[RedisStorageBackend] SCAN failed, falling back to KEYS:", error);
200
200
  return await this.redis.keys(pattern);
201
201
  }
202
202
  return Array.from(keys);
@@ -204,11 +204,11 @@ var RedisStorageBackend = class {
204
204
  generateSessionId() {
205
205
  return generateSessionId();
206
206
  }
207
- async createSession(session, ttl) {
208
- const { sessionId, identity } = session;
209
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
210
- const sessionKey = this.getSessionKey(identity, sessionId);
211
- const identityKey = this.getIdentityKey(identity);
207
+ async create(session, ttl) {
208
+ const { sessionId, userId } = session;
209
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
210
+ const sessionKey = this.getSessionKey(userId, sessionId);
211
+ const userIdKey = this.getUserIdKey(userId);
212
212
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
213
213
  const result = await this.redis.set(
214
214
  sessionKey,
@@ -220,10 +220,10 @@ var RedisStorageBackend = class {
220
220
  if (result !== "OK") {
221
221
  throw new Error(`Session ${sessionId} already exists`);
222
222
  }
223
- await this.redis.sadd(identityKey, sessionId);
223
+ await this.redis.sadd(userIdKey, sessionId);
224
224
  }
225
- async updateSession(identity, sessionId, data, ttl) {
226
- const sessionKey = this.getSessionKey(identity, sessionId);
225
+ async update(userId, sessionId, data, ttl) {
226
+ const sessionKey = this.getSessionKey(userId, sessionId);
227
227
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
228
228
  const script = `
229
229
  local currentStr = redis.call("GET", KEYS[1])
@@ -249,62 +249,62 @@ var RedisStorageBackend = class {
249
249
  effectiveTtl
250
250
  );
251
251
  if (result === 0) {
252
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
252
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
253
253
  }
254
254
  }
255
- async getSession(identity, sessionId) {
255
+ async get(userId, sessionId) {
256
256
  try {
257
- const sessionKey = this.getSessionKey(identity, sessionId);
257
+ const sessionKey = this.getSessionKey(userId, sessionId);
258
258
  const sessionDataStr = await this.redis.get(sessionKey);
259
259
  if (!sessionDataStr) {
260
260
  return null;
261
261
  }
262
- const sessionData = JSON.parse(sessionDataStr);
263
- return sessionData;
262
+ const Session = JSON.parse(sessionDataStr);
263
+ return Session;
264
264
  } catch (error) {
265
- console.error("[RedisStorage] Failed to get session:", error);
265
+ console.error("[RedisStorageBackend] Failed to get session:", error);
266
266
  return null;
267
267
  }
268
268
  }
269
- async getIdentityMcpSessions(identity) {
270
- const sessions = await this.getIdentitySessionsData(identity);
271
- return sessions.map((session) => session.sessionId);
269
+ async listIds(userId) {
270
+ const sessions2 = await this.list(userId);
271
+ return sessions2.map((session) => session.sessionId);
272
272
  }
273
- async getIdentitySessionsData(identity) {
273
+ async list(userId) {
274
274
  try {
275
- const identityKey = this.getIdentityKey(identity);
276
- const sessionIds = await this.redis.smembers(identityKey);
275
+ const userIdKey = this.getUserIdKey(userId);
276
+ const sessionIds = await this.redis.smembers(userIdKey);
277
277
  if (sessionIds.length === 0) return [];
278
278
  const results = await Promise.all(
279
279
  sessionIds.map(async (sessionId) => {
280
- const data = await this.redis.get(this.getSessionKey(identity, sessionId));
280
+ const data = await this.redis.get(this.getSessionKey(userId, sessionId));
281
281
  return data ? JSON.parse(data) : null;
282
282
  })
283
283
  );
284
284
  const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
285
285
  if (staleSessionIds.length > 0) {
286
- await this.redis.srem(identityKey, ...staleSessionIds);
286
+ await this.redis.srem(userIdKey, ...staleSessionIds);
287
287
  }
288
288
  return results.filter((session) => session !== null);
289
289
  } catch (error) {
290
- console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
290
+ console.error(`[RedisStorageBackend] Failed to get session data for ${userId}:`, error);
291
291
  return [];
292
292
  }
293
293
  }
294
- async removeSession(identity, sessionId) {
294
+ async delete(userId, sessionId) {
295
295
  try {
296
- const sessionKey = this.getSessionKey(identity, sessionId);
297
- const identityKey = this.getIdentityKey(identity);
298
- await this.redis.srem(identityKey, sessionId);
296
+ const sessionKey = this.getSessionKey(userId, sessionId);
297
+ const userIdKey = this.getUserIdKey(userId);
298
+ await this.redis.srem(userIdKey, sessionId);
299
299
  await this.redis.del(sessionKey);
300
300
  } catch (error) {
301
- console.error("[RedisStorage] Failed to remove session:", error);
301
+ console.error("[RedisStorageBackend] Failed to remove session:", error);
302
302
  }
303
303
  }
304
- async getAllSessionIds() {
304
+ async listAllIds() {
305
305
  try {
306
306
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
307
- const sessions = await Promise.all(
307
+ const sessions2 = await Promise.all(
308
308
  keys.map(async (key) => {
309
309
  const data = await this.redis.get(key);
310
310
  if (!data) {
@@ -313,60 +313,60 @@ var RedisStorageBackend = class {
313
313
  try {
314
314
  return JSON.parse(data).sessionId;
315
315
  } catch (error) {
316
- console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
316
+ console.error("[RedisStorageBackend] Failed to parse session while listing all session IDs:", error);
317
317
  return null;
318
318
  }
319
319
  })
320
320
  );
321
- return sessions.filter((sessionId) => sessionId !== null);
321
+ return sessions2.filter((sessionId) => sessionId !== null);
322
322
  } catch (error) {
323
- console.error("[RedisStorage] Failed to get all sessions:", error);
323
+ console.error("[RedisStorageBackend] Failed to get all sessions:", error);
324
324
  return [];
325
325
  }
326
326
  }
327
327
  async clearAll() {
328
328
  try {
329
329
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
330
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
331
- const allKeys = [...keys, ...identityKeys];
330
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
331
+ const allKeys = [...keys, ...userIdKeys];
332
332
  if (allKeys.length > 0) {
333
333
  await this.redis.del(...allKeys);
334
334
  }
335
335
  } catch (error) {
336
- console.error("[RedisStorage] Failed to clear sessions:", error);
336
+ console.error("[RedisStorageBackend] Failed to clear sessions:", error);
337
337
  }
338
338
  }
339
- async cleanupExpiredSessions() {
339
+ async cleanupExpired() {
340
340
  try {
341
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
342
- for (const identityKey of identityKeys) {
343
- const identity = this.parseIdentityFromKey(identityKey);
344
- const sessionIds = await this.redis.smembers(identityKey);
341
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
342
+ for (const userIdKey of userIdKeys) {
343
+ const userId = this.parseUserIdFromKey(userIdKey);
344
+ const sessionIds = await this.redis.smembers(userIdKey);
345
345
  if (sessionIds.length === 0) {
346
- await this.redis.del(identityKey);
346
+ await this.redis.del(userIdKey);
347
347
  continue;
348
348
  }
349
349
  const existenceChecks = await Promise.all(
350
- sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
350
+ sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(userId, sessionId)))
351
351
  );
352
352
  const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
353
353
  if (staleSessionIds.length > 0) {
354
- await this.redis.srem(identityKey, ...staleSessionIds);
354
+ await this.redis.srem(userIdKey, ...staleSessionIds);
355
355
  }
356
- const remainingCount = await this.redis.scard(identityKey);
356
+ const remainingCount = await this.redis.scard(userIdKey);
357
357
  if (remainingCount === 0) {
358
- await this.redis.del(identityKey);
358
+ await this.redis.del(userIdKey);
359
359
  }
360
360
  }
361
361
  } catch (error) {
362
- console.error("[RedisStorage] Failed to cleanup expired sessions:", error);
362
+ console.error("[RedisStorageBackend] Failed to cleanup expired sessions:", error);
363
363
  }
364
364
  }
365
365
  async disconnect() {
366
366
  try {
367
367
  await this.redis.quit();
368
368
  } catch (error) {
369
- console.error("[RedisStorage] Failed to disconnect:", error);
369
+ console.error("[RedisStorageBackend] Failed to disconnect:", error);
370
370
  }
371
371
  }
372
372
  };
@@ -374,36 +374,36 @@ var RedisStorageBackend = class {
374
374
  // src/server/storage/memory-backend.ts
375
375
  var MemoryStorageBackend = class {
376
376
  constructor() {
377
- // Map<identity:sessionId, SessionData>
377
+ // Map<userId:sessionId, Session>
378
378
  __publicField(this, "sessions", /* @__PURE__ */ new Map());
379
- // Map<identity, Set<sessionId>>
380
- __publicField(this, "identitySessions", /* @__PURE__ */ new Map());
379
+ // Map<userId, Set<sessionId>>
380
+ __publicField(this, "userIdSessions", /* @__PURE__ */ new Map());
381
381
  }
382
382
  async init() {
383
383
  console.log("[mcp-ts][Storage] Memory: \u2713 internal memory store active.");
384
384
  }
385
- getSessionKey(identity, sessionId) {
386
- return `${identity}:${sessionId}`;
385
+ getSessionKey(userId, sessionId) {
386
+ return `${userId}:${sessionId}`;
387
387
  }
388
388
  generateSessionId() {
389
389
  return generateSessionId();
390
390
  }
391
- async createSession(session, ttl) {
392
- const { sessionId, identity } = session;
393
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
394
- const sessionKey = this.getSessionKey(identity, sessionId);
391
+ async create(session, ttl) {
392
+ const { sessionId, userId } = session;
393
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
394
+ const sessionKey = this.getSessionKey(userId, sessionId);
395
395
  if (this.sessions.has(sessionKey)) {
396
396
  throw new Error(`Session ${sessionId} already exists`);
397
397
  }
398
398
  this.sessions.set(sessionKey, session);
399
- if (!this.identitySessions.has(identity)) {
400
- this.identitySessions.set(identity, /* @__PURE__ */ new Set());
399
+ if (!this.userIdSessions.has(userId)) {
400
+ this.userIdSessions.set(userId, /* @__PURE__ */ new Set());
401
401
  }
402
- this.identitySessions.get(identity).add(sessionId);
402
+ this.userIdSessions.get(userId).add(sessionId);
403
403
  }
404
- async updateSession(identity, sessionId, data, ttl) {
405
- if (!identity || !sessionId) throw new Error("identity and sessionId required");
406
- const sessionKey = this.getSessionKey(identity, sessionId);
404
+ async update(userId, sessionId, data, ttl) {
405
+ if (!userId || !sessionId) throw new Error("userId and sessionId required");
406
+ const sessionKey = this.getSessionKey(userId, sessionId);
407
407
  const current = this.sessions.get(sessionKey);
408
408
  if (!current) {
409
409
  throw new Error(`Session ${sessionId} not found`);
@@ -414,45 +414,45 @@ var MemoryStorageBackend = class {
414
414
  };
415
415
  this.sessions.set(sessionKey, updated);
416
416
  }
417
- async getSession(identity, sessionId) {
418
- const sessionKey = this.getSessionKey(identity, sessionId);
417
+ async get(userId, sessionId) {
418
+ const sessionKey = this.getSessionKey(userId, sessionId);
419
419
  return this.sessions.get(sessionKey) || null;
420
420
  }
421
- async getIdentityMcpSessions(identity) {
422
- const set = this.identitySessions.get(identity);
421
+ async listIds(userId) {
422
+ const set = this.userIdSessions.get(userId);
423
423
  return set ? Array.from(set) : [];
424
424
  }
425
- async getIdentitySessionsData(identity) {
426
- const set = this.identitySessions.get(identity);
425
+ async list(userId) {
426
+ const set = this.userIdSessions.get(userId);
427
427
  if (!set) return [];
428
428
  const results = [];
429
429
  for (const sessionId of set) {
430
- const session = this.sessions.get(this.getSessionKey(identity, sessionId));
430
+ const session = this.sessions.get(this.getSessionKey(userId, sessionId));
431
431
  if (session) {
432
432
  results.push(session);
433
433
  }
434
434
  }
435
435
  return results;
436
436
  }
437
- async removeSession(identity, sessionId) {
438
- const sessionKey = this.getSessionKey(identity, sessionId);
437
+ async delete(userId, sessionId) {
438
+ const sessionKey = this.getSessionKey(userId, sessionId);
439
439
  this.sessions.delete(sessionKey);
440
- const set = this.identitySessions.get(identity);
440
+ const set = this.userIdSessions.get(userId);
441
441
  if (set) {
442
442
  set.delete(sessionId);
443
443
  if (set.size === 0) {
444
- this.identitySessions.delete(identity);
444
+ this.userIdSessions.delete(userId);
445
445
  }
446
446
  }
447
447
  }
448
- async getAllSessionIds() {
448
+ async listAllIds() {
449
449
  return Array.from(this.sessions.values()).map((s) => s.sessionId);
450
450
  }
451
451
  async clearAll() {
452
452
  this.sessions.clear();
453
- this.identitySessions.clear();
453
+ this.userIdSessions.clear();
454
454
  }
455
- async cleanupExpiredSessions() {
455
+ async cleanupExpired() {
456
456
  }
457
457
  async disconnect() {
458
458
  }
@@ -480,7 +480,7 @@ var FileStorageBackend = class {
480
480
  this.memoryCache = /* @__PURE__ */ new Map();
481
481
  if (Array.isArray(json)) {
482
482
  json.forEach((s) => {
483
- this.memoryCache.set(this.getSessionKey(s.identity || "unknown", s.sessionId), s);
483
+ this.memoryCache.set(this.getSessionKey(s.userId || "unknown", s.sessionId), s);
484
484
  });
485
485
  }
486
486
  } catch (error) {
@@ -500,30 +500,30 @@ var FileStorageBackend = class {
500
500
  }
501
501
  async flush() {
502
502
  if (!this.memoryCache) return;
503
- const sessions = Array.from(this.memoryCache.values());
504
- await promises.writeFile(this.filePath, JSON.stringify(sessions, null, 2), "utf-8");
503
+ const sessions2 = Array.from(this.memoryCache.values());
504
+ await promises.writeFile(this.filePath, JSON.stringify(sessions2, null, 2), "utf-8");
505
505
  }
506
- getSessionKey(identity, sessionId) {
507
- return `${identity}:${sessionId}`;
506
+ getSessionKey(userId, sessionId) {
507
+ return `${userId}:${sessionId}`;
508
508
  }
509
509
  generateSessionId() {
510
510
  return generateSessionId();
511
511
  }
512
- async createSession(session, ttl) {
512
+ async create(session, ttl) {
513
513
  await this.ensureInitialized();
514
- const { sessionId, identity } = session;
515
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
516
- const sessionKey = this.getSessionKey(identity, sessionId);
514
+ const { sessionId, userId } = session;
515
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
516
+ const sessionKey = this.getSessionKey(userId, sessionId);
517
517
  if (this.memoryCache.has(sessionKey)) {
518
518
  throw new Error(`Session ${sessionId} already exists`);
519
519
  }
520
520
  this.memoryCache.set(sessionKey, session);
521
521
  await this.flush();
522
522
  }
523
- async updateSession(identity, sessionId, data, ttl) {
523
+ async update(userId, sessionId, data, ttl) {
524
524
  await this.ensureInitialized();
525
- if (!identity || !sessionId) throw new Error("identity and sessionId required");
526
- const sessionKey = this.getSessionKey(identity, sessionId);
525
+ if (!userId || !sessionId) throw new Error("userId and sessionId required");
526
+ const sessionKey = this.getSessionKey(userId, sessionId);
527
527
  const current = this.memoryCache.get(sessionKey);
528
528
  if (!current) {
529
529
  throw new Error(`Session ${sessionId} not found`);
@@ -535,27 +535,27 @@ var FileStorageBackend = class {
535
535
  this.memoryCache.set(sessionKey, updated);
536
536
  await this.flush();
537
537
  }
538
- async getSession(identity, sessionId) {
538
+ async get(userId, sessionId) {
539
539
  await this.ensureInitialized();
540
- const sessionKey = this.getSessionKey(identity, sessionId);
540
+ const sessionKey = this.getSessionKey(userId, sessionId);
541
541
  return this.memoryCache.get(sessionKey) || null;
542
542
  }
543
- async getIdentitySessionsData(identity) {
543
+ async list(userId) {
544
544
  await this.ensureInitialized();
545
- return Array.from(this.memoryCache.values()).filter((s) => s.identity === identity);
545
+ return Array.from(this.memoryCache.values()).filter((s) => s.userId === userId);
546
546
  }
547
- async getIdentityMcpSessions(identity) {
547
+ async listIds(userId) {
548
548
  await this.ensureInitialized();
549
- return Array.from(this.memoryCache.values()).filter((s) => s.identity === identity).map((s) => s.sessionId);
549
+ return Array.from(this.memoryCache.values()).filter((s) => s.userId === userId).map((s) => s.sessionId);
550
550
  }
551
- async removeSession(identity, sessionId) {
551
+ async delete(userId, sessionId) {
552
552
  await this.ensureInitialized();
553
- const sessionKey = this.getSessionKey(identity, sessionId);
553
+ const sessionKey = this.getSessionKey(userId, sessionId);
554
554
  if (this.memoryCache.delete(sessionKey)) {
555
555
  await this.flush();
556
556
  }
557
557
  }
558
- async getAllSessionIds() {
558
+ async listAllIds() {
559
559
  await this.ensureInitialized();
560
560
  return Array.from(this.memoryCache.values()).map((s) => s.sessionId);
561
561
  }
@@ -564,7 +564,7 @@ var FileStorageBackend = class {
564
564
  this.memoryCache.clear();
565
565
  await this.flush();
566
566
  }
567
- async cleanupExpiredSessions() {
567
+ async cleanupExpired() {
568
568
  await this.ensureInitialized();
569
569
  }
570
570
  async disconnect() {
@@ -591,11 +591,11 @@ var SqliteStorage = class {
591
591
  this.db.exec(`
592
592
  CREATE TABLE IF NOT EXISTS ${this.table} (
593
593
  sessionId TEXT PRIMARY KEY,
594
- identity TEXT NOT NULL,
594
+ userId TEXT NOT NULL,
595
595
  data TEXT NOT NULL,
596
596
  expiresAt INTEGER
597
597
  );
598
- CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
598
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_userId ON ${this.table}(userId);
599
599
  `);
600
600
  this.initialized = true;
601
601
  console.log(`[mcp-ts][Storage] SQLite: \u2713 database at ${this.dbPath} verified.`);
@@ -616,18 +616,18 @@ var SqliteStorage = class {
616
616
  generateSessionId() {
617
617
  return generateSessionId();
618
618
  }
619
- async createSession(session, ttl) {
619
+ async create(session, ttl) {
620
620
  this.ensureInitialized();
621
- const { sessionId, identity } = session;
622
- if (!sessionId || !identity) {
623
- throw new Error("identity and sessionId required");
621
+ const { sessionId, userId } = session;
622
+ if (!sessionId || !userId) {
623
+ throw new Error("userId and sessionId required");
624
624
  }
625
625
  const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
626
626
  try {
627
627
  const stmt = this.db.prepare(
628
- `INSERT INTO ${this.table} (sessionId, identity, data, expiresAt) VALUES (?, ?, ?, ?)`
628
+ `INSERT INTO ${this.table} (sessionId, userId, data, expiresAt) VALUES (?, ?, ?, ?)`
629
629
  );
630
- stmt.run(sessionId, identity, JSON.stringify(session), expiresAt);
630
+ stmt.run(sessionId, userId, JSON.stringify(session), expiresAt);
631
631
  } catch (error) {
632
632
  if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
633
633
  throw new Error(`Session ${sessionId} already exists`);
@@ -635,55 +635,55 @@ var SqliteStorage = class {
635
635
  throw error;
636
636
  }
637
637
  }
638
- async updateSession(identity, sessionId, data, ttl) {
638
+ async update(userId, sessionId, data, ttl) {
639
639
  this.ensureInitialized();
640
- if (!sessionId || !identity) {
641
- throw new Error("identity and sessionId required");
640
+ if (!sessionId || !userId) {
641
+ throw new Error("userId and sessionId required");
642
642
  }
643
- const currentSession = await this.getSession(identity, sessionId);
643
+ const currentSession = await this.get(userId, sessionId);
644
644
  if (!currentSession) {
645
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
645
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
646
646
  }
647
647
  const updatedSession = { ...currentSession, ...data };
648
648
  const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
649
649
  const stmt = this.db.prepare(
650
- `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND identity = ?`
650
+ `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND userId = ?`
651
651
  );
652
- stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, identity);
652
+ stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, userId);
653
653
  }
654
- async getSession(identity, sessionId) {
654
+ async get(userId, sessionId) {
655
655
  this.ensureInitialized();
656
656
  const stmt = this.db.prepare(
657
- `SELECT data FROM ${this.table} WHERE sessionId = ? AND identity = ?`
657
+ `SELECT data FROM ${this.table} WHERE sessionId = ? AND userId = ?`
658
658
  );
659
- const row = stmt.get(sessionId, identity);
659
+ const row = stmt.get(sessionId, userId);
660
660
  if (!row) return null;
661
661
  return JSON.parse(row.data);
662
662
  }
663
- async getIdentitySessionsData(identity) {
663
+ async list(userId) {
664
664
  this.ensureInitialized();
665
665
  const stmt = this.db.prepare(
666
- `SELECT data FROM ${this.table} WHERE identity = ?`
666
+ `SELECT data FROM ${this.table} WHERE userId = ?`
667
667
  );
668
- const rows = stmt.all(identity);
668
+ const rows = stmt.all(userId);
669
669
  return rows.map((row) => JSON.parse(row.data));
670
670
  }
671
- async getIdentityMcpSessions(identity) {
671
+ async listIds(userId) {
672
672
  this.ensureInitialized();
673
673
  const stmt = this.db.prepare(
674
- `SELECT sessionId FROM ${this.table} WHERE identity = ?`
674
+ `SELECT sessionId FROM ${this.table} WHERE userId = ?`
675
675
  );
676
- const rows = stmt.all(identity);
676
+ const rows = stmt.all(userId);
677
677
  return rows.map((row) => row.sessionId);
678
678
  }
679
- async removeSession(identity, sessionId) {
679
+ async delete(userId, sessionId) {
680
680
  this.ensureInitialized();
681
681
  const stmt = this.db.prepare(
682
- `DELETE FROM ${this.table} WHERE sessionId = ? AND identity = ?`
682
+ `DELETE FROM ${this.table} WHERE sessionId = ? AND userId = ?`
683
683
  );
684
- stmt.run(sessionId, identity);
684
+ stmt.run(sessionId, userId);
685
685
  }
686
- async getAllSessionIds() {
686
+ async listAllIds() {
687
687
  this.ensureInitialized();
688
688
  const stmt = this.db.prepare(`SELECT sessionId FROM ${this.table}`);
689
689
  const rows = stmt.all();
@@ -694,7 +694,7 @@ var SqliteStorage = class {
694
694
  const stmt = this.db.prepare(`DELETE FROM ${this.table}`);
695
695
  stmt.run();
696
696
  }
697
- async cleanupExpiredSessions() {
697
+ async cleanupExpired() {
698
698
  this.ensureInitialized();
699
699
  const now = Date.now();
700
700
  const stmt = this.db.prepare(
@@ -805,7 +805,7 @@ var SupabaseStorageBackend = class {
805
805
  transportType: row.transport_type,
806
806
  callbackUrl: row.callback_url,
807
807
  createdAt: new Date(row.created_at).getTime(),
808
- identity: row.identity,
808
+ userId: row.user_id,
809
809
  headers: decryptObject(row.headers),
810
810
  active: row.active,
811
811
  clientInformation: row.client_information,
@@ -814,22 +814,21 @@ var SupabaseStorageBackend = class {
814
814
  clientId: row.client_id
815
815
  };
816
816
  }
817
- async createSession(session, ttl) {
818
- const { sessionId, identity } = session;
819
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
817
+ async create(session, ttl) {
818
+ const { sessionId, userId } = session;
819
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
820
820
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
821
821
  const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
822
822
  const { error } = await this.supabase.from("mcp_sessions").insert({
823
823
  session_id: sessionId,
824
- user_id: identity,
825
- // Maps user_id to identity to support RLS using auth.uid()
824
+ user_id: userId,
825
+ // Maps user_id to userId to support RLS using auth.uid()
826
826
  server_id: session.serverId,
827
827
  server_name: session.serverName,
828
828
  server_url: session.serverUrl,
829
829
  transport_type: session.transportType,
830
830
  callback_url: session.callbackUrl,
831
831
  created_at: new Date(session.createdAt || Date.now()).toISOString(),
832
- identity,
833
832
  headers: encryptObject(session.headers),
834
833
  active: session.active ?? false,
835
834
  client_information: session.clientInformation,
@@ -845,7 +844,7 @@ var SupabaseStorageBackend = class {
845
844
  throw new Error(`Failed to create session in Supabase: ${error.message}`);
846
845
  }
847
846
  }
848
- async updateSession(identity, sessionId, data, ttl) {
847
+ async update(userId, sessionId, data, ttl) {
849
848
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
850
849
  const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
851
850
  const updateData = {
@@ -863,16 +862,16 @@ var SupabaseStorageBackend = class {
863
862
  if ("tokens" in data) updateData.tokens = encryptObject(data.tokens);
864
863
  if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
865
864
  if ("clientId" in data) updateData.client_id = data.clientId;
866
- const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
865
+ const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("user_id", userId).eq("session_id", sessionId).select("id");
867
866
  if (error) {
868
867
  throw new Error(`Failed to update session: ${error.message}`);
869
868
  }
870
869
  if (!updatedRows || updatedRows.length === 0) {
871
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
870
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
872
871
  }
873
872
  }
874
- async getSession(identity, sessionId) {
875
- const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity).eq("session_id", sessionId).maybeSingle();
873
+ async get(userId, sessionId) {
874
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("user_id", userId).eq("session_id", sessionId).maybeSingle();
876
875
  if (error) {
877
876
  console.error("[SupabaseStorage] Failed to get session:", error);
878
877
  return null;
@@ -880,29 +879,29 @@ var SupabaseStorageBackend = class {
880
879
  if (!data) return null;
881
880
  return this.mapRowToSessionData(data);
882
881
  }
883
- async getIdentitySessionsData(identity) {
884
- const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity);
882
+ async list(userId) {
883
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("user_id", userId);
885
884
  if (error) {
886
- console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
885
+ console.error(`[SupabaseStorage] Failed to get session data for ${userId}:`, error);
887
886
  return [];
888
887
  }
889
888
  return data.map((row) => this.mapRowToSessionData(row));
890
889
  }
891
- async removeSession(identity, sessionId) {
892
- const { error } = await this.supabase.from("mcp_sessions").delete().eq("identity", identity).eq("session_id", sessionId);
890
+ async delete(userId, sessionId) {
891
+ const { error } = await this.supabase.from("mcp_sessions").delete().eq("user_id", userId).eq("session_id", sessionId);
893
892
  if (error) {
894
893
  console.error("[SupabaseStorage] Failed to remove session:", error);
895
894
  }
896
895
  }
897
- async getIdentityMcpSessions(identity) {
898
- const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("identity", identity);
896
+ async listIds(userId) {
897
+ const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("user_id", userId);
899
898
  if (error) {
900
- console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
899
+ console.error(`[SupabaseStorage] Failed to get sessions for ${userId}:`, error);
901
900
  return [];
902
901
  }
903
902
  return data.map((row) => row.session_id);
904
903
  }
905
- async getAllSessionIds() {
904
+ async listAllIds() {
906
905
  const { data, error } = await this.supabase.from("mcp_sessions").select("session_id");
907
906
  if (error) {
908
907
  console.error("[SupabaseStorage] Failed to get all sessions:", error);
@@ -916,7 +915,7 @@ var SupabaseStorageBackend = class {
916
915
  console.error("[SupabaseStorage] Failed to clear sessions:", error);
917
916
  }
918
917
  }
919
- async cleanupExpiredSessions() {
918
+ async cleanupExpired() {
920
919
  const { error } = await this.supabase.from("mcp_sessions").delete().lt("expires_at", (/* @__PURE__ */ new Date()).toISOString());
921
920
  if (error) {
922
921
  console.error("[SupabaseStorage] Failed to cleanup expired sessions:", error);
@@ -926,7 +925,250 @@ var SupabaseStorageBackend = class {
926
925
  }
927
926
  };
928
927
 
928
+ // src/server/storage/neon-backend.ts
929
+ var NeonStorageBackend = class {
930
+ constructor(sql, options = {}) {
931
+ this.sql = sql;
932
+ __publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
933
+ __publicField(this, "tableName");
934
+ const schema = options.schema || "public";
935
+ const table = options.table || "mcp_sessions";
936
+ this.tableName = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(table)}`;
937
+ }
938
+ async init() {
939
+ const [{ exists } = { exists: null }] = await this.sql.query(
940
+ "SELECT to_regclass($1) AS exists",
941
+ [this.tableName.replace(/"/g, "")]
942
+ );
943
+ if (!exists) {
944
+ throw new Error(
945
+ '[NeonStorage] Table "mcp_sessions" not found in your database. Please create it using the Neon storage guide in docs/storage-backends/neon.md.'
946
+ );
947
+ }
948
+ console.log('[mcp-ts][Storage] Neon: "mcp_sessions" table verified.');
949
+ }
950
+ generateSessionId() {
951
+ return generateSessionId();
952
+ }
953
+ quoteIdentifier(identifier) {
954
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
955
+ throw new Error(`Invalid Neon storage identifier: ${identifier}`);
956
+ }
957
+ return `"${identifier}"`;
958
+ }
959
+ mapRowToSessionData(row) {
960
+ return {
961
+ sessionId: row.session_id,
962
+ serverId: row.server_id ?? void 0,
963
+ serverName: row.server_name ?? void 0,
964
+ serverUrl: row.server_url,
965
+ transportType: row.transport_type,
966
+ callbackUrl: row.callback_url,
967
+ createdAt: new Date(row.created_at).getTime(),
968
+ userId: row.user_id,
969
+ headers: decryptObject(row.headers),
970
+ active: row.active ?? false,
971
+ clientInformation: row.client_information,
972
+ tokens: decryptObject(row.tokens),
973
+ codeVerifier: row.code_verifier ?? void 0,
974
+ clientId: row.client_id ?? void 0
975
+ };
976
+ }
977
+ async create(session, ttl) {
978
+ const { sessionId, userId } = session;
979
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
980
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
981
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
982
+ try {
983
+ await this.sql.query(
984
+ `INSERT INTO ${this.tableName} (
985
+ session_id,
986
+ user_id,
987
+ server_id,
988
+ server_name,
989
+ server_url,
990
+ transport_type,
991
+ callback_url,
992
+ created_at,
993
+ headers,
994
+ active,
995
+ client_information,
996
+ tokens,
997
+ code_verifier,
998
+ client_id,
999
+ expires_at
1000
+ ) VALUES (
1001
+ $1, $2, $3, $4, $5, $6, $7, $8,
1002
+ $9, $10, $11, $12, $13, $14, $15
1003
+ )`,
1004
+ [
1005
+ sessionId,
1006
+ userId,
1007
+ session.serverId,
1008
+ session.serverName,
1009
+ session.serverUrl,
1010
+ session.transportType,
1011
+ session.callbackUrl,
1012
+ new Date(session.createdAt || Date.now()).toISOString(),
1013
+ encryptObject(session.headers),
1014
+ session.active ?? false,
1015
+ session.clientInformation,
1016
+ encryptObject(session.tokens),
1017
+ session.codeVerifier,
1018
+ session.clientId,
1019
+ expiresAt
1020
+ ]
1021
+ );
1022
+ } catch (error) {
1023
+ if (error.code === "23505") {
1024
+ throw new Error(`Session ${sessionId} already exists`);
1025
+ }
1026
+ throw new Error(`Failed to create session in Neon: ${error.message}`);
1027
+ }
1028
+ }
1029
+ async update(userId, sessionId, data, ttl) {
1030
+ const currentSession = await this.get(userId, sessionId);
1031
+ if (!currentSession) {
1032
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
1033
+ }
1034
+ const updatedSession = { ...currentSession, ...data };
1035
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
1036
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
1037
+ const updatedRows = await this.sql.query(
1038
+ `UPDATE ${this.tableName}
1039
+ SET
1040
+ server_id = $1,
1041
+ server_name = $2,
1042
+ server_url = $3,
1043
+ transport_type = $4,
1044
+ callback_url = $5,
1045
+ active = $6,
1046
+ headers = $7,
1047
+ client_information = $8,
1048
+ tokens = $9,
1049
+ code_verifier = $10,
1050
+ client_id = $11,
1051
+ expires_at = $12,
1052
+ updated_at = now()
1053
+ WHERE user_id = $13 AND session_id = $14
1054
+ RETURNING id`,
1055
+ [
1056
+ updatedSession.serverId,
1057
+ updatedSession.serverName,
1058
+ updatedSession.serverUrl,
1059
+ updatedSession.transportType,
1060
+ updatedSession.callbackUrl,
1061
+ updatedSession.active ?? false,
1062
+ encryptObject(updatedSession.headers),
1063
+ updatedSession.clientInformation,
1064
+ encryptObject(updatedSession.tokens),
1065
+ updatedSession.codeVerifier,
1066
+ updatedSession.clientId,
1067
+ expiresAt,
1068
+ userId,
1069
+ sessionId
1070
+ ]
1071
+ );
1072
+ if (updatedRows.length === 0) {
1073
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
1074
+ }
1075
+ }
1076
+ async get(userId, sessionId) {
1077
+ try {
1078
+ const rows = await this.sql.query(
1079
+ `SELECT * FROM ${this.tableName} WHERE user_id = $1 AND session_id = $2`,
1080
+ [userId, sessionId]
1081
+ );
1082
+ return rows[0] ? this.mapRowToSessionData(rows[0]) : null;
1083
+ } catch (error) {
1084
+ console.error("[NeonStorage] Failed to get session:", error);
1085
+ return null;
1086
+ }
1087
+ }
1088
+ async list(userId) {
1089
+ try {
1090
+ const rows = await this.sql.query(
1091
+ `SELECT * FROM ${this.tableName} WHERE user_id = $1`,
1092
+ [userId]
1093
+ );
1094
+ return rows.map((row) => this.mapRowToSessionData(row));
1095
+ } catch (error) {
1096
+ console.error(`[NeonStorage] Failed to get session data for ${userId}:`, error);
1097
+ return [];
1098
+ }
1099
+ }
1100
+ async delete(userId, sessionId) {
1101
+ try {
1102
+ await this.sql.query(
1103
+ `DELETE FROM ${this.tableName} WHERE user_id = $1 AND session_id = $2`,
1104
+ [userId, sessionId]
1105
+ );
1106
+ } catch (error) {
1107
+ console.error("[NeonStorage] Failed to remove session:", error);
1108
+ }
1109
+ }
1110
+ async listIds(userId) {
1111
+ try {
1112
+ const rows = await this.sql.query(
1113
+ `SELECT session_id FROM ${this.tableName} WHERE user_id = $1`,
1114
+ [userId]
1115
+ );
1116
+ return rows.map((row) => row.session_id);
1117
+ } catch (error) {
1118
+ console.error(`[NeonStorage] Failed to get sessions for ${userId}:`, error);
1119
+ return [];
1120
+ }
1121
+ }
1122
+ async listAllIds() {
1123
+ try {
1124
+ const rows = await this.sql.query(
1125
+ `SELECT session_id FROM ${this.tableName}`
1126
+ );
1127
+ return rows.map((row) => row.session_id);
1128
+ } catch (error) {
1129
+ console.error("[NeonStorage] Failed to get all sessions:", error);
1130
+ return [];
1131
+ }
1132
+ }
1133
+ async clearAll() {
1134
+ try {
1135
+ await this.sql.query(`DELETE FROM ${this.tableName}`);
1136
+ } catch (error) {
1137
+ console.error("[NeonStorage] Failed to clear sessions:", error);
1138
+ }
1139
+ }
1140
+ async cleanupExpired() {
1141
+ try {
1142
+ await this.sql.query(
1143
+ `DELETE FROM ${this.tableName} WHERE expires_at < $1`,
1144
+ [(/* @__PURE__ */ new Date()).toISOString()]
1145
+ );
1146
+ } catch (error) {
1147
+ console.error("[NeonStorage] Failed to cleanup expired sessions:", error);
1148
+ }
1149
+ }
1150
+ async disconnect() {
1151
+ }
1152
+ };
1153
+
929
1154
  // src/server/storage/index.ts
1155
+ function warnIfNeonConnectionStringIsInsecure(connectionString) {
1156
+ try {
1157
+ const url = new URL(connectionString);
1158
+ const sslMode = url.searchParams.get("sslmode");
1159
+ const channelBinding = url.searchParams.get("channel_binding");
1160
+ if (!sslMode) {
1161
+ console.warn("[mcp-ts][Storage] Neon connection string does not include sslmode. Neon recommends sslmode=verify-full for the strongest certificate verification.");
1162
+ } else if (!["verify-full", "require"].includes(sslMode)) {
1163
+ console.warn(`[mcp-ts][Storage] Neon connection string uses sslmode=${sslMode}. Use sslmode=verify-full or sslmode=require for secure connections.`);
1164
+ }
1165
+ if (!channelBinding) {
1166
+ console.warn("[mcp-ts][Storage] Neon connection string does not include channel_binding=require. Add it when supported by your runtime and connection path.");
1167
+ }
1168
+ } catch {
1169
+ console.warn("[mcp-ts][Storage] Neon connection string could not be parsed for SSL checks.");
1170
+ }
1171
+ }
930
1172
  var storageInstance = null;
931
1173
  var storagePromise = null;
932
1174
  async function initializeStorage(store) {
@@ -983,6 +1225,24 @@ async function createStorage() {
983
1225
  }
984
1226
  }
985
1227
  }
1228
+ if (type === "neon") {
1229
+ const connectionString = process.env.NEON_DATABASE_URL || process.env.DATABASE_URL;
1230
+ if (!connectionString) {
1231
+ console.warn('[mcp-ts][Storage] Explicit selection "neon" requires NEON_DATABASE_URL or DATABASE_URL.');
1232
+ } else {
1233
+ try {
1234
+ const { neon } = await import('@neondatabase/serverless');
1235
+ warnIfNeonConnectionStringIsInsecure(connectionString);
1236
+ const sql = neon(connectionString);
1237
+ console.log('[mcp-ts][Storage] Explicit selection: "neon"');
1238
+ return await initializeStorage(new NeonStorageBackend(sql));
1239
+ } catch (error) {
1240
+ console.error("[mcp-ts][Storage] Failed to initialize Neon:", error.message);
1241
+ console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
1242
+ return await initializeStorage(new MemoryStorageBackend());
1243
+ }
1244
+ }
1245
+ }
986
1246
  if (type === "memory") {
987
1247
  console.log('[mcp-ts][Storage] Explicit selection: "memory"');
988
1248
  return await initializeStorage(new MemoryStorageBackend());
@@ -1021,6 +1281,17 @@ async function createStorage() {
1021
1281
  console.error("[mcp-ts][Storage] Supabase auto-detection failed:", error.message);
1022
1282
  }
1023
1283
  }
1284
+ if (process.env.NEON_DATABASE_URL) {
1285
+ try {
1286
+ const { neon } = await import('@neondatabase/serverless');
1287
+ warnIfNeonConnectionStringIsInsecure(process.env.NEON_DATABASE_URL);
1288
+ const sql = neon(process.env.NEON_DATABASE_URL);
1289
+ console.log('[mcp-ts][Storage] Auto-detection: "neon" (via NEON_DATABASE_URL)');
1290
+ return await initializeStorage(new NeonStorageBackend(sql));
1291
+ } catch (error) {
1292
+ console.error("[mcp-ts][Storage] Neon auto-detection failed:", error.message);
1293
+ }
1294
+ }
1024
1295
  console.log('[mcp-ts][Storage] Defaulting to: "memory"');
1025
1296
  return await initializeStorage(new MemoryStorageBackend());
1026
1297
  }
@@ -1037,7 +1308,7 @@ async function getStorage() {
1037
1308
  storageInstance = await storagePromise;
1038
1309
  return storageInstance;
1039
1310
  }
1040
- var storage = new Proxy({}, {
1311
+ var sessions = new Proxy({}, {
1041
1312
  get(_target, prop) {
1042
1313
  return async (...args) => {
1043
1314
  const instance = await getStorage();
@@ -1053,11 +1324,11 @@ var storage = new Proxy({}, {
1053
1324
  // src/server/mcp/storage-oauth-provider.ts
1054
1325
  var StorageOAuthClientProvider = class {
1055
1326
  /**
1056
- * Creates a new storage-backed OAuth provider
1327
+ * Creates a new session-backed OAuth provider
1057
1328
  * @param options - Provider configuration
1058
1329
  */
1059
1330
  constructor(options) {
1060
- __publicField(this, "identity");
1331
+ __publicField(this, "userId");
1061
1332
  __publicField(this, "serverId");
1062
1333
  __publicField(this, "sessionId");
1063
1334
  __publicField(this, "redirectUrl");
@@ -1070,7 +1341,7 @@ var StorageOAuthClientProvider = class {
1070
1341
  __publicField(this, "_clientId");
1071
1342
  __publicField(this, "onRedirectCallback");
1072
1343
  __publicField(this, "tokenExpiresAt");
1073
- this.identity = options.identity;
1344
+ this.userId = options.userId;
1074
1345
  this.serverId = options.serverId;
1075
1346
  this.sessionId = options.sessionId;
1076
1347
  this.redirectUrl = options.redirectUrl;
@@ -1103,24 +1374,24 @@ var StorageOAuthClientProvider = class {
1103
1374
  this._clientId = clientId_;
1104
1375
  }
1105
1376
  /**
1106
- * Loads OAuth data from storage session
1377
+ * Loads OAuth data from the session store
1107
1378
  * @private
1108
1379
  */
1109
1380
  async getSessionData() {
1110
- const data = await storage.getSession(this.identity, this.sessionId);
1381
+ const data = await sessions.get(this.userId, this.sessionId);
1111
1382
  if (!data) {
1112
1383
  return {};
1113
1384
  }
1114
1385
  return data;
1115
1386
  }
1116
1387
  /**
1117
- * Saves OAuth data to storage
1388
+ * Saves OAuth data to the session store
1118
1389
  * @param data - Partial OAuth data to save
1119
1390
  * @private
1120
1391
  * @throws Error if session doesn't exist (session must be created by controller layer)
1121
1392
  */
1122
1393
  async saveSessionData(data) {
1123
- await storage.updateSession(this.identity, this.sessionId, data);
1394
+ await sessions.update(this.userId, this.sessionId, data);
1124
1395
  }
1125
1396
  /**
1126
1397
  * Retrieves stored OAuth client information
@@ -1168,7 +1439,7 @@ var StorageOAuthClientProvider = class {
1168
1439
  return this.sessionId;
1169
1440
  }
1170
1441
  async checkState(_state) {
1171
- const data = await storage.getSession(this.identity, this.sessionId);
1442
+ const data = await sessions.get(this.userId, this.sessionId);
1172
1443
  if (!data) {
1173
1444
  return { valid: false, error: "Session not found" };
1174
1445
  }
@@ -1184,7 +1455,7 @@ var StorageOAuthClientProvider = class {
1184
1455
  }
1185
1456
  async invalidateCredentials(scope) {
1186
1457
  if (scope === "all") {
1187
- await storage.removeSession(this.identity, this.sessionId);
1458
+ await sessions.delete(this.userId, this.sessionId);
1188
1459
  } else {
1189
1460
  const updates = {};
1190
1461
  if (scope === "client") {
@@ -1310,14 +1581,14 @@ var RpcErrorCodes = {
1310
1581
  var MCPClient = class _MCPClient {
1311
1582
  /**
1312
1583
  * Creates a new MCP client instance
1313
- * Can be initialized with minimal options (identity + sessionId) for session restoration
1584
+ * Can be initialized with minimal options (userId + sessionId) for session restoration
1314
1585
  * @param options - Client configuration options
1315
1586
  */
1316
1587
  constructor(options) {
1317
1588
  __publicField(this, "client", null);
1318
1589
  __publicField(this, "oauthProvider", null);
1319
1590
  __publicField(this, "transport", null);
1320
- __publicField(this, "identity");
1591
+ __publicField(this, "userId");
1321
1592
  __publicField(this, "serverId");
1322
1593
  __publicField(this, "sessionId");
1323
1594
  __publicField(this, "serverName");
@@ -1344,7 +1615,7 @@ var MCPClient = class _MCPClient {
1344
1615
  this.serverName = options.serverName;
1345
1616
  this.callbackUrl = options.callbackUrl;
1346
1617
  this.onRedirect = options.onRedirect;
1347
- this.identity = options.identity;
1618
+ this.userId = options.userId;
1348
1619
  this.serverId = options.serverId;
1349
1620
  this.sessionId = options.sessionId;
1350
1621
  this.transportType = options.transportType;
@@ -1483,7 +1754,7 @@ var MCPClient = class _MCPClient {
1483
1754
  this.emitStateChange("INITIALIZING");
1484
1755
  this.emitProgress("Loading session configuration...");
1485
1756
  if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
1486
- const sessionData = await storage.getSession(this.identity, this.sessionId);
1757
+ const sessionData = await sessions.get(this.userId, this.sessionId);
1487
1758
  if (!sessionData) {
1488
1759
  throw new Error(`Session not found: ${this.sessionId}`);
1489
1760
  }
@@ -1502,7 +1773,7 @@ var MCPClient = class _MCPClient {
1502
1773
  throw new Error("serverId required for OAuth provider initialization");
1503
1774
  }
1504
1775
  this.oauthProvider = new StorageOAuthClientProvider({
1505
- identity: this.identity,
1776
+ userId: this.userId,
1506
1777
  serverId: this.serverId,
1507
1778
  sessionId: this.sessionId,
1508
1779
  redirectUrl: this.callbackUrl,
@@ -1536,18 +1807,18 @@ var MCPClient = class _MCPClient {
1536
1807
  }
1537
1808
  );
1538
1809
  }
1539
- const existingSession = await storage.getSession(this.identity, this.sessionId);
1810
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1540
1811
  if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
1541
1812
  this.createdAt = Date.now();
1542
1813
  console.log(`[MCPClient] Creating initial session ${this.sessionId} for OAuth flow`);
1543
- await storage.createSession({
1814
+ await sessions.create({
1544
1815
  sessionId: this.sessionId,
1545
- identity: this.identity,
1816
+ userId: this.userId,
1546
1817
  serverId: this.serverId,
1547
1818
  serverName: this.serverName,
1548
1819
  serverUrl: this.serverUrl,
1549
1820
  callbackUrl: this.callbackUrl,
1550
- transportType: this.transportType || "streamable_http",
1821
+ transportType: this.transportType || "streamable-http",
1551
1822
  headers: this.headers,
1552
1823
  createdAt: this.createdAt,
1553
1824
  active: false
@@ -1555,7 +1826,7 @@ var MCPClient = class _MCPClient {
1555
1826
  }
1556
1827
  }
1557
1828
  /**
1558
- * Saves current session state to storage
1829
+ * Saves current session state to the session store
1559
1830
  * Creates new session if it doesn't exist, updates if it does
1560
1831
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
1561
1832
  * @param active - Session status marker used to avoid unnecessary TTL rewrites
@@ -1567,21 +1838,21 @@ var MCPClient = class _MCPClient {
1567
1838
  }
1568
1839
  const sessionData = {
1569
1840
  sessionId: this.sessionId,
1570
- identity: this.identity,
1841
+ userId: this.userId,
1571
1842
  serverId: this.serverId,
1572
1843
  serverName: this.serverName,
1573
1844
  serverUrl: this.serverUrl,
1574
1845
  callbackUrl: this.callbackUrl,
1575
- transportType: this.transportType || "streamable_http",
1846
+ transportType: this.transportType || "streamable-http",
1576
1847
  headers: this.headers,
1577
1848
  createdAt: this.createdAt || Date.now(),
1578
1849
  active
1579
1850
  };
1580
- const existingSession = await storage.getSession(this.identity, this.sessionId);
1851
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1581
1852
  if (existingSession) {
1582
- await storage.updateSession(this.identity, this.sessionId, sessionData, ttl);
1853
+ await sessions.update(this.userId, this.sessionId, sessionData, ttl);
1583
1854
  } else {
1584
- await storage.createSession(sessionData, ttl);
1855
+ await sessions.create(sessionData, ttl);
1585
1856
  }
1586
1857
  }
1587
1858
  /**
@@ -1590,7 +1861,7 @@ var MCPClient = class _MCPClient {
1590
1861
  * @private
1591
1862
  */
1592
1863
  async tryConnect() {
1593
- const transportsToTry = this.transportType ? [this.transportType] : ["streamable_http", "sse"];
1864
+ const transportsToTry = this.transportType ? [this.transportType] : ["streamable-http", "sse"];
1594
1865
  let lastError;
1595
1866
  for (const currentType of transportsToTry) {
1596
1867
  const isLastAttempt = currentType === transportsToTry[transportsToTry.length - 1];
@@ -1662,7 +1933,7 @@ var MCPClient = class _MCPClient {
1662
1933
  this.emitError(message, "auth");
1663
1934
  this.emitStateChange("FAILED");
1664
1935
  try {
1665
- await storage.removeSession(this.identity, this.sessionId);
1936
+ await sessions.delete(this.userId, this.sessionId);
1666
1937
  } catch {
1667
1938
  }
1668
1939
  throw new Error(message);
@@ -1688,9 +1959,9 @@ var MCPClient = class _MCPClient {
1688
1959
  this.emitError(errorMessage, "connection");
1689
1960
  this.emitStateChange("FAILED");
1690
1961
  try {
1691
- const existingSession = await storage.getSession(this.identity, this.sessionId);
1962
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1692
1963
  if (!existingSession || existingSession.active !== true) {
1693
- await storage.removeSession(this.identity, this.sessionId);
1964
+ await sessions.delete(this.userId, this.sessionId);
1694
1965
  }
1695
1966
  } catch {
1696
1967
  }
@@ -1714,7 +1985,7 @@ var MCPClient = class _MCPClient {
1714
1985
  this.emitStateChange("FAILED");
1715
1986
  throw new Error(error);
1716
1987
  }
1717
- const transportsToTry = this.transportType ? [this.transportType] : ["streamable_http", "sse"];
1988
+ const transportsToTry = this.transportType ? [this.transportType] : ["streamable-http", "sse"];
1718
1989
  let lastError;
1719
1990
  let tokensExchanged = false;
1720
1991
  let authenticatedStateEmitted = false;
@@ -2038,7 +2309,7 @@ var MCPClient = class _MCPClient {
2038
2309
  },
2039
2310
  { capabilities: {} }
2040
2311
  );
2041
- const tt = this.transportType || "streamable_http";
2312
+ const tt = this.transportType || "streamable-http";
2042
2313
  this.transport = this.getTransport(tt);
2043
2314
  await this.client.connect(this.transport);
2044
2315
  }
@@ -2055,7 +2326,7 @@ var MCPClient = class _MCPClient {
2055
2326
  if (this.oauthProvider) {
2056
2327
  await this.oauthProvider.invalidateCredentials("all");
2057
2328
  }
2058
- await storage.removeSession(this.identity, this.sessionId);
2329
+ await sessions.delete(this.userId, this.sessionId);
2059
2330
  this.disconnect();
2060
2331
  }
2061
2332
  /**
@@ -2123,10 +2394,10 @@ var MCPClient = class _MCPClient {
2123
2394
  }
2124
2395
  /**
2125
2396
  * Gets the transport type being used
2126
- * @returns Transport type (defaults to 'streamable_http')
2397
+ * @returns Transport type (defaults to 'streamable-http')
2127
2398
  */
2128
2399
  getTransportType() {
2129
- return this.transportType || "streamable_http";
2400
+ return this.transportType || "streamable-http";
2130
2401
  }
2131
2402
  /**
2132
2403
  * Gets the human-readable server name
@@ -2153,25 +2424,25 @@ var MCPClient = class _MCPClient {
2153
2424
  * Gets MCP server configuration for all active user sessions
2154
2425
  * Loads sessions from Redis, validates OAuth tokens, refreshes if expired
2155
2426
  * Returns ready-to-use configuration with valid auth headers
2156
- * @param identity - User ID to fetch sessions for
2427
+ * @param userId - User ID to fetch sessions for
2157
2428
  * @returns Object keyed by sanitized server labels containing transport, url, headers, etc.
2158
2429
  * @static
2159
2430
  */
2160
- static async getMcpServerConfig(identity) {
2431
+ static async getMcpServerConfig(userId) {
2161
2432
  const mcpConfig = {};
2162
- const sessions = await storage.getIdentitySessionsData(identity);
2433
+ const sessionList = await sessions.list(userId);
2163
2434
  await Promise.all(
2164
- sessions.map(async (sessionData) => {
2435
+ sessionList.map(async (sessionData) => {
2165
2436
  const { sessionId } = sessionData;
2166
2437
  try {
2167
2438
  if (!sessionData.serverId || !sessionData.transportType || !sessionData.serverUrl || !sessionData.callbackUrl) {
2168
- await storage.removeSession(identity, sessionId);
2439
+ await sessions.delete(userId, sessionId);
2169
2440
  return;
2170
2441
  }
2171
2442
  let headers;
2172
2443
  try {
2173
2444
  const client = new _MCPClient({
2174
- identity,
2445
+ userId,
2175
2446
  sessionId,
2176
2447
  serverId: sessionData.serverId,
2177
2448
  serverUrl: sessionData.serverUrl,
@@ -2204,7 +2475,7 @@ var MCPClient = class _MCPClient {
2204
2475
  ...headers && { headers }
2205
2476
  };
2206
2477
  } catch (error) {
2207
- await storage.removeSession(identity, sessionId);
2478
+ await sessions.delete(userId, sessionId);
2208
2479
  console.warn(`[MCP] Failed to process session ${sessionId}:`, error);
2209
2480
  }
2210
2481
  })
@@ -2220,18 +2491,18 @@ var DEFAULT_RETRY_DELAY_MS = 1e3;
2220
2491
  var CONNECTION_BATCH_SIZE = 5;
2221
2492
  var MultiSessionClient = class {
2222
2493
  /**
2223
- * Creates a new MultiSessionClient for the given user identity.
2494
+ * Creates a new MultiSessionClient for the given user userId.
2224
2495
  *
2225
- * @param identity - A unique string identifying the user (e.g. user ID or email).
2496
+ * @param userId - A unique string identifying the user (e.g. user ID or email).
2226
2497
  * @param options - Optional tuning for connection timeout, retry count, and retry delay.
2227
2498
  * Falls back to sensible defaults if not provided.
2228
2499
  */
2229
- constructor(identity, options = {}) {
2500
+ constructor(userId, options = {}) {
2230
2501
  __publicField(this, "clients", []);
2231
- __publicField(this, "identity");
2502
+ __publicField(this, "userId");
2232
2503
  __publicField(this, "options");
2233
2504
  __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
2234
- this.identity = identity;
2505
+ this.userId = userId;
2235
2506
  this.options = {
2236
2507
  timeout: DEFAULT_TIMEOUT_MS,
2237
2508
  maxRetries: DEFAULT_MAX_RETRIES,
@@ -2240,7 +2511,7 @@ var MultiSessionClient = class {
2240
2511
  };
2241
2512
  }
2242
2513
  /**
2243
- * Fetches all sessions for this identity from storage and returns only the
2514
+ * Fetches all sessions for this userId from storage and returns only the
2244
2515
  * ones that are ready to connect.
2245
2516
  *
2246
2517
  * A session is considered connectable when:
@@ -2253,8 +2524,8 @@ var MultiSessionClient = class {
2253
2524
  * for backwards compatibility.
2254
2525
  */
2255
2526
  async getActiveSessions() {
2256
- const sessions = await storage.getIdentitySessionsData(this.identity);
2257
- const valid = sessions.filter(
2527
+ const sessionList = await sessions.list(this.userId);
2528
+ const valid = sessionList.filter(
2258
2529
  (s) => s.serverId && s.serverUrl && s.callbackUrl && s.active !== false
2259
2530
  // exclude OAuth-pending / failed sessions
2260
2531
  );
@@ -2267,9 +2538,9 @@ var MultiSessionClient = class {
2267
2538
  * has many active MCP sessions (e.g. 20+ servers). Within each batch, sessions
2268
2539
  * are connected concurrently using `Promise.all` for speed.
2269
2540
  */
2270
- async connectInBatches(sessions) {
2271
- for (let i = 0; i < sessions.length; i += CONNECTION_BATCH_SIZE) {
2272
- const batch = sessions.slice(i, i + CONNECTION_BATCH_SIZE);
2541
+ async connectInBatches(sessions2) {
2542
+ for (let i = 0; i < sessions2.length; i += CONNECTION_BATCH_SIZE) {
2543
+ const batch = sessions2.slice(i, i + CONNECTION_BATCH_SIZE);
2273
2544
  await Promise.all(batch.map((session) => this.connectSession(session)));
2274
2545
  }
2275
2546
  }
@@ -2320,7 +2591,7 @@ var MultiSessionClient = class {
2320
2591
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
2321
2592
  try {
2322
2593
  const client = new MCPClient({
2323
- identity: this.identity,
2594
+ userId: this.userId,
2324
2595
  sessionId: session.sessionId,
2325
2596
  serverId: session.serverId,
2326
2597
  serverUrl: session.serverUrl,
@@ -2352,7 +2623,7 @@ var MultiSessionClient = class {
2352
2623
  console.error(`[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`, lastError);
2353
2624
  }
2354
2625
  /**
2355
- * The main entry point. Fetches all active sessions for this identity from
2626
+ * The main entry point. Fetches all active sessions for this userId from
2356
2627
  * storage and establishes connections to all of them in batches.
2357
2628
  *
2358
2629
  * Call this once after creating the client. On traditional servers, you can
@@ -2360,8 +2631,8 @@ var MultiSessionClient = class {
2360
2631
  * re-fetching and re-connecting on every request.
2361
2632
  */
2362
2633
  async connect() {
2363
- const sessions = await this.getActiveSessions();
2364
- await this.connectInBatches(sessions);
2634
+ const sessions2 = await this.getActiveSessions();
2635
+ await this.connectInBatches(sessions2);
2365
2636
  }
2366
2637
  /**
2367
2638
  * Returns all currently connected `MCPClient` instances.
@@ -2416,11 +2687,11 @@ var SSEConnectionManager = class {
2416
2687
  constructor(options, sendEvent) {
2417
2688
  this.options = options;
2418
2689
  this.sendEvent = sendEvent;
2419
- __publicField(this, "identity");
2690
+ __publicField(this, "userId");
2420
2691
  __publicField(this, "clients", /* @__PURE__ */ new Map());
2421
2692
  __publicField(this, "heartbeatTimer");
2422
2693
  __publicField(this, "isActive", true);
2423
- this.identity = options.identity;
2694
+ this.userId = options.userId;
2424
2695
  this.startHeartbeat();
2425
2696
  }
2426
2697
  /**
@@ -2460,8 +2731,8 @@ var SSEConnectionManager = class {
2460
2731
  try {
2461
2732
  let result;
2462
2733
  switch (request.method) {
2463
- case "getSessions":
2464
- result = await this.getSessions();
2734
+ case "listSessions":
2735
+ result = await this.listSessions();
2465
2736
  break;
2466
2737
  case "connect":
2467
2738
  result = await this.connect(request.params);
@@ -2475,8 +2746,8 @@ var SSEConnectionManager = class {
2475
2746
  case "callTool":
2476
2747
  result = await this.callTool(request.params);
2477
2748
  break;
2478
- case "restoreSession":
2479
- result = await this.restoreSession(request.params);
2749
+ case "getSession":
2750
+ result = await this.getSession(request.params);
2480
2751
  break;
2481
2752
  case "finishAuth":
2482
2753
  result = await this.finishAuth(request.params);
@@ -2515,12 +2786,12 @@ var SSEConnectionManager = class {
2515
2786
  }
2516
2787
  }
2517
2788
  /**
2518
- * Get all sessions for the current identity
2789
+ * Get all sessions for the current userId
2519
2790
  */
2520
- async getSessions() {
2521
- const sessions = await storage.getIdentitySessionsData(this.identity);
2791
+ async listSessions() {
2792
+ const sessionList = await sessions.list(this.userId);
2522
2793
  return {
2523
- sessions: sessions.map((s) => ({
2794
+ sessions: sessionList.map((s) => ({
2524
2795
  sessionId: s.sessionId,
2525
2796
  serverId: s.serverId,
2526
2797
  serverName: s.serverName,
@@ -2537,14 +2808,14 @@ var SSEConnectionManager = class {
2537
2808
  async connect(params) {
2538
2809
  const { serverName, serverUrl, callbackUrl, transportType } = params;
2539
2810
  const headers = normalizeHeaders(params.headers);
2540
- const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await storage.generateSessionId();
2541
- const existingSessions = await storage.getIdentitySessionsData(this.identity);
2811
+ const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await sessions.generateSessionId();
2812
+ const existingSessions = await sessions.list(this.userId);
2542
2813
  const duplicate = existingSessions.find(
2543
2814
  (s) => s.serverId === serverId || s.serverUrl === serverUrl
2544
2815
  );
2545
2816
  if (duplicate) {
2546
2817
  if (duplicate.active === false) {
2547
- await this.restoreSession({ sessionId: duplicate.sessionId });
2818
+ await this.getSession({ sessionId: duplicate.sessionId });
2548
2819
  return {
2549
2820
  sessionId: duplicate.sessionId,
2550
2821
  success: true
@@ -2552,11 +2823,11 @@ var SSEConnectionManager = class {
2552
2823
  }
2553
2824
  throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
2554
2825
  }
2555
- const sessionId = await storage.generateSessionId();
2826
+ const sessionId = await sessions.generateSessionId();
2556
2827
  try {
2557
2828
  const clientMetadata = await this.getResolvedClientMetadata();
2558
2829
  const client = new MCPClient({
2559
- identity: this.identity,
2830
+ userId: this.userId,
2560
2831
  sessionId,
2561
2832
  serverId,
2562
2833
  serverName,
@@ -2611,7 +2882,7 @@ var SSEConnectionManager = class {
2611
2882
  client.disconnect();
2612
2883
  this.clients.delete(sessionId);
2613
2884
  } else {
2614
- await storage.removeSession(this.identity, sessionId);
2885
+ await sessions.delete(this.userId, sessionId);
2615
2886
  }
2616
2887
  return { success: true };
2617
2888
  }
@@ -2623,12 +2894,12 @@ var SSEConnectionManager = class {
2623
2894
  if (existing) {
2624
2895
  return existing;
2625
2896
  }
2626
- const session = await storage.getSession(this.identity, sessionId);
2897
+ const session = await sessions.get(this.userId, sessionId);
2627
2898
  if (!session) {
2628
2899
  throw new Error("Session not found");
2629
2900
  }
2630
2901
  const client = new MCPClient({
2631
- identity: this.identity,
2902
+ userId: this.userId,
2632
2903
  sessionId,
2633
2904
  // These fields are optional in MCPClient, but when rehydrating a known
2634
2905
  // stored session on the server we pass them explicitly to preserve the
@@ -2675,9 +2946,9 @@ var SSEConnectionManager = class {
2675
2946
  /**
2676
2947
  * Restore and validate an existing session
2677
2948
  */
2678
- async restoreSession(params) {
2949
+ async getSession(params) {
2679
2950
  const { sessionId } = params;
2680
- const session = await storage.getSession(this.identity, sessionId);
2951
+ const session = await sessions.get(this.userId, sessionId);
2681
2952
  if (!session) {
2682
2953
  throw new Error("Session not found");
2683
2954
  }
@@ -2694,7 +2965,7 @@ var SSEConnectionManager = class {
2694
2965
  try {
2695
2966
  const clientMetadata = await this.getResolvedClientMetadata();
2696
2967
  const client = new MCPClient({
2697
- identity: this.identity,
2968
+ userId: this.userId,
2698
2969
  sessionId,
2699
2970
  // These fields are optional in MCPClient, but when rehydrating a known
2700
2971
  // stored session on the server we pass them explicitly to preserve the
@@ -2731,13 +3002,13 @@ var SSEConnectionManager = class {
2731
3002
  */
2732
3003
  async finishAuth(params) {
2733
3004
  const { sessionId, code } = params;
2734
- const session = await storage.getSession(this.identity, sessionId);
3005
+ const session = await sessions.get(this.userId, sessionId);
2735
3006
  if (!session) {
2736
3007
  throw new Error("Session not found");
2737
3008
  }
2738
3009
  try {
2739
3010
  const client = new MCPClient({
2740
- identity: this.identity,
3011
+ userId: this.userId,
2741
3012
  sessionId,
2742
3013
  // These fields are optional in MCPClient, but when rehydrating a known
2743
3014
  // stored session on the server we pass them explicitly to preserve the
@@ -2748,7 +3019,7 @@ var SSEConnectionManager = class {
2748
3019
  serverUrl: session.serverUrl,
2749
3020
  callbackUrl: session.callbackUrl,
2750
3021
  // NOTE: transportType is intentionally omitted here.
2751
- // The session's stored transportType is a placeholder ('streamable_http')
3022
+ // The session's stored transportType is a placeholder ('streamable-http')
2752
3023
  // set before transport negotiation. Omitting it lets MCPClient auto-negotiate
2753
3024
  // (try streamable_http → SSE fallback), which is critical for servers like
2754
3025
  // Neon that only support SSE transport.
@@ -2870,18 +3141,21 @@ function writeSSEEvent(res, event, data) {
2870
3141
  // src/server/handlers/nextjs-handler.ts
2871
3142
  function createNextMcpHandler(options = {}) {
2872
3143
  const {
2873
- getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
3144
+ getUserId = (request) => request.headers.get("x-mcp-user-id"),
2874
3145
  getAuthToken = (request) => {
2875
- const url = new URL(request.url);
2876
- return url.searchParams.get("token") || request.headers.get("authorization");
3146
+ const authHeader = request.headers.get("authorization");
3147
+ if (authHeader?.toLowerCase().startsWith("bearer ")) {
3148
+ return authHeader.slice(7);
3149
+ }
3150
+ return authHeader;
2877
3151
  },
2878
3152
  authenticate = () => true,
2879
3153
  heartbeatInterval = 3e4,
2880
3154
  clientDefaults,
2881
3155
  getClientMetadata
2882
3156
  } = options;
2883
- const toManagerOptions = (identity, resolvedClientMetadata) => ({
2884
- identity,
3157
+ const toManagerOptions = (userId, resolvedClientMetadata) => ({
3158
+ userId,
2885
3159
  heartbeatInterval,
2886
3160
  clientDefaults: resolvedClientMetadata
2887
3161
  });
@@ -2900,13 +3174,13 @@ function createNextMcpHandler(options = {}) {
2900
3174
  );
2901
3175
  }
2902
3176
  async function POST(request) {
2903
- const identity = getIdentity(request);
3177
+ const userId = getUserId(request);
2904
3178
  const authToken = getAuthToken(request);
2905
3179
  const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
2906
- if (!identity) {
2907
- return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
3180
+ if (!userId) {
3181
+ return Response.json({ error: { code: "MISSING_userId", message: "Missing userId" } }, { status: 400 });
2908
3182
  }
2909
- const isAuthorized = await authenticate(identity, authToken);
3183
+ const isAuthorized = await authenticate(userId, authToken);
2910
3184
  if (!isAuthorized) {
2911
3185
  return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
2912
3186
  }
@@ -2928,7 +3202,7 @@ function createNextMcpHandler(options = {}) {
2928
3202
  const resolvedClientMetadata = await resolveClientMetadata(request);
2929
3203
  if (!acceptsEventStream) {
2930
3204
  const manager2 = new SSEConnectionManager(
2931
- toManagerOptions(identity, resolvedClientMetadata),
3205
+ toManagerOptions(userId, resolvedClientMetadata),
2932
3206
  () => {
2933
3207
  }
2934
3208
  );
@@ -2954,7 +3228,7 @@ data: ${JSON.stringify(data)}
2954
3228
  });
2955
3229
  };
2956
3230
  const manager = new SSEConnectionManager(
2957
- toManagerOptions(identity, resolvedClientMetadata),
3231
+ toManagerOptions(userId, resolvedClientMetadata),
2958
3232
  (event) => {
2959
3233
  if (isRpcResponseEvent(event)) {
2960
3234
  sendSSE("rpc-response", event);
@@ -2997,7 +3271,7 @@ data: ${JSON.stringify(data)}
2997
3271
  } catch (error) {
2998
3272
  const err = error instanceof Error ? error : new Error("Unknown error");
2999
3273
  console.error("[MCP Next Handler] Failed to handle RPC", {
3000
- identity,
3274
+ userId,
3001
3275
  message: err.message,
3002
3276
  stack: err.stack,
3003
3277
  rawBody: rawBody.slice(0, 500)
@@ -3016,6 +3290,6 @@ data: ${JSON.stringify(data)}
3016
3290
  return { GET, POST };
3017
3291
  }
3018
3292
 
3019
- export { MCPClient, MultiSessionClient, SSEConnectionManager, StorageOAuthClientProvider, UnauthorizedError, createNextMcpHandler, createSSEHandler, sanitizeServerLabel, storage };
3293
+ export { MCPClient, MultiSessionClient, SSEConnectionManager, StorageOAuthClientProvider, UnauthorizedError, createNextMcpHandler, createSSEHandler, sanitizeServerLabel, sessions };
3020
3294
  //# sourceMappingURL=index.mjs.map
3021
3295
  //# sourceMappingURL=index.mjs.map