@mcp-ts/sdk 1.6.2 → 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 (113) 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 +15 -19
  41. package/dist/client/react.js.map +1 -1
  42. package/dist/client/react.mjs +15 -19
  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/use-mcp-apps.tsx +1 -1
  90. package/src/client/react/use-mcp.ts +11 -11
  91. package/src/client/vue/use-mcp.ts +10 -10
  92. package/src/server/handlers/nextjs-handler.ts +18 -15
  93. package/src/server/handlers/sse-handler.ts +29 -29
  94. package/src/server/index.ts +1 -1
  95. package/src/server/mcp/multi-session-client.ts +17 -17
  96. package/src/server/mcp/oauth-client.ts +37 -37
  97. package/src/server/mcp/storage-oauth-provider.ts +17 -17
  98. package/src/server/storage/file-backend.ts +25 -25
  99. package/src/server/storage/index.ts +67 -10
  100. package/src/server/storage/memory-backend.ts +34 -34
  101. package/src/server/storage/neon-backend.ts +281 -0
  102. package/src/server/storage/redis-backend.ts +64 -64
  103. package/src/server/storage/sqlite-backend.ts +33 -33
  104. package/src/server/storage/supabase-backend.ts +23 -24
  105. package/src/server/storage/types.ts +18 -21
  106. package/src/shared/errors.ts +1 -1
  107. package/src/shared/index.ts +1 -2
  108. package/src/shared/meta-tools.ts +4 -6
  109. package/src/shared/schema-compressor.ts +2 -42
  110. package/src/shared/tool-index.ts +89 -84
  111. package/src/shared/tool-router.ts +0 -24
  112. package/src/shared/types.ts +4 -4
  113. /package/{supabase/migrations → migrations/supabase}/20260421010000_add_session_cleanup_cron.sql +0 -0
package/dist/index.mjs CHANGED
@@ -152,35 +152,35 @@ var RedisStorageBackend = class {
152
152
  this.redis = redis2;
153
153
  __publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
154
154
  __publicField(this, "KEY_PREFIX", "mcp:session:");
155
- __publicField(this, "IDENTITY_KEY_PREFIX", "mcp:identity:");
156
- __publicField(this, "IDENTITY_KEY_SUFFIX", ":sessions");
155
+ __publicField(this, "USER_ID_KEY_PREFIX", "mcp:userId:");
156
+ __publicField(this, "USER_ID_KEY_SUFFIX", ":sessions");
157
157
  }
158
158
  async init() {
159
159
  try {
160
160
  await this.redis.ping();
161
161
  console.log("[mcp-ts][Storage] Redis: \u2713 Connected to server.");
162
162
  } catch (error) {
163
- throw new Error(`[RedisStorage] Failed to connect to Redis: ${error.message}`);
163
+ throw new Error(`[RedisStorageBackend] Failed to connect to Redis: ${error.message}`);
164
164
  }
165
165
  }
166
166
  /**
167
167
  * Generates Redis key for a specific session
168
168
  * @private
169
169
  */
170
- getSessionKey(identity, sessionId) {
171
- return `${this.KEY_PREFIX}${identity}:${sessionId}`;
170
+ getSessionKey(userId, sessionId) {
171
+ return `${this.KEY_PREFIX}${userId}:${sessionId}`;
172
172
  }
173
173
  /**
174
- * Generates Redis key for tracking all sessions for an identity
174
+ * Generates Redis key for tracking all sessions for a user
175
175
  * @private
176
176
  */
177
- getIdentityKey(identity) {
178
- return `${this.IDENTITY_KEY_PREFIX}${identity}${this.IDENTITY_KEY_SUFFIX}`;
177
+ getUserIdKey(userId) {
178
+ return `${this.USER_ID_KEY_PREFIX}${userId}${this.USER_ID_KEY_SUFFIX}`;
179
179
  }
180
- parseIdentityFromKey(identityKey) {
181
- return identityKey.slice(
182
- this.IDENTITY_KEY_PREFIX.length,
183
- identityKey.length - this.IDENTITY_KEY_SUFFIX.length
180
+ parseUserIdFromKey(userIdKey) {
181
+ return userIdKey.slice(
182
+ this.USER_ID_KEY_PREFIX.length,
183
+ userIdKey.length - this.USER_ID_KEY_SUFFIX.length
184
184
  );
185
185
  }
186
186
  async scanKeys(pattern) {
@@ -199,7 +199,7 @@ var RedisStorageBackend = class {
199
199
  }
200
200
  } while (cursor !== "0");
201
201
  } catch (error) {
202
- console.warn("[RedisStorage] SCAN failed, falling back to KEYS:", error);
202
+ console.warn("[RedisStorageBackend] SCAN failed, falling back to KEYS:", error);
203
203
  return await this.redis.keys(pattern);
204
204
  }
205
205
  return Array.from(keys);
@@ -207,11 +207,11 @@ var RedisStorageBackend = class {
207
207
  generateSessionId() {
208
208
  return generateSessionId();
209
209
  }
210
- async createSession(session, ttl) {
211
- const { sessionId, identity } = session;
212
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
213
- const sessionKey = this.getSessionKey(identity, sessionId);
214
- const identityKey = this.getIdentityKey(identity);
210
+ async create(session, ttl) {
211
+ const { sessionId, userId } = session;
212
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
213
+ const sessionKey = this.getSessionKey(userId, sessionId);
214
+ const userIdKey = this.getUserIdKey(userId);
215
215
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
216
216
  const result = await this.redis.set(
217
217
  sessionKey,
@@ -223,10 +223,10 @@ var RedisStorageBackend = class {
223
223
  if (result !== "OK") {
224
224
  throw new Error(`Session ${sessionId} already exists`);
225
225
  }
226
- await this.redis.sadd(identityKey, sessionId);
226
+ await this.redis.sadd(userIdKey, sessionId);
227
227
  }
228
- async updateSession(identity, sessionId, data, ttl) {
229
- const sessionKey = this.getSessionKey(identity, sessionId);
228
+ async update(userId, sessionId, data, ttl) {
229
+ const sessionKey = this.getSessionKey(userId, sessionId);
230
230
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
231
231
  const script = `
232
232
  local currentStr = redis.call("GET", KEYS[1])
@@ -252,62 +252,62 @@ var RedisStorageBackend = class {
252
252
  effectiveTtl
253
253
  );
254
254
  if (result === 0) {
255
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
255
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
256
256
  }
257
257
  }
258
- async getSession(identity, sessionId) {
258
+ async get(userId, sessionId) {
259
259
  try {
260
- const sessionKey = this.getSessionKey(identity, sessionId);
260
+ const sessionKey = this.getSessionKey(userId, sessionId);
261
261
  const sessionDataStr = await this.redis.get(sessionKey);
262
262
  if (!sessionDataStr) {
263
263
  return null;
264
264
  }
265
- const sessionData = JSON.parse(sessionDataStr);
266
- return sessionData;
265
+ const Session = JSON.parse(sessionDataStr);
266
+ return Session;
267
267
  } catch (error) {
268
- console.error("[RedisStorage] Failed to get session:", error);
268
+ console.error("[RedisStorageBackend] Failed to get session:", error);
269
269
  return null;
270
270
  }
271
271
  }
272
- async getIdentityMcpSessions(identity) {
273
- const sessions = await this.getIdentitySessionsData(identity);
274
- return sessions.map((session) => session.sessionId);
272
+ async listIds(userId) {
273
+ const sessions2 = await this.list(userId);
274
+ return sessions2.map((session) => session.sessionId);
275
275
  }
276
- async getIdentitySessionsData(identity) {
276
+ async list(userId) {
277
277
  try {
278
- const identityKey = this.getIdentityKey(identity);
279
- const sessionIds = await this.redis.smembers(identityKey);
278
+ const userIdKey = this.getUserIdKey(userId);
279
+ const sessionIds = await this.redis.smembers(userIdKey);
280
280
  if (sessionIds.length === 0) return [];
281
281
  const results = await Promise.all(
282
282
  sessionIds.map(async (sessionId) => {
283
- const data = await this.redis.get(this.getSessionKey(identity, sessionId));
283
+ const data = await this.redis.get(this.getSessionKey(userId, sessionId));
284
284
  return data ? JSON.parse(data) : null;
285
285
  })
286
286
  );
287
287
  const staleSessionIds = sessionIds.filter((_, index) => results[index] === null);
288
288
  if (staleSessionIds.length > 0) {
289
- await this.redis.srem(identityKey, ...staleSessionIds);
289
+ await this.redis.srem(userIdKey, ...staleSessionIds);
290
290
  }
291
291
  return results.filter((session) => session !== null);
292
292
  } catch (error) {
293
- console.error(`[RedisStorage] Failed to get session data for ${identity}:`, error);
293
+ console.error(`[RedisStorageBackend] Failed to get session data for ${userId}:`, error);
294
294
  return [];
295
295
  }
296
296
  }
297
- async removeSession(identity, sessionId) {
297
+ async delete(userId, sessionId) {
298
298
  try {
299
- const sessionKey = this.getSessionKey(identity, sessionId);
300
- const identityKey = this.getIdentityKey(identity);
301
- await this.redis.srem(identityKey, sessionId);
299
+ const sessionKey = this.getSessionKey(userId, sessionId);
300
+ const userIdKey = this.getUserIdKey(userId);
301
+ await this.redis.srem(userIdKey, sessionId);
302
302
  await this.redis.del(sessionKey);
303
303
  } catch (error) {
304
- console.error("[RedisStorage] Failed to remove session:", error);
304
+ console.error("[RedisStorageBackend] Failed to remove session:", error);
305
305
  }
306
306
  }
307
- async getAllSessionIds() {
307
+ async listAllIds() {
308
308
  try {
309
309
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
310
- const sessions = await Promise.all(
310
+ const sessions2 = await Promise.all(
311
311
  keys.map(async (key) => {
312
312
  const data = await this.redis.get(key);
313
313
  if (!data) {
@@ -316,60 +316,60 @@ var RedisStorageBackend = class {
316
316
  try {
317
317
  return JSON.parse(data).sessionId;
318
318
  } catch (error) {
319
- console.error("[RedisStorage] Failed to parse session while listing all session IDs:", error);
319
+ console.error("[RedisStorageBackend] Failed to parse session while listing all session IDs:", error);
320
320
  return null;
321
321
  }
322
322
  })
323
323
  );
324
- return sessions.filter((sessionId) => sessionId !== null);
324
+ return sessions2.filter((sessionId) => sessionId !== null);
325
325
  } catch (error) {
326
- console.error("[RedisStorage] Failed to get all sessions:", error);
326
+ console.error("[RedisStorageBackend] Failed to get all sessions:", error);
327
327
  return [];
328
328
  }
329
329
  }
330
330
  async clearAll() {
331
331
  try {
332
332
  const keys = await this.scanKeys(`${this.KEY_PREFIX}*`);
333
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
334
- const allKeys = [...keys, ...identityKeys];
333
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
334
+ const allKeys = [...keys, ...userIdKeys];
335
335
  if (allKeys.length > 0) {
336
336
  await this.redis.del(...allKeys);
337
337
  }
338
338
  } catch (error) {
339
- console.error("[RedisStorage] Failed to clear sessions:", error);
339
+ console.error("[RedisStorageBackend] Failed to clear sessions:", error);
340
340
  }
341
341
  }
342
- async cleanupExpiredSessions() {
342
+ async cleanupExpired() {
343
343
  try {
344
- const identityKeys = await this.scanKeys(`${this.IDENTITY_KEY_PREFIX}*${this.IDENTITY_KEY_SUFFIX}`);
345
- for (const identityKey of identityKeys) {
346
- const identity = this.parseIdentityFromKey(identityKey);
347
- const sessionIds = await this.redis.smembers(identityKey);
344
+ const userIdKeys = await this.scanKeys(`${this.USER_ID_KEY_PREFIX}*${this.USER_ID_KEY_SUFFIX}`);
345
+ for (const userIdKey of userIdKeys) {
346
+ const userId = this.parseUserIdFromKey(userIdKey);
347
+ const sessionIds = await this.redis.smembers(userIdKey);
348
348
  if (sessionIds.length === 0) {
349
- await this.redis.del(identityKey);
349
+ await this.redis.del(userIdKey);
350
350
  continue;
351
351
  }
352
352
  const existenceChecks = await Promise.all(
353
- sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(identity, sessionId)))
353
+ sessionIds.map((sessionId) => this.redis.exists(this.getSessionKey(userId, sessionId)))
354
354
  );
355
355
  const staleSessionIds = sessionIds.filter((_, index) => existenceChecks[index] === 0);
356
356
  if (staleSessionIds.length > 0) {
357
- await this.redis.srem(identityKey, ...staleSessionIds);
357
+ await this.redis.srem(userIdKey, ...staleSessionIds);
358
358
  }
359
- const remainingCount = await this.redis.scard(identityKey);
359
+ const remainingCount = await this.redis.scard(userIdKey);
360
360
  if (remainingCount === 0) {
361
- await this.redis.del(identityKey);
361
+ await this.redis.del(userIdKey);
362
362
  }
363
363
  }
364
364
  } catch (error) {
365
- console.error("[RedisStorage] Failed to cleanup expired sessions:", error);
365
+ console.error("[RedisStorageBackend] Failed to cleanup expired sessions:", error);
366
366
  }
367
367
  }
368
368
  async disconnect() {
369
369
  try {
370
370
  await this.redis.quit();
371
371
  } catch (error) {
372
- console.error("[RedisStorage] Failed to disconnect:", error);
372
+ console.error("[RedisStorageBackend] Failed to disconnect:", error);
373
373
  }
374
374
  }
375
375
  };
@@ -377,36 +377,36 @@ var RedisStorageBackend = class {
377
377
  // src/server/storage/memory-backend.ts
378
378
  var MemoryStorageBackend = class {
379
379
  constructor() {
380
- // Map<identity:sessionId, SessionData>
380
+ // Map<userId:sessionId, Session>
381
381
  __publicField(this, "sessions", /* @__PURE__ */ new Map());
382
- // Map<identity, Set<sessionId>>
383
- __publicField(this, "identitySessions", /* @__PURE__ */ new Map());
382
+ // Map<userId, Set<sessionId>>
383
+ __publicField(this, "userIdSessions", /* @__PURE__ */ new Map());
384
384
  }
385
385
  async init() {
386
386
  console.log("[mcp-ts][Storage] Memory: \u2713 internal memory store active.");
387
387
  }
388
- getSessionKey(identity, sessionId) {
389
- return `${identity}:${sessionId}`;
388
+ getSessionKey(userId, sessionId) {
389
+ return `${userId}:${sessionId}`;
390
390
  }
391
391
  generateSessionId() {
392
392
  return generateSessionId();
393
393
  }
394
- async createSession(session, ttl) {
395
- const { sessionId, identity } = session;
396
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
397
- const sessionKey = this.getSessionKey(identity, sessionId);
394
+ async create(session, ttl) {
395
+ const { sessionId, userId } = session;
396
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
397
+ const sessionKey = this.getSessionKey(userId, sessionId);
398
398
  if (this.sessions.has(sessionKey)) {
399
399
  throw new Error(`Session ${sessionId} already exists`);
400
400
  }
401
401
  this.sessions.set(sessionKey, session);
402
- if (!this.identitySessions.has(identity)) {
403
- this.identitySessions.set(identity, /* @__PURE__ */ new Set());
402
+ if (!this.userIdSessions.has(userId)) {
403
+ this.userIdSessions.set(userId, /* @__PURE__ */ new Set());
404
404
  }
405
- this.identitySessions.get(identity).add(sessionId);
405
+ this.userIdSessions.get(userId).add(sessionId);
406
406
  }
407
- async updateSession(identity, sessionId, data, ttl) {
408
- if (!identity || !sessionId) throw new Error("identity and sessionId required");
409
- const sessionKey = this.getSessionKey(identity, sessionId);
407
+ async update(userId, sessionId, data, ttl) {
408
+ if (!userId || !sessionId) throw new Error("userId and sessionId required");
409
+ const sessionKey = this.getSessionKey(userId, sessionId);
410
410
  const current = this.sessions.get(sessionKey);
411
411
  if (!current) {
412
412
  throw new Error(`Session ${sessionId} not found`);
@@ -417,45 +417,45 @@ var MemoryStorageBackend = class {
417
417
  };
418
418
  this.sessions.set(sessionKey, updated);
419
419
  }
420
- async getSession(identity, sessionId) {
421
- const sessionKey = this.getSessionKey(identity, sessionId);
420
+ async get(userId, sessionId) {
421
+ const sessionKey = this.getSessionKey(userId, sessionId);
422
422
  return this.sessions.get(sessionKey) || null;
423
423
  }
424
- async getIdentityMcpSessions(identity) {
425
- const set = this.identitySessions.get(identity);
424
+ async listIds(userId) {
425
+ const set = this.userIdSessions.get(userId);
426
426
  return set ? Array.from(set) : [];
427
427
  }
428
- async getIdentitySessionsData(identity) {
429
- const set = this.identitySessions.get(identity);
428
+ async list(userId) {
429
+ const set = this.userIdSessions.get(userId);
430
430
  if (!set) return [];
431
431
  const results = [];
432
432
  for (const sessionId of set) {
433
- const session = this.sessions.get(this.getSessionKey(identity, sessionId));
433
+ const session = this.sessions.get(this.getSessionKey(userId, sessionId));
434
434
  if (session) {
435
435
  results.push(session);
436
436
  }
437
437
  }
438
438
  return results;
439
439
  }
440
- async removeSession(identity, sessionId) {
441
- const sessionKey = this.getSessionKey(identity, sessionId);
440
+ async delete(userId, sessionId) {
441
+ const sessionKey = this.getSessionKey(userId, sessionId);
442
442
  this.sessions.delete(sessionKey);
443
- const set = this.identitySessions.get(identity);
443
+ const set = this.userIdSessions.get(userId);
444
444
  if (set) {
445
445
  set.delete(sessionId);
446
446
  if (set.size === 0) {
447
- this.identitySessions.delete(identity);
447
+ this.userIdSessions.delete(userId);
448
448
  }
449
449
  }
450
450
  }
451
- async getAllSessionIds() {
451
+ async listAllIds() {
452
452
  return Array.from(this.sessions.values()).map((s) => s.sessionId);
453
453
  }
454
454
  async clearAll() {
455
455
  this.sessions.clear();
456
- this.identitySessions.clear();
456
+ this.userIdSessions.clear();
457
457
  }
458
- async cleanupExpiredSessions() {
458
+ async cleanupExpired() {
459
459
  }
460
460
  async disconnect() {
461
461
  }
@@ -483,7 +483,7 @@ var FileStorageBackend = class {
483
483
  this.memoryCache = /* @__PURE__ */ new Map();
484
484
  if (Array.isArray(json)) {
485
485
  json.forEach((s) => {
486
- this.memoryCache.set(this.getSessionKey(s.identity || "unknown", s.sessionId), s);
486
+ this.memoryCache.set(this.getSessionKey(s.userId || "unknown", s.sessionId), s);
487
487
  });
488
488
  }
489
489
  } catch (error) {
@@ -503,30 +503,30 @@ var FileStorageBackend = class {
503
503
  }
504
504
  async flush() {
505
505
  if (!this.memoryCache) return;
506
- const sessions = Array.from(this.memoryCache.values());
507
- await promises.writeFile(this.filePath, JSON.stringify(sessions, null, 2), "utf-8");
506
+ const sessions2 = Array.from(this.memoryCache.values());
507
+ await promises.writeFile(this.filePath, JSON.stringify(sessions2, null, 2), "utf-8");
508
508
  }
509
- getSessionKey(identity, sessionId) {
510
- return `${identity}:${sessionId}`;
509
+ getSessionKey(userId, sessionId) {
510
+ return `${userId}:${sessionId}`;
511
511
  }
512
512
  generateSessionId() {
513
513
  return generateSessionId();
514
514
  }
515
- async createSession(session, ttl) {
515
+ async create(session, ttl) {
516
516
  await this.ensureInitialized();
517
- const { sessionId, identity } = session;
518
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
519
- const sessionKey = this.getSessionKey(identity, sessionId);
517
+ const { sessionId, userId } = session;
518
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
519
+ const sessionKey = this.getSessionKey(userId, sessionId);
520
520
  if (this.memoryCache.has(sessionKey)) {
521
521
  throw new Error(`Session ${sessionId} already exists`);
522
522
  }
523
523
  this.memoryCache.set(sessionKey, session);
524
524
  await this.flush();
525
525
  }
526
- async updateSession(identity, sessionId, data, ttl) {
526
+ async update(userId, sessionId, data, ttl) {
527
527
  await this.ensureInitialized();
528
- if (!identity || !sessionId) throw new Error("identity and sessionId required");
529
- const sessionKey = this.getSessionKey(identity, sessionId);
528
+ if (!userId || !sessionId) throw new Error("userId and sessionId required");
529
+ const sessionKey = this.getSessionKey(userId, sessionId);
530
530
  const current = this.memoryCache.get(sessionKey);
531
531
  if (!current) {
532
532
  throw new Error(`Session ${sessionId} not found`);
@@ -538,27 +538,27 @@ var FileStorageBackend = class {
538
538
  this.memoryCache.set(sessionKey, updated);
539
539
  await this.flush();
540
540
  }
541
- async getSession(identity, sessionId) {
541
+ async get(userId, sessionId) {
542
542
  await this.ensureInitialized();
543
- const sessionKey = this.getSessionKey(identity, sessionId);
543
+ const sessionKey = this.getSessionKey(userId, sessionId);
544
544
  return this.memoryCache.get(sessionKey) || null;
545
545
  }
546
- async getIdentitySessionsData(identity) {
546
+ async list(userId) {
547
547
  await this.ensureInitialized();
548
- return Array.from(this.memoryCache.values()).filter((s) => s.identity === identity);
548
+ return Array.from(this.memoryCache.values()).filter((s) => s.userId === userId);
549
549
  }
550
- async getIdentityMcpSessions(identity) {
550
+ async listIds(userId) {
551
551
  await this.ensureInitialized();
552
- return Array.from(this.memoryCache.values()).filter((s) => s.identity === identity).map((s) => s.sessionId);
552
+ return Array.from(this.memoryCache.values()).filter((s) => s.userId === userId).map((s) => s.sessionId);
553
553
  }
554
- async removeSession(identity, sessionId) {
554
+ async delete(userId, sessionId) {
555
555
  await this.ensureInitialized();
556
- const sessionKey = this.getSessionKey(identity, sessionId);
556
+ const sessionKey = this.getSessionKey(userId, sessionId);
557
557
  if (this.memoryCache.delete(sessionKey)) {
558
558
  await this.flush();
559
559
  }
560
560
  }
561
- async getAllSessionIds() {
561
+ async listAllIds() {
562
562
  await this.ensureInitialized();
563
563
  return Array.from(this.memoryCache.values()).map((s) => s.sessionId);
564
564
  }
@@ -567,7 +567,7 @@ var FileStorageBackend = class {
567
567
  this.memoryCache.clear();
568
568
  await this.flush();
569
569
  }
570
- async cleanupExpiredSessions() {
570
+ async cleanupExpired() {
571
571
  await this.ensureInitialized();
572
572
  }
573
573
  async disconnect() {
@@ -594,11 +594,11 @@ var SqliteStorage = class {
594
594
  this.db.exec(`
595
595
  CREATE TABLE IF NOT EXISTS ${this.table} (
596
596
  sessionId TEXT PRIMARY KEY,
597
- identity TEXT NOT NULL,
597
+ userId TEXT NOT NULL,
598
598
  data TEXT NOT NULL,
599
599
  expiresAt INTEGER
600
600
  );
601
- CREATE INDEX IF NOT EXISTS idx_${this.table}_identity ON ${this.table}(identity);
601
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_userId ON ${this.table}(userId);
602
602
  `);
603
603
  this.initialized = true;
604
604
  console.log(`[mcp-ts][Storage] SQLite: \u2713 database at ${this.dbPath} verified.`);
@@ -619,18 +619,18 @@ var SqliteStorage = class {
619
619
  generateSessionId() {
620
620
  return generateSessionId();
621
621
  }
622
- async createSession(session, ttl) {
622
+ async create(session, ttl) {
623
623
  this.ensureInitialized();
624
- const { sessionId, identity } = session;
625
- if (!sessionId || !identity) {
626
- throw new Error("identity and sessionId required");
624
+ const { sessionId, userId } = session;
625
+ if (!sessionId || !userId) {
626
+ throw new Error("userId and sessionId required");
627
627
  }
628
628
  const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
629
629
  try {
630
630
  const stmt = this.db.prepare(
631
- `INSERT INTO ${this.table} (sessionId, identity, data, expiresAt) VALUES (?, ?, ?, ?)`
631
+ `INSERT INTO ${this.table} (sessionId, userId, data, expiresAt) VALUES (?, ?, ?, ?)`
632
632
  );
633
- stmt.run(sessionId, identity, JSON.stringify(session), expiresAt);
633
+ stmt.run(sessionId, userId, JSON.stringify(session), expiresAt);
634
634
  } catch (error) {
635
635
  if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
636
636
  throw new Error(`Session ${sessionId} already exists`);
@@ -638,55 +638,55 @@ var SqliteStorage = class {
638
638
  throw error;
639
639
  }
640
640
  }
641
- async updateSession(identity, sessionId, data, ttl) {
641
+ async update(userId, sessionId, data, ttl) {
642
642
  this.ensureInitialized();
643
- if (!sessionId || !identity) {
644
- throw new Error("identity and sessionId required");
643
+ if (!sessionId || !userId) {
644
+ throw new Error("userId and sessionId required");
645
645
  }
646
- const currentSession = await this.getSession(identity, sessionId);
646
+ const currentSession = await this.get(userId, sessionId);
647
647
  if (!currentSession) {
648
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
648
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
649
649
  }
650
650
  const updatedSession = { ...currentSession, ...data };
651
651
  const expiresAt = ttl ? Date.now() + ttl * 1e3 : null;
652
652
  const stmt = this.db.prepare(
653
- `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND identity = ?`
653
+ `UPDATE ${this.table} SET data = ?, expiresAt = ? WHERE sessionId = ? AND userId = ?`
654
654
  );
655
- stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, identity);
655
+ stmt.run(JSON.stringify(updatedSession), expiresAt, sessionId, userId);
656
656
  }
657
- async getSession(identity, sessionId) {
657
+ async get(userId, sessionId) {
658
658
  this.ensureInitialized();
659
659
  const stmt = this.db.prepare(
660
- `SELECT data FROM ${this.table} WHERE sessionId = ? AND identity = ?`
660
+ `SELECT data FROM ${this.table} WHERE sessionId = ? AND userId = ?`
661
661
  );
662
- const row = stmt.get(sessionId, identity);
662
+ const row = stmt.get(sessionId, userId);
663
663
  if (!row) return null;
664
664
  return JSON.parse(row.data);
665
665
  }
666
- async getIdentitySessionsData(identity) {
666
+ async list(userId) {
667
667
  this.ensureInitialized();
668
668
  const stmt = this.db.prepare(
669
- `SELECT data FROM ${this.table} WHERE identity = ?`
669
+ `SELECT data FROM ${this.table} WHERE userId = ?`
670
670
  );
671
- const rows = stmt.all(identity);
671
+ const rows = stmt.all(userId);
672
672
  return rows.map((row) => JSON.parse(row.data));
673
673
  }
674
- async getIdentityMcpSessions(identity) {
674
+ async listIds(userId) {
675
675
  this.ensureInitialized();
676
676
  const stmt = this.db.prepare(
677
- `SELECT sessionId FROM ${this.table} WHERE identity = ?`
677
+ `SELECT sessionId FROM ${this.table} WHERE userId = ?`
678
678
  );
679
- const rows = stmt.all(identity);
679
+ const rows = stmt.all(userId);
680
680
  return rows.map((row) => row.sessionId);
681
681
  }
682
- async removeSession(identity, sessionId) {
682
+ async delete(userId, sessionId) {
683
683
  this.ensureInitialized();
684
684
  const stmt = this.db.prepare(
685
- `DELETE FROM ${this.table} WHERE sessionId = ? AND identity = ?`
685
+ `DELETE FROM ${this.table} WHERE sessionId = ? AND userId = ?`
686
686
  );
687
- stmt.run(sessionId, identity);
687
+ stmt.run(sessionId, userId);
688
688
  }
689
- async getAllSessionIds() {
689
+ async listAllIds() {
690
690
  this.ensureInitialized();
691
691
  const stmt = this.db.prepare(`SELECT sessionId FROM ${this.table}`);
692
692
  const rows = stmt.all();
@@ -697,7 +697,7 @@ var SqliteStorage = class {
697
697
  const stmt = this.db.prepare(`DELETE FROM ${this.table}`);
698
698
  stmt.run();
699
699
  }
700
- async cleanupExpiredSessions() {
700
+ async cleanupExpired() {
701
701
  this.ensureInitialized();
702
702
  const now = Date.now();
703
703
  const stmt = this.db.prepare(
@@ -808,7 +808,7 @@ var SupabaseStorageBackend = class {
808
808
  transportType: row.transport_type,
809
809
  callbackUrl: row.callback_url,
810
810
  createdAt: new Date(row.created_at).getTime(),
811
- identity: row.identity,
811
+ userId: row.user_id,
812
812
  headers: decryptObject(row.headers),
813
813
  active: row.active,
814
814
  clientInformation: row.client_information,
@@ -817,22 +817,21 @@ var SupabaseStorageBackend = class {
817
817
  clientId: row.client_id
818
818
  };
819
819
  }
820
- async createSession(session, ttl) {
821
- const { sessionId, identity } = session;
822
- if (!sessionId || !identity) throw new Error("identity and sessionId required");
820
+ async create(session, ttl) {
821
+ const { sessionId, userId } = session;
822
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
823
823
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
824
824
  const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
825
825
  const { error } = await this.supabase.from("mcp_sessions").insert({
826
826
  session_id: sessionId,
827
- user_id: identity,
828
- // Maps user_id to identity to support RLS using auth.uid()
827
+ user_id: userId,
828
+ // Maps user_id to userId to support RLS using auth.uid()
829
829
  server_id: session.serverId,
830
830
  server_name: session.serverName,
831
831
  server_url: session.serverUrl,
832
832
  transport_type: session.transportType,
833
833
  callback_url: session.callbackUrl,
834
834
  created_at: new Date(session.createdAt || Date.now()).toISOString(),
835
- identity,
836
835
  headers: encryptObject(session.headers),
837
836
  active: session.active ?? false,
838
837
  client_information: session.clientInformation,
@@ -848,7 +847,7 @@ var SupabaseStorageBackend = class {
848
847
  throw new Error(`Failed to create session in Supabase: ${error.message}`);
849
848
  }
850
849
  }
851
- async updateSession(identity, sessionId, data, ttl) {
850
+ async update(userId, sessionId, data, ttl) {
852
851
  const effectiveTtl = ttl ?? this.DEFAULT_TTL;
853
852
  const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
854
853
  const updateData = {
@@ -866,16 +865,16 @@ var SupabaseStorageBackend = class {
866
865
  if ("tokens" in data) updateData.tokens = encryptObject(data.tokens);
867
866
  if ("codeVerifier" in data) updateData.code_verifier = data.codeVerifier;
868
867
  if ("clientId" in data) updateData.client_id = data.clientId;
869
- const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("identity", identity).eq("session_id", sessionId).select("id");
868
+ const { data: updatedRows, error } = await this.supabase.from("mcp_sessions").update(updateData).eq("user_id", userId).eq("session_id", sessionId).select("id");
870
869
  if (error) {
871
870
  throw new Error(`Failed to update session: ${error.message}`);
872
871
  }
873
872
  if (!updatedRows || updatedRows.length === 0) {
874
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
873
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
875
874
  }
876
875
  }
877
- async getSession(identity, sessionId) {
878
- const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity).eq("session_id", sessionId).maybeSingle();
876
+ async get(userId, sessionId) {
877
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("user_id", userId).eq("session_id", sessionId).maybeSingle();
879
878
  if (error) {
880
879
  console.error("[SupabaseStorage] Failed to get session:", error);
881
880
  return null;
@@ -883,29 +882,29 @@ var SupabaseStorageBackend = class {
883
882
  if (!data) return null;
884
883
  return this.mapRowToSessionData(data);
885
884
  }
886
- async getIdentitySessionsData(identity) {
887
- const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("identity", identity);
885
+ async list(userId) {
886
+ const { data, error } = await this.supabase.from("mcp_sessions").select("*").eq("user_id", userId);
888
887
  if (error) {
889
- console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
888
+ console.error(`[SupabaseStorage] Failed to get session data for ${userId}:`, error);
890
889
  return [];
891
890
  }
892
891
  return data.map((row) => this.mapRowToSessionData(row));
893
892
  }
894
- async removeSession(identity, sessionId) {
895
- const { error } = await this.supabase.from("mcp_sessions").delete().eq("identity", identity).eq("session_id", sessionId);
893
+ async delete(userId, sessionId) {
894
+ const { error } = await this.supabase.from("mcp_sessions").delete().eq("user_id", userId).eq("session_id", sessionId);
896
895
  if (error) {
897
896
  console.error("[SupabaseStorage] Failed to remove session:", error);
898
897
  }
899
898
  }
900
- async getIdentityMcpSessions(identity) {
901
- const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("identity", identity);
899
+ async listIds(userId) {
900
+ const { data, error } = await this.supabase.from("mcp_sessions").select("session_id").eq("user_id", userId);
902
901
  if (error) {
903
- console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
902
+ console.error(`[SupabaseStorage] Failed to get sessions for ${userId}:`, error);
904
903
  return [];
905
904
  }
906
905
  return data.map((row) => row.session_id);
907
906
  }
908
- async getAllSessionIds() {
907
+ async listAllIds() {
909
908
  const { data, error } = await this.supabase.from("mcp_sessions").select("session_id");
910
909
  if (error) {
911
910
  console.error("[SupabaseStorage] Failed to get all sessions:", error);
@@ -919,7 +918,7 @@ var SupabaseStorageBackend = class {
919
918
  console.error("[SupabaseStorage] Failed to clear sessions:", error);
920
919
  }
921
920
  }
922
- async cleanupExpiredSessions() {
921
+ async cleanupExpired() {
923
922
  const { error } = await this.supabase.from("mcp_sessions").delete().lt("expires_at", (/* @__PURE__ */ new Date()).toISOString());
924
923
  if (error) {
925
924
  console.error("[SupabaseStorage] Failed to cleanup expired sessions:", error);
@@ -929,7 +928,250 @@ var SupabaseStorageBackend = class {
929
928
  }
930
929
  };
931
930
 
931
+ // src/server/storage/neon-backend.ts
932
+ var NeonStorageBackend = class {
933
+ constructor(sql, options = {}) {
934
+ this.sql = sql;
935
+ __publicField(this, "DEFAULT_TTL", SESSION_TTL_SECONDS);
936
+ __publicField(this, "tableName");
937
+ const schema = options.schema || "public";
938
+ const table = options.table || "mcp_sessions";
939
+ this.tableName = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(table)}`;
940
+ }
941
+ async init() {
942
+ const [{ exists } = { exists: null }] = await this.sql.query(
943
+ "SELECT to_regclass($1) AS exists",
944
+ [this.tableName.replace(/"/g, "")]
945
+ );
946
+ if (!exists) {
947
+ throw new Error(
948
+ '[NeonStorage] Table "mcp_sessions" not found in your database. Please create it using the Neon storage guide in docs/storage-backends/neon.md.'
949
+ );
950
+ }
951
+ console.log('[mcp-ts][Storage] Neon: "mcp_sessions" table verified.');
952
+ }
953
+ generateSessionId() {
954
+ return generateSessionId();
955
+ }
956
+ quoteIdentifier(identifier) {
957
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
958
+ throw new Error(`Invalid Neon storage identifier: ${identifier}`);
959
+ }
960
+ return `"${identifier}"`;
961
+ }
962
+ mapRowToSessionData(row) {
963
+ return {
964
+ sessionId: row.session_id,
965
+ serverId: row.server_id ?? void 0,
966
+ serverName: row.server_name ?? void 0,
967
+ serverUrl: row.server_url,
968
+ transportType: row.transport_type,
969
+ callbackUrl: row.callback_url,
970
+ createdAt: new Date(row.created_at).getTime(),
971
+ userId: row.user_id,
972
+ headers: decryptObject(row.headers),
973
+ active: row.active ?? false,
974
+ clientInformation: row.client_information,
975
+ tokens: decryptObject(row.tokens),
976
+ codeVerifier: row.code_verifier ?? void 0,
977
+ clientId: row.client_id ?? void 0
978
+ };
979
+ }
980
+ async create(session, ttl) {
981
+ const { sessionId, userId } = session;
982
+ if (!sessionId || !userId) throw new Error("userId and sessionId required");
983
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
984
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
985
+ try {
986
+ await this.sql.query(
987
+ `INSERT INTO ${this.tableName} (
988
+ session_id,
989
+ user_id,
990
+ server_id,
991
+ server_name,
992
+ server_url,
993
+ transport_type,
994
+ callback_url,
995
+ created_at,
996
+ headers,
997
+ active,
998
+ client_information,
999
+ tokens,
1000
+ code_verifier,
1001
+ client_id,
1002
+ expires_at
1003
+ ) VALUES (
1004
+ $1, $2, $3, $4, $5, $6, $7, $8,
1005
+ $9, $10, $11, $12, $13, $14, $15
1006
+ )`,
1007
+ [
1008
+ sessionId,
1009
+ userId,
1010
+ session.serverId,
1011
+ session.serverName,
1012
+ session.serverUrl,
1013
+ session.transportType,
1014
+ session.callbackUrl,
1015
+ new Date(session.createdAt || Date.now()).toISOString(),
1016
+ encryptObject(session.headers),
1017
+ session.active ?? false,
1018
+ session.clientInformation,
1019
+ encryptObject(session.tokens),
1020
+ session.codeVerifier,
1021
+ session.clientId,
1022
+ expiresAt
1023
+ ]
1024
+ );
1025
+ } catch (error) {
1026
+ if (error.code === "23505") {
1027
+ throw new Error(`Session ${sessionId} already exists`);
1028
+ }
1029
+ throw new Error(`Failed to create session in Neon: ${error.message}`);
1030
+ }
1031
+ }
1032
+ async update(userId, sessionId, data, ttl) {
1033
+ const currentSession = await this.get(userId, sessionId);
1034
+ if (!currentSession) {
1035
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
1036
+ }
1037
+ const updatedSession = { ...currentSession, ...data };
1038
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
1039
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1e3).toISOString();
1040
+ const updatedRows = await this.sql.query(
1041
+ `UPDATE ${this.tableName}
1042
+ SET
1043
+ server_id = $1,
1044
+ server_name = $2,
1045
+ server_url = $3,
1046
+ transport_type = $4,
1047
+ callback_url = $5,
1048
+ active = $6,
1049
+ headers = $7,
1050
+ client_information = $8,
1051
+ tokens = $9,
1052
+ code_verifier = $10,
1053
+ client_id = $11,
1054
+ expires_at = $12,
1055
+ updated_at = now()
1056
+ WHERE user_id = $13 AND session_id = $14
1057
+ RETURNING id`,
1058
+ [
1059
+ updatedSession.serverId,
1060
+ updatedSession.serverName,
1061
+ updatedSession.serverUrl,
1062
+ updatedSession.transportType,
1063
+ updatedSession.callbackUrl,
1064
+ updatedSession.active ?? false,
1065
+ encryptObject(updatedSession.headers),
1066
+ updatedSession.clientInformation,
1067
+ encryptObject(updatedSession.tokens),
1068
+ updatedSession.codeVerifier,
1069
+ updatedSession.clientId,
1070
+ expiresAt,
1071
+ userId,
1072
+ sessionId
1073
+ ]
1074
+ );
1075
+ if (updatedRows.length === 0) {
1076
+ throw new Error(`Session ${sessionId} not found for userId ${userId}`);
1077
+ }
1078
+ }
1079
+ async get(userId, sessionId) {
1080
+ try {
1081
+ const rows = await this.sql.query(
1082
+ `SELECT * FROM ${this.tableName} WHERE user_id = $1 AND session_id = $2`,
1083
+ [userId, sessionId]
1084
+ );
1085
+ return rows[0] ? this.mapRowToSessionData(rows[0]) : null;
1086
+ } catch (error) {
1087
+ console.error("[NeonStorage] Failed to get session:", error);
1088
+ return null;
1089
+ }
1090
+ }
1091
+ async list(userId) {
1092
+ try {
1093
+ const rows = await this.sql.query(
1094
+ `SELECT * FROM ${this.tableName} WHERE user_id = $1`,
1095
+ [userId]
1096
+ );
1097
+ return rows.map((row) => this.mapRowToSessionData(row));
1098
+ } catch (error) {
1099
+ console.error(`[NeonStorage] Failed to get session data for ${userId}:`, error);
1100
+ return [];
1101
+ }
1102
+ }
1103
+ async delete(userId, sessionId) {
1104
+ try {
1105
+ await this.sql.query(
1106
+ `DELETE FROM ${this.tableName} WHERE user_id = $1 AND session_id = $2`,
1107
+ [userId, sessionId]
1108
+ );
1109
+ } catch (error) {
1110
+ console.error("[NeonStorage] Failed to remove session:", error);
1111
+ }
1112
+ }
1113
+ async listIds(userId) {
1114
+ try {
1115
+ const rows = await this.sql.query(
1116
+ `SELECT session_id FROM ${this.tableName} WHERE user_id = $1`,
1117
+ [userId]
1118
+ );
1119
+ return rows.map((row) => row.session_id);
1120
+ } catch (error) {
1121
+ console.error(`[NeonStorage] Failed to get sessions for ${userId}:`, error);
1122
+ return [];
1123
+ }
1124
+ }
1125
+ async listAllIds() {
1126
+ try {
1127
+ const rows = await this.sql.query(
1128
+ `SELECT session_id FROM ${this.tableName}`
1129
+ );
1130
+ return rows.map((row) => row.session_id);
1131
+ } catch (error) {
1132
+ console.error("[NeonStorage] Failed to get all sessions:", error);
1133
+ return [];
1134
+ }
1135
+ }
1136
+ async clearAll() {
1137
+ try {
1138
+ await this.sql.query(`DELETE FROM ${this.tableName}`);
1139
+ } catch (error) {
1140
+ console.error("[NeonStorage] Failed to clear sessions:", error);
1141
+ }
1142
+ }
1143
+ async cleanupExpired() {
1144
+ try {
1145
+ await this.sql.query(
1146
+ `DELETE FROM ${this.tableName} WHERE expires_at < $1`,
1147
+ [(/* @__PURE__ */ new Date()).toISOString()]
1148
+ );
1149
+ } catch (error) {
1150
+ console.error("[NeonStorage] Failed to cleanup expired sessions:", error);
1151
+ }
1152
+ }
1153
+ async disconnect() {
1154
+ }
1155
+ };
1156
+
932
1157
  // src/server/storage/index.ts
1158
+ function warnIfNeonConnectionStringIsInsecure(connectionString) {
1159
+ try {
1160
+ const url = new URL(connectionString);
1161
+ const sslMode = url.searchParams.get("sslmode");
1162
+ const channelBinding = url.searchParams.get("channel_binding");
1163
+ if (!sslMode) {
1164
+ console.warn("[mcp-ts][Storage] Neon connection string does not include sslmode. Neon recommends sslmode=verify-full for the strongest certificate verification.");
1165
+ } else if (!["verify-full", "require"].includes(sslMode)) {
1166
+ console.warn(`[mcp-ts][Storage] Neon connection string uses sslmode=${sslMode}. Use sslmode=verify-full or sslmode=require for secure connections.`);
1167
+ }
1168
+ if (!channelBinding) {
1169
+ console.warn("[mcp-ts][Storage] Neon connection string does not include channel_binding=require. Add it when supported by your runtime and connection path.");
1170
+ }
1171
+ } catch {
1172
+ console.warn("[mcp-ts][Storage] Neon connection string could not be parsed for SSL checks.");
1173
+ }
1174
+ }
933
1175
  var storageInstance = null;
934
1176
  var storagePromise = null;
935
1177
  async function initializeStorage(store) {
@@ -986,6 +1228,24 @@ async function createStorage() {
986
1228
  }
987
1229
  }
988
1230
  }
1231
+ if (type === "neon") {
1232
+ const connectionString = process.env.NEON_DATABASE_URL || process.env.DATABASE_URL;
1233
+ if (!connectionString) {
1234
+ console.warn('[mcp-ts][Storage] Explicit selection "neon" requires NEON_DATABASE_URL or DATABASE_URL.');
1235
+ } else {
1236
+ try {
1237
+ const { neon } = await import('@neondatabase/serverless');
1238
+ warnIfNeonConnectionStringIsInsecure(connectionString);
1239
+ const sql = neon(connectionString);
1240
+ console.log('[mcp-ts][Storage] Explicit selection: "neon"');
1241
+ return await initializeStorage(new NeonStorageBackend(sql));
1242
+ } catch (error) {
1243
+ console.error("[mcp-ts][Storage] Failed to initialize Neon:", error.message);
1244
+ console.log("[mcp-ts][Storage] Falling back to In-Memory storage");
1245
+ return await initializeStorage(new MemoryStorageBackend());
1246
+ }
1247
+ }
1248
+ }
989
1249
  if (type === "memory") {
990
1250
  console.log('[mcp-ts][Storage] Explicit selection: "memory"');
991
1251
  return await initializeStorage(new MemoryStorageBackend());
@@ -1024,6 +1284,17 @@ async function createStorage() {
1024
1284
  console.error("[mcp-ts][Storage] Supabase auto-detection failed:", error.message);
1025
1285
  }
1026
1286
  }
1287
+ if (process.env.NEON_DATABASE_URL) {
1288
+ try {
1289
+ const { neon } = await import('@neondatabase/serverless');
1290
+ warnIfNeonConnectionStringIsInsecure(process.env.NEON_DATABASE_URL);
1291
+ const sql = neon(process.env.NEON_DATABASE_URL);
1292
+ console.log('[mcp-ts][Storage] Auto-detection: "neon" (via NEON_DATABASE_URL)');
1293
+ return await initializeStorage(new NeonStorageBackend(sql));
1294
+ } catch (error) {
1295
+ console.error("[mcp-ts][Storage] Neon auto-detection failed:", error.message);
1296
+ }
1297
+ }
1027
1298
  console.log('[mcp-ts][Storage] Defaulting to: "memory"');
1028
1299
  return await initializeStorage(new MemoryStorageBackend());
1029
1300
  }
@@ -1040,7 +1311,7 @@ async function getStorage() {
1040
1311
  storageInstance = await storagePromise;
1041
1312
  return storageInstance;
1042
1313
  }
1043
- var storage = new Proxy({}, {
1314
+ var sessions = new Proxy({}, {
1044
1315
  get(_target, prop) {
1045
1316
  return async (...args) => {
1046
1317
  const instance = await getStorage();
@@ -1056,11 +1327,11 @@ var storage = new Proxy({}, {
1056
1327
  // src/server/mcp/storage-oauth-provider.ts
1057
1328
  var StorageOAuthClientProvider = class {
1058
1329
  /**
1059
- * Creates a new storage-backed OAuth provider
1330
+ * Creates a new session-backed OAuth provider
1060
1331
  * @param options - Provider configuration
1061
1332
  */
1062
1333
  constructor(options) {
1063
- __publicField(this, "identity");
1334
+ __publicField(this, "userId");
1064
1335
  __publicField(this, "serverId");
1065
1336
  __publicField(this, "sessionId");
1066
1337
  __publicField(this, "redirectUrl");
@@ -1073,7 +1344,7 @@ var StorageOAuthClientProvider = class {
1073
1344
  __publicField(this, "_clientId");
1074
1345
  __publicField(this, "onRedirectCallback");
1075
1346
  __publicField(this, "tokenExpiresAt");
1076
- this.identity = options.identity;
1347
+ this.userId = options.userId;
1077
1348
  this.serverId = options.serverId;
1078
1349
  this.sessionId = options.sessionId;
1079
1350
  this.redirectUrl = options.redirectUrl;
@@ -1106,24 +1377,24 @@ var StorageOAuthClientProvider = class {
1106
1377
  this._clientId = clientId_;
1107
1378
  }
1108
1379
  /**
1109
- * Loads OAuth data from storage session
1380
+ * Loads OAuth data from the session store
1110
1381
  * @private
1111
1382
  */
1112
1383
  async getSessionData() {
1113
- const data = await storage.getSession(this.identity, this.sessionId);
1384
+ const data = await sessions.get(this.userId, this.sessionId);
1114
1385
  if (!data) {
1115
1386
  return {};
1116
1387
  }
1117
1388
  return data;
1118
1389
  }
1119
1390
  /**
1120
- * Saves OAuth data to storage
1391
+ * Saves OAuth data to the session store
1121
1392
  * @param data - Partial OAuth data to save
1122
1393
  * @private
1123
1394
  * @throws Error if session doesn't exist (session must be created by controller layer)
1124
1395
  */
1125
1396
  async saveSessionData(data) {
1126
- await storage.updateSession(this.identity, this.sessionId, data);
1397
+ await sessions.update(this.userId, this.sessionId, data);
1127
1398
  }
1128
1399
  /**
1129
1400
  * Retrieves stored OAuth client information
@@ -1171,7 +1442,7 @@ var StorageOAuthClientProvider = class {
1171
1442
  return this.sessionId;
1172
1443
  }
1173
1444
  async checkState(_state) {
1174
- const data = await storage.getSession(this.identity, this.sessionId);
1445
+ const data = await sessions.get(this.userId, this.sessionId);
1175
1446
  if (!data) {
1176
1447
  return { valid: false, error: "Session not found" };
1177
1448
  }
@@ -1187,7 +1458,7 @@ var StorageOAuthClientProvider = class {
1187
1458
  }
1188
1459
  async invalidateCredentials(scope) {
1189
1460
  if (scope === "all") {
1190
- await storage.removeSession(this.identity, this.sessionId);
1461
+ await sessions.delete(this.userId, this.sessionId);
1191
1462
  } else {
1192
1463
  const updates = {};
1193
1464
  if (scope === "client") {
@@ -1370,7 +1641,7 @@ var ToolExecutionError = class extends McpError {
1370
1641
  };
1371
1642
  var RpcErrorCodes = {
1372
1643
  EXECUTION_ERROR: "EXECUTION_ERROR",
1373
- MISSING_IDENTITY: "MISSING_IDENTITY",
1644
+ MISSING_USER_ID: "MISSING_USER_ID",
1374
1645
  UNAUTHORIZED: "UNAUTHORIZED",
1375
1646
  NO_CONNECTION: "NO_CONNECTION",
1376
1647
  UNKNOWN_METHOD: "UNKNOWN_METHOD",
@@ -1381,14 +1652,14 @@ var RpcErrorCodes = {
1381
1652
  var MCPClient = class _MCPClient {
1382
1653
  /**
1383
1654
  * Creates a new MCP client instance
1384
- * Can be initialized with minimal options (identity + sessionId) for session restoration
1655
+ * Can be initialized with minimal options (userId + sessionId) for session restoration
1385
1656
  * @param options - Client configuration options
1386
1657
  */
1387
1658
  constructor(options) {
1388
1659
  __publicField(this, "client", null);
1389
1660
  __publicField(this, "oauthProvider", null);
1390
1661
  __publicField(this, "transport", null);
1391
- __publicField(this, "identity");
1662
+ __publicField(this, "userId");
1392
1663
  __publicField(this, "serverId");
1393
1664
  __publicField(this, "sessionId");
1394
1665
  __publicField(this, "serverName");
@@ -1415,7 +1686,7 @@ var MCPClient = class _MCPClient {
1415
1686
  this.serverName = options.serverName;
1416
1687
  this.callbackUrl = options.callbackUrl;
1417
1688
  this.onRedirect = options.onRedirect;
1418
- this.identity = options.identity;
1689
+ this.userId = options.userId;
1419
1690
  this.serverId = options.serverId;
1420
1691
  this.sessionId = options.sessionId;
1421
1692
  this.transportType = options.transportType;
@@ -1554,7 +1825,7 @@ var MCPClient = class _MCPClient {
1554
1825
  this.emitStateChange("INITIALIZING");
1555
1826
  this.emitProgress("Loading session configuration...");
1556
1827
  if (!this.serverUrl || !this.callbackUrl || !this.serverId) {
1557
- const sessionData = await storage.getSession(this.identity, this.sessionId);
1828
+ const sessionData = await sessions.get(this.userId, this.sessionId);
1558
1829
  if (!sessionData) {
1559
1830
  throw new Error(`Session not found: ${this.sessionId}`);
1560
1831
  }
@@ -1573,7 +1844,7 @@ var MCPClient = class _MCPClient {
1573
1844
  throw new Error("serverId required for OAuth provider initialization");
1574
1845
  }
1575
1846
  this.oauthProvider = new StorageOAuthClientProvider({
1576
- identity: this.identity,
1847
+ userId: this.userId,
1577
1848
  serverId: this.serverId,
1578
1849
  sessionId: this.sessionId,
1579
1850
  redirectUrl: this.callbackUrl,
@@ -1607,18 +1878,18 @@ var MCPClient = class _MCPClient {
1607
1878
  }
1608
1879
  );
1609
1880
  }
1610
- const existingSession = await storage.getSession(this.identity, this.sessionId);
1881
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1611
1882
  if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
1612
1883
  this.createdAt = Date.now();
1613
1884
  console.log(`[MCPClient] Creating initial session ${this.sessionId} for OAuth flow`);
1614
- await storage.createSession({
1885
+ await sessions.create({
1615
1886
  sessionId: this.sessionId,
1616
- identity: this.identity,
1887
+ userId: this.userId,
1617
1888
  serverId: this.serverId,
1618
1889
  serverName: this.serverName,
1619
1890
  serverUrl: this.serverUrl,
1620
1891
  callbackUrl: this.callbackUrl,
1621
- transportType: this.transportType || "streamable_http",
1892
+ transportType: this.transportType || "streamable-http",
1622
1893
  headers: this.headers,
1623
1894
  createdAt: this.createdAt,
1624
1895
  active: false
@@ -1626,7 +1897,7 @@ var MCPClient = class _MCPClient {
1626
1897
  }
1627
1898
  }
1628
1899
  /**
1629
- * Saves current session state to storage
1900
+ * Saves current session state to the session store
1630
1901
  * Creates new session if it doesn't exist, updates if it does
1631
1902
  * @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
1632
1903
  * @param active - Session status marker used to avoid unnecessary TTL rewrites
@@ -1638,21 +1909,21 @@ var MCPClient = class _MCPClient {
1638
1909
  }
1639
1910
  const sessionData = {
1640
1911
  sessionId: this.sessionId,
1641
- identity: this.identity,
1912
+ userId: this.userId,
1642
1913
  serverId: this.serverId,
1643
1914
  serverName: this.serverName,
1644
1915
  serverUrl: this.serverUrl,
1645
1916
  callbackUrl: this.callbackUrl,
1646
- transportType: this.transportType || "streamable_http",
1917
+ transportType: this.transportType || "streamable-http",
1647
1918
  headers: this.headers,
1648
1919
  createdAt: this.createdAt || Date.now(),
1649
1920
  active
1650
1921
  };
1651
- const existingSession = await storage.getSession(this.identity, this.sessionId);
1922
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1652
1923
  if (existingSession) {
1653
- await storage.updateSession(this.identity, this.sessionId, sessionData, ttl);
1924
+ await sessions.update(this.userId, this.sessionId, sessionData, ttl);
1654
1925
  } else {
1655
- await storage.createSession(sessionData, ttl);
1926
+ await sessions.create(sessionData, ttl);
1656
1927
  }
1657
1928
  }
1658
1929
  /**
@@ -1661,7 +1932,7 @@ var MCPClient = class _MCPClient {
1661
1932
  * @private
1662
1933
  */
1663
1934
  async tryConnect() {
1664
- const transportsToTry = this.transportType ? [this.transportType] : ["streamable_http", "sse"];
1935
+ const transportsToTry = this.transportType ? [this.transportType] : ["streamable-http", "sse"];
1665
1936
  let lastError;
1666
1937
  for (const currentType of transportsToTry) {
1667
1938
  const isLastAttempt = currentType === transportsToTry[transportsToTry.length - 1];
@@ -1733,7 +2004,7 @@ var MCPClient = class _MCPClient {
1733
2004
  this.emitError(message, "auth");
1734
2005
  this.emitStateChange("FAILED");
1735
2006
  try {
1736
- await storage.removeSession(this.identity, this.sessionId);
2007
+ await sessions.delete(this.userId, this.sessionId);
1737
2008
  } catch {
1738
2009
  }
1739
2010
  throw new Error(message);
@@ -1759,9 +2030,9 @@ var MCPClient = class _MCPClient {
1759
2030
  this.emitError(errorMessage, "connection");
1760
2031
  this.emitStateChange("FAILED");
1761
2032
  try {
1762
- const existingSession = await storage.getSession(this.identity, this.sessionId);
2033
+ const existingSession = await sessions.get(this.userId, this.sessionId);
1763
2034
  if (!existingSession || existingSession.active !== true) {
1764
- await storage.removeSession(this.identity, this.sessionId);
2035
+ await sessions.delete(this.userId, this.sessionId);
1765
2036
  }
1766
2037
  } catch {
1767
2038
  }
@@ -1785,7 +2056,7 @@ var MCPClient = class _MCPClient {
1785
2056
  this.emitStateChange("FAILED");
1786
2057
  throw new Error(error);
1787
2058
  }
1788
- const transportsToTry = this.transportType ? [this.transportType] : ["streamable_http", "sse"];
2059
+ const transportsToTry = this.transportType ? [this.transportType] : ["streamable-http", "sse"];
1789
2060
  let lastError;
1790
2061
  let tokensExchanged = false;
1791
2062
  let authenticatedStateEmitted = false;
@@ -2109,7 +2380,7 @@ var MCPClient = class _MCPClient {
2109
2380
  },
2110
2381
  { capabilities: {} }
2111
2382
  );
2112
- const tt = this.transportType || "streamable_http";
2383
+ const tt = this.transportType || "streamable-http";
2113
2384
  this.transport = this.getTransport(tt);
2114
2385
  await this.client.connect(this.transport);
2115
2386
  }
@@ -2126,7 +2397,7 @@ var MCPClient = class _MCPClient {
2126
2397
  if (this.oauthProvider) {
2127
2398
  await this.oauthProvider.invalidateCredentials("all");
2128
2399
  }
2129
- await storage.removeSession(this.identity, this.sessionId);
2400
+ await sessions.delete(this.userId, this.sessionId);
2130
2401
  this.disconnect();
2131
2402
  }
2132
2403
  /**
@@ -2194,10 +2465,10 @@ var MCPClient = class _MCPClient {
2194
2465
  }
2195
2466
  /**
2196
2467
  * Gets the transport type being used
2197
- * @returns Transport type (defaults to 'streamable_http')
2468
+ * @returns Transport type (defaults to 'streamable-http')
2198
2469
  */
2199
2470
  getTransportType() {
2200
- return this.transportType || "streamable_http";
2471
+ return this.transportType || "streamable-http";
2201
2472
  }
2202
2473
  /**
2203
2474
  * Gets the human-readable server name
@@ -2224,25 +2495,25 @@ var MCPClient = class _MCPClient {
2224
2495
  * Gets MCP server configuration for all active user sessions
2225
2496
  * Loads sessions from Redis, validates OAuth tokens, refreshes if expired
2226
2497
  * Returns ready-to-use configuration with valid auth headers
2227
- * @param identity - User ID to fetch sessions for
2498
+ * @param userId - User ID to fetch sessions for
2228
2499
  * @returns Object keyed by sanitized server labels containing transport, url, headers, etc.
2229
2500
  * @static
2230
2501
  */
2231
- static async getMcpServerConfig(identity) {
2502
+ static async getMcpServerConfig(userId) {
2232
2503
  const mcpConfig = {};
2233
- const sessions = await storage.getIdentitySessionsData(identity);
2504
+ const sessionList = await sessions.list(userId);
2234
2505
  await Promise.all(
2235
- sessions.map(async (sessionData) => {
2506
+ sessionList.map(async (sessionData) => {
2236
2507
  const { sessionId } = sessionData;
2237
2508
  try {
2238
2509
  if (!sessionData.serverId || !sessionData.transportType || !sessionData.serverUrl || !sessionData.callbackUrl) {
2239
- await storage.removeSession(identity, sessionId);
2510
+ await sessions.delete(userId, sessionId);
2240
2511
  return;
2241
2512
  }
2242
2513
  let headers;
2243
2514
  try {
2244
2515
  const client = new _MCPClient({
2245
- identity,
2516
+ userId,
2246
2517
  sessionId,
2247
2518
  serverId: sessionData.serverId,
2248
2519
  serverUrl: sessionData.serverUrl,
@@ -2275,7 +2546,7 @@ var MCPClient = class _MCPClient {
2275
2546
  ...headers && { headers }
2276
2547
  };
2277
2548
  } catch (error) {
2278
- await storage.removeSession(identity, sessionId);
2549
+ await sessions.delete(userId, sessionId);
2279
2550
  console.warn(`[MCP] Failed to process session ${sessionId}:`, error);
2280
2551
  }
2281
2552
  })
@@ -2291,18 +2562,18 @@ var DEFAULT_RETRY_DELAY_MS = 1e3;
2291
2562
  var CONNECTION_BATCH_SIZE = 5;
2292
2563
  var MultiSessionClient = class {
2293
2564
  /**
2294
- * Creates a new MultiSessionClient for the given user identity.
2565
+ * Creates a new MultiSessionClient for the given user userId.
2295
2566
  *
2296
- * @param identity - A unique string identifying the user (e.g. user ID or email).
2567
+ * @param userId - A unique string identifying the user (e.g. user ID or email).
2297
2568
  * @param options - Optional tuning for connection timeout, retry count, and retry delay.
2298
2569
  * Falls back to sensible defaults if not provided.
2299
2570
  */
2300
- constructor(identity, options = {}) {
2571
+ constructor(userId, options = {}) {
2301
2572
  __publicField(this, "clients", []);
2302
- __publicField(this, "identity");
2573
+ __publicField(this, "userId");
2303
2574
  __publicField(this, "options");
2304
2575
  __publicField(this, "connectionPromises", /* @__PURE__ */ new Map());
2305
- this.identity = identity;
2576
+ this.userId = userId;
2306
2577
  this.options = {
2307
2578
  timeout: DEFAULT_TIMEOUT_MS,
2308
2579
  maxRetries: DEFAULT_MAX_RETRIES,
@@ -2311,7 +2582,7 @@ var MultiSessionClient = class {
2311
2582
  };
2312
2583
  }
2313
2584
  /**
2314
- * Fetches all sessions for this identity from storage and returns only the
2585
+ * Fetches all sessions for this userId from storage and returns only the
2315
2586
  * ones that are ready to connect.
2316
2587
  *
2317
2588
  * A session is considered connectable when:
@@ -2324,8 +2595,8 @@ var MultiSessionClient = class {
2324
2595
  * for backwards compatibility.
2325
2596
  */
2326
2597
  async getActiveSessions() {
2327
- const sessions = await storage.getIdentitySessionsData(this.identity);
2328
- const valid = sessions.filter(
2598
+ const sessionList = await sessions.list(this.userId);
2599
+ const valid = sessionList.filter(
2329
2600
  (s) => s.serverId && s.serverUrl && s.callbackUrl && s.active !== false
2330
2601
  // exclude OAuth-pending / failed sessions
2331
2602
  );
@@ -2338,9 +2609,9 @@ var MultiSessionClient = class {
2338
2609
  * has many active MCP sessions (e.g. 20+ servers). Within each batch, sessions
2339
2610
  * are connected concurrently using `Promise.all` for speed.
2340
2611
  */
2341
- async connectInBatches(sessions) {
2342
- for (let i = 0; i < sessions.length; i += CONNECTION_BATCH_SIZE) {
2343
- const batch = sessions.slice(i, i + CONNECTION_BATCH_SIZE);
2612
+ async connectInBatches(sessions2) {
2613
+ for (let i = 0; i < sessions2.length; i += CONNECTION_BATCH_SIZE) {
2614
+ const batch = sessions2.slice(i, i + CONNECTION_BATCH_SIZE);
2344
2615
  await Promise.all(batch.map((session) => this.connectSession(session)));
2345
2616
  }
2346
2617
  }
@@ -2391,7 +2662,7 @@ var MultiSessionClient = class {
2391
2662
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
2392
2663
  try {
2393
2664
  const client = new MCPClient({
2394
- identity: this.identity,
2665
+ userId: this.userId,
2395
2666
  sessionId: session.sessionId,
2396
2667
  serverId: session.serverId,
2397
2668
  serverUrl: session.serverUrl,
@@ -2423,7 +2694,7 @@ var MultiSessionClient = class {
2423
2694
  console.error(`[MultiSessionClient] Failed to connect to session ${session.sessionId} after ${maxRetries + 1} attempts:`, lastError);
2424
2695
  }
2425
2696
  /**
2426
- * The main entry point. Fetches all active sessions for this identity from
2697
+ * The main entry point. Fetches all active sessions for this userId from
2427
2698
  * storage and establishes connections to all of them in batches.
2428
2699
  *
2429
2700
  * Call this once after creating the client. On traditional servers, you can
@@ -2431,8 +2702,8 @@ var MultiSessionClient = class {
2431
2702
  * re-fetching and re-connecting on every request.
2432
2703
  */
2433
2704
  async connect() {
2434
- const sessions = await this.getActiveSessions();
2435
- await this.connectInBatches(sessions);
2705
+ const sessions2 = await this.getActiveSessions();
2706
+ await this.connectInBatches(sessions2);
2436
2707
  }
2437
2708
  /**
2438
2709
  * Returns all currently connected `MCPClient` instances.
@@ -2487,11 +2758,11 @@ var SSEConnectionManager = class {
2487
2758
  constructor(options, sendEvent) {
2488
2759
  this.options = options;
2489
2760
  this.sendEvent = sendEvent;
2490
- __publicField(this, "identity");
2761
+ __publicField(this, "userId");
2491
2762
  __publicField(this, "clients", /* @__PURE__ */ new Map());
2492
2763
  __publicField(this, "heartbeatTimer");
2493
2764
  __publicField(this, "isActive", true);
2494
- this.identity = options.identity;
2765
+ this.userId = options.userId;
2495
2766
  this.startHeartbeat();
2496
2767
  }
2497
2768
  /**
@@ -2531,8 +2802,8 @@ var SSEConnectionManager = class {
2531
2802
  try {
2532
2803
  let result;
2533
2804
  switch (request.method) {
2534
- case "getSessions":
2535
- result = await this.getSessions();
2805
+ case "listSessions":
2806
+ result = await this.listSessions();
2536
2807
  break;
2537
2808
  case "connect":
2538
2809
  result = await this.connect(request.params);
@@ -2546,8 +2817,8 @@ var SSEConnectionManager = class {
2546
2817
  case "callTool":
2547
2818
  result = await this.callTool(request.params);
2548
2819
  break;
2549
- case "restoreSession":
2550
- result = await this.restoreSession(request.params);
2820
+ case "getSession":
2821
+ result = await this.getSession(request.params);
2551
2822
  break;
2552
2823
  case "finishAuth":
2553
2824
  result = await this.finishAuth(request.params);
@@ -2586,12 +2857,12 @@ var SSEConnectionManager = class {
2586
2857
  }
2587
2858
  }
2588
2859
  /**
2589
- * Get all sessions for the current identity
2860
+ * Get all sessions for the current userId
2590
2861
  */
2591
- async getSessions() {
2592
- const sessions = await storage.getIdentitySessionsData(this.identity);
2862
+ async listSessions() {
2863
+ const sessionList = await sessions.list(this.userId);
2593
2864
  return {
2594
- sessions: sessions.map((s) => ({
2865
+ sessions: sessionList.map((s) => ({
2595
2866
  sessionId: s.sessionId,
2596
2867
  serverId: s.serverId,
2597
2868
  serverName: s.serverName,
@@ -2608,14 +2879,14 @@ var SSEConnectionManager = class {
2608
2879
  async connect(params) {
2609
2880
  const { serverName, serverUrl, callbackUrl, transportType } = params;
2610
2881
  const headers = normalizeHeaders(params.headers);
2611
- const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await storage.generateSessionId();
2612
- const existingSessions = await storage.getIdentitySessionsData(this.identity);
2882
+ const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : await sessions.generateSessionId();
2883
+ const existingSessions = await sessions.list(this.userId);
2613
2884
  const duplicate = existingSessions.find(
2614
2885
  (s) => s.serverId === serverId || s.serverUrl === serverUrl
2615
2886
  );
2616
2887
  if (duplicate) {
2617
2888
  if (duplicate.active === false) {
2618
- await this.restoreSession({ sessionId: duplicate.sessionId });
2889
+ await this.getSession({ sessionId: duplicate.sessionId });
2619
2890
  return {
2620
2891
  sessionId: duplicate.sessionId,
2621
2892
  success: true
@@ -2623,11 +2894,11 @@ var SSEConnectionManager = class {
2623
2894
  }
2624
2895
  throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
2625
2896
  }
2626
- const sessionId = await storage.generateSessionId();
2897
+ const sessionId = await sessions.generateSessionId();
2627
2898
  try {
2628
2899
  const clientMetadata = await this.getResolvedClientMetadata();
2629
2900
  const client = new MCPClient({
2630
- identity: this.identity,
2901
+ userId: this.userId,
2631
2902
  sessionId,
2632
2903
  serverId,
2633
2904
  serverName,
@@ -2682,7 +2953,7 @@ var SSEConnectionManager = class {
2682
2953
  client.disconnect();
2683
2954
  this.clients.delete(sessionId);
2684
2955
  } else {
2685
- await storage.removeSession(this.identity, sessionId);
2956
+ await sessions.delete(this.userId, sessionId);
2686
2957
  }
2687
2958
  return { success: true };
2688
2959
  }
@@ -2694,12 +2965,12 @@ var SSEConnectionManager = class {
2694
2965
  if (existing) {
2695
2966
  return existing;
2696
2967
  }
2697
- const session = await storage.getSession(this.identity, sessionId);
2968
+ const session = await sessions.get(this.userId, sessionId);
2698
2969
  if (!session) {
2699
2970
  throw new Error("Session not found");
2700
2971
  }
2701
2972
  const client = new MCPClient({
2702
- identity: this.identity,
2973
+ userId: this.userId,
2703
2974
  sessionId,
2704
2975
  // These fields are optional in MCPClient, but when rehydrating a known
2705
2976
  // stored session on the server we pass them explicitly to preserve the
@@ -2746,9 +3017,9 @@ var SSEConnectionManager = class {
2746
3017
  /**
2747
3018
  * Restore and validate an existing session
2748
3019
  */
2749
- async restoreSession(params) {
3020
+ async getSession(params) {
2750
3021
  const { sessionId } = params;
2751
- const session = await storage.getSession(this.identity, sessionId);
3022
+ const session = await sessions.get(this.userId, sessionId);
2752
3023
  if (!session) {
2753
3024
  throw new Error("Session not found");
2754
3025
  }
@@ -2765,7 +3036,7 @@ var SSEConnectionManager = class {
2765
3036
  try {
2766
3037
  const clientMetadata = await this.getResolvedClientMetadata();
2767
3038
  const client = new MCPClient({
2768
- identity: this.identity,
3039
+ userId: this.userId,
2769
3040
  sessionId,
2770
3041
  // These fields are optional in MCPClient, but when rehydrating a known
2771
3042
  // stored session on the server we pass them explicitly to preserve the
@@ -2802,13 +3073,13 @@ var SSEConnectionManager = class {
2802
3073
  */
2803
3074
  async finishAuth(params) {
2804
3075
  const { sessionId, code } = params;
2805
- const session = await storage.getSession(this.identity, sessionId);
3076
+ const session = await sessions.get(this.userId, sessionId);
2806
3077
  if (!session) {
2807
3078
  throw new Error("Session not found");
2808
3079
  }
2809
3080
  try {
2810
3081
  const client = new MCPClient({
2811
- identity: this.identity,
3082
+ userId: this.userId,
2812
3083
  sessionId,
2813
3084
  // These fields are optional in MCPClient, but when rehydrating a known
2814
3085
  // stored session on the server we pass them explicitly to preserve the
@@ -2819,7 +3090,7 @@ var SSEConnectionManager = class {
2819
3090
  serverUrl: session.serverUrl,
2820
3091
  callbackUrl: session.callbackUrl,
2821
3092
  // NOTE: transportType is intentionally omitted here.
2822
- // The session's stored transportType is a placeholder ('streamable_http')
3093
+ // The session's stored transportType is a placeholder ('streamable-http')
2823
3094
  // set before transport negotiation. Omitting it lets MCPClient auto-negotiate
2824
3095
  // (try streamable_http → SSE fallback), which is critical for servers like
2825
3096
  // Neon that only support SSE transport.
@@ -2941,18 +3212,21 @@ function writeSSEEvent(res, event, data) {
2941
3212
  // src/server/handlers/nextjs-handler.ts
2942
3213
  function createNextMcpHandler(options = {}) {
2943
3214
  const {
2944
- getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
3215
+ getUserId = (request) => request.headers.get("x-mcp-user-id"),
2945
3216
  getAuthToken = (request) => {
2946
- const url = new URL(request.url);
2947
- return url.searchParams.get("token") || request.headers.get("authorization");
3217
+ const authHeader = request.headers.get("authorization");
3218
+ if (authHeader?.toLowerCase().startsWith("bearer ")) {
3219
+ return authHeader.slice(7);
3220
+ }
3221
+ return authHeader;
2948
3222
  },
2949
3223
  authenticate = () => true,
2950
3224
  heartbeatInterval = 3e4,
2951
3225
  clientDefaults,
2952
3226
  getClientMetadata
2953
3227
  } = options;
2954
- const toManagerOptions = (identity, resolvedClientMetadata) => ({
2955
- identity,
3228
+ const toManagerOptions = (userId, resolvedClientMetadata) => ({
3229
+ userId,
2956
3230
  heartbeatInterval,
2957
3231
  clientDefaults: resolvedClientMetadata
2958
3232
  });
@@ -2971,13 +3245,13 @@ function createNextMcpHandler(options = {}) {
2971
3245
  );
2972
3246
  }
2973
3247
  async function POST(request) {
2974
- const identity = getIdentity(request);
3248
+ const userId = getUserId(request);
2975
3249
  const authToken = getAuthToken(request);
2976
3250
  const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
2977
- if (!identity) {
2978
- return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
3251
+ if (!userId) {
3252
+ return Response.json({ error: { code: "MISSING_userId", message: "Missing userId" } }, { status: 400 });
2979
3253
  }
2980
- const isAuthorized = await authenticate(identity, authToken);
3254
+ const isAuthorized = await authenticate(userId, authToken);
2981
3255
  if (!isAuthorized) {
2982
3256
  return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
2983
3257
  }
@@ -2999,7 +3273,7 @@ function createNextMcpHandler(options = {}) {
2999
3273
  const resolvedClientMetadata = await resolveClientMetadata(request);
3000
3274
  if (!acceptsEventStream) {
3001
3275
  const manager2 = new SSEConnectionManager(
3002
- toManagerOptions(identity, resolvedClientMetadata),
3276
+ toManagerOptions(userId, resolvedClientMetadata),
3003
3277
  () => {
3004
3278
  }
3005
3279
  );
@@ -3025,7 +3299,7 @@ data: ${JSON.stringify(data)}
3025
3299
  });
3026
3300
  };
3027
3301
  const manager = new SSEConnectionManager(
3028
- toManagerOptions(identity, resolvedClientMetadata),
3302
+ toManagerOptions(userId, resolvedClientMetadata),
3029
3303
  (event) => {
3030
3304
  if (isRpcResponseEvent(event)) {
3031
3305
  sendSSE("rpc-response", event);
@@ -3068,7 +3342,7 @@ data: ${JSON.stringify(data)}
3068
3342
  } catch (error) {
3069
3343
  const err = error instanceof Error ? error : new Error("Unknown error");
3070
3344
  console.error("[MCP Next Handler] Failed to handle RPC", {
3071
- identity,
3345
+ userId,
3072
3346
  message: err.message,
3073
3347
  stack: err.stack,
3074
3348
  rawBody: rawBody.slice(0, 500)
@@ -3108,8 +3382,8 @@ var SSEClient = class {
3108
3382
  isConnected() {
3109
3383
  return this.connected;
3110
3384
  }
3111
- async getSessions() {
3112
- return this.sendRequest("getSessions");
3385
+ async listSessions() {
3386
+ return this.sendRequest("listSessions");
3113
3387
  }
3114
3388
  async connectToServer(params) {
3115
3389
  return this.sendRequest("connect", params);
@@ -3125,8 +3399,8 @@ var SSEClient = class {
3125
3399
  this.emitUiEventIfPresent(result, sessionId, toolName);
3126
3400
  return result;
3127
3401
  }
3128
- async restoreSession(sessionId) {
3129
- return this.sendRequest("restoreSession", { sessionId });
3402
+ async getSession(sessionId) {
3403
+ return this.sendRequest("getSession", { sessionId });
3130
3404
  }
3131
3405
  async finishAuth(sessionId, code) {
3132
3406
  return this.sendRequest("finishAuth", { sessionId, code });
@@ -3192,7 +3466,7 @@ var SSEClient = class {
3192
3466
  return this.parseRpcResponse(data2);
3193
3467
  }
3194
3468
  const data = await this.readRpcResponseFromStream(response, {
3195
- delayConnectionEvents: method === "connect" || method === "restoreSession" || method === "finishAuth"
3469
+ delayConnectionEvents: method === "connect" || method === "getSession" || method === "finishAuth"
3196
3470
  });
3197
3471
  return this.parseRpcResponse(data);
3198
3472
  }
@@ -3282,17 +3556,13 @@ var SSEClient = class {
3282
3556
  throw new Error("Invalid RPC response format");
3283
3557
  }
3284
3558
  buildUrl() {
3285
- const url = new URL(this.options.url, globalThis.location?.origin);
3286
- url.searchParams.set("identity", this.options.identity);
3287
- if (this.options.authToken) {
3288
- url.searchParams.set("token", this.options.authToken);
3289
- }
3290
- return url.toString();
3559
+ return new URL(this.options.url, globalThis.location?.origin).toString();
3291
3560
  }
3292
3561
  buildHeaders() {
3293
3562
  const headers = {
3294
3563
  "Content-Type": "application/json",
3295
- "Accept": "text/event-stream"
3564
+ "Accept": "text/event-stream",
3565
+ "x-mcp-user-id": this.options.userId
3296
3566
  };
3297
3567
  if (this.options.authToken) {
3298
3568
  headers["Authorization"] = `Bearer ${this.options.authToken}`;
@@ -3754,7 +4024,7 @@ var AppHost = class {
3754
4024
  async getSessionId() {
3755
4025
  if (this.sessionId) return this.sessionId;
3756
4026
  if (!this.client) return void 0;
3757
- const result = await this.client.getSessions();
4027
+ const result = await this.client.listSessions();
3758
4028
  return result.sessions?.[0]?.sessionId;
3759
4029
  }
3760
4030
  isMcpUri(url) {
@@ -3826,17 +4096,7 @@ function findToolByName(connections, toolName) {
3826
4096
  }
3827
4097
 
3828
4098
  // src/shared/tool-index.ts
3829
- var CALIBRATION_DIVISOR = 3.6;
3830
- function classifyChar(ch) {
3831
- const code = ch.charCodeAt(0);
3832
- if (code <= 32 || ch === "{" || ch === "}" || ch === "[" || ch === "]" || ch === ":" || ch === ",") return 1;
3833
- if (code >= 33 && code <= 47) return 1.5;
3834
- if (code >= 48 && code <= 57) return 2;
3835
- if (code >= 65 && code <= 90) return 3.5;
3836
- if (code >= 97 && code <= 122) return 4;
3837
- return 2.5;
3838
- }
3839
- var ToolIndex = class _ToolIndex {
4099
+ var ToolIndex = class {
3840
4100
  constructor(options = {}) {
3841
4101
  /** All indexed tools keyed by name (supports duplicates). */
3842
4102
  __publicField(this, "tools", /* @__PURE__ */ new Map());
@@ -3854,8 +4114,6 @@ var ToolIndex = class _ToolIndex {
3854
4114
  __publicField(this, "docLengths", /* @__PURE__ */ new Map());
3855
4115
  /** BM25: average document length across the entire index. */
3856
4116
  __publicField(this, "avgDocLength", 0);
3857
- /** Cached total estimated token cost across all indexed tools. */
3858
- __publicField(this, "totalTokenCost", 0);
3859
4117
  __publicField(this, "options");
3860
4118
  this.options = {
3861
4119
  embedFn: options.embedFn ?? void 0,
@@ -3878,7 +4136,6 @@ var ToolIndex = class _ToolIndex {
3878
4136
  this.embeddings.clear();
3879
4137
  this.docLengths.clear();
3880
4138
  this.avgDocLength = 0;
3881
- this.totalTokenCost = 0;
3882
4139
  const allTokenSets = /* @__PURE__ */ new Map();
3883
4140
  let totalLength = 0;
3884
4141
  for (const tool of tools) {
@@ -3887,19 +4144,17 @@ var ToolIndex = class _ToolIndex {
3887
4144
  this.tools.set(tool.name, []);
3888
4145
  }
3889
4146
  this.tools.get(tool.name).push(tool);
3890
- const estimatedTokens = _ToolIndex.estimateTokens(tool);
3891
4147
  this.toolSummaries.set(docKey, {
3892
4148
  name: tool.name,
3893
4149
  description: tool.description ?? "",
3894
4150
  serverName: tool.serverName,
3895
4151
  serverId: tool.serverId,
3896
- sessionId: tool.sessionId,
3897
- estimatedTokens
4152
+ sessionId: tool.sessionId
3898
4153
  });
3899
- this.totalTokenCost += estimatedTokens;
3900
- const text = this.buildSearchableText(tool).toLowerCase();
4154
+ const rawText = this.buildSearchableText(tool);
4155
+ const text = rawText.toLowerCase();
3901
4156
  this.searchTexts.set(docKey, text);
3902
- const tokens = this.tokenize(text);
4157
+ const tokens = this.tokenize(rawText);
3903
4158
  const tf = /* @__PURE__ */ new Map();
3904
4159
  const uniqueTokens = /* @__PURE__ */ new Set();
3905
4160
  for (const tok of tokens) {
@@ -4163,30 +4418,6 @@ var ToolIndex = class _ToolIndex {
4163
4418
  }
4164
4419
  return count;
4165
4420
  }
4166
- /** Total estimated token cost of all indexed tool schemas. */
4167
- getTotalTokenCost() {
4168
- return this.totalTokenCost;
4169
- }
4170
- // -----------------------------------------------------------------------
4171
- // Static Helpers
4172
- // -----------------------------------------------------------------------
4173
- /**
4174
- * Estimate token count of a tool's full schema (name + description + inputSchema).
4175
- *
4176
- * Uses character-class weighted counting calibrated against cl100k_base.
4177
- * Accuracy is typically within ±10% for JSON Schema payloads.
4178
- */
4179
- static estimateTokens(tool) {
4180
- const parts = [tool.name];
4181
- if (tool.description) parts.push(tool.description);
4182
- if (tool.inputSchema) parts.push(JSON.stringify(tool.inputSchema));
4183
- const text = parts.join(" ");
4184
- let weightedLen = 0;
4185
- for (let i = 0; i < text.length; i++) {
4186
- weightedLen += 1 / classifyChar(text[i]);
4187
- }
4188
- return Math.ceil(weightedLen / (1 / CALIBRATION_DIVISOR));
4189
- }
4190
4421
  // -----------------------------------------------------------------------
4191
4422
  // Internals
4192
4423
  // -----------------------------------------------------------------------
@@ -4195,18 +4426,74 @@ var ToolIndex = class _ToolIndex {
4195
4426
  const parts = [tool.name];
4196
4427
  if (tool.description) parts.push(tool.description);
4197
4428
  if (tool.inputSchema && typeof tool.inputSchema === "object") {
4198
- const schema = tool.inputSchema;
4199
- const props = schema.properties;
4200
- if (props) {
4201
- for (const [key, val] of Object.entries(props)) {
4202
- parts.push(key);
4203
- if (val && typeof val === "object" && val.description) {
4204
- parts.push(val.description);
4205
- }
4429
+ this.collectSchemaSearchText(tool.inputSchema, parts);
4430
+ }
4431
+ return parts.join(" ");
4432
+ }
4433
+ /** Recursively collect JSON Schema argument names and descriptions. */
4434
+ collectSchemaSearchText(schema, parts, seen = /* @__PURE__ */ new WeakSet()) {
4435
+ if (!schema || typeof schema !== "object") return;
4436
+ if (seen.has(schema)) return;
4437
+ seen.add(schema);
4438
+ if (Array.isArray(schema)) {
4439
+ for (const item of schema) {
4440
+ this.collectSchemaSearchText(item, parts, seen);
4441
+ }
4442
+ return;
4443
+ }
4444
+ const schemaObject = schema;
4445
+ this.pushStringValue(schemaObject.description, parts);
4446
+ this.pushStringValue(schemaObject.title, parts);
4447
+ const properties = schemaObject.properties;
4448
+ if (properties && typeof properties === "object" && !Array.isArray(properties)) {
4449
+ for (const [propertyName, propertySchema] of Object.entries(properties)) {
4450
+ parts.push(propertyName);
4451
+ this.collectSchemaSearchText(propertySchema, parts, seen);
4452
+ }
4453
+ }
4454
+ const patternProperties = schemaObject.patternProperties;
4455
+ if (patternProperties && typeof patternProperties === "object" && !Array.isArray(patternProperties)) {
4456
+ for (const [propertyPattern, propertySchema] of Object.entries(patternProperties)) {
4457
+ parts.push(propertyPattern);
4458
+ this.collectSchemaSearchText(propertySchema, parts, seen);
4459
+ }
4460
+ }
4461
+ const dependentSchemas = schemaObject.dependentSchemas;
4462
+ if (dependentSchemas && typeof dependentSchemas === "object" && !Array.isArray(dependentSchemas)) {
4463
+ for (const [propertyName, dependentSchema] of Object.entries(dependentSchemas)) {
4464
+ parts.push(propertyName);
4465
+ this.collectSchemaSearchText(dependentSchema, parts, seen);
4466
+ }
4467
+ }
4468
+ for (const key of [
4469
+ "items",
4470
+ "additionalProperties",
4471
+ "contains",
4472
+ "propertyNames",
4473
+ "if",
4474
+ "then",
4475
+ "else",
4476
+ "not"
4477
+ ]) {
4478
+ this.collectSchemaSearchText(schemaObject[key], parts, seen);
4479
+ }
4480
+ for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
4481
+ this.collectSchemaSearchText(schemaObject[key], parts, seen);
4482
+ }
4483
+ for (const key of ["$defs", "definitions"]) {
4484
+ const definitions = schemaObject[key];
4485
+ if (definitions && typeof definitions === "object" && !Array.isArray(definitions)) {
4486
+ for (const [definitionName, definitionSchema] of Object.entries(definitions)) {
4487
+ parts.push(definitionName);
4488
+ this.collectSchemaSearchText(definitionSchema, parts, seen);
4206
4489
  }
4207
4490
  }
4208
4491
  }
4209
- return parts.join(" ");
4492
+ }
4493
+ pushStringValue(value, parts) {
4494
+ if (typeof value === "string" && value.trim()) {
4495
+ parts.push(value);
4496
+ }
4210
4497
  }
4211
4498
  getDocumentKey(tool) {
4212
4499
  return `${tool.sessionId}::${tool.serverId}::${tool.name}`;
@@ -4227,7 +4514,7 @@ var ToolIndex = class _ToolIndex {
4227
4514
  }
4228
4515
  /** Simple whitespace + camelCase + snake_case tokenizer. */
4229
4516
  tokenize(text) {
4230
- return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((t) => t.length > 1);
4517
+ return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((t) => t.length > 1);
4231
4518
  }
4232
4519
  /** Cosine similarity between two vectors. */
4233
4520
  cosineSimilarity(a, b) {
@@ -4282,27 +4569,6 @@ var SchemaCompressor = class _SchemaCompressor {
4282
4569
  const limited = options?.maxTools ? tools.slice(0, options.maxTools) : tools;
4283
4570
  return limited.map((t) => _SchemaCompressor.toCompact(t));
4284
4571
  }
4285
- /**
4286
- * Estimate token savings from using compact vs full tool schemas.
4287
- */
4288
- static estimateSavings(tools) {
4289
- let fullTokens = 0;
4290
- let compactTokens = 0;
4291
- for (const tool of tools) {
4292
- fullTokens += ToolIndex.estimateTokens(tool);
4293
- const compact = _SchemaCompressor.toCompact(tool);
4294
- const text = [compact.name, compact.description ?? "", compact.parameterHint ?? ""].join(" ");
4295
- compactTokens += Math.ceil(text.length / 4);
4296
- }
4297
- const saved = fullTokens - compactTokens;
4298
- const pct = fullTokens > 0 ? (saved / fullTokens * 100).toFixed(1) : "0.0";
4299
- return {
4300
- fullTokens,
4301
- compactTokens,
4302
- savedTokens: saved,
4303
- savingsPercent: `${pct}%`
4304
- };
4305
- }
4306
4572
  };
4307
4573
 
4308
4574
  // src/shared/meta-tools.ts
@@ -4498,7 +4764,7 @@ async function executeMetaTool(toolName, args, router, callToolFn) {
4498
4764
  const lines = [];
4499
4765
  if (found.length > 0) {
4500
4766
  lines.push(...found.map(
4501
- (t, i) => `${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})
4767
+ (t, i) => `${i + 1}. **${t.name}** (serverName: ${t.serverName}, serverId: ${t.serverId})
4502
4768
  ${t.description}`
4503
4769
  ));
4504
4770
  }
@@ -4526,7 +4792,7 @@ async function executeMetaTool(toolName, args, router, callToolFn) {
4526
4792
  serverName: query || void 0
4527
4793
  });
4528
4794
  const text = servers.length === 0 ? "No connected servers found." : servers.map(
4529
- (server, i) => `${i + 1}. **${server.serverName}** (serverId: ${server.serverId}, sessionId: ${server.sessionId})
4795
+ (server, i) => `${i + 1}. **${server.serverName}** (serverId: ${server.serverId})
4530
4796
  Tool count: ${server.toolCount}`
4531
4797
  ).join("\n");
4532
4798
  return {
@@ -4633,9 +4899,8 @@ async function executeMetaTool(toolName, args, router, callToolFn) {
4633
4899
  }
4634
4900
  function formatToolSummaries(tools) {
4635
4901
  return tools.map(
4636
- (t, i) => `${i + 1}. **${t.name}** (server: ${t.serverName}, serverId: ${t.serverId})
4637
- ${t.description}
4638
- Estimated tokens: ${t.estimatedTokens}`
4902
+ (t, i) => `${i + 1}. **${t.name}** (serverName: ${t.serverName}, serverId: ${t.serverId})
4903
+ ${t.description}`
4639
4904
  );
4640
4905
  }
4641
4906
  function isMetaTool(toolName) {
@@ -4778,26 +5043,6 @@ var ToolRouter = class {
4778
5043
  getActiveGroups() {
4779
5044
  return [...this.activeGroups];
4780
5045
  }
4781
- // -----------------------------------------------------------------------
4782
- // Stats & Introspection
4783
- // -----------------------------------------------------------------------
4784
- /** Total token cost of all tools if loaded without filtering. */
4785
- getTotalTokenCost() {
4786
- return this.index.getTotalTokenCost();
4787
- }
4788
- /** Estimate token cost of the currently filtered tool set. */
4789
- async getFilteredTokenCost() {
4790
- const tools = await this.getFilteredTools();
4791
- let total = 0;
4792
- for (const tool of tools) {
4793
- total += ToolIndex.estimateTokens(tool);
4794
- }
4795
- return total;
4796
- }
4797
- /** Get compression stats showing savings from current strategy. */
4798
- getCompressionStats() {
4799
- return SchemaCompressor.estimateSavings(this.allTools);
4800
- }
4801
5046
  /** Number of total indexed tools. */
4802
5047
  get totalToolCount() {
4803
5048
  return this.allTools.length;
@@ -4943,6 +5188,6 @@ var ToolRouter = class {
4943
5188
  }
4944
5189
  };
4945
5190
 
4946
- export { APP_HOST_DEFAULTS, AppHost, AuthenticationError, ConfigurationError, ConnectionError, DEFAULT_CLIENT_NAME, DEFAULT_CLIENT_URI, DEFAULT_HEARTBEAT_INTERVAL_MS, DEFAULT_LOGO_URI, DEFAULT_MCP_APP_CSP, DEFAULT_POLICY_URI, DisposableStore, Emitter, InvalidStateError, MCPClient, MCP_CLIENT_NAME, MCP_CLIENT_VERSION, McpError, MultiSessionClient, NotConnectedError, REDIS_KEY_PREFIX, RpcErrorCodes, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, SESSION_TTL_SECONDS, SOFTWARE_ID, SOFTWARE_VERSION, SSEClient, SSEConnectionManager, STATE_EXPIRATION_MS, SchemaCompressor, SessionNotFoundError, SessionValidationError, StorageOAuthClientProvider, TOKEN_EXPIRY_BUFFER_MS, ToolExecutionError, ToolIndex, ToolRouter, UnauthorizedError, createExecuteToolDefinition, createGetSchemaToolDefinition, createListServersToolDefinition, createNextMcpHandler, createRegexSearchToolDefinition, createSSEHandler, createSearchToolDefinition, executeMetaTool, findToolByName, getToolUiResourceUri, isCallToolSuccess, isConnectAuthRequired, isConnectError, isConnectSuccess, isListToolsSuccess, isMetaTool, resolveMetaToolProxy, sanitizeServerLabel, storage };
5191
+ export { APP_HOST_DEFAULTS, AppHost, AuthenticationError, ConfigurationError, ConnectionError, DEFAULT_CLIENT_NAME, DEFAULT_CLIENT_URI, DEFAULT_HEARTBEAT_INTERVAL_MS, DEFAULT_LOGO_URI, DEFAULT_MCP_APP_CSP, DEFAULT_POLICY_URI, DisposableStore, Emitter, InvalidStateError, MCPClient, MCP_CLIENT_NAME, MCP_CLIENT_VERSION, McpError, MultiSessionClient, NotConnectedError, REDIS_KEY_PREFIX, RpcErrorCodes, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, SESSION_TTL_SECONDS, SOFTWARE_ID, SOFTWARE_VERSION, SSEClient, SSEConnectionManager, STATE_EXPIRATION_MS, SchemaCompressor, SessionNotFoundError, SessionValidationError, StorageOAuthClientProvider, TOKEN_EXPIRY_BUFFER_MS, ToolExecutionError, ToolIndex, ToolRouter, UnauthorizedError, createExecuteToolDefinition, createGetSchemaToolDefinition, createListServersToolDefinition, createNextMcpHandler, createRegexSearchToolDefinition, createSSEHandler, createSearchToolDefinition, executeMetaTool, findToolByName, getToolUiResourceUri, isCallToolSuccess, isConnectAuthRequired, isConnectError, isConnectSuccess, isListToolsSuccess, isMetaTool, resolveMetaToolProxy, sanitizeServerLabel, sessions };
4947
5192
  //# sourceMappingURL=index.mjs.map
4948
5193
  //# sourceMappingURL=index.mjs.map