@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
@@ -5,7 +5,7 @@ import type {
5
5
  OAuthClientMetadata,
6
6
  OAuthTokens
7
7
  } from "@modelcontextprotocol/sdk/shared/auth.js";
8
- import { storage, SessionData } from "../storage/index.js";
8
+ import { sessions, type Session } from "../storage/index.js";
9
9
  import {
10
10
  DEFAULT_CLIENT_NAME,
11
11
  DEFAULT_CLIENT_URI,
@@ -34,7 +34,7 @@ export interface AgentsOAuthProvider extends OAuthClientProvider {
34
34
  }
35
35
 
36
36
  export interface StorageOAuthClientProviderOptions {
37
- identity: string;
37
+ userId: string;
38
38
  serverId: string;
39
39
  sessionId: string;
40
40
  redirectUrl: string;
@@ -49,10 +49,10 @@ export interface StorageOAuthClientProviderOptions {
49
49
 
50
50
  /**
51
51
  * Storage-backed OAuth provider implementation for MCP
52
- * Stores OAuth tokens, client information, and PKCE verifiers using the configured StorageBackend
52
+ * Stores OAuth tokens, client information, and PKCE verifiers using the configured SessionStore
53
53
  */
54
54
  export class StorageOAuthClientProvider implements AgentsOAuthProvider {
55
- public readonly identity: string;
55
+ public readonly userId: string;
56
56
  public readonly serverId: string;
57
57
  public readonly sessionId: string;
58
58
  public readonly redirectUrl: string;
@@ -69,11 +69,11 @@ export class StorageOAuthClientProvider implements AgentsOAuthProvider {
69
69
  private tokenExpiresAt?: number;
70
70
 
71
71
  /**
72
- * Creates a new storage-backed OAuth provider
72
+ * Creates a new session-backed OAuth provider
73
73
  * @param options - Provider configuration
74
74
  */
75
75
  constructor(options: StorageOAuthClientProviderOptions) {
76
- this.identity = options.identity;
76
+ this.userId = options.userId;
77
77
  this.serverId = options.serverId;
78
78
  this.sessionId = options.sessionId;
79
79
  this.redirectUrl = options.redirectUrl;
@@ -110,25 +110,25 @@ export class StorageOAuthClientProvider implements AgentsOAuthProvider {
110
110
  }
111
111
 
112
112
  /**
113
- * Loads OAuth data from storage session
113
+ * Loads OAuth data from the session store
114
114
  * @private
115
115
  */
116
- private async getSessionData(): Promise<SessionData> {
117
- const data = await storage.getSession(this.identity, this.sessionId);
116
+ private async getSessionData(): Promise<Session> {
117
+ const data = await sessions.get(this.userId, this.sessionId);
118
118
  if (!data) {
119
- return {} as SessionData;
119
+ return {} as Session;
120
120
  }
121
121
  return data;
122
122
  }
123
123
 
124
124
  /**
125
- * Saves OAuth data to storage
125
+ * Saves OAuth data to the session store
126
126
  * @param data - Partial OAuth data to save
127
127
  * @private
128
128
  * @throws Error if session doesn't exist (session must be created by controller layer)
129
129
  */
130
- private async saveSessionData(data: Partial<SessionData>): Promise<void> {
131
- await storage.updateSession(this.identity, this.sessionId, data);
130
+ private async saveSessionData(data: Partial<Session>): Promise<void> {
131
+ await sessions.update(this.userId, this.sessionId, data);
132
132
  }
133
133
 
134
134
  /**
@@ -170,7 +170,7 @@ export class StorageOAuthClientProvider implements AgentsOAuthProvider {
170
170
  * Stores OAuth tokens
171
171
  */
172
172
  async saveTokens(tokens: OAuthTokens): Promise<void> {
173
- const data: Partial<SessionData> = { tokens };
173
+ const data: Partial<Session> = { tokens };
174
174
 
175
175
  if (tokens.expires_in) {
176
176
  this.tokenExpiresAt = Date.now() + (tokens.expires_in * 1000) - TOKEN_EXPIRY_BUFFER_MS;
@@ -188,7 +188,7 @@ export class StorageOAuthClientProvider implements AgentsOAuthProvider {
188
188
  }
189
189
 
190
190
  async checkState(_state: string): Promise<{ valid: boolean; serverId?: string; error?: string }> {
191
- const data = await storage.getSession(this.identity, this.sessionId);
191
+ const data = await sessions.get(this.userId, this.sessionId);
192
192
 
193
193
  if (!data) {
194
194
  return { valid: false, error: "Session not found" };
@@ -212,9 +212,9 @@ export class StorageOAuthClientProvider implements AgentsOAuthProvider {
212
212
  scope: "all" | "client" | "tokens" | "verifier"
213
213
  ): Promise<void> {
214
214
  if (scope === "all") {
215
- await storage.removeSession(this.identity, this.sessionId);
215
+ await sessions.delete(this.userId, this.sessionId);
216
216
  } else {
217
- const updates: Partial<SessionData> = {};
217
+ const updates: Partial<Session> = {};
218
218
 
219
219
  if (scope === "client") {
220
220
  updates.clientInformation = undefined;
@@ -1,15 +1,15 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import * as path from 'path';
3
- import { StorageBackend, SessionData, SetClientOptions } from './types.js';
3
+ import type { SessionStore, Session, SetClientOptions } from './types.js';
4
4
  import { generateSessionId } from '../../shared/utils.js';
5
5
 
6
6
  /**
7
- * File system implementation of StorageBackend
7
+ * File system implementation of SessionStore
8
8
  * Persists sessions to a JSON file
9
9
  */
10
- export class FileStorageBackend implements StorageBackend {
10
+ export class FileStorageBackend implements SessionStore {
11
11
  private filePath: string;
12
- private memoryCache: Map<string, SessionData> | null = null;
12
+ private memoryCache: Map<string, Session> | null = null;
13
13
  private initialized = false;
14
14
 
15
15
  /**
@@ -36,8 +36,8 @@ export class FileStorageBackend implements StorageBackend {
36
36
 
37
37
  this.memoryCache = new Map();
38
38
  if (Array.isArray(json)) {
39
- json.forEach((s: SessionData) => {
40
- this.memoryCache!.set(this.getSessionKey(s.identity || 'unknown', s.sessionId), s);
39
+ json.forEach((s: Session) => {
40
+ this.memoryCache!.set(this.getSessionKey(s.userId || 'unknown', s.sessionId), s);
41
41
  });
42
42
  }
43
43
  } catch (error: any) {
@@ -65,20 +65,20 @@ export class FileStorageBackend implements StorageBackend {
65
65
  await fs.writeFile(this.filePath, JSON.stringify(sessions, null, 2), 'utf-8');
66
66
  }
67
67
 
68
- private getSessionKey(identity: string, sessionId: string): string {
69
- return `${identity}:${sessionId}`;
68
+ private getSessionKey(userId: string, sessionId: string): string {
69
+ return `${userId}:${sessionId}`;
70
70
  }
71
71
 
72
72
  generateSessionId(): string {
73
73
  return generateSessionId();
74
74
  }
75
75
 
76
- async createSession(session: SessionData, ttl?: number): Promise<void> {
76
+ async create(session: Session, ttl?: number): Promise<void> {
77
77
  await this.ensureInitialized();
78
- const { sessionId, identity } = session;
79
- if (!sessionId || !identity) throw new Error('identity and sessionId required');
78
+ const { sessionId, userId } = session;
79
+ if (!sessionId || !userId) throw new Error('userId and sessionId required');
80
80
 
81
- const sessionKey = this.getSessionKey(identity, sessionId);
81
+ const sessionKey = this.getSessionKey(userId, sessionId);
82
82
  if (this.memoryCache!.has(sessionKey)) {
83
83
  throw new Error(`Session ${sessionId} already exists`);
84
84
  }
@@ -88,11 +88,11 @@ export class FileStorageBackend implements StorageBackend {
88
88
  // Note: TTL is ignored in file backend - sessions don't auto-expire
89
89
  }
90
90
 
91
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
91
+ async update(userId: string, sessionId: string, data: Partial<Session>, ttl?: number): Promise<void> {
92
92
  await this.ensureInitialized();
93
- if (!identity || !sessionId) throw new Error('identity and sessionId required');
93
+ if (!userId || !sessionId) throw new Error('userId and sessionId required');
94
94
 
95
- const sessionKey = this.getSessionKey(identity, sessionId);
95
+ const sessionKey = this.getSessionKey(userId, sessionId);
96
96
  const current = this.memoryCache!.get(sessionKey);
97
97
 
98
98
  if (!current) {
@@ -109,33 +109,33 @@ export class FileStorageBackend implements StorageBackend {
109
109
  // Note: TTL is ignored in file backend - sessions don't auto-expire
110
110
  }
111
111
 
112
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
112
+ async get(userId: string, sessionId: string): Promise<Session | null> {
113
113
  await this.ensureInitialized();
114
- const sessionKey = this.getSessionKey(identity, sessionId);
114
+ const sessionKey = this.getSessionKey(userId, sessionId);
115
115
  return this.memoryCache!.get(sessionKey) || null;
116
116
  }
117
117
 
118
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
118
+ async list(userId: string): Promise<Session[]> {
119
119
  await this.ensureInitialized();
120
- return Array.from(this.memoryCache!.values()).filter(s => s.identity === identity);
120
+ return Array.from(this.memoryCache!.values()).filter(s => s.userId === userId);
121
121
  }
122
122
 
123
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
123
+ async listIds(userId: string): Promise<string[]> {
124
124
  await this.ensureInitialized();
125
125
  return Array.from(this.memoryCache!.values())
126
- .filter(s => s.identity === identity)
126
+ .filter(s => s.userId === userId)
127
127
  .map(s => s.sessionId);
128
128
  }
129
129
 
130
- async removeSession(identity: string, sessionId: string): Promise<void> {
130
+ async delete(userId: string, sessionId: string): Promise<void> {
131
131
  await this.ensureInitialized();
132
- const sessionKey = this.getSessionKey(identity, sessionId);
132
+ const sessionKey = this.getSessionKey(userId, sessionId);
133
133
  if (this.memoryCache!.delete(sessionKey)) {
134
134
  await this.flush();
135
135
  }
136
136
  }
137
137
 
138
- async getAllSessionIds(): Promise<string[]> {
138
+ async listAllIds(): Promise<string[]> {
139
139
  await this.ensureInitialized();
140
140
  return Array.from(this.memoryCache!.values()).map(s => s.sessionId);
141
141
  }
@@ -146,7 +146,7 @@ export class FileStorageBackend implements StorageBackend {
146
146
  await this.flush();
147
147
  }
148
148
 
149
- async cleanupExpiredSessions(): Promise<void> {
149
+ async cleanupExpired(): Promise<void> {
150
150
  // Could implement TTL check here using createdAt
151
151
  await this.ensureInitialized();
152
152
  }
@@ -4,28 +4,53 @@ import { MemoryStorageBackend } from './memory-backend';
4
4
  import { FileStorageBackend } from './file-backend';
5
5
  import { SqliteStorage } from './sqlite-backend.js';
6
6
  import { SupabaseStorageBackend } from './supabase-backend.js';
7
- import type { StorageBackend } from './types.js';
7
+ import { NeonStorageBackend, type NeonStorageOptions } from './neon-backend.js';
8
+ import type { SessionStore } from './types.js';
8
9
 
9
10
  // Re-export types
10
11
  export * from './types.js';
11
12
  export { generateSessionId } from '../../shared/utils.js';
12
- export { RedisStorageBackend, MemoryStorageBackend, FileStorageBackend, SqliteStorage, SupabaseStorageBackend };
13
+ export { RedisStorageBackend, MemoryStorageBackend, FileStorageBackend, SqliteStorage, SupabaseStorageBackend, NeonStorageBackend };
13
14
 
14
15
  export function createSupabaseStorageBackend(client: any): SupabaseStorageBackend {
15
16
  return new SupabaseStorageBackend(client);
16
17
  }
17
18
 
18
- let storageInstance: StorageBackend | null = null;
19
- let storagePromise: Promise<StorageBackend> | null = null;
19
+ export function createNeonStorageBackend(sql: any, options?: NeonStorageOptions): NeonStorageBackend {
20
+ return new NeonStorageBackend(sql, options);
21
+ }
22
+
23
+ function warnIfNeonConnectionStringIsInsecure(connectionString: string): void {
24
+ try {
25
+ const url = new URL(connectionString);
26
+ const sslMode = url.searchParams.get('sslmode');
27
+ const channelBinding = url.searchParams.get('channel_binding');
28
+
29
+ if (!sslMode) {
30
+ console.warn('[mcp-ts][Storage] Neon connection string does not include sslmode. Neon recommends sslmode=verify-full for the strongest certificate verification.');
31
+ } else if (!['verify-full', 'require'].includes(sslMode)) {
32
+ console.warn(`[mcp-ts][Storage] Neon connection string uses sslmode=${sslMode}. Use sslmode=verify-full or sslmode=require for secure connections.`);
33
+ }
34
+
35
+ if (!channelBinding) {
36
+ console.warn('[mcp-ts][Storage] Neon connection string does not include channel_binding=require. Add it when supported by your runtime and connection path.');
37
+ }
38
+ } catch {
39
+ console.warn('[mcp-ts][Storage] Neon connection string could not be parsed for SSL checks.');
40
+ }
41
+ }
42
+
43
+ let storageInstance: SessionStore | null = null;
44
+ let storagePromise: Promise<SessionStore> | null = null;
20
45
 
21
- async function initializeStorage<T extends StorageBackend>(store: T): Promise<T> {
46
+ async function initializeStorage<T extends SessionStore>(store: T): Promise<T> {
22
47
  if (typeof store.init === 'function') {
23
48
  await store.init();
24
49
  }
25
50
  return store;
26
51
  }
27
52
 
28
- async function createStorage(): Promise<StorageBackend> {
53
+ async function createStorage(): Promise<SessionStore> {
29
54
  const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
30
55
 
31
56
  // Explicit selection
@@ -80,6 +105,26 @@ async function createStorage(): Promise<StorageBackend> {
80
105
  }
81
106
  }
82
107
 
108
+ if (type === 'neon') {
109
+ const connectionString = process.env.NEON_DATABASE_URL || process.env.DATABASE_URL;
110
+
111
+ if (!connectionString) {
112
+ console.warn('[mcp-ts][Storage] Explicit selection "neon" requires NEON_DATABASE_URL or DATABASE_URL.');
113
+ } else {
114
+ try {
115
+ const { neon } = await import('@neondatabase/serverless');
116
+ warnIfNeonConnectionStringIsInsecure(connectionString);
117
+ const sql = neon(connectionString);
118
+ console.log('[mcp-ts][Storage] Explicit selection: "neon"');
119
+ return await initializeStorage(new NeonStorageBackend(sql));
120
+ } catch (error: any) {
121
+ console.error('[mcp-ts][Storage] Failed to initialize Neon:', error.message);
122
+ console.log('[mcp-ts][Storage] Falling back to In-Memory storage');
123
+ return await initializeStorage(new MemoryStorageBackend());
124
+ }
125
+ }
126
+ }
127
+
83
128
  if (type === 'memory') {
84
129
  console.log('[mcp-ts][Storage] Explicit selection: "memory"');
85
130
  return await initializeStorage(new MemoryStorageBackend());
@@ -126,11 +171,23 @@ async function createStorage(): Promise<StorageBackend> {
126
171
  }
127
172
  }
128
173
 
174
+ if (process.env.NEON_DATABASE_URL) {
175
+ try {
176
+ const { neon } = await import('@neondatabase/serverless');
177
+ warnIfNeonConnectionStringIsInsecure(process.env.NEON_DATABASE_URL);
178
+ const sql = neon(process.env.NEON_DATABASE_URL);
179
+ console.log('[mcp-ts][Storage] Auto-detection: "neon" (via NEON_DATABASE_URL)');
180
+ return await initializeStorage(new NeonStorageBackend(sql));
181
+ } catch (error: any) {
182
+ console.error('[mcp-ts][Storage] Neon auto-detection failed:', error.message);
183
+ }
184
+ }
185
+
129
186
  console.log('[mcp-ts][Storage] Defaulting to: "memory"');
130
187
  return await initializeStorage(new MemoryStorageBackend());
131
188
  }
132
189
 
133
- async function getStorage(): Promise<StorageBackend> {
190
+ async function getStorage(): Promise<SessionStore> {
134
191
  if (storageInstance) {
135
192
  return storageInstance;
136
193
  }
@@ -149,9 +206,9 @@ async function getStorage(): Promise<StorageBackend> {
149
206
  /**
150
207
  * Set the storage instance (for testing)
151
208
  * @internal
152
- * @param instance - StorageBackend instance or null to reset
209
+ * @param instance - SessionStore instance or null to reset
153
210
  */
154
- export function _setStorageInstanceForTesting(instance: StorageBackend | null): void {
211
+ export function _setStorageInstanceForTesting(instance: SessionStore | null): void {
155
212
  storageInstance = instance;
156
213
  if (!instance) {
157
214
  storagePromise = null;
@@ -162,7 +219,7 @@ export function _setStorageInstanceForTesting(instance: StorageBackend | null):
162
219
  * Global session store instance
163
220
  * Uses lazy initialization with a Proxy to handle async setup transparently
164
221
  */
165
- export const storage: StorageBackend = new Proxy({} as StorageBackend, {
222
+ export const sessions: SessionStore = new Proxy({} as SessionStore, {
166
223
  get(_target, prop) {
167
224
  return async (...args: any[]) => {
168
225
  const instance = await getStorage();
@@ -1,16 +1,16 @@
1
- import { StorageBackend, SessionData, SetClientOptions } from './types.js';
1
+ import type { SessionStore, Session, SetClientOptions } from './types.js';
2
2
  import { generateSessionId } from '../../shared/utils.js';
3
3
 
4
4
  /**
5
- * In-memory implementation of StorageBackend
5
+ * In-memory implementation of SessionStore
6
6
  * Useful for local development or testing
7
7
  */
8
- export class MemoryStorageBackend implements StorageBackend {
9
- // Map<identity:sessionId, SessionData>
10
- private sessions = new Map<string, SessionData>();
8
+ export class MemoryStorageBackend implements SessionStore {
9
+ // Map<userId:sessionId, Session>
10
+ private sessions = new Map<string, Session>();
11
11
 
12
- // Map<identity, Set<sessionId>>
13
- private identitySessions = new Map<string, Set<string>>();
12
+ // Map<userId, Set<sessionId>>
13
+ private userIdSessions = new Map<string, Set<string>>();
14
14
 
15
15
  constructor() { }
16
16
 
@@ -18,19 +18,19 @@ export class MemoryStorageBackend implements StorageBackend {
18
18
  console.log('[mcp-ts][Storage] Memory: ✓ internal memory store active.');
19
19
  }
20
20
 
21
- private getSessionKey(identity: string, sessionId: string): string {
22
- return `${identity}:${sessionId}`;
21
+ private getSessionKey(userId: string, sessionId: string): string {
22
+ return `${userId}:${sessionId}`;
23
23
  }
24
24
 
25
25
  generateSessionId(): string {
26
26
  return generateSessionId();
27
27
  }
28
28
 
29
- async createSession(session: SessionData, ttl?: number): Promise<void> {
30
- const { sessionId, identity } = session;
31
- if (!sessionId || !identity) throw new Error('identity and sessionId required');
29
+ async create(session: Session, ttl?: number): Promise<void> {
30
+ const { sessionId, userId } = session;
31
+ if (!sessionId || !userId) throw new Error('userId and sessionId required');
32
32
 
33
- const sessionKey = this.getSessionKey(identity, sessionId);
33
+ const sessionKey = this.getSessionKey(userId, sessionId);
34
34
  if (this.sessions.has(sessionKey)) {
35
35
  throw new Error(`Session ${sessionId} already exists`);
36
36
  }
@@ -38,17 +38,17 @@ export class MemoryStorageBackend implements StorageBackend {
38
38
  this.sessions.set(sessionKey, session);
39
39
 
40
40
  // Update index
41
- if (!this.identitySessions.has(identity)) {
42
- this.identitySessions.set(identity, new Set());
41
+ if (!this.userIdSessions.has(userId)) {
42
+ this.userIdSessions.set(userId, new Set());
43
43
  }
44
- this.identitySessions.get(identity)!.add(sessionId);
44
+ this.userIdSessions.get(userId)!.add(sessionId);
45
45
  // Note: TTL is ignored in memory backend - sessions don't auto-expire
46
46
  }
47
47
 
48
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
49
- if (!identity || !sessionId) throw new Error('identity and sessionId required');
48
+ async update(userId: string, sessionId: string, data: Partial<Session>, ttl?: number): Promise<void> {
49
+ if (!userId || !sessionId) throw new Error('userId and sessionId required');
50
50
 
51
- const sessionKey = this.getSessionKey(identity, sessionId);
51
+ const sessionKey = this.getSessionKey(userId, sessionId);
52
52
  const current = this.sessions.get(sessionKey);
53
53
 
54
54
  if (!current) {
@@ -65,23 +65,23 @@ export class MemoryStorageBackend implements StorageBackend {
65
65
  }
66
66
 
67
67
 
68
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
69
- const sessionKey = this.getSessionKey(identity, sessionId);
68
+ async get(userId: string, sessionId: string): Promise<Session | null> {
69
+ const sessionKey = this.getSessionKey(userId, sessionId);
70
70
  return this.sessions.get(sessionKey) || null;
71
71
  }
72
72
 
73
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
74
- const set = this.identitySessions.get(identity);
73
+ async listIds(userId: string): Promise<string[]> {
74
+ const set = this.userIdSessions.get(userId);
75
75
  return set ? Array.from(set) : [];
76
76
  }
77
77
 
78
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
79
- const set = this.identitySessions.get(identity);
78
+ async list(userId: string): Promise<Session[]> {
79
+ const set = this.userIdSessions.get(userId);
80
80
  if (!set) return [];
81
81
 
82
- const results: SessionData[] = [];
82
+ const results: Session[] = [];
83
83
  for (const sessionId of set) {
84
- const session = this.sessions.get(this.getSessionKey(identity, sessionId));
84
+ const session = this.sessions.get(this.getSessionKey(userId, sessionId));
85
85
  if (session) {
86
86
  results.push(session);
87
87
  }
@@ -89,29 +89,29 @@ export class MemoryStorageBackend implements StorageBackend {
89
89
  return results;
90
90
  }
91
91
 
92
- async removeSession(identity: string, sessionId: string): Promise<void> {
93
- const sessionKey = this.getSessionKey(identity, sessionId);
92
+ async delete(userId: string, sessionId: string): Promise<void> {
93
+ const sessionKey = this.getSessionKey(userId, sessionId);
94
94
  this.sessions.delete(sessionKey);
95
95
 
96
- const set = this.identitySessions.get(identity);
96
+ const set = this.userIdSessions.get(userId);
97
97
  if (set) {
98
98
  set.delete(sessionId);
99
99
  if (set.size === 0) {
100
- this.identitySessions.delete(identity);
100
+ this.userIdSessions.delete(userId);
101
101
  }
102
102
  }
103
103
  }
104
104
 
105
- async getAllSessionIds(): Promise<string[]> {
105
+ async listAllIds(): Promise<string[]> {
106
106
  return Array.from(this.sessions.values()).map(s => s.sessionId);
107
107
  }
108
108
 
109
109
  async clearAll(): Promise<void> {
110
110
  this.sessions.clear();
111
- this.identitySessions.clear();
111
+ this.userIdSessions.clear();
112
112
  }
113
113
 
114
- async cleanupExpiredSessions(): Promise<void> {
114
+ async cleanupExpired(): Promise<void> {
115
115
  // In-memory doesn't implement TTL automatically,
116
116
  // but we could check createdAt + TTL here if needed.
117
117
  // For now, no-op.