@aeriondyseti/vector-memory-mcp 2.1.1 → 2.2.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/http/server.ts +2 -1
- package/src/mcp/handlers.ts +23 -34
- package/src/mcp/tools.ts +12 -1
- package/src/services/memory.service.ts +24 -7
- package/src/types/conversation.ts +1 -0
package/package.json
CHANGED
package/src/http/server.ts
CHANGED
|
@@ -189,7 +189,8 @@ export function createHttpApp(memoryService: MemoryService, config: Config): Hon
|
|
|
189
189
|
// Get latest waypoint
|
|
190
190
|
app.get("/waypoint", async (c) => {
|
|
191
191
|
try {
|
|
192
|
-
const
|
|
192
|
+
const project = c.req.query("project");
|
|
193
|
+
const waypoint = await memoryService.getLatestWaypoint(project);
|
|
193
194
|
|
|
194
195
|
if (!waypoint) {
|
|
195
196
|
return c.json({ error: "No waypoint found" }, 404);
|
package/src/mcp/handlers.ts
CHANGED
|
@@ -38,6 +38,10 @@ function errorText(e: unknown): string {
|
|
|
38
38
|
return e instanceof Error ? e.message : String(e);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function errorResult(text: string): CallToolResult {
|
|
42
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
function parseDate(value: unknown, fieldName: string): Date | undefined {
|
|
42
46
|
if (value === undefined) return undefined;
|
|
43
47
|
const date = new Date(value as string);
|
|
@@ -59,7 +63,7 @@ export async function handleStoreMemories(
|
|
|
59
63
|
try {
|
|
60
64
|
memories = asArray(args?.memories, "memories");
|
|
61
65
|
} catch (e) {
|
|
62
|
-
return
|
|
66
|
+
return errorResult(errorText(e));
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
const ids: string[] = [];
|
|
@@ -93,7 +97,7 @@ export async function handleDeleteMemories(
|
|
|
93
97
|
try {
|
|
94
98
|
ids = asArray(args?.ids, "ids");
|
|
95
99
|
} catch (e) {
|
|
96
|
-
return
|
|
100
|
+
return errorResult(errorText(e));
|
|
97
101
|
}
|
|
98
102
|
const results: string[] = [];
|
|
99
103
|
|
|
@@ -128,7 +132,7 @@ export async function handleUpdateMemories(
|
|
|
128
132
|
try {
|
|
129
133
|
updates = asArray(args?.updates, "updates");
|
|
130
134
|
} catch (e) {
|
|
131
|
-
return
|
|
135
|
+
return errorResult(errorText(e));
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
const results: string[] = [];
|
|
@@ -168,10 +172,11 @@ export async function handleSearchMemories(
|
|
|
168
172
|
): Promise<CallToolResult> {
|
|
169
173
|
const query = args?.query;
|
|
170
174
|
if (typeof query !== "string" || query.trim() === "") {
|
|
171
|
-
return
|
|
175
|
+
return errorResult("query is required and must be a non-empty string");
|
|
172
176
|
}
|
|
173
177
|
const intent = (args?.intent as SearchIntent) ?? "fact_check";
|
|
174
178
|
const limit = (args?.limit as number) ?? 10;
|
|
179
|
+
const offset = (args?.offset as number) ?? 0;
|
|
175
180
|
const includeDeleted = (args?.include_deleted as boolean) ?? false;
|
|
176
181
|
const historyOnly = (args?.history_only as boolean) ?? false;
|
|
177
182
|
// history_only implies include_history
|
|
@@ -181,13 +186,14 @@ export async function handleSearchMemories(
|
|
|
181
186
|
try {
|
|
182
187
|
historyFilters = parseHistoryFilters(args);
|
|
183
188
|
} catch (e) {
|
|
184
|
-
return
|
|
189
|
+
return errorResult(errorText(e));
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
const results = await service.search(query, intent, limit, includeDeleted, {
|
|
188
193
|
includeHistory,
|
|
189
194
|
historyOnly,
|
|
190
195
|
historyFilters,
|
|
196
|
+
offset,
|
|
191
197
|
});
|
|
192
198
|
|
|
193
199
|
if (results.length === 0) {
|
|
@@ -243,7 +249,7 @@ export async function handleGetMemories(
|
|
|
243
249
|
try {
|
|
244
250
|
ids = asArray(args?.ids, "ids");
|
|
245
251
|
} catch (e) {
|
|
246
|
-
return
|
|
252
|
+
return errorResult(errorText(e));
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
const memories = await service.getMultiple(ids);
|
|
@@ -267,10 +273,7 @@ export async function handleReportMemoryUsefulness(
|
|
|
267
273
|
const memory = await service.vote(memoryId, useful ? 1 : -1);
|
|
268
274
|
|
|
269
275
|
if (!memory) {
|
|
270
|
-
return {
|
|
271
|
-
content: [{ type: "text", text: `Memory ${memoryId} not found` }],
|
|
272
|
-
isError: true,
|
|
273
|
-
};
|
|
276
|
+
return errorResult(`Memory ${memoryId} not found`);
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
return {
|
|
@@ -305,10 +308,11 @@ export async function handleSetWaypoint(
|
|
|
305
308
|
}
|
|
306
309
|
|
|
307
310
|
export async function handleGetWaypoint(
|
|
308
|
-
|
|
311
|
+
args: Record<string, unknown> | undefined,
|
|
309
312
|
service: MemoryService
|
|
310
313
|
): Promise<CallToolResult> {
|
|
311
|
-
const
|
|
314
|
+
const project = args?.project as string | undefined;
|
|
315
|
+
const waypoint = await service.getLatestWaypoint(project);
|
|
312
316
|
|
|
313
317
|
if (!waypoint) {
|
|
314
318
|
return {
|
|
@@ -350,15 +354,9 @@ function requireConversationService(
|
|
|
350
354
|
const conversationService = service.getConversationService();
|
|
351
355
|
if (!conversationService) {
|
|
352
356
|
return {
|
|
353
|
-
error:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
type: "text",
|
|
357
|
-
text: "Conversation history indexing is not enabled. Enable it with --enable-history.",
|
|
358
|
-
},
|
|
359
|
-
],
|
|
360
|
-
isError: true,
|
|
361
|
-
},
|
|
357
|
+
error: errorResult(
|
|
358
|
+
"Conversation history indexing is not enabled. Enable it with --enable-history."
|
|
359
|
+
),
|
|
362
360
|
};
|
|
363
361
|
}
|
|
364
362
|
return { service: conversationService };
|
|
@@ -376,7 +374,7 @@ export async function handleIndexConversations(
|
|
|
376
374
|
const sinceStr = args?.since as string | undefined;
|
|
377
375
|
const since = sinceStr ? new Date(sinceStr) : undefined;
|
|
378
376
|
if (since && isNaN(since.getTime())) {
|
|
379
|
-
return
|
|
377
|
+
return errorResult("Invalid 'since' date format");
|
|
380
378
|
}
|
|
381
379
|
|
|
382
380
|
const result = await conversationService.indexConversations(path, since);
|
|
@@ -444,18 +442,12 @@ export async function handleReindexSession(
|
|
|
444
442
|
|
|
445
443
|
const sessionId = args?.session_id as string | undefined;
|
|
446
444
|
if (!sessionId) {
|
|
447
|
-
return
|
|
448
|
-
content: [{ type: "text", text: "session_id is required" }],
|
|
449
|
-
isError: true,
|
|
450
|
-
};
|
|
445
|
+
return errorResult("session_id is required");
|
|
451
446
|
}
|
|
452
447
|
const result = await conversationService.reindexSession(sessionId);
|
|
453
448
|
|
|
454
449
|
if (!result.success) {
|
|
455
|
-
return {
|
|
456
|
-
content: [{ type: "text", text: `Reindex failed: ${result.error}` }],
|
|
457
|
-
isError: true,
|
|
458
|
-
};
|
|
450
|
+
return errorResult(`Reindex failed: ${result.error}`);
|
|
459
451
|
}
|
|
460
452
|
|
|
461
453
|
return {
|
|
@@ -497,9 +489,6 @@ export async function handleToolCall(
|
|
|
497
489
|
case "reindex_session":
|
|
498
490
|
return handleReindexSession(args, service);
|
|
499
491
|
default:
|
|
500
|
-
return {
|
|
501
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
502
|
-
isError: true,
|
|
503
|
-
};
|
|
492
|
+
return errorResult(`Unknown tool: ${name}`);
|
|
504
493
|
}
|
|
505
494
|
}
|
package/src/mcp/tools.ts
CHANGED
|
@@ -162,6 +162,11 @@ When in doubt, search. Missing context is costlier than an extra query.`,
|
|
|
162
162
|
description: "Maximum results to return (default: 10).",
|
|
163
163
|
default: 10,
|
|
164
164
|
},
|
|
165
|
+
offset: {
|
|
166
|
+
type: "integer",
|
|
167
|
+
description: "Number of results to skip for pagination (default: 0).",
|
|
168
|
+
default: 0,
|
|
169
|
+
},
|
|
165
170
|
include_deleted: {
|
|
166
171
|
type: "boolean",
|
|
167
172
|
description: "Include soft-deleted memories in results (default: false). Useful for recovering prior information.",
|
|
@@ -297,7 +302,13 @@ export const getWaypointTool: Tool = {
|
|
|
297
302
|
"Load the current project waypoint snapshot. Call at conversation start or when resuming a project.",
|
|
298
303
|
inputSchema: {
|
|
299
304
|
type: "object",
|
|
300
|
-
properties: {
|
|
305
|
+
properties: {
|
|
306
|
+
project: {
|
|
307
|
+
type: "string",
|
|
308
|
+
description:
|
|
309
|
+
"Project name to retrieve waypoint for. If omitted, retrieves the default (legacy) waypoint.",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
301
312
|
},
|
|
302
313
|
};
|
|
303
314
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomUUID } from "crypto";
|
|
1
|
+
import { randomUUID, createHash } from "crypto";
|
|
2
2
|
import type { Memory, SearchIntent, IntentProfile, HybridRow } from "../types/memory.js";
|
|
3
3
|
import { isDeleted } from "../types/memory.js";
|
|
4
4
|
import type { SearchResult, SearchOptions } from "../types/conversation.js";
|
|
@@ -185,6 +185,7 @@ export class MemoryService {
|
|
|
185
185
|
const queryEmbedding = await this.embeddings.embed(query);
|
|
186
186
|
const profile = INTENT_PROFILES[intent];
|
|
187
187
|
const now = new Date();
|
|
188
|
+
const offset = Math.min(options?.offset ?? 0, 500);
|
|
188
189
|
|
|
189
190
|
const hasConversationService = this.conversationService !== null;
|
|
190
191
|
const historyOnly = (options?.historyOnly ?? false) && hasConversationService;
|
|
@@ -195,11 +196,14 @@ export class MemoryService {
|
|
|
195
196
|
this.conversationService?.config.historyWeight ??
|
|
196
197
|
0.75;
|
|
197
198
|
|
|
199
|
+
// Widen the candidate pool to account for offset
|
|
200
|
+
const effectiveLimit = offset + limit;
|
|
201
|
+
|
|
198
202
|
// Run memory + history queries in parallel
|
|
199
203
|
const memoryPromise =
|
|
200
204
|
!historyOnly
|
|
201
205
|
? this.repository
|
|
202
|
-
.findHybrid(queryEmbedding, query,
|
|
206
|
+
.findHybrid(queryEmbedding, query, effectiveLimit * 5)
|
|
203
207
|
.then((candidates) =>
|
|
204
208
|
candidates
|
|
205
209
|
.filter((m) => includeDeleted || !isDeleted(m))
|
|
@@ -225,7 +229,7 @@ export class MemoryService {
|
|
|
225
229
|
.searchHistory(
|
|
226
230
|
query,
|
|
227
231
|
queryEmbedding,
|
|
228
|
-
historyOnly ?
|
|
232
|
+
historyOnly ? effectiveLimit * 5 : effectiveLimit * 3,
|
|
229
233
|
options?.historyFilters
|
|
230
234
|
)
|
|
231
235
|
.then((historyRows) =>
|
|
@@ -255,7 +259,7 @@ export class MemoryService {
|
|
|
255
259
|
const merged = [...memoryResults, ...historyResults];
|
|
256
260
|
merged.sort((a, b) => b.score - a.score);
|
|
257
261
|
|
|
258
|
-
return merged.slice(
|
|
262
|
+
return merged.slice(offset, offset + limit);
|
|
259
263
|
}
|
|
260
264
|
|
|
261
265
|
async trackAccess(ids: string[]): Promise<void> {
|
|
@@ -278,6 +282,19 @@ export class MemoryService {
|
|
|
278
282
|
private static readonly UUID_ZERO =
|
|
279
283
|
"00000000-0000-0000-0000-000000000000";
|
|
280
284
|
|
|
285
|
+
private static waypointId(project?: string): string {
|
|
286
|
+
if (!project?.length) return MemoryService.UUID_ZERO;
|
|
287
|
+
const hex = createHash("sha256").update(`waypoint:${project}`).digest("hex");
|
|
288
|
+
// Format as UUID: 8-4-4-4-12
|
|
289
|
+
return [
|
|
290
|
+
hex.slice(0, 8),
|
|
291
|
+
hex.slice(8, 12),
|
|
292
|
+
hex.slice(12, 16),
|
|
293
|
+
hex.slice(16, 20),
|
|
294
|
+
hex.slice(20, 32),
|
|
295
|
+
].join("-");
|
|
296
|
+
}
|
|
297
|
+
|
|
281
298
|
async setWaypoint(args: {
|
|
282
299
|
project: string;
|
|
283
300
|
branch?: string;
|
|
@@ -336,7 +353,7 @@ ${list(args.memory_ids)}`;
|
|
|
336
353
|
};
|
|
337
354
|
|
|
338
355
|
const memory: Memory = {
|
|
339
|
-
id: MemoryService.
|
|
356
|
+
id: MemoryService.waypointId(args.project),
|
|
340
357
|
content,
|
|
341
358
|
embedding: new Array(this.embeddings.dimension).fill(0),
|
|
342
359
|
metadata,
|
|
@@ -352,7 +369,7 @@ ${list(args.memory_ids)}`;
|
|
|
352
369
|
return memory;
|
|
353
370
|
}
|
|
354
371
|
|
|
355
|
-
async getLatestWaypoint(): Promise<Memory | null> {
|
|
356
|
-
return await this.get(MemoryService.
|
|
372
|
+
async getLatestWaypoint(project?: string): Promise<Memory | null> {
|
|
373
|
+
return await this.get(MemoryService.waypointId(project));
|
|
357
374
|
}
|
|
358
375
|
}
|