@aeriondyseti/vector-memory-mcp 1.1.0-dev.2 → 1.1.0-dev.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.
Files changed (92) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/config/index.d.ts +17 -10
  3. package/dist/src/config/index.d.ts.map +1 -1
  4. package/dist/src/config/index.js +25 -11
  5. package/dist/src/config/index.js.map +1 -1
  6. package/dist/src/db/conversation.repository.d.ts +26 -0
  7. package/dist/src/db/conversation.repository.d.ts.map +1 -0
  8. package/dist/src/db/conversation.repository.js +72 -0
  9. package/dist/src/db/conversation.repository.js.map +1 -0
  10. package/dist/src/db/conversation.schema.d.ts +4 -0
  11. package/dist/src/db/conversation.schema.d.ts.map +1 -0
  12. package/dist/src/db/conversation.schema.js +15 -0
  13. package/dist/src/db/conversation.schema.js.map +1 -0
  14. package/dist/src/db/lancedb-utils.d.ts +13 -3
  15. package/dist/src/db/lancedb-utils.d.ts.map +1 -1
  16. package/dist/src/db/lancedb-utils.js +36 -7
  17. package/dist/src/db/lancedb-utils.js.map +1 -1
  18. package/dist/src/db/memory.repository.js +7 -7
  19. package/dist/src/db/memory.repository.js.map +1 -1
  20. package/dist/src/http/server.d.ts.map +1 -1
  21. package/dist/src/http/server.js +26 -7
  22. package/dist/src/http/server.js.map +1 -1
  23. package/dist/src/index.js +7 -6
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/mcp/handlers.d.ts +1 -1
  26. package/dist/src/mcp/handlers.d.ts.map +1 -1
  27. package/dist/src/mcp/handlers.js +106 -117
  28. package/dist/src/mcp/handlers.js.map +1 -1
  29. package/dist/src/mcp/tools.d.ts.map +1 -1
  30. package/dist/src/mcp/tools.js +43 -14
  31. package/dist/src/mcp/tools.js.map +1 -1
  32. package/dist/src/services/conversation.service.d.ts +38 -0
  33. package/dist/src/services/conversation.service.d.ts.map +1 -0
  34. package/dist/src/services/conversation.service.js +252 -0
  35. package/dist/src/services/conversation.service.js.map +1 -0
  36. package/dist/src/services/memory.service.d.ts +7 -25
  37. package/dist/src/services/memory.service.d.ts.map +1 -1
  38. package/dist/src/services/memory.service.js +66 -80
  39. package/dist/src/services/memory.service.js.map +1 -1
  40. package/dist/src/services/parsers/claude-code.parser.d.ts +8 -0
  41. package/dist/src/services/parsers/claude-code.parser.d.ts.map +1 -0
  42. package/dist/src/services/parsers/claude-code.parser.js +191 -0
  43. package/dist/src/services/parsers/claude-code.parser.js.map +1 -0
  44. package/dist/src/services/parsers/types.d.ts +9 -0
  45. package/dist/src/services/parsers/types.d.ts.map +1 -0
  46. package/dist/src/services/parsers/types.js +2 -0
  47. package/dist/src/services/parsers/types.js.map +1 -0
  48. package/dist/src/types/conversation.d.ts +99 -0
  49. package/dist/src/types/conversation.d.ts.map +1 -0
  50. package/dist/src/types/conversation.js +2 -0
  51. package/dist/src/types/conversation.js.map +1 -0
  52. package/hooks/session-start.ts +60 -42
  53. package/package.json +1 -1
  54. package/src/config/index.ts +39 -21
  55. package/src/db/conversation.repository.ts +120 -0
  56. package/src/db/conversation.schema.ts +33 -0
  57. package/src/db/lancedb-utils.ts +35 -7
  58. package/src/db/memory.repository.ts +7 -7
  59. package/src/http/server.ts +31 -7
  60. package/src/index.ts +10 -11
  61. package/src/mcp/handlers.ts +121 -123
  62. package/src/mcp/tools.ts +44 -15
  63. package/src/services/conversation.service.ts +354 -0
  64. package/src/services/memory.service.ts +101 -105
  65. package/src/services/parsers/claude-code.parser.ts +242 -0
  66. package/src/services/parsers/types.ts +14 -0
  67. package/src/types/conversation.ts +108 -0
  68. package/dist/src/db/conversation-history.repository.d.ts +0 -24
  69. package/dist/src/db/conversation-history.repository.d.ts.map +0 -1
  70. package/dist/src/db/conversation-history.repository.js +0 -184
  71. package/dist/src/db/conversation-history.repository.js.map +0 -1
  72. package/dist/src/db/conversation-history.schema.d.ts +0 -10
  73. package/dist/src/db/conversation-history.schema.d.ts.map +0 -1
  74. package/dist/src/db/conversation-history.schema.js +0 -31
  75. package/dist/src/db/conversation-history.schema.js.map +0 -1
  76. package/dist/src/services/conversation-history.service.d.ts +0 -64
  77. package/dist/src/services/conversation-history.service.d.ts.map +0 -1
  78. package/dist/src/services/conversation-history.service.js +0 -244
  79. package/dist/src/services/conversation-history.service.js.map +0 -1
  80. package/dist/src/services/session-parser.d.ts +0 -59
  81. package/dist/src/services/session-parser.d.ts.map +0 -1
  82. package/dist/src/services/session-parser.js +0 -147
  83. package/dist/src/services/session-parser.js.map +0 -1
  84. package/dist/src/types/conversation-history.d.ts +0 -74
  85. package/dist/src/types/conversation-history.d.ts.map +0 -1
  86. package/dist/src/types/conversation-history.js +0 -2
  87. package/dist/src/types/conversation-history.js.map +0 -1
  88. package/src/db/conversation-history.repository.ts +0 -255
  89. package/src/db/conversation-history.schema.ts +0 -40
  90. package/src/services/conversation-history.service.ts +0 -320
  91. package/src/services/session-parser.ts +0 -232
  92. package/src/types/conversation-history.ts +0 -82
@@ -1,27 +1,8 @@
1
1
  import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import type { MemoryService } from "../services/memory.service.js";
3
- import type { ConversationHistoryService } from "../services/conversation-history.service.js";
3
+ import type { ConversationHistoryService } from "../services/conversation.service.js";
4
4
  import type { SearchIntent } from "../types/memory.js";
5
- import type { SearchResult } from "../types/conversation-history.js";
6
-
7
- /**
8
- * Guard: returns the ConversationHistoryService or an error CallToolResult.
9
- */
10
- function requireHistoryService(
11
- service: MemoryService,
12
- ): ConversationHistoryService | CallToolResult {
13
- const historyService = service.getConversationHistory();
14
- if (!historyService) {
15
- return {
16
- content: [{
17
- type: "text",
18
- text: "Conversation history indexing is not enabled. Set conversationHistory.enabled = true in config.",
19
- }],
20
- isError: true,
21
- };
22
- }
23
- return historyService;
24
- }
5
+ import type { HistoryFilters, SearchResult } from "../types/conversation.js";
25
6
 
26
7
  export async function handleStoreMemories(
27
8
  args: Record<string, unknown> | undefined,
@@ -126,69 +107,38 @@ export async function handleSearchMemories(
126
107
  const intent = (args?.intent as SearchIntent) ?? "fact_check";
127
108
  const limit = (args?.limit as number) ?? 10;
128
109
  const includeDeleted = (args?.include_deleted as boolean) ?? false;
129
- const includeHistory = (args?.include_history as boolean) ?? false;
130
110
  const historyOnly = (args?.history_only as boolean) ?? false;
111
+ // history_only implies include_history
112
+ const includeHistory = historyOnly ? true : (args?.include_history as boolean | undefined);
131
113
 
132
- if (includeHistory && historyOnly) {
133
- return {
134
- content: [{
135
- type: "text",
136
- text: "Cannot set both include_history and history_only to true. Use history_only for conversation history only, or include_history to merge with memories.",
137
- }],
138
- isError: true,
139
- };
140
- }
141
-
142
- // History-only: search only conversation history via the history service
143
- if (historyOnly) {
144
- const result = requireHistoryService(service);
145
- if ("content" in result) return result;
146
- const historyResults = await result.search(query, limit);
147
- if (historyResults.length === 0) {
148
- return {
149
- content: [{ type: "text", text: "No conversation history found matching your query." }],
150
- };
151
- }
152
- return {
153
- content: [{ type: "text", text: historyResults.map((r) => formatSearchResult(r)).join("\n\n---\n\n") }],
154
- };
155
- }
156
-
157
- // Unified search: merge memories + history
158
- if (includeHistory) {
159
- const results = await service.searchUnified(query, intent, limit, includeDeleted);
160
- if (results.length === 0) {
161
- return {
162
- content: [{ type: "text", text: "No results found matching your query." }],
163
- };
164
- }
165
- return {
166
- content: [{ type: "text", text: results.map((r) => formatSearchResult(r, includeDeleted)).join("\n\n---\n\n") }],
167
- };
168
- }
169
-
170
- // Default: memory-only search (original behavior)
171
- const memories = await service.search(query, intent, limit, includeDeleted);
114
+ const results = await service.search(query, intent, limit, includeDeleted, {
115
+ includeHistory,
116
+ historyOnly,
117
+ historyFilters: parseHistoryFilters(args),
118
+ });
172
119
 
173
- if (memories.length === 0) {
120
+ if (results.length === 0) {
174
121
  return {
175
- content: [{ type: "text", text: "No memories found matching your query." }],
122
+ content: [{ type: "text", text: "No results found matching your query." }],
176
123
  };
177
124
  }
178
125
 
179
- const results = memories.map((mem) => {
180
- let result = `ID: ${mem.id}\nContent: ${mem.content}`;
181
- if (Object.keys(mem.metadata).length > 0) {
182
- result += `\nMetadata: ${JSON.stringify(mem.metadata)}`;
126
+ const formatted = results.map((r: SearchResult) => {
127
+ let result = `[${r.source}] ID: ${r.id}\nContent: ${r.content}`;
128
+ if (r.metadata && Object.keys(r.metadata).length > 0) {
129
+ result += `\nMetadata: ${JSON.stringify(r.metadata)}`;
183
130
  }
184
- if (includeDeleted && mem.supersededBy) {
131
+ if (r.source === "memory" && includeDeleted && r.supersededBy) {
185
132
  result += `\n[DELETED]`;
186
133
  }
134
+ if (r.source === "conversation_history" && r.sessionId) {
135
+ result += `\nSession: ${r.sessionId}`;
136
+ }
187
137
  return result;
188
138
  });
189
139
 
190
140
  return {
191
- content: [{ type: "text", text: results.join("\n\n---\n\n") }],
141
+ content: [{ type: "text", text: formatted.join("\n\n---\n\n") }],
192
142
  };
193
143
  }
194
144
 
@@ -201,7 +151,7 @@ function formatMemoryDetail(
201
151
  }
202
152
 
203
153
  let result = `ID: ${memory.id}\nContent: ${memory.content}`;
204
- if (Object.keys(memory.metadata).length > 0) {
154
+ if (memory.metadata && Object.keys(memory.metadata).length > 0) {
205
155
  result += `\nMetadata: ${JSON.stringify(memory.metadata)}`;
206
156
  }
207
157
  result += `\nCreated: ${memory.createdAt.toISOString()}`;
@@ -212,31 +162,6 @@ function formatMemoryDetail(
212
162
  return result;
213
163
  }
214
164
 
215
- /**
216
- * Format a unified SearchResult (memory or history) for display.
217
- * TODO: The default memory-only search path in handleSearchMemories formats results inline
218
- * with similar logic but without the "Source:" label. Consolidating would add "Source: memory"
219
- * to existing output, which may break consumers that parse it. (#5)
220
- */
221
- function formatSearchResult(result: SearchResult, includeDeleted: boolean = false): string {
222
- if (result.source === "memory") {
223
- let text = `ID: ${result.id}\nSource: memory\nContent: ${result.content}`;
224
- if (Object.keys(result.metadata).length > 0) {
225
- text += `\nMetadata: ${JSON.stringify(result.metadata)}`;
226
- }
227
- if (includeDeleted && result.supersededBy) {
228
- text += `\n[DELETED]`;
229
- }
230
- return text;
231
- }
232
- // conversation_history
233
- let text = `ID: ${result.id}\nSource: conversation_history\nSession: ${result.sessionId}\nRole: ${result.role}\nTimestamp: ${result.timestamp.toISOString()}\nContent: ${result.content}`;
234
- if (Object.keys(result.metadata).length > 0) {
235
- text += `\nMetadata: ${JSON.stringify(result.metadata)}`;
236
- }
237
- return text;
238
- }
239
-
240
165
  export async function handleGetMemories(
241
166
  args: Record<string, unknown> | undefined,
242
167
  service: MemoryService
@@ -335,48 +260,105 @@ export async function handleGetCheckpoint(
335
260
  };
336
261
  }
337
262
 
263
+ function parseHistoryFilters(
264
+ args: Record<string, unknown> | undefined
265
+ ): HistoryFilters {
266
+ return {
267
+ sessionId: args?.session_id as string | undefined,
268
+ role: args?.role_filter as string | undefined,
269
+ after: args?.history_after
270
+ ? new Date(args.history_after as string)
271
+ : undefined,
272
+ before: args?.history_before
273
+ ? new Date(args.history_before as string)
274
+ : undefined,
275
+ };
276
+ }
277
+
278
+ function requireConversationService(
279
+ service: MemoryService
280
+ ): { service: ConversationHistoryService } | { error: CallToolResult } {
281
+ const conversationService = service.getConversationService();
282
+ if (!conversationService) {
283
+ return {
284
+ error: {
285
+ content: [
286
+ {
287
+ type: "text",
288
+ text: "Conversation history indexing is not enabled. Enable it with --enable-history.",
289
+ },
290
+ ],
291
+ isError: true,
292
+ },
293
+ };
294
+ }
295
+ return { service: conversationService };
296
+ }
297
+
338
298
  export async function handleIndexConversations(
339
299
  args: Record<string, unknown> | undefined,
340
300
  service: MemoryService
341
301
  ): Promise<CallToolResult> {
342
- const result = requireHistoryService(service);
343
- if ("content" in result) return result;
302
+ const conv = requireConversationService(service);
303
+ if ("error" in conv) return conv.error;
304
+ const conversationService = conv.service;
344
305
 
345
306
  const path = args?.path as string | undefined;
346
- const summary = await result.indexConversations(path);
307
+ const sinceStr = args?.since as string | undefined;
308
+ const since = sinceStr ? new Date(sinceStr) : undefined;
309
+
310
+ const result = await conversationService.indexConversations(path, since);
347
311
 
348
312
  return {
349
- content: [{
350
- type: "text",
351
- text: `Indexing complete.\n- Sessions discovered: ${summary.sessionsDiscovered}\n- Sessions indexed: ${summary.sessionsIndexed}\n- Sessions skipped (unchanged): ${summary.sessionsSkipped}\n- Messages indexed: ${summary.messagesIndexed}`,
352
- }],
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text:
317
+ `Indexing complete:\n- Indexed: ${result.indexed} sessions\n- Skipped: ${result.skipped} sessions (unchanged)\n` +
318
+ (result.errors.length > 0
319
+ ? `- Errors: ${result.errors.length}\n${result.errors.map((e) => ` - ${e}`).join("\n")}`
320
+ : "- No errors"),
321
+ },
322
+ ],
353
323
  };
354
324
  }
355
325
 
356
326
  export async function handleListIndexedSessions(
357
- _args: Record<string, unknown> | undefined,
327
+ args: Record<string, unknown> | undefined,
358
328
  service: MemoryService
359
329
  ): Promise<CallToolResult> {
360
- const result = requireHistoryService(service);
361
- if ("content" in result) return result;
330
+ const conv = requireConversationService(service);
331
+ if ("error" in conv) return conv.error;
332
+ const conversationService = conv.service;
362
333
 
363
- const sessions = await result.listIndexedSessions();
334
+ const limit = (args?.limit as number) ?? 20;
335
+ const offset = (args?.offset as number) ?? 0;
336
+ const { sessions, total } =
337
+ await conversationService.listIndexedSessions(limit, offset);
364
338
 
365
339
  if (sessions.length === 0) {
366
340
  return {
367
- content: [{ type: "text", text: "No indexed sessions found. Run index_conversations first." }],
341
+ content: [
342
+ {
343
+ type: "text",
344
+ text: "No indexed sessions found. Run index_conversations first.",
345
+ },
346
+ ],
368
347
  };
369
348
  }
370
349
 
371
- const lines = sessions.map((s) => {
372
- let line = `Session: ${s.sessionId}\n Messages: ${s.messageCount}\n First: ${s.firstMessageAt.toISOString()}\n Last: ${s.lastMessageAt.toISOString()}\n Indexed: ${s.indexedAt.toISOString()}`;
373
- if (s.project) line += `\n Project: ${s.project}`;
374
- if (s.gitBranch) line += `\n Branch: ${s.gitBranch}`;
375
- return line;
376
- });
350
+ const lines = sessions.map(
351
+ (s) =>
352
+ `Session: ${s.sessionId}\n Project: ${s.project}\n Messages: ${s.messageCount} | Chunks: ${s.chunkCount}\n Period: ${s.firstMessageAt.toISOString()} to ${s.lastMessageAt.toISOString()}\n Indexed: ${s.indexedAt.toISOString()}`
353
+ );
377
354
 
378
355
  return {
379
- content: [{ type: "text", text: `${sessions.length} indexed session(s):\n\n${lines.join("\n\n")}` }],
356
+ content: [
357
+ {
358
+ type: "text",
359
+ text: `Showing ${offset + 1}-${offset + sessions.length} of ${total} sessions:\n\n${lines.join("\n\n")}`,
360
+ },
361
+ ],
380
362
  };
381
363
  }
382
364
 
@@ -384,17 +366,33 @@ export async function handleReindexSession(
384
366
  args: Record<string, unknown> | undefined,
385
367
  service: MemoryService
386
368
  ): Promise<CallToolResult> {
387
- const result = requireHistoryService(service);
388
- if ("content" in result) return result;
369
+ const conv = requireConversationService(service);
370
+ if ("error" in conv) return conv.error;
371
+ const conversationService = conv.service;
372
+
373
+ const sessionId = args?.session_id as string | undefined;
374
+ if (!sessionId) {
375
+ return {
376
+ content: [{ type: "text", text: "session_id is required" }],
377
+ isError: true,
378
+ };
379
+ }
380
+ const result = await conversationService.reindexSession(sessionId);
389
381
 
390
- const sessionId = args?.session_id as string;
391
- const summary = await result.reindexSession(sessionId);
382
+ if (!result.success) {
383
+ return {
384
+ content: [{ type: "text", text: `Reindex failed: ${result.error}` }],
385
+ isError: true,
386
+ };
387
+ }
392
388
 
393
389
  return {
394
- content: [{
395
- type: "text",
396
- text: `Reindex complete for session ${sessionId}.\n- Messages indexed: ${summary.messagesIndexed}`,
397
- }],
390
+ content: [
391
+ {
392
+ type: "text",
393
+ text: `Session ${sessionId} reindexed successfully. ${result.chunkCount} chunks created.`,
394
+ },
395
+ ],
398
396
  };
399
397
  }
400
398
 
package/src/mcp/tools.ts CHANGED
@@ -170,17 +170,32 @@ When in doubt, search. Missing context is costlier than an extra query.`,
170
170
  include_history: {
171
171
  type: "boolean",
172
172
  description:
173
- "Include conversation history results alongside memories (default: false). " +
174
- "Requires conversation history indexing to be enabled. History results are weighted lower than explicit memories.",
175
- default: false,
173
+ "Include conversation history results (default: true when history indexing is enabled).",
174
+ default: true,
176
175
  },
177
176
  history_only: {
178
177
  type: "boolean",
179
178
  description:
180
- "Search only conversation history, excluding explicit memories (default: false). " +
181
- "Requires conversation history indexing to be enabled.",
179
+ "Search only conversation history, not explicit memories. Implies include_history: true (default: false).",
182
180
  default: false,
183
181
  },
182
+ session_id: {
183
+ type: "string",
184
+ description: "Filter conversation history results to a specific session ID.",
185
+ },
186
+ role_filter: {
187
+ type: "string",
188
+ enum: ["user", "assistant"],
189
+ description: "Filter conversation history results by message role.",
190
+ },
191
+ history_after: {
192
+ type: "string",
193
+ description: "Filter conversation history results after this ISO date.",
194
+ },
195
+ history_before: {
196
+ type: "string",
197
+ description: "Filter conversation history results before this ISO date.",
198
+ },
184
199
  },
185
200
  required: ["query", "intent", "reason_for_search"],
186
201
  },
@@ -288,17 +303,22 @@ export const getCheckpointTool: Tool = {
288
303
 
289
304
  export const indexConversationsTool: Tool = {
290
305
  name: "index_conversations",
291
- description:
292
- "Index Claude Code session logs for searchable conversation history. " +
293
- "Scans for JSONL session files, detects new/changed sessions, and indexes text messages. " +
294
- "Skips unchanged files. Run periodically or after significant work sessions.",
306
+ description: `Scan session log directory for new or updated conversation sessions and index them as searchable history.
307
+
308
+ Indexing is idempotent: sessions that haven't changed since last indexing are skipped.
309
+ Requires conversation history indexing to be enabled in configuration (--enable-history).`,
295
310
  inputSchema: {
296
311
  type: "object",
297
312
  properties: {
298
313
  path: {
299
314
  type: "string",
300
315
  description:
301
- "Directory containing session JSONL files. If omitted, auto-detects the Claude Code sessions directory.",
316
+ "Override session log directory path. Defaults to configured path or Claude Code's session directory.",
317
+ },
318
+ since: {
319
+ type: "string",
320
+ description:
321
+ "Only index sessions modified after this ISO date. Example: '2026-03-01'",
302
322
  },
303
323
  },
304
324
  },
@@ -307,19 +327,28 @@ export const indexConversationsTool: Tool = {
307
327
  export const listIndexedSessionsTool: Tool = {
308
328
  name: "list_indexed_sessions",
309
329
  description:
310
- "List all conversation sessions that have been indexed. " +
311
- "Shows session ID, message count, time range, and associated project/branch.",
330
+ "Browse indexed conversation sessions with timestamps and chunk counts.",
312
331
  inputSchema: {
313
332
  type: "object",
314
- properties: {},
333
+ properties: {
334
+ limit: {
335
+ type: "integer",
336
+ description: "Maximum sessions to return (default: 20).",
337
+ default: 20,
338
+ },
339
+ offset: {
340
+ type: "integer",
341
+ description: "Number of sessions to skip for pagination (default: 0).",
342
+ default: 0,
343
+ },
344
+ },
315
345
  },
316
346
  };
317
347
 
318
348
  export const reindexSessionTool: Tool = {
319
349
  name: "reindex_session",
320
350
  description:
321
- "Force a full re-index of a specific session. Deletes all existing entries for the session and re-parses from scratch. " +
322
- "Use when session data appears corrupted or after parser improvements.",
351
+ "Force reindex of a specific conversation session. Useful if the session was updated or indexing failed previously.",
323
352
  inputSchema: {
324
353
  type: "object",
325
354
  properties: {