@mcp-ts/sdk 1.3.7 → 1.3.10

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 (66) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +397 -404
  3. package/dist/adapters/agui-middleware.js.map +1 -1
  4. package/dist/adapters/agui-middleware.mjs.map +1 -1
  5. package/dist/bin/mcp-ts.js +0 -0
  6. package/dist/bin/mcp-ts.js.map +1 -1
  7. package/dist/bin/mcp-ts.mjs +0 -0
  8. package/dist/bin/mcp-ts.mjs.map +1 -1
  9. package/dist/client/index.js.map +1 -1
  10. package/dist/client/index.mjs.map +1 -1
  11. package/dist/client/react.d.mts +10 -28
  12. package/dist/client/react.d.ts +10 -28
  13. package/dist/client/react.js +101 -52
  14. package/dist/client/react.js.map +1 -1
  15. package/dist/client/react.mjs +102 -53
  16. package/dist/client/react.mjs.map +1 -1
  17. package/dist/client/vue.js.map +1 -1
  18. package/dist/client/vue.mjs.map +1 -1
  19. package/dist/index.js +78 -6
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +73 -6
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/server/index.js +78 -6
  24. package/dist/server/index.js.map +1 -1
  25. package/dist/server/index.mjs +73 -6
  26. package/dist/server/index.mjs.map +1 -1
  27. package/dist/shared/index.js.map +1 -1
  28. package/dist/shared/index.mjs.map +1 -1
  29. package/package.json +185 -185
  30. package/src/adapters/agui-middleware.ts +382 -382
  31. package/src/bin/mcp-ts.ts +102 -102
  32. package/src/client/core/app-host.ts +417 -417
  33. package/src/client/core/sse-client.ts +371 -371
  34. package/src/client/core/types.ts +31 -31
  35. package/src/client/index.ts +27 -27
  36. package/src/client/react/index.ts +20 -16
  37. package/src/client/react/use-app-host.ts +74 -73
  38. package/src/client/react/use-mcp-apps.tsx +224 -214
  39. package/src/client/react/use-mcp.ts +669 -641
  40. package/src/client/vue/index.ts +10 -10
  41. package/src/client/vue/use-mcp.ts +617 -617
  42. package/src/index.ts +11 -11
  43. package/src/server/handlers/nextjs-handler.ts +204 -204
  44. package/src/server/handlers/sse-handler.ts +631 -631
  45. package/src/server/index.ts +57 -57
  46. package/src/server/mcp/multi-session-client.ts +228 -228
  47. package/src/server/mcp/oauth-client.ts +1188 -1188
  48. package/src/server/mcp/storage-oauth-provider.ts +272 -272
  49. package/src/server/storage/crypto.ts +92 -0
  50. package/src/server/storage/file-backend.ts +157 -157
  51. package/src/server/storage/index.ts +176 -176
  52. package/src/server/storage/memory-backend.ts +123 -123
  53. package/src/server/storage/redis-backend.ts +276 -276
  54. package/src/server/storage/redis.ts +160 -160
  55. package/src/server/storage/sqlite-backend.ts +182 -182
  56. package/src/server/storage/supabase-backend.ts +229 -228
  57. package/src/server/storage/types.ts +116 -116
  58. package/src/shared/constants.ts +29 -29
  59. package/src/shared/errors.ts +133 -133
  60. package/src/shared/event-routing.ts +28 -28
  61. package/src/shared/events.ts +180 -180
  62. package/src/shared/index.ts +75 -75
  63. package/src/shared/tool-utils.ts +61 -61
  64. package/src/shared/types.ts +282 -282
  65. package/src/shared/utils.ts +38 -38
  66. package/supabase/migrations/20260330195700_install_mcp_sessions.sql +84 -84
@@ -1,176 +1,176 @@
1
-
2
- import { RedisStorageBackend } from './redis-backend';
3
- import { MemoryStorageBackend } from './memory-backend';
4
- import { FileStorageBackend } from './file-backend';
5
- import { SqliteStorage } from './sqlite-backend.js';
6
- import { SupabaseStorageBackend } from './supabase-backend.js';
7
- import type { StorageBackend } from './types.js';
8
-
9
- // Re-export types
10
- export * from './types.js';
11
- export { generateSessionId } from '../../shared/utils.js';
12
- export { RedisStorageBackend, MemoryStorageBackend, FileStorageBackend, SqliteStorage, SupabaseStorageBackend };
13
-
14
- export function createSupabaseStorageBackend(client: any): SupabaseStorageBackend {
15
- return new SupabaseStorageBackend(client);
16
- }
17
-
18
- let storageInstance: StorageBackend | null = null;
19
- let storagePromise: Promise<StorageBackend> | null = null;
20
-
21
- async function initializeStorage<T extends StorageBackend>(store: T): Promise<T> {
22
- if (typeof store.init === 'function') {
23
- await store.init();
24
- }
25
- return store;
26
- }
27
-
28
- async function createStorage(): Promise<StorageBackend> {
29
- const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
30
-
31
- // Explicit selection
32
- if (type === 'redis') {
33
- if (!process.env.REDIS_URL) {
34
- console.warn('[Storage] MCP_TS_STORAGE_TYPE is "redis" but REDIS_URL is missing');
35
- }
36
- try {
37
- const { getRedis } = await import('./redis.js');
38
- const redis = await getRedis();
39
- console.log('[mcp-ts][Storage] Explicit selection: "redis"');
40
- return await initializeStorage(new RedisStorageBackend(redis));
41
- } catch (error: any) {
42
- console.error('[mcp-ts][Storage] Failed to initialize Redis:', error.message);
43
- console.log('[mcp-ts][Storage] Falling back to In-Memory storage');
44
- return await initializeStorage(new MemoryStorageBackend());
45
- }
46
- }
47
-
48
- if (type === 'file') {
49
- const filePath = process.env.MCP_TS_STORAGE_FILE;
50
- console.log(`[mcp-ts][Storage] Explicit selection: "file" (${filePath || 'default'})`);
51
- return await initializeStorage(new FileStorageBackend({ path: filePath }));
52
- }
53
-
54
- if (type === 'sqlite') {
55
- const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
56
- console.log(`[mcp-ts][Storage] Explicit selection: "sqlite" (${dbPath || 'default'})`);
57
- return await initializeStorage(new SqliteStorage({ path: dbPath }));
58
- }
59
-
60
- if (type === 'supabase') {
61
- const url = process.env.SUPABASE_URL;
62
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
63
-
64
- if (!url || !key) {
65
- console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
66
- } else {
67
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
68
- console.warn('[mcp-ts][Storage] ⚠️ Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
69
- }
70
- try {
71
- const { createClient } = await import('@supabase/supabase-js');
72
- const client = createClient(url, key);
73
- console.log('[mcp-ts][Storage] Explicit selection: "supabase"');
74
- return await initializeStorage(new SupabaseStorageBackend(client as any));
75
- } catch (error: any) {
76
- console.error('[mcp-ts][Storage] Failed to initialize Supabase:', error.message);
77
- console.log('[mcp-ts][Storage] Falling back to In-Memory storage');
78
- return await initializeStorage(new MemoryStorageBackend());
79
- }
80
- }
81
- }
82
-
83
- if (type === 'memory') {
84
- console.log('[mcp-ts][Storage] Explicit selection: "memory"');
85
- return await initializeStorage(new MemoryStorageBackend());
86
- }
87
-
88
- // Automatic inference (Fallback)
89
- if (process.env.REDIS_URL) {
90
- try {
91
- const { getRedis } = await import('./redis.js');
92
- const redis = await getRedis();
93
- console.log('[mcp-ts][Storage] Auto-detection: "redis" (via REDIS_URL)');
94
- return await initializeStorage(new RedisStorageBackend(redis));
95
- } catch (error: any) {
96
- console.error('[mcp-ts][Storage] Redis auto-detection failed:', error.message);
97
- console.log('[mcp-ts][Storage] Falling back to next available backend');
98
- }
99
- }
100
-
101
- if (process.env.MCP_TS_STORAGE_FILE) {
102
- console.log(`[mcp-ts][Storage] Auto-detection: "file" (${process.env.MCP_TS_STORAGE_FILE})`);
103
- return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
104
- }
105
-
106
- if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
107
- console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
108
- return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
109
- }
110
-
111
- if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
112
- try {
113
- const { createClient } = await import('@supabase/supabase-js');
114
- const url = process.env.SUPABASE_URL;
115
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY!;
116
-
117
- if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
118
- console.warn('[mcp-ts][Storage] ⚠️ Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
119
- }
120
-
121
- const client = createClient(url, key);
122
- console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
123
- return await initializeStorage(new SupabaseStorageBackend(client as any));
124
- } catch (error: any) {
125
- console.error('[mcp-ts][Storage] Supabase auto-detection failed:', error.message);
126
- }
127
- }
128
-
129
- console.log('[mcp-ts][Storage] Defaulting to: "memory"');
130
- return await initializeStorage(new MemoryStorageBackend());
131
- }
132
-
133
- async function getStorage(): Promise<StorageBackend> {
134
- if (storageInstance) {
135
- return storageInstance;
136
- }
137
-
138
- if (!storagePromise) {
139
- storagePromise = createStorage().catch((error) => {
140
- storagePromise = null;
141
- throw error;
142
- });
143
- }
144
-
145
- storageInstance = await storagePromise;
146
- return storageInstance;
147
- }
148
-
149
- /**
150
- * Set the storage instance (for testing)
151
- * @internal
152
- * @param instance - StorageBackend instance or null to reset
153
- */
154
- export function _setStorageInstanceForTesting(instance: StorageBackend | null): void {
155
- storageInstance = instance;
156
- if (!instance) {
157
- storagePromise = null;
158
- }
159
- }
160
-
161
- /**
162
- * Global session store instance
163
- * Uses lazy initialization with a Proxy to handle async setup transparently
164
- */
165
- export const storage: StorageBackend = new Proxy({} as StorageBackend, {
166
- get(_target, prop) {
167
- return async (...args: any[]) => {
168
- const instance = await getStorage();
169
- const value = (instance as any)[prop];
170
- if (typeof value === 'function') {
171
- return value.apply(instance, args);
172
- }
173
- return value;
174
- };
175
- },
176
- });
1
+
2
+ import { RedisStorageBackend } from './redis-backend';
3
+ import { MemoryStorageBackend } from './memory-backend';
4
+ import { FileStorageBackend } from './file-backend';
5
+ import { SqliteStorage } from './sqlite-backend.js';
6
+ import { SupabaseStorageBackend } from './supabase-backend.js';
7
+ import type { StorageBackend } from './types.js';
8
+
9
+ // Re-export types
10
+ export * from './types.js';
11
+ export { generateSessionId } from '../../shared/utils.js';
12
+ export { RedisStorageBackend, MemoryStorageBackend, FileStorageBackend, SqliteStorage, SupabaseStorageBackend };
13
+
14
+ export function createSupabaseStorageBackend(client: any): SupabaseStorageBackend {
15
+ return new SupabaseStorageBackend(client);
16
+ }
17
+
18
+ let storageInstance: StorageBackend | null = null;
19
+ let storagePromise: Promise<StorageBackend> | null = null;
20
+
21
+ async function initializeStorage<T extends StorageBackend>(store: T): Promise<T> {
22
+ if (typeof store.init === 'function') {
23
+ await store.init();
24
+ }
25
+ return store;
26
+ }
27
+
28
+ async function createStorage(): Promise<StorageBackend> {
29
+ const type = process.env.MCP_TS_STORAGE_TYPE?.toLowerCase();
30
+
31
+ // Explicit selection
32
+ if (type === 'redis') {
33
+ if (!process.env.REDIS_URL) {
34
+ console.warn('[Storage] MCP_TS_STORAGE_TYPE is "redis" but REDIS_URL is missing');
35
+ }
36
+ try {
37
+ const { getRedis } = await import('./redis.js');
38
+ const redis = await getRedis();
39
+ console.log('[mcp-ts][Storage] Explicit selection: "redis"');
40
+ return await initializeStorage(new RedisStorageBackend(redis));
41
+ } catch (error: any) {
42
+ console.error('[mcp-ts][Storage] Failed to initialize Redis:', error.message);
43
+ console.log('[mcp-ts][Storage] Falling back to In-Memory storage');
44
+ return await initializeStorage(new MemoryStorageBackend());
45
+ }
46
+ }
47
+
48
+ if (type === 'file') {
49
+ const filePath = process.env.MCP_TS_STORAGE_FILE;
50
+ console.log(`[mcp-ts][Storage] Explicit selection: "file" (${filePath || 'default'})`);
51
+ return await initializeStorage(new FileStorageBackend({ path: filePath }));
52
+ }
53
+
54
+ if (type === 'sqlite') {
55
+ const dbPath = process.env.MCP_TS_STORAGE_SQLITE_PATH;
56
+ console.log(`[mcp-ts][Storage] Explicit selection: "sqlite" (${dbPath || 'default'})`);
57
+ return await initializeStorage(new SqliteStorage({ path: dbPath }));
58
+ }
59
+
60
+ if (type === 'supabase') {
61
+ const url = process.env.SUPABASE_URL;
62
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY;
63
+
64
+ if (!url || !key) {
65
+ console.warn('[mcp-ts][Storage] Explicit selection "supabase" requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.');
66
+ } else {
67
+ if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
68
+ console.warn('[mcp-ts][Storage] ⚠️ Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
69
+ }
70
+ try {
71
+ const { createClient } = await import('@supabase/supabase-js');
72
+ const client = createClient(url, key);
73
+ console.log('[mcp-ts][Storage] Explicit selection: "supabase"');
74
+ return await initializeStorage(new SupabaseStorageBackend(client as any));
75
+ } catch (error: any) {
76
+ console.error('[mcp-ts][Storage] Failed to initialize Supabase:', error.message);
77
+ console.log('[mcp-ts][Storage] Falling back to In-Memory storage');
78
+ return await initializeStorage(new MemoryStorageBackend());
79
+ }
80
+ }
81
+ }
82
+
83
+ if (type === 'memory') {
84
+ console.log('[mcp-ts][Storage] Explicit selection: "memory"');
85
+ return await initializeStorage(new MemoryStorageBackend());
86
+ }
87
+
88
+ // Automatic inference (Fallback)
89
+ if (process.env.REDIS_URL) {
90
+ try {
91
+ const { getRedis } = await import('./redis.js');
92
+ const redis = await getRedis();
93
+ console.log('[mcp-ts][Storage] Auto-detection: "redis" (via REDIS_URL)');
94
+ return await initializeStorage(new RedisStorageBackend(redis));
95
+ } catch (error: any) {
96
+ console.error('[mcp-ts][Storage] Redis auto-detection failed:', error.message);
97
+ console.log('[mcp-ts][Storage] Falling back to next available backend');
98
+ }
99
+ }
100
+
101
+ if (process.env.MCP_TS_STORAGE_FILE) {
102
+ console.log(`[mcp-ts][Storage] Auto-detection: "file" (${process.env.MCP_TS_STORAGE_FILE})`);
103
+ return await initializeStorage(new FileStorageBackend({ path: process.env.MCP_TS_STORAGE_FILE }));
104
+ }
105
+
106
+ if (process.env.MCP_TS_STORAGE_SQLITE_PATH) {
107
+ console.log(`[mcp-ts][Storage] Auto-detection: "sqlite" (${process.env.MCP_TS_STORAGE_SQLITE_PATH})`);
108
+ return await initializeStorage(new SqliteStorage({ path: process.env.MCP_TS_STORAGE_SQLITE_PATH }));
109
+ }
110
+
111
+ if (process.env.SUPABASE_URL && (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY)) {
112
+ try {
113
+ const { createClient } = await import('@supabase/supabase-js');
114
+ const url = process.env.SUPABASE_URL;
115
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY!;
116
+
117
+ if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
118
+ console.warn('[mcp-ts][Storage] ⚠️ Warning: Using "SUPABASE_ANON_KEY" for server-side storage. You may encounter RLS policy violations. "SUPABASE_SERVICE_ROLE_KEY" is recommended.');
119
+ }
120
+
121
+ const client = createClient(url, key);
122
+ console.log('[mcp-ts][Storage] Auto-detection: "supabase" (via SUPABASE_URL)');
123
+ return await initializeStorage(new SupabaseStorageBackend(client as any));
124
+ } catch (error: any) {
125
+ console.error('[mcp-ts][Storage] Supabase auto-detection failed:', error.message);
126
+ }
127
+ }
128
+
129
+ console.log('[mcp-ts][Storage] Defaulting to: "memory"');
130
+ return await initializeStorage(new MemoryStorageBackend());
131
+ }
132
+
133
+ async function getStorage(): Promise<StorageBackend> {
134
+ if (storageInstance) {
135
+ return storageInstance;
136
+ }
137
+
138
+ if (!storagePromise) {
139
+ storagePromise = createStorage().catch((error) => {
140
+ storagePromise = null;
141
+ throw error;
142
+ });
143
+ }
144
+
145
+ storageInstance = await storagePromise;
146
+ return storageInstance;
147
+ }
148
+
149
+ /**
150
+ * Set the storage instance (for testing)
151
+ * @internal
152
+ * @param instance - StorageBackend instance or null to reset
153
+ */
154
+ export function _setStorageInstanceForTesting(instance: StorageBackend | null): void {
155
+ storageInstance = instance;
156
+ if (!instance) {
157
+ storagePromise = null;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Global session store instance
163
+ * Uses lazy initialization with a Proxy to handle async setup transparently
164
+ */
165
+ export const storage: StorageBackend = new Proxy({} as StorageBackend, {
166
+ get(_target, prop) {
167
+ return async (...args: any[]) => {
168
+ const instance = await getStorage();
169
+ const value = (instance as any)[prop];
170
+ if (typeof value === 'function') {
171
+ return value.apply(instance, args);
172
+ }
173
+ return value;
174
+ };
175
+ },
176
+ });
@@ -1,123 +1,123 @@
1
- import { StorageBackend, SessionData, SetClientOptions } from './types.js';
2
- import { generateSessionId } from '../../shared/utils.js';
3
-
4
- /**
5
- * In-memory implementation of StorageBackend
6
- * Useful for local development or testing
7
- */
8
- export class MemoryStorageBackend implements StorageBackend {
9
- // Map<identity:sessionId, SessionData>
10
- private sessions = new Map<string, SessionData>();
11
-
12
- // Map<identity, Set<sessionId>>
13
- private identitySessions = new Map<string, Set<string>>();
14
-
15
- constructor() { }
16
-
17
- async init(): Promise<void> {
18
- console.log('[mcp-ts][Storage] Memory: ✓ internal memory store active.');
19
- }
20
-
21
- private getSessionKey(identity: string, sessionId: string): string {
22
- return `${identity}:${sessionId}`;
23
- }
24
-
25
- generateSessionId(): string {
26
- return generateSessionId();
27
- }
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');
32
-
33
- const sessionKey = this.getSessionKey(identity, sessionId);
34
- if (this.sessions.has(sessionKey)) {
35
- throw new Error(`Session ${sessionId} already exists`);
36
- }
37
-
38
- this.sessions.set(sessionKey, session);
39
-
40
- // Update index
41
- if (!this.identitySessions.has(identity)) {
42
- this.identitySessions.set(identity, new Set());
43
- }
44
- this.identitySessions.get(identity)!.add(sessionId);
45
- // Note: TTL is ignored in memory backend - sessions don't auto-expire
46
- }
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');
50
-
51
- const sessionKey = this.getSessionKey(identity, sessionId);
52
- const current = this.sessions.get(sessionKey);
53
-
54
- if (!current) {
55
- throw new Error(`Session ${sessionId} not found`);
56
- }
57
-
58
- const updated = {
59
- ...current,
60
- ...data
61
- };
62
-
63
- this.sessions.set(sessionKey, updated);
64
- // Note: TTL is ignored in memory backend - sessions don't auto-expire
65
- }
66
-
67
-
68
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
69
- const sessionKey = this.getSessionKey(identity, sessionId);
70
- return this.sessions.get(sessionKey) || null;
71
- }
72
-
73
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
74
- const set = this.identitySessions.get(identity);
75
- return set ? Array.from(set) : [];
76
- }
77
-
78
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
79
- const set = this.identitySessions.get(identity);
80
- if (!set) return [];
81
-
82
- const results: SessionData[] = [];
83
- for (const sessionId of set) {
84
- const session = this.sessions.get(this.getSessionKey(identity, sessionId));
85
- if (session) {
86
- results.push(session);
87
- }
88
- }
89
- return results;
90
- }
91
-
92
- async removeSession(identity: string, sessionId: string): Promise<void> {
93
- const sessionKey = this.getSessionKey(identity, sessionId);
94
- this.sessions.delete(sessionKey);
95
-
96
- const set = this.identitySessions.get(identity);
97
- if (set) {
98
- set.delete(sessionId);
99
- if (set.size === 0) {
100
- this.identitySessions.delete(identity);
101
- }
102
- }
103
- }
104
-
105
- async getAllSessionIds(): Promise<string[]> {
106
- return Array.from(this.sessions.values()).map(s => s.sessionId);
107
- }
108
-
109
- async clearAll(): Promise<void> {
110
- this.sessions.clear();
111
- this.identitySessions.clear();
112
- }
113
-
114
- async cleanupExpiredSessions(): Promise<void> {
115
- // In-memory doesn't implement TTL automatically,
116
- // but we could check createdAt + TTL here if needed.
117
- // For now, no-op.
118
- }
119
-
120
- async disconnect(): Promise<void> {
121
- // No-op for memory
122
- }
123
- }
1
+ import { StorageBackend, SessionData, SetClientOptions } from './types.js';
2
+ import { generateSessionId } from '../../shared/utils.js';
3
+
4
+ /**
5
+ * In-memory implementation of StorageBackend
6
+ * Useful for local development or testing
7
+ */
8
+ export class MemoryStorageBackend implements StorageBackend {
9
+ // Map<identity:sessionId, SessionData>
10
+ private sessions = new Map<string, SessionData>();
11
+
12
+ // Map<identity, Set<sessionId>>
13
+ private identitySessions = new Map<string, Set<string>>();
14
+
15
+ constructor() { }
16
+
17
+ async init(): Promise<void> {
18
+ console.log('[mcp-ts][Storage] Memory: ✓ internal memory store active.');
19
+ }
20
+
21
+ private getSessionKey(identity: string, sessionId: string): string {
22
+ return `${identity}:${sessionId}`;
23
+ }
24
+
25
+ generateSessionId(): string {
26
+ return generateSessionId();
27
+ }
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');
32
+
33
+ const sessionKey = this.getSessionKey(identity, sessionId);
34
+ if (this.sessions.has(sessionKey)) {
35
+ throw new Error(`Session ${sessionId} already exists`);
36
+ }
37
+
38
+ this.sessions.set(sessionKey, session);
39
+
40
+ // Update index
41
+ if (!this.identitySessions.has(identity)) {
42
+ this.identitySessions.set(identity, new Set());
43
+ }
44
+ this.identitySessions.get(identity)!.add(sessionId);
45
+ // Note: TTL is ignored in memory backend - sessions don't auto-expire
46
+ }
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');
50
+
51
+ const sessionKey = this.getSessionKey(identity, sessionId);
52
+ const current = this.sessions.get(sessionKey);
53
+
54
+ if (!current) {
55
+ throw new Error(`Session ${sessionId} not found`);
56
+ }
57
+
58
+ const updated = {
59
+ ...current,
60
+ ...data
61
+ };
62
+
63
+ this.sessions.set(sessionKey, updated);
64
+ // Note: TTL is ignored in memory backend - sessions don't auto-expire
65
+ }
66
+
67
+
68
+ async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
69
+ const sessionKey = this.getSessionKey(identity, sessionId);
70
+ return this.sessions.get(sessionKey) || null;
71
+ }
72
+
73
+ async getIdentityMcpSessions(identity: string): Promise<string[]> {
74
+ const set = this.identitySessions.get(identity);
75
+ return set ? Array.from(set) : [];
76
+ }
77
+
78
+ async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
79
+ const set = this.identitySessions.get(identity);
80
+ if (!set) return [];
81
+
82
+ const results: SessionData[] = [];
83
+ for (const sessionId of set) {
84
+ const session = this.sessions.get(this.getSessionKey(identity, sessionId));
85
+ if (session) {
86
+ results.push(session);
87
+ }
88
+ }
89
+ return results;
90
+ }
91
+
92
+ async removeSession(identity: string, sessionId: string): Promise<void> {
93
+ const sessionKey = this.getSessionKey(identity, sessionId);
94
+ this.sessions.delete(sessionKey);
95
+
96
+ const set = this.identitySessions.get(identity);
97
+ if (set) {
98
+ set.delete(sessionId);
99
+ if (set.size === 0) {
100
+ this.identitySessions.delete(identity);
101
+ }
102
+ }
103
+ }
104
+
105
+ async getAllSessionIds(): Promise<string[]> {
106
+ return Array.from(this.sessions.values()).map(s => s.sessionId);
107
+ }
108
+
109
+ async clearAll(): Promise<void> {
110
+ this.sessions.clear();
111
+ this.identitySessions.clear();
112
+ }
113
+
114
+ async cleanupExpiredSessions(): Promise<void> {
115
+ // In-memory doesn't implement TTL automatically,
116
+ // but we could check createdAt + TTL here if needed.
117
+ // For now, no-op.
118
+ }
119
+
120
+ async disconnect(): Promise<void> {
121
+ // No-op for memory
122
+ }
123
+ }