@mcp-ts/sdk 1.3.6 → 1.3.9

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