@mcp-ts/sdk 1.3.7 → 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.
- package/LICENSE +21 -21
- package/README.md +398 -404
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/bin/mcp-ts.js +0 -0
- package/dist/bin/mcp-ts.js.map +1 -1
- package/dist/bin/mcp-ts.mjs +0 -0
- package/dist/bin/mcp-ts.mjs.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +2 -2
- package/dist/client/react.d.ts +2 -2
- package/dist/client/react.js +25 -2
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +26 -3
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +185 -185
- package/src/adapters/agui-middleware.ts +382 -382
- package/src/bin/mcp-ts.ts +102 -102
- package/src/client/core/app-host.ts +417 -417
- package/src/client/core/sse-client.ts +371 -371
- package/src/client/core/types.ts +31 -31
- package/src/client/index.ts +27 -27
- package/src/client/react/index.ts +16 -16
- package/src/client/react/use-app-host.ts +73 -73
- package/src/client/react/use-mcp-apps.tsx +247 -214
- package/src/client/react/use-mcp.ts +641 -641
- package/src/client/vue/index.ts +10 -10
- package/src/client/vue/use-mcp.ts +617 -617
- package/src/index.ts +11 -11
- package/src/server/handlers/nextjs-handler.ts +204 -204
- package/src/server/handlers/sse-handler.ts +631 -631
- package/src/server/index.ts +57 -57
- package/src/server/mcp/multi-session-client.ts +228 -228
- package/src/server/mcp/oauth-client.ts +1188 -1188
- package/src/server/mcp/storage-oauth-provider.ts +272 -272
- package/src/server/storage/file-backend.ts +157 -157
- package/src/server/storage/index.ts +176 -176
- package/src/server/storage/memory-backend.ts +123 -123
- package/src/server/storage/redis-backend.ts +276 -276
- package/src/server/storage/redis.ts +160 -160
- package/src/server/storage/sqlite-backend.ts +182 -182
- package/src/server/storage/supabase-backend.ts +228 -228
- package/src/server/storage/types.ts +116 -116
- package/src/shared/constants.ts +29 -29
- package/src/shared/errors.ts +133 -133
- package/src/shared/event-routing.ts +28 -28
- package/src/shared/events.ts +180 -180
- package/src/shared/index.ts +75 -75
- package/src/shared/tool-utils.ts +61 -61
- package/src/shared/types.ts +282 -282
- package/src/shared/utils.ts +38 -38
- 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
|
+
}
|