@aitytech/agentkits-memory 1.0.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.
Files changed (116) hide show
  1. package/README.md +250 -0
  2. package/dist/cache-manager.d.ts +134 -0
  3. package/dist/cache-manager.d.ts.map +1 -0
  4. package/dist/cache-manager.js +407 -0
  5. package/dist/cache-manager.js.map +1 -0
  6. package/dist/cli/save.d.ts +20 -0
  7. package/dist/cli/save.d.ts.map +1 -0
  8. package/dist/cli/save.js +94 -0
  9. package/dist/cli/save.js.map +1 -0
  10. package/dist/cli/setup.d.ts +18 -0
  11. package/dist/cli/setup.d.ts.map +1 -0
  12. package/dist/cli/setup.js +163 -0
  13. package/dist/cli/setup.js.map +1 -0
  14. package/dist/cli/viewer.d.ts +21 -0
  15. package/dist/cli/viewer.d.ts.map +1 -0
  16. package/dist/cli/viewer.js +182 -0
  17. package/dist/cli/viewer.js.map +1 -0
  18. package/dist/hnsw-index.d.ts +111 -0
  19. package/dist/hnsw-index.d.ts.map +1 -0
  20. package/dist/hnsw-index.js +781 -0
  21. package/dist/hnsw-index.js.map +1 -0
  22. package/dist/hooks/cli.d.ts +20 -0
  23. package/dist/hooks/cli.d.ts.map +1 -0
  24. package/dist/hooks/cli.js +102 -0
  25. package/dist/hooks/cli.js.map +1 -0
  26. package/dist/hooks/context.d.ts +31 -0
  27. package/dist/hooks/context.d.ts.map +1 -0
  28. package/dist/hooks/context.js +64 -0
  29. package/dist/hooks/context.js.map +1 -0
  30. package/dist/hooks/index.d.ts +16 -0
  31. package/dist/hooks/index.d.ts.map +1 -0
  32. package/dist/hooks/index.js +20 -0
  33. package/dist/hooks/index.js.map +1 -0
  34. package/dist/hooks/observation.d.ts +30 -0
  35. package/dist/hooks/observation.d.ts.map +1 -0
  36. package/dist/hooks/observation.js +79 -0
  37. package/dist/hooks/observation.js.map +1 -0
  38. package/dist/hooks/service.d.ts +102 -0
  39. package/dist/hooks/service.d.ts.map +1 -0
  40. package/dist/hooks/service.js +454 -0
  41. package/dist/hooks/service.js.map +1 -0
  42. package/dist/hooks/session-init.d.ts +30 -0
  43. package/dist/hooks/session-init.d.ts.map +1 -0
  44. package/dist/hooks/session-init.js +54 -0
  45. package/dist/hooks/session-init.js.map +1 -0
  46. package/dist/hooks/summarize.d.ts +30 -0
  47. package/dist/hooks/summarize.d.ts.map +1 -0
  48. package/dist/hooks/summarize.js +74 -0
  49. package/dist/hooks/summarize.js.map +1 -0
  50. package/dist/hooks/types.d.ts +193 -0
  51. package/dist/hooks/types.d.ts.map +1 -0
  52. package/dist/hooks/types.js +137 -0
  53. package/dist/hooks/types.js.map +1 -0
  54. package/dist/index.d.ts +173 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +564 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/mcp/index.d.ts +9 -0
  59. package/dist/mcp/index.d.ts.map +1 -0
  60. package/dist/mcp/index.js +9 -0
  61. package/dist/mcp/index.js.map +1 -0
  62. package/dist/mcp/server.d.ts +22 -0
  63. package/dist/mcp/server.d.ts.map +1 -0
  64. package/dist/mcp/server.js +368 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/mcp/tools.d.ts +14 -0
  67. package/dist/mcp/tools.d.ts.map +1 -0
  68. package/dist/mcp/tools.js +110 -0
  69. package/dist/mcp/tools.js.map +1 -0
  70. package/dist/mcp/types.d.ts +100 -0
  71. package/dist/mcp/types.d.ts.map +1 -0
  72. package/dist/mcp/types.js +9 -0
  73. package/dist/mcp/types.js.map +1 -0
  74. package/dist/migration.d.ts +77 -0
  75. package/dist/migration.d.ts.map +1 -0
  76. package/dist/migration.js +457 -0
  77. package/dist/migration.js.map +1 -0
  78. package/dist/sqljs-backend.d.ts +128 -0
  79. package/dist/sqljs-backend.d.ts.map +1 -0
  80. package/dist/sqljs-backend.js +623 -0
  81. package/dist/sqljs-backend.js.map +1 -0
  82. package/dist/types.d.ts +481 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +73 -0
  85. package/dist/types.js.map +1 -0
  86. package/hooks.json +46 -0
  87. package/package.json +67 -0
  88. package/src/__tests__/index.test.ts +407 -0
  89. package/src/__tests__/sqljs-backend.test.ts +410 -0
  90. package/src/cache-manager.ts +515 -0
  91. package/src/cli/save.ts +109 -0
  92. package/src/cli/setup.ts +203 -0
  93. package/src/cli/viewer.ts +218 -0
  94. package/src/hnsw-index.ts +1013 -0
  95. package/src/hooks/__tests__/handlers.test.ts +298 -0
  96. package/src/hooks/__tests__/integration.test.ts +431 -0
  97. package/src/hooks/__tests__/service.test.ts +487 -0
  98. package/src/hooks/__tests__/types.test.ts +341 -0
  99. package/src/hooks/cli.ts +121 -0
  100. package/src/hooks/context.ts +77 -0
  101. package/src/hooks/index.ts +23 -0
  102. package/src/hooks/observation.ts +102 -0
  103. package/src/hooks/service.ts +582 -0
  104. package/src/hooks/session-init.ts +70 -0
  105. package/src/hooks/summarize.ts +89 -0
  106. package/src/hooks/types.ts +365 -0
  107. package/src/index.ts +755 -0
  108. package/src/mcp/__tests__/server.test.ts +181 -0
  109. package/src/mcp/index.ts +9 -0
  110. package/src/mcp/server.ts +441 -0
  111. package/src/mcp/tools.ts +113 -0
  112. package/src/mcp/types.ts +109 -0
  113. package/src/migration.ts +574 -0
  114. package/src/sql.js.d.ts +70 -0
  115. package/src/sqljs-backend.ts +789 -0
  116. package/src/types.ts +715 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * MCP Server Tests
3
+ *
4
+ * Tests for the Memory MCP Server tools.
5
+ *
6
+ * @module @agentkits/memory/mcp/__tests__/server.test
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
10
+ import { ProjectMemoryService, DEFAULT_NAMESPACES } from '../../index.js';
11
+ import { MEMORY_TOOLS } from '../tools.js';
12
+ import type {
13
+ MemorySaveArgs,
14
+ MemorySearchArgs,
15
+ MemoryRecallArgs,
16
+ MemoryListArgs,
17
+ } from '../types.js';
18
+
19
+ // Mock ProjectMemoryService for isolated testing
20
+ vi.mock('../../index.js', async () => {
21
+ const actual = await vi.importActual('../../index.js');
22
+ return {
23
+ ...actual,
24
+ ProjectMemoryService: vi.fn(),
25
+ };
26
+ });
27
+
28
+ describe('MCP Server', () => {
29
+ describe('MEMORY_TOOLS', () => {
30
+ it('should export all required tools', () => {
31
+ const toolNames = MEMORY_TOOLS.map(t => t.name);
32
+
33
+ expect(toolNames).toContain('memory_save');
34
+ expect(toolNames).toContain('memory_search');
35
+ expect(toolNames).toContain('memory_recall');
36
+ expect(toolNames).toContain('memory_list');
37
+ expect(toolNames).toContain('memory_status');
38
+ });
39
+
40
+ it('should have valid input schemas for all tools', () => {
41
+ for (const tool of MEMORY_TOOLS) {
42
+ expect(tool.inputSchema).toBeDefined();
43
+ expect(tool.inputSchema.type).toBe('object');
44
+ expect(tool.inputSchema.properties).toBeDefined();
45
+ }
46
+ });
47
+
48
+ describe('memory_save tool', () => {
49
+ const saveTool = MEMORY_TOOLS.find(t => t.name === 'memory_save')!;
50
+
51
+ it('should require content parameter', () => {
52
+ expect(saveTool.inputSchema.required).toContain('content');
53
+ });
54
+
55
+ it('should have valid category enum', () => {
56
+ const categoryProp = saveTool.inputSchema.properties.category;
57
+ expect(categoryProp.enum).toEqual(['decision', 'pattern', 'error', 'context', 'observation']);
58
+ });
59
+
60
+ it('should have valid importance enum', () => {
61
+ const importanceProp = saveTool.inputSchema.properties.importance;
62
+ expect(importanceProp.enum).toEqual(['low', 'medium', 'high', 'critical']);
63
+ });
64
+ });
65
+
66
+ describe('memory_search tool', () => {
67
+ const searchTool = MEMORY_TOOLS.find(t => t.name === 'memory_search')!;
68
+
69
+ it('should require query parameter', () => {
70
+ expect(searchTool.inputSchema.required).toContain('query');
71
+ });
72
+
73
+ it('should have category filter option', () => {
74
+ expect(searchTool.inputSchema.properties.category).toBeDefined();
75
+ });
76
+
77
+ it('should have limit option', () => {
78
+ expect(searchTool.inputSchema.properties.limit).toBeDefined();
79
+ });
80
+ });
81
+
82
+ describe('memory_recall tool', () => {
83
+ const recallTool = MEMORY_TOOLS.find(t => t.name === 'memory_recall')!;
84
+
85
+ it('should require topic parameter', () => {
86
+ expect(recallTool.inputSchema.required).toContain('topic');
87
+ });
88
+
89
+ it('should have valid timeRange enum', () => {
90
+ const timeRangeProp = recallTool.inputSchema.properties.timeRange;
91
+ expect(timeRangeProp.enum).toEqual(['today', 'week', 'month', 'all']);
92
+ });
93
+ });
94
+
95
+ describe('memory_list tool', () => {
96
+ const listTool = MEMORY_TOOLS.find(t => t.name === 'memory_list')!;
97
+
98
+ it('should have optional category filter', () => {
99
+ expect(listTool.inputSchema.properties.category).toBeDefined();
100
+ // required is undefined or doesn't contain 'category'
101
+ const required = listTool.inputSchema.required || [];
102
+ expect(required).not.toContain('category');
103
+ });
104
+
105
+ it('should have optional limit parameter', () => {
106
+ expect(listTool.inputSchema.properties.limit).toBeDefined();
107
+ });
108
+ });
109
+
110
+ describe('memory_status tool', () => {
111
+ const statusTool = MEMORY_TOOLS.find(t => t.name === 'memory_status')!;
112
+
113
+ it('should have no required parameters', () => {
114
+ expect(statusTool.inputSchema.required).toBeUndefined();
115
+ });
116
+
117
+ it('should have empty properties', () => {
118
+ expect(Object.keys(statusTool.inputSchema.properties)).toHaveLength(0);
119
+ });
120
+ });
121
+ });
122
+
123
+ describe('Tool Argument Types', () => {
124
+ it('MemorySaveArgs should accept valid arguments', () => {
125
+ const args: MemorySaveArgs = {
126
+ content: 'Test content',
127
+ category: 'pattern',
128
+ tags: 'tag1,tag2',
129
+ importance: 'high',
130
+ };
131
+
132
+ expect(args.content).toBe('Test content');
133
+ expect(args.category).toBe('pattern');
134
+ expect(args.tags).toBe('tag1,tag2');
135
+ expect(args.importance).toBe('high');
136
+ });
137
+
138
+ it('MemorySaveArgs should work with minimal arguments', () => {
139
+ const args: MemorySaveArgs = {
140
+ content: 'Minimal content',
141
+ };
142
+
143
+ expect(args.content).toBe('Minimal content');
144
+ expect(args.category).toBeUndefined();
145
+ expect(args.tags).toBeUndefined();
146
+ expect(args.importance).toBeUndefined();
147
+ });
148
+
149
+ it('MemorySearchArgs should accept valid arguments', () => {
150
+ const args: MemorySearchArgs = {
151
+ query: 'search term',
152
+ limit: 10,
153
+ category: 'decision',
154
+ };
155
+
156
+ expect(args.query).toBe('search term');
157
+ expect(args.limit).toBe(10);
158
+ expect(args.category).toBe('decision');
159
+ });
160
+
161
+ it('MemoryRecallArgs should accept valid arguments', () => {
162
+ const args: MemoryRecallArgs = {
163
+ topic: 'authentication',
164
+ timeRange: 'week',
165
+ };
166
+
167
+ expect(args.topic).toBe('authentication');
168
+ expect(args.timeRange).toBe('week');
169
+ });
170
+
171
+ it('MemoryListArgs should accept valid arguments', () => {
172
+ const args: MemoryListArgs = {
173
+ category: 'error',
174
+ limit: 5,
175
+ };
176
+
177
+ expect(args.category).toBe('error');
178
+ expect(args.limit).toBe(5);
179
+ });
180
+ });
181
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * MCP Module Exports
3
+ *
4
+ * @module @agentkits/memory/mcp
5
+ */
6
+
7
+ export * from './types.js';
8
+ export * from './tools.js';
9
+ export { default as MEMORY_TOOLS } from './tools.js';
@@ -0,0 +1,441 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentKits Memory MCP Server
4
+ *
5
+ * Model Context Protocol server for Claude Code memory access.
6
+ * Provides tools for saving, searching, and recalling memories.
7
+ *
8
+ * Usage:
9
+ * Add to .mcp.json:
10
+ * {
11
+ * "mcpServers": {
12
+ * "memory": {
13
+ * "command": "npx",
14
+ * "args": ["agentkits-memory-server"]
15
+ * }
16
+ * }
17
+ * }
18
+ *
19
+ * @module @agentkits/memory/mcp/server
20
+ */
21
+
22
+ import * as readline from 'node:readline';
23
+ import { ProjectMemoryService, MemoryEntry, MemoryQuery, DEFAULT_NAMESPACES } from '../index.js';
24
+ import { MEMORY_TOOLS } from './tools.js';
25
+ import type {
26
+ JSONRPCRequest,
27
+ JSONRPCResponse,
28
+ ToolCallRequest,
29
+ ToolCallResult,
30
+ MemorySaveArgs,
31
+ MemorySearchArgs,
32
+ MemoryRecallArgs,
33
+ MemoryListArgs,
34
+ } from './types.js';
35
+
36
+ // Map category names to namespaces
37
+ const CATEGORY_TO_NAMESPACE: Record<string, string> = {
38
+ decision: DEFAULT_NAMESPACES.DECISIONS,
39
+ pattern: DEFAULT_NAMESPACES.PATTERNS,
40
+ error: DEFAULT_NAMESPACES.ERRORS,
41
+ context: DEFAULT_NAMESPACES.CONTEXT,
42
+ observation: DEFAULT_NAMESPACES.ACTIVE,
43
+ };
44
+
45
+ /**
46
+ * Memory MCP Server
47
+ */
48
+ class MemoryMCPServer {
49
+ private service: ProjectMemoryService | null = null;
50
+ private projectDir: string;
51
+ private initialized = false;
52
+
53
+ constructor() {
54
+ this.projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
55
+ }
56
+
57
+ /**
58
+ * Initialize the memory service
59
+ */
60
+ private async ensureInitialized(): Promise<ProjectMemoryService> {
61
+ if (!this.service || !this.initialized) {
62
+ this.service = new ProjectMemoryService({
63
+ baseDir: `${this.projectDir}/.claude/memory`,
64
+ dbFilename: 'memory.db',
65
+ });
66
+ await this.service.initialize();
67
+ this.initialized = true;
68
+ }
69
+ return this.service;
70
+ }
71
+
72
+ /**
73
+ * Handle JSON-RPC request
74
+ */
75
+ async handleRequest(request: JSONRPCRequest): Promise<JSONRPCResponse> {
76
+ try {
77
+ switch (request.method) {
78
+ case 'initialize':
79
+ return this.handleInitialize(request);
80
+
81
+ case 'tools/list':
82
+ return this.handleToolsList(request);
83
+
84
+ case 'tools/call':
85
+ return this.handleToolCall(request);
86
+
87
+ case 'notifications/initialized':
88
+ // Client initialized notification - acknowledge silently
89
+ return { jsonrpc: '2.0', id: request.id, result: {} };
90
+
91
+ default:
92
+ return {
93
+ jsonrpc: '2.0',
94
+ id: request.id,
95
+ error: {
96
+ code: -32601,
97
+ message: `Method not found: ${request.method}`,
98
+ },
99
+ };
100
+ }
101
+ } catch (error) {
102
+ return {
103
+ jsonrpc: '2.0',
104
+ id: request.id,
105
+ error: {
106
+ code: -32603,
107
+ message: error instanceof Error ? error.message : 'Internal error',
108
+ },
109
+ };
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Handle initialize request
115
+ */
116
+ private handleInitialize(request: JSONRPCRequest): JSONRPCResponse {
117
+ return {
118
+ jsonrpc: '2.0',
119
+ id: request.id,
120
+ result: {
121
+ protocolVersion: '2024-11-05',
122
+ capabilities: {
123
+ tools: {},
124
+ },
125
+ serverInfo: {
126
+ name: 'agentkits-memory',
127
+ version: '1.0.0',
128
+ },
129
+ },
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Handle tools/list request
135
+ */
136
+ private handleToolsList(request: JSONRPCRequest): JSONRPCResponse {
137
+ return {
138
+ jsonrpc: '2.0',
139
+ id: request.id,
140
+ result: {
141
+ tools: MEMORY_TOOLS,
142
+ },
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Handle tools/call request
148
+ */
149
+ private async handleToolCall(request: JSONRPCRequest): Promise<JSONRPCResponse> {
150
+ const params = request.params as ToolCallRequest;
151
+ const result = await this.executeTool(params.name, params.arguments);
152
+
153
+ return {
154
+ jsonrpc: '2.0',
155
+ id: request.id,
156
+ result,
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Execute a tool
162
+ */
163
+ private async executeTool(
164
+ name: string,
165
+ args: Record<string, unknown>
166
+ ): Promise<ToolCallResult> {
167
+ try {
168
+ const service = await this.ensureInitialized();
169
+
170
+ switch (name) {
171
+ case 'memory_save':
172
+ return this.toolSave(service, args as unknown as MemorySaveArgs);
173
+
174
+ case 'memory_search':
175
+ return this.toolSearch(service, args as unknown as MemorySearchArgs);
176
+
177
+ case 'memory_recall':
178
+ return this.toolRecall(service, args as unknown as MemoryRecallArgs);
179
+
180
+ case 'memory_list':
181
+ return this.toolList(service, args as unknown as MemoryListArgs);
182
+
183
+ case 'memory_status':
184
+ return this.toolStatus(service);
185
+
186
+ default:
187
+ return {
188
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
189
+ isError: true,
190
+ };
191
+ }
192
+ } catch (error) {
193
+ return {
194
+ content: [{
195
+ type: 'text',
196
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
197
+ }],
198
+ isError: true,
199
+ };
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Save memory tool
205
+ */
206
+ private async toolSave(
207
+ service: ProjectMemoryService,
208
+ args: MemorySaveArgs
209
+ ): Promise<ToolCallResult> {
210
+ const tags = typeof args.tags === 'string'
211
+ ? args.tags.split(',').map((t: string) => t.trim())
212
+ : args.tags || [];
213
+
214
+ // Map category to namespace
215
+ const category = args.category || 'observation';
216
+ const namespace = CATEGORY_TO_NAMESPACE[category] || DEFAULT_NAMESPACES.ACTIVE;
217
+
218
+ // Store entry using storeEntry convenience method
219
+ const entry = await service.storeEntry({
220
+ key: `${category}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
221
+ content: args.content,
222
+ namespace,
223
+ tags: [...tags, category],
224
+ metadata: {
225
+ importance: args.importance || 'medium',
226
+ source: 'mcp',
227
+ savedAt: new Date().toISOString(),
228
+ },
229
+ });
230
+
231
+ return {
232
+ content: [{
233
+ type: 'text',
234
+ text: `Saved to memory (${category}): "${args.content.slice(0, 100)}${args.content.length > 100 ? '...' : ''}"`,
235
+ }],
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Search memory tool
241
+ */
242
+ private async toolSearch(
243
+ service: ProjectMemoryService,
244
+ args: MemorySearchArgs
245
+ ): Promise<ToolCallResult> {
246
+ const limit = typeof args.limit === 'string' ? parseInt(args.limit, 10) : (args.limit || 5);
247
+
248
+ // Map category to namespace
249
+ const namespace = args.category ? CATEGORY_TO_NAMESPACE[args.category] : undefined;
250
+
251
+ // Build query
252
+ const query: MemoryQuery = {
253
+ type: 'hybrid',
254
+ limit,
255
+ namespace,
256
+ content: args.query,
257
+ };
258
+
259
+ const results = await service.query(query);
260
+
261
+ if (results.length === 0) {
262
+ return {
263
+ content: [{
264
+ type: 'text',
265
+ text: `No memories found for: "${args.query}"`,
266
+ }],
267
+ };
268
+ }
269
+
270
+ const formatted = results.map((entry: MemoryEntry, i: number) => {
271
+ const category = entry.tags.find(t => Object.keys(CATEGORY_TO_NAMESPACE).includes(t)) || entry.namespace;
272
+ return `${i + 1}. [${category}]\n ${entry.content}\n Tags: ${entry.tags.join(', ') || 'none'}`;
273
+ }).join('\n\n');
274
+
275
+ return {
276
+ content: [{
277
+ type: 'text',
278
+ text: `Found ${results.length} memories:\n\n${formatted}`,
279
+ }],
280
+ };
281
+ }
282
+
283
+ /**
284
+ * Recall topic tool
285
+ */
286
+ private async toolRecall(
287
+ service: ProjectMemoryService,
288
+ args: MemoryRecallArgs
289
+ ): Promise<ToolCallResult> {
290
+ // Search for topic
291
+ const query: MemoryQuery = {
292
+ type: 'hybrid',
293
+ limit: 10,
294
+ content: args.topic,
295
+ };
296
+
297
+ const results = await service.query(query);
298
+
299
+ if (results.length === 0) {
300
+ return {
301
+ content: [{
302
+ type: 'text',
303
+ text: `No memories found about: "${args.topic}"`,
304
+ }],
305
+ };
306
+ }
307
+
308
+ // Group by namespace
309
+ const byNamespace: Record<string, string[]> = {};
310
+ for (const entry of results) {
311
+ const ns = entry.namespace || 'general';
312
+ if (!byNamespace[ns]) byNamespace[ns] = [];
313
+ byNamespace[ns].push(entry.content);
314
+ }
315
+
316
+ // Format output
317
+ let output = `## Memory Recall: ${args.topic}\n\n`;
318
+ for (const [namespace, items] of Object.entries(byNamespace)) {
319
+ output += `### ${namespace.charAt(0).toUpperCase() + namespace.slice(1)}\n`;
320
+ items.forEach((item: string) => {
321
+ output += `- ${item}\n`;
322
+ });
323
+ output += '\n';
324
+ }
325
+
326
+ return {
327
+ content: [{ type: 'text', text: output }],
328
+ };
329
+ }
330
+
331
+ /**
332
+ * List memories tool
333
+ */
334
+ private async toolList(
335
+ service: ProjectMemoryService,
336
+ args: MemoryListArgs
337
+ ): Promise<ToolCallResult> {
338
+ const limit = typeof args.limit === 'string' ? parseInt(args.limit, 10) : (args.limit || 10);
339
+
340
+ // Map category to namespace
341
+ const namespace = args.category ? CATEGORY_TO_NAMESPACE[args.category] : undefined;
342
+
343
+ // Get recent entries
344
+ const query: MemoryQuery = {
345
+ type: 'hybrid',
346
+ limit,
347
+ namespace,
348
+ };
349
+
350
+ const results = await service.query(query);
351
+
352
+ if (results.length === 0) {
353
+ return {
354
+ content: [{
355
+ type: 'text',
356
+ text: 'No memories stored yet.',
357
+ }],
358
+ };
359
+ }
360
+
361
+ const formatted = results.map((entry: MemoryEntry, i: number) => {
362
+ const date = new Date(entry.createdAt).toLocaleString();
363
+ const category = entry.tags.find(t => Object.keys(CATEGORY_TO_NAMESPACE).includes(t)) || entry.namespace;
364
+ return `${i + 1}. [${category}] ${entry.content.slice(0, 80)}${entry.content.length > 80 ? '...' : ''}\n Created: ${date}`;
365
+ }).join('\n\n');
366
+
367
+ return {
368
+ content: [{
369
+ type: 'text',
370
+ text: `Recent memories (${results.length}):\n\n${formatted}`,
371
+ }],
372
+ };
373
+ }
374
+
375
+ /**
376
+ * Memory status tool
377
+ */
378
+ private async toolStatus(service: ProjectMemoryService): Promise<ToolCallResult> {
379
+ const stats = await service.getStats();
380
+
381
+ const output = `## Memory System Status
382
+
383
+ - **Entries**: ${stats.totalEntries}
384
+ - **Namespaces**: ${Object.keys(stats.entriesByNamespace || {}).join(', ') || 'none'}
385
+ - **Database**: ${this.projectDir}/.claude/memory/memory.db
386
+ - **Status**: Connected
387
+
388
+ ### Namespace Breakdown
389
+ ${Object.entries(stats.entriesByNamespace || {}).map(([ns, count]) => `- ${ns}: ${count}`).join('\n') || '- No entries yet'}
390
+ `;
391
+
392
+ return {
393
+ content: [{ type: 'text', text: output }],
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Start the server
399
+ */
400
+ async start(): Promise<void> {
401
+ const rl = readline.createInterface({
402
+ input: process.stdin,
403
+ output: process.stdout,
404
+ terminal: false,
405
+ });
406
+
407
+ // Handle each line as a JSON-RPC request
408
+ rl.on('line', async (line) => {
409
+ try {
410
+ const request = JSON.parse(line) as JSONRPCRequest;
411
+ const response = await this.handleRequest(request);
412
+
413
+ // Only send response if there's an id (not a notification)
414
+ if (request.id !== undefined) {
415
+ console.log(JSON.stringify(response));
416
+ }
417
+ } catch (error) {
418
+ // Parse error
419
+ console.log(JSON.stringify({
420
+ jsonrpc: '2.0',
421
+ id: null,
422
+ error: {
423
+ code: -32700,
424
+ message: 'Parse error',
425
+ },
426
+ }));
427
+ }
428
+ });
429
+
430
+ rl.on('close', () => {
431
+ process.exit(0);
432
+ });
433
+ }
434
+ }
435
+
436
+ // Start server
437
+ const server = new MemoryMCPServer();
438
+ server.start().catch((error) => {
439
+ console.error('Failed to start MCP server:', error);
440
+ process.exit(1);
441
+ });