@qwickapps/qwickbrain-proxy 1.0.2 → 1.0.3
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/.claude/engineering/bugs/BUG-qwickbrain-proxy-cache-and-design.md +840 -0
- package/.github/workflows/publish.yml +13 -0
- package/CHANGELOG.md +17 -0
- package/dist/db/schema.d.ts +63 -6
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +17 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/lib/__tests__/cache-manager.test.js +146 -83
- package/dist/lib/__tests__/cache-manager.test.js.map +1 -1
- package/dist/lib/__tests__/proxy-server.test.js +16 -44
- package/dist/lib/__tests__/proxy-server.test.js.map +1 -1
- package/dist/lib/__tests__/sse-invalidation-listener.test.d.ts +2 -0
- package/dist/lib/__tests__/sse-invalidation-listener.test.d.ts.map +1 -0
- package/dist/lib/__tests__/sse-invalidation-listener.test.js +245 -0
- package/dist/lib/__tests__/sse-invalidation-listener.test.js.map +1 -0
- package/dist/lib/__tests__/write-queue-manager.test.d.ts +2 -0
- package/dist/lib/__tests__/write-queue-manager.test.d.ts.map +1 -0
- package/dist/lib/__tests__/write-queue-manager.test.js +291 -0
- package/dist/lib/__tests__/write-queue-manager.test.js.map +1 -0
- package/dist/lib/cache-manager.d.ts +35 -6
- package/dist/lib/cache-manager.d.ts.map +1 -1
- package/dist/lib/cache-manager.js +154 -41
- package/dist/lib/cache-manager.js.map +1 -1
- package/dist/lib/proxy-server.d.ts +5 -0
- package/dist/lib/proxy-server.d.ts.map +1 -1
- package/dist/lib/proxy-server.js +150 -84
- package/dist/lib/proxy-server.js.map +1 -1
- package/dist/lib/qwickbrain-client.d.ts +4 -0
- package/dist/lib/qwickbrain-client.d.ts.map +1 -1
- package/dist/lib/qwickbrain-client.js +131 -2
- package/dist/lib/qwickbrain-client.js.map +1 -1
- package/dist/lib/sse-invalidation-listener.d.ts +27 -0
- package/dist/lib/sse-invalidation-listener.d.ts.map +1 -0
- package/dist/lib/sse-invalidation-listener.js +145 -0
- package/dist/lib/sse-invalidation-listener.js.map +1 -0
- package/dist/lib/tools.d.ts +21 -0
- package/dist/lib/tools.d.ts.map +1 -0
- package/dist/lib/tools.js +488 -0
- package/dist/lib/tools.js.map +1 -0
- package/dist/lib/write-queue-manager.d.ts +88 -0
- package/dist/lib/write-queue-manager.d.ts.map +1 -0
- package/dist/lib/write-queue-manager.js +191 -0
- package/dist/lib/write-queue-manager.js.map +1 -0
- package/dist/types/config.d.ts +7 -42
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +1 -6
- package/dist/types/config.js.map +1 -1
- package/drizzle/0002_lru_cache_migration.sql +94 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +6 -2
- package/scripts/rebuild-sqlite.sh +26 -0
- package/src/db/schema.ts +17 -2
- package/src/lib/__tests__/cache-manager.test.ts +180 -90
- package/src/lib/__tests__/proxy-server.test.ts +16 -51
- package/src/lib/__tests__/sse-invalidation-listener.test.ts +326 -0
- package/src/lib/__tests__/write-queue-manager.test.ts +383 -0
- package/src/lib/cache-manager.ts +198 -46
- package/src/lib/proxy-server.ts +190 -86
- package/src/lib/qwickbrain-client.ts +142 -2
- package/src/lib/sse-invalidation-listener.ts +171 -0
- package/src/lib/tools.ts +500 -0
- package/src/lib/write-queue-manager.ts +271 -0
- package/src/types/config.ts +1 -6
package/src/lib/proxy-server.ts
CHANGED
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
import { ConnectionManager } from './connection-manager.js';
|
|
8
8
|
import { CacheManager } from './cache-manager.js';
|
|
9
9
|
import { QwickBrainClient } from './qwickbrain-client.js';
|
|
10
|
+
import { WriteQueueManager } from './write-queue-manager.js';
|
|
11
|
+
import { SSEInvalidationListener } from './sse-invalidation-listener.js';
|
|
12
|
+
import { QWICKBRAIN_TOOLS, requiresConnection } from './tools.js';
|
|
10
13
|
import type { Config } from '../types/config.js';
|
|
11
14
|
import type { DB } from '../db/client.js';
|
|
12
15
|
import type { MCPResponse, MCPResponseMetadata } from '../types/mcp.js';
|
|
@@ -17,6 +20,8 @@ export class ProxyServer {
|
|
|
17
20
|
private connectionManager: ConnectionManager;
|
|
18
21
|
private cacheManager: CacheManager;
|
|
19
22
|
private qwickbrainClient: QwickBrainClient;
|
|
23
|
+
private writeQueueManager: WriteQueueManager;
|
|
24
|
+
private sseInvalidationListener: SSEInvalidationListener | null = null;
|
|
20
25
|
private config: Config;
|
|
21
26
|
|
|
22
27
|
constructor(db: DB, config: Config) {
|
|
@@ -41,6 +46,16 @@ export class ProxyServer {
|
|
|
41
46
|
);
|
|
42
47
|
|
|
43
48
|
this.cacheManager = new CacheManager(db, config.cache);
|
|
49
|
+
this.writeQueueManager = new WriteQueueManager(db, this.qwickbrainClient);
|
|
50
|
+
|
|
51
|
+
// Initialize SSE invalidation listener if in SSE mode
|
|
52
|
+
if (config.qwickbrain.mode === 'sse' && config.qwickbrain.url) {
|
|
53
|
+
this.sseInvalidationListener = new SSEInvalidationListener(
|
|
54
|
+
config.qwickbrain.url,
|
|
55
|
+
this.cacheManager,
|
|
56
|
+
config.qwickbrain.apiKey
|
|
57
|
+
);
|
|
58
|
+
}
|
|
44
59
|
|
|
45
60
|
this.setupHandlers();
|
|
46
61
|
this.setupConnectionListeners();
|
|
@@ -61,6 +76,10 @@ export class ProxyServer {
|
|
|
61
76
|
this.onConnectionRestored().catch(err => {
|
|
62
77
|
console.error('Background sync error:', err);
|
|
63
78
|
});
|
|
79
|
+
// Sync pending write operations
|
|
80
|
+
this.syncWriteQueue().catch(err => {
|
|
81
|
+
console.error('Write queue sync error:', err);
|
|
82
|
+
});
|
|
64
83
|
});
|
|
65
84
|
|
|
66
85
|
this.connectionManager.on('disconnected', ({ error }) => {
|
|
@@ -90,61 +109,21 @@ export class ProxyServer {
|
|
|
90
109
|
console.error('Background cache sync complete');
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
private async syncWriteQueue(): Promise<void> {
|
|
113
|
+
const pendingCount = await this.writeQueueManager.getPendingCount();
|
|
114
|
+
if (pendingCount === 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.error(`Syncing ${pendingCount} pending write operations...`);
|
|
119
|
+
const { synced, failed } = await this.writeQueueManager.syncPendingOperations();
|
|
120
|
+
console.error(`Write queue sync complete: ${synced} synced, ${failed} failed`);
|
|
121
|
+
}
|
|
122
|
+
|
|
93
123
|
private setupHandlers(): void {
|
|
124
|
+
// Static tool listing - always returns all tools regardless of connection state
|
|
94
125
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
95
|
-
|
|
96
|
-
if (this.connectionManager.getState() === 'connected') {
|
|
97
|
-
try {
|
|
98
|
-
const tools = await this.connectionManager.execute(async () => {
|
|
99
|
-
return await this.qwickbrainClient.listTools();
|
|
100
|
-
});
|
|
101
|
-
return { tools };
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.error('Failed to list tools from upstream:', error);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Fallback to minimal tool set when offline or error
|
|
108
|
-
return {
|
|
109
|
-
tools: [
|
|
110
|
-
{
|
|
111
|
-
name: 'get_workflow',
|
|
112
|
-
description: 'Get a workflow definition by name (cached)',
|
|
113
|
-
inputSchema: {
|
|
114
|
-
type: 'object',
|
|
115
|
-
properties: {
|
|
116
|
-
name: { type: 'string', description: 'Workflow name' },
|
|
117
|
-
},
|
|
118
|
-
required: ['name'],
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'get_document',
|
|
123
|
-
description: 'Get a document by name and type (cached)',
|
|
124
|
-
inputSchema: {
|
|
125
|
-
type: 'object',
|
|
126
|
-
properties: {
|
|
127
|
-
name: { type: 'string', description: 'Document name' },
|
|
128
|
-
doc_type: { type: 'string', description: 'Document type (rule, frd, design, etc.)' },
|
|
129
|
-
project: { type: 'string', description: 'Project name (optional)' },
|
|
130
|
-
},
|
|
131
|
-
required: ['name', 'doc_type'],
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
name: 'get_memory',
|
|
136
|
-
description: 'Get a memory/context document by name (cached)',
|
|
137
|
-
inputSchema: {
|
|
138
|
-
type: 'object',
|
|
139
|
-
properties: {
|
|
140
|
-
name: { type: 'string', description: 'Memory name' },
|
|
141
|
-
project: { type: 'string', description: 'Project name (optional)' },
|
|
142
|
-
},
|
|
143
|
-
required: ['name'],
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
};
|
|
126
|
+
return { tools: QWICKBRAIN_TOOLS };
|
|
148
127
|
});
|
|
149
128
|
|
|
150
129
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -171,8 +150,54 @@ export class ProxyServer {
|
|
|
171
150
|
args?.project as string | undefined
|
|
172
151
|
);
|
|
173
152
|
break;
|
|
153
|
+
case 'create_document':
|
|
154
|
+
case 'update_document':
|
|
155
|
+
result = await this.handleCreateDocument(
|
|
156
|
+
args?.doc_type as string,
|
|
157
|
+
args?.name as string,
|
|
158
|
+
args?.content as string,
|
|
159
|
+
args?.project as string | undefined,
|
|
160
|
+
args?.metadata as Record<string, unknown> | undefined
|
|
161
|
+
);
|
|
162
|
+
break;
|
|
163
|
+
case 'set_memory':
|
|
164
|
+
case 'update_memory':
|
|
165
|
+
result = await this.handleSetMemory(
|
|
166
|
+
args?.name as string,
|
|
167
|
+
args?.content as string,
|
|
168
|
+
args?.project as string | undefined,
|
|
169
|
+
args?.metadata as Record<string, unknown> | undefined
|
|
170
|
+
);
|
|
171
|
+
break;
|
|
174
172
|
default:
|
|
175
173
|
// Generic forwarding for all other tools (analyze_repository, search_codebase, etc.)
|
|
174
|
+
// Check if tool requires connection
|
|
175
|
+
if (requiresConnection(name) && this.connectionManager.getState() !== 'connected') {
|
|
176
|
+
// Return offline error for non-cacheable tools
|
|
177
|
+
return {
|
|
178
|
+
content: [
|
|
179
|
+
{
|
|
180
|
+
type: 'text',
|
|
181
|
+
text: JSON.stringify({
|
|
182
|
+
error: {
|
|
183
|
+
code: 'OFFLINE',
|
|
184
|
+
message: `QwickBrain offline - "${name}" requires active connection`,
|
|
185
|
+
suggestions: [
|
|
186
|
+
'Check internet connection',
|
|
187
|
+
'Wait for automatic reconnection',
|
|
188
|
+
'Cached tools (get_workflow, get_document, get_memory) work offline',
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
_metadata: {
|
|
192
|
+
source: 'cache',
|
|
193
|
+
status: this.connectionManager.getState(),
|
|
194
|
+
},
|
|
195
|
+
}, null, 2),
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
isError: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
176
201
|
result = await this.handleGenericTool(name, args || {});
|
|
177
202
|
break;
|
|
178
203
|
}
|
|
@@ -226,17 +251,17 @@ export class ProxyServer {
|
|
|
226
251
|
name: string,
|
|
227
252
|
project?: string
|
|
228
253
|
): Promise<MCPResponse> {
|
|
229
|
-
// Try cache first
|
|
254
|
+
// Try cache first (LRU cache never expires, always valid if present)
|
|
230
255
|
const cached = await this.cacheManager.getDocument(docType, name, project);
|
|
231
256
|
|
|
232
|
-
if (cached
|
|
257
|
+
if (cached) {
|
|
233
258
|
return {
|
|
234
259
|
data: cached.data,
|
|
235
260
|
_metadata: this.createMetadata('cache', cached.age),
|
|
236
261
|
};
|
|
237
262
|
}
|
|
238
263
|
|
|
239
|
-
//
|
|
264
|
+
// Not cached - try remote if connected
|
|
240
265
|
if (this.connectionManager.getState() === 'connected') {
|
|
241
266
|
try {
|
|
242
267
|
const result = await this.connectionManager.execute(async () => {
|
|
@@ -258,22 +283,11 @@ export class ProxyServer {
|
|
|
258
283
|
};
|
|
259
284
|
} catch (error) {
|
|
260
285
|
console.error('Failed to fetch from QwickBrain:', error);
|
|
261
|
-
// Fall through to
|
|
286
|
+
// Fall through to error
|
|
262
287
|
}
|
|
263
288
|
}
|
|
264
289
|
|
|
265
|
-
//
|
|
266
|
-
if (cached) {
|
|
267
|
-
return {
|
|
268
|
-
data: cached.data,
|
|
269
|
-
_metadata: {
|
|
270
|
-
...this.createMetadata('stale_cache', cached.age),
|
|
271
|
-
warning: `QwickBrain unavailable - serving cached data (${cached.age}s old)`,
|
|
272
|
-
},
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// No cache, no connection
|
|
290
|
+
// No cache and remote failed/unavailable
|
|
277
291
|
return {
|
|
278
292
|
error: {
|
|
279
293
|
code: 'UNAVAILABLE',
|
|
@@ -289,16 +303,17 @@ export class ProxyServer {
|
|
|
289
303
|
}
|
|
290
304
|
|
|
291
305
|
private async handleGetMemory(name: string, project?: string): Promise<MCPResponse> {
|
|
292
|
-
//
|
|
306
|
+
// Try cache first (LRU cache never expires, always valid if present)
|
|
293
307
|
const cached = await this.cacheManager.getMemory(name, project);
|
|
294
308
|
|
|
295
|
-
if (cached
|
|
309
|
+
if (cached) {
|
|
296
310
|
return {
|
|
297
311
|
data: cached.data,
|
|
298
312
|
_metadata: this.createMetadata('cache', cached.age),
|
|
299
313
|
};
|
|
300
314
|
}
|
|
301
315
|
|
|
316
|
+
// Not cached - try remote if connected
|
|
302
317
|
if (this.connectionManager.getState() === 'connected') {
|
|
303
318
|
try {
|
|
304
319
|
const result = await this.connectionManager.execute(async () => {
|
|
@@ -313,19 +328,11 @@ export class ProxyServer {
|
|
|
313
328
|
};
|
|
314
329
|
} catch (error) {
|
|
315
330
|
console.error('Failed to fetch memory from QwickBrain:', error);
|
|
331
|
+
// Fall through to error
|
|
316
332
|
}
|
|
317
333
|
}
|
|
318
334
|
|
|
319
|
-
|
|
320
|
-
return {
|
|
321
|
-
data: cached.data,
|
|
322
|
-
_metadata: {
|
|
323
|
-
...this.createMetadata('stale_cache', cached.age),
|
|
324
|
-
warning: `QwickBrain unavailable - serving cached memory (${cached.age}s old)`,
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
335
|
+
// No cache and remote failed/unavailable
|
|
329
336
|
return {
|
|
330
337
|
error: {
|
|
331
338
|
code: 'UNAVAILABLE',
|
|
@@ -336,6 +343,94 @@ export class ProxyServer {
|
|
|
336
343
|
};
|
|
337
344
|
}
|
|
338
345
|
|
|
346
|
+
private async handleCreateDocument(
|
|
347
|
+
docType: string,
|
|
348
|
+
name: string,
|
|
349
|
+
content: string,
|
|
350
|
+
project?: string,
|
|
351
|
+
metadata?: Record<string, unknown>
|
|
352
|
+
): Promise<MCPResponse> {
|
|
353
|
+
// Always update local cache first
|
|
354
|
+
await this.cacheManager.setDocument(docType, name, content, project, metadata);
|
|
355
|
+
|
|
356
|
+
// If connected, sync immediately
|
|
357
|
+
if (this.connectionManager.getState() === 'connected') {
|
|
358
|
+
try {
|
|
359
|
+
await this.connectionManager.execute(async () => {
|
|
360
|
+
await this.qwickbrainClient.createDocument(docType, name, content, project, metadata);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
data: { success: true },
|
|
365
|
+
_metadata: this.createMetadata('live'),
|
|
366
|
+
};
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('Failed to create document on QwickBrain:', error);
|
|
369
|
+
// Fall through to queue
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// If offline or sync failed, queue for later
|
|
374
|
+
await this.writeQueueManager.queueOperation('create_document', {
|
|
375
|
+
docType,
|
|
376
|
+
name,
|
|
377
|
+
content,
|
|
378
|
+
project,
|
|
379
|
+
metadata,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
data: { success: true, queued: true },
|
|
384
|
+
_metadata: {
|
|
385
|
+
...this.createMetadata('cache'),
|
|
386
|
+
warning: 'Operation queued - will sync when connection restored',
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private async handleSetMemory(
|
|
392
|
+
name: string,
|
|
393
|
+
content: string,
|
|
394
|
+
project?: string,
|
|
395
|
+
metadata?: Record<string, unknown>
|
|
396
|
+
): Promise<MCPResponse> {
|
|
397
|
+
// Always update local cache first
|
|
398
|
+
await this.cacheManager.setMemory(name, content, project, metadata);
|
|
399
|
+
|
|
400
|
+
// If connected, sync immediately
|
|
401
|
+
if (this.connectionManager.getState() === 'connected') {
|
|
402
|
+
try {
|
|
403
|
+
await this.connectionManager.execute(async () => {
|
|
404
|
+
await this.qwickbrainClient.setMemory(name, content, project, metadata);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
data: { success: true },
|
|
409
|
+
_metadata: this.createMetadata('live'),
|
|
410
|
+
};
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error('Failed to set memory on QwickBrain:', error);
|
|
413
|
+
// Fall through to queue
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// If offline or sync failed, queue for later
|
|
418
|
+
await this.writeQueueManager.queueOperation('set_memory', {
|
|
419
|
+
name,
|
|
420
|
+
content,
|
|
421
|
+
project,
|
|
422
|
+
metadata,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
data: { success: true, queued: true },
|
|
427
|
+
_metadata: {
|
|
428
|
+
...this.createMetadata('cache'),
|
|
429
|
+
warning: 'Operation queued - will sync when connection restored',
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
339
434
|
private async handleGenericTool(name: string, args: Record<string, unknown>): Promise<MCPResponse> {
|
|
340
435
|
// Generic tool forwarding - no caching for non-document tools
|
|
341
436
|
if (this.connectionManager.getState() !== 'connected') {
|
|
@@ -388,15 +483,18 @@ export class ProxyServer {
|
|
|
388
483
|
}
|
|
389
484
|
|
|
390
485
|
async start(): Promise<void> {
|
|
391
|
-
//
|
|
392
|
-
|
|
393
|
-
if (documentsDeleted > 0 || memoriesDeleted > 0) {
|
|
394
|
-
console.error(`Cache cleanup: removed ${documentsDeleted} documents, ${memoriesDeleted} memories`);
|
|
395
|
-
}
|
|
486
|
+
// LRU cache handles eviction automatically when storage limit reached
|
|
487
|
+
// No need for startup cleanup with LRU-based cache
|
|
396
488
|
|
|
397
489
|
// Start connection manager (handles connection gracefully, doesn't throw)
|
|
398
490
|
await this.connectionManager.start();
|
|
399
491
|
|
|
492
|
+
// Start SSE invalidation listener if configured
|
|
493
|
+
if (this.sseInvalidationListener) {
|
|
494
|
+
await this.sseInvalidationListener.start();
|
|
495
|
+
console.error('SSE cache invalidation listener started');
|
|
496
|
+
}
|
|
497
|
+
|
|
400
498
|
// Start MCP server
|
|
401
499
|
const transport = new StdioServerTransport();
|
|
402
500
|
await this.server.connect(transport);
|
|
@@ -406,6 +504,12 @@ export class ProxyServer {
|
|
|
406
504
|
|
|
407
505
|
async stop(): Promise<void> {
|
|
408
506
|
this.connectionManager.stop();
|
|
507
|
+
|
|
508
|
+
// Stop SSE invalidation listener
|
|
509
|
+
if (this.sseInvalidationListener) {
|
|
510
|
+
this.sseInvalidationListener.stop();
|
|
511
|
+
}
|
|
512
|
+
|
|
409
513
|
try {
|
|
410
514
|
await this.qwickbrainClient.disconnect();
|
|
411
515
|
} catch (error) {
|
|
@@ -232,8 +232,11 @@ export class QwickBrainClient {
|
|
|
232
232
|
if (this.mode === 'mcp' || this.mode === 'sse') {
|
|
233
233
|
// For MCP/SSE mode, check if client is connected
|
|
234
234
|
if (!this.client) {
|
|
235
|
-
//
|
|
236
|
-
|
|
235
|
+
// Initialize connection on first health check
|
|
236
|
+
await this.connect();
|
|
237
|
+
if (!this.client) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
237
240
|
}
|
|
238
241
|
// Try listing tools as health check
|
|
239
242
|
await this.client.listTools();
|
|
@@ -310,6 +313,143 @@ export class QwickBrainClient {
|
|
|
310
313
|
}
|
|
311
314
|
}
|
|
312
315
|
|
|
316
|
+
async createDocument(
|
|
317
|
+
docType: string,
|
|
318
|
+
name: string,
|
|
319
|
+
content: string,
|
|
320
|
+
project?: string,
|
|
321
|
+
metadata?: Record<string, unknown>
|
|
322
|
+
): Promise<void> {
|
|
323
|
+
if (this.mode === 'mcp' || this.mode === 'sse') {
|
|
324
|
+
if (!this.client) {
|
|
325
|
+
throw new Error('MCP client not connected');
|
|
326
|
+
}
|
|
327
|
+
await this.client.callTool({
|
|
328
|
+
name: 'create_document',
|
|
329
|
+
arguments: {
|
|
330
|
+
doc_type: docType,
|
|
331
|
+
name,
|
|
332
|
+
content,
|
|
333
|
+
project,
|
|
334
|
+
metadata,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
if (!this.config.url) {
|
|
339
|
+
throw new Error('HTTP mode requires url to be configured');
|
|
340
|
+
}
|
|
341
|
+
const response = await fetch(`${this.config.url}/mcp/document/create`, {
|
|
342
|
+
method: 'POST',
|
|
343
|
+
headers: {
|
|
344
|
+
'Content-Type': 'application/json',
|
|
345
|
+
...(this.config.apiKey && { 'Authorization': `Bearer ${this.config.apiKey}` }),
|
|
346
|
+
},
|
|
347
|
+
body: JSON.stringify({ doc_type: docType, name, content, project, metadata }),
|
|
348
|
+
});
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async setMemory(
|
|
356
|
+
name: string,
|
|
357
|
+
content: string,
|
|
358
|
+
project?: string,
|
|
359
|
+
metadata?: Record<string, unknown>
|
|
360
|
+
): Promise<void> {
|
|
361
|
+
if (this.mode === 'mcp' || this.mode === 'sse') {
|
|
362
|
+
if (!this.client) {
|
|
363
|
+
throw new Error('MCP client not connected');
|
|
364
|
+
}
|
|
365
|
+
await this.client.callTool({
|
|
366
|
+
name: 'set_memory',
|
|
367
|
+
arguments: {
|
|
368
|
+
name,
|
|
369
|
+
content,
|
|
370
|
+
project,
|
|
371
|
+
metadata,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
} else {
|
|
375
|
+
if (!this.config.url) {
|
|
376
|
+
throw new Error('HTTP mode requires url to be configured');
|
|
377
|
+
}
|
|
378
|
+
const response = await fetch(`${this.config.url}/mcp/memory/set`, {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
headers: {
|
|
381
|
+
'Content-Type': 'application/json',
|
|
382
|
+
...(this.config.apiKey && { 'Authorization': `Bearer ${this.config.apiKey}` }),
|
|
383
|
+
},
|
|
384
|
+
body: JSON.stringify({ name, content, project, metadata }),
|
|
385
|
+
});
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async deleteDocument(docType: string, name: string, project?: string): Promise<void> {
|
|
393
|
+
if (this.mode === 'mcp' || this.mode === 'sse') {
|
|
394
|
+
if (!this.client) {
|
|
395
|
+
throw new Error('MCP client not connected');
|
|
396
|
+
}
|
|
397
|
+
await this.client.callTool({
|
|
398
|
+
name: 'delete_document',
|
|
399
|
+
arguments: {
|
|
400
|
+
doc_type: docType,
|
|
401
|
+
name,
|
|
402
|
+
project,
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
} else {
|
|
406
|
+
if (!this.config.url) {
|
|
407
|
+
throw new Error('HTTP mode requires url to be configured');
|
|
408
|
+
}
|
|
409
|
+
const response = await fetch(`${this.config.url}/mcp/document/delete`, {
|
|
410
|
+
method: 'POST',
|
|
411
|
+
headers: {
|
|
412
|
+
'Content-Type': 'application/json',
|
|
413
|
+
...(this.config.apiKey && { 'Authorization': `Bearer ${this.config.apiKey}` }),
|
|
414
|
+
},
|
|
415
|
+
body: JSON.stringify({ doc_type: docType, name, project }),
|
|
416
|
+
});
|
|
417
|
+
if (!response.ok) {
|
|
418
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async deleteMemory(name: string, project?: string): Promise<void> {
|
|
424
|
+
if (this.mode === 'mcp' || this.mode === 'sse') {
|
|
425
|
+
if (!this.client) {
|
|
426
|
+
throw new Error('MCP client not connected');
|
|
427
|
+
}
|
|
428
|
+
await this.client.callTool({
|
|
429
|
+
name: 'delete_memory',
|
|
430
|
+
arguments: {
|
|
431
|
+
name,
|
|
432
|
+
project,
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
if (!this.config.url) {
|
|
437
|
+
throw new Error('HTTP mode requires url to be configured');
|
|
438
|
+
}
|
|
439
|
+
const response = await fetch(`${this.config.url}/mcp/memory/delete`, {
|
|
440
|
+
method: 'POST',
|
|
441
|
+
headers: {
|
|
442
|
+
'Content-Type': 'application/json',
|
|
443
|
+
...(this.config.apiKey && { 'Authorization': `Bearer ${this.config.apiKey}` }),
|
|
444
|
+
},
|
|
445
|
+
body: JSON.stringify({ name, project }),
|
|
446
|
+
});
|
|
447
|
+
if (!response.ok) {
|
|
448
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
313
453
|
async disconnect(): Promise<void> {
|
|
314
454
|
if ((this.mode === 'mcp' || this.mode === 'sse') && this.client && this.transport) {
|
|
315
455
|
await this.client.close();
|