@slorenzot/memento-mcp-server 0.9.0 → 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.
- package/dist/index.js +442 -222
- package/dist/index.js.map +1 -1
- package/package.json +11 -6
- package/skills/memento/SKILL.md +267 -0
- package/src/index.ts +0 -537
- package/tsconfig.json +0 -22
package/dist/index.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
37
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
38
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
@@ -7,6 +40,7 @@ const zod_1 = require("zod");
|
|
|
7
40
|
const memento_core_1 = require("@slorenzot/memento-core");
|
|
8
41
|
const fs_1 = require("fs");
|
|
9
42
|
const path_1 = require("path");
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
10
44
|
// Helper function to handle errors in tool execution
|
|
11
45
|
function handleToolError(error) {
|
|
12
46
|
console.error('Tool execution error:', error.message);
|
|
@@ -19,11 +53,7 @@ function handleToolError(error) {
|
|
|
19
53
|
content: [
|
|
20
54
|
{
|
|
21
55
|
type: 'text',
|
|
22
|
-
text: JSON.stringify({
|
|
23
|
-
success: false,
|
|
24
|
-
error: error.message,
|
|
25
|
-
hint,
|
|
26
|
-
}, null, 2),
|
|
56
|
+
text: JSON.stringify({ success: false, error: error.message, hint }, null, 2),
|
|
27
57
|
},
|
|
28
58
|
],
|
|
29
59
|
};
|
|
@@ -34,17 +64,21 @@ const projectId = (0, memento_core_1.getProjectId)(config);
|
|
|
34
64
|
const engine = new memento_core_1.MemoryEngine(dbPath);
|
|
35
65
|
let activeSessionId = null;
|
|
36
66
|
const server = new mcp_js_1.McpServer({
|
|
37
|
-
name: 'memento
|
|
38
|
-
version: '0.
|
|
67
|
+
name: 'memento',
|
|
68
|
+
version: '1.0.0',
|
|
39
69
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
70
|
+
// ─── Observation Tools ──────────────────────────────────────
|
|
71
|
+
server.tool('memento_mem_save', 'Save an observation to persistent memory. Types: decision, bug, discovery, note. Call this PROACTIVELY after making decisions, fixing bugs, or discovering something non-obvious.', {
|
|
72
|
+
title: zod_1.z.string().describe('Short, searchable title (e.g. "Fixed N+1 in UserList")'),
|
|
73
|
+
content: zod_1.z.string().describe('Structured content: What/Why/Where/Learned format'),
|
|
43
74
|
type: zod_1.z
|
|
44
75
|
.enum(['decision', 'bug', 'discovery', 'note'])
|
|
45
76
|
.optional()
|
|
46
77
|
.describe('Type of observation (default: note)'),
|
|
47
|
-
topic_key: zod_1.z
|
|
78
|
+
topic_key: zod_1.z
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe('Stable topic key for grouping (e.g. "architecture/auth-model")'),
|
|
48
82
|
project_id: zod_1.z.string().optional().describe('Project identifier'),
|
|
49
83
|
metadata: zod_1.z.record(zod_1.z.unknown()).optional().describe('Additional metadata'),
|
|
50
84
|
}, async ({ title, content, type, topic_key, project_id, metadata }) => {
|
|
@@ -82,185 +116,362 @@ server.tool('mem_save', 'Save an observation to memory. Types: decision, bug, di
|
|
|
82
116
|
return handleToolError(error);
|
|
83
117
|
}
|
|
84
118
|
});
|
|
85
|
-
server.tool('
|
|
86
|
-
query: zod_1.z.string().optional().describe('Search query'),
|
|
119
|
+
server.tool('memento_mem_search', 'Search observations using full-text search (FTS5). Start with small limits and expand only if needed.', {
|
|
120
|
+
query: zod_1.z.string().optional().describe('Search query (FTS5 syntax)'),
|
|
87
121
|
type: zod_1.z.enum(['decision', 'bug', 'discovery', 'note']).optional(),
|
|
88
122
|
project_id: zod_1.z.string().optional(),
|
|
89
123
|
topic_key: zod_1.z.string().optional(),
|
|
90
|
-
limit: zod_1.z.number().optional(),
|
|
124
|
+
limit: zod_1.z.number().optional().describe('Max results (default: 10)'),
|
|
91
125
|
offset: zod_1.z.number().optional(),
|
|
92
|
-
|
|
126
|
+
include_deleted: zod_1.z.boolean().optional().describe('Include soft-deleted observations'),
|
|
127
|
+
}, async ({ query, type, project_id, topic_key, limit, offset, include_deleted }) => {
|
|
93
128
|
try {
|
|
94
129
|
const result = await engine.search({
|
|
95
130
|
query,
|
|
96
131
|
type: type,
|
|
97
132
|
projectId: project_id,
|
|
98
133
|
topicKey: topic_key,
|
|
99
|
-
limit,
|
|
134
|
+
limit: limit || 10,
|
|
100
135
|
offset,
|
|
136
|
+
includeDeleted: include_deleted,
|
|
101
137
|
});
|
|
102
138
|
return {
|
|
103
|
-
content: [
|
|
104
|
-
{
|
|
105
|
-
type: 'text',
|
|
106
|
-
text: JSON.stringify(result, null, 2),
|
|
107
|
-
},
|
|
108
|
-
],
|
|
139
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
109
140
|
};
|
|
110
141
|
}
|
|
111
142
|
catch (error) {
|
|
112
143
|
return handleToolError(error);
|
|
113
144
|
}
|
|
114
145
|
});
|
|
115
|
-
server.tool('
|
|
146
|
+
server.tool('memento_mem_get_observation', 'Get full content of a specific observation by ID.', {
|
|
116
147
|
id: zod_1.z.number().describe('Observation ID'),
|
|
117
148
|
}, async ({ id }) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
149
|
+
try {
|
|
150
|
+
const obs = await engine.getObservation(id);
|
|
151
|
+
if (!obs)
|
|
152
|
+
throw new Error(`Observation ${id} not found`);
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: 'text', text: JSON.stringify(obs, null, 2) }],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return handleToolError(error);
|
|
159
|
+
}
|
|
124
160
|
});
|
|
125
|
-
server.tool('
|
|
161
|
+
server.tool('memento_mem_update', 'Update an existing observation.', {
|
|
126
162
|
id: zod_1.z.number().describe('Observation ID'),
|
|
127
163
|
title: zod_1.z.string().optional(),
|
|
128
164
|
content: zod_1.z.string().optional(),
|
|
129
165
|
type: zod_1.z.enum(['decision', 'bug', 'discovery', 'note']).optional(),
|
|
130
166
|
topic_key: zod_1.z.string().optional(),
|
|
131
167
|
}, async ({ id, title, content, type, topic_key }) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
type: 'text',
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
try {
|
|
169
|
+
const updated = await engine.updateObservation(id, {
|
|
170
|
+
title,
|
|
171
|
+
content,
|
|
172
|
+
type: type,
|
|
173
|
+
topicKey: topic_key,
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{ type: 'text', text: JSON.stringify({ id: updated.id, success: true }, null, 2) },
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return handleToolError(error);
|
|
183
|
+
}
|
|
146
184
|
});
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
// ─── Soft Delete / Restore / Purge ──────────────────────────
|
|
186
|
+
server.tool('memento_mem_delete', 'Soft-delete an observation. The record is hidden from searches but can be restored with memento_mem_restore. Use memento_mem_purge for permanent deletion.', {
|
|
187
|
+
id: zod_1.z.number().describe('Observation ID to soft-delete'),
|
|
188
|
+
reason: zod_1.z.string().optional().describe('Reason for deletion (stored in metadata)'),
|
|
189
|
+
}, async ({ id, reason }) => {
|
|
190
|
+
try {
|
|
191
|
+
await engine.deleteObservation(id, reason);
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{ type: 'text', text: JSON.stringify({ id, deleted: true, success: true }, null, 2) },
|
|
195
|
+
],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return handleToolError(error);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
server.tool('memento_mem_restore', 'Restore a soft-deleted observation back to active state.', {
|
|
203
|
+
id: zod_1.z.number().describe('Observation ID to restore'),
|
|
149
204
|
}, async ({ id }) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
205
|
+
try {
|
|
206
|
+
const restored = await engine.restoreObservation(id);
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: 'text',
|
|
211
|
+
text: JSON.stringify({ id: restored.id, restored: true, success: true }, null, 2),
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
return handleToolError(error);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
server.tool('memento_mem_purge', 'PERMANENTLY delete soft-deleted observations. This is IRREVERSIBLE. Requires confirm: true.', {
|
|
221
|
+
confirm: zod_1.z.boolean().describe('Must be true to execute purge'),
|
|
222
|
+
project_id: zod_1.z.string().optional().describe('Purge all deleted obs in this project'),
|
|
223
|
+
observation_ids: zod_1.z
|
|
224
|
+
.array(zod_1.z.number())
|
|
225
|
+
.optional()
|
|
226
|
+
.describe('Specific soft-deleted observation IDs to purge'),
|
|
227
|
+
}, async ({ confirm, project_id, observation_ids }) => {
|
|
228
|
+
try {
|
|
229
|
+
if (!confirm) {
|
|
230
|
+
return {
|
|
231
|
+
content: [
|
|
232
|
+
{
|
|
233
|
+
type: 'text',
|
|
234
|
+
text: JSON.stringify({ success: false, error: 'confirm must be true to execute purge' }, null, 2),
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const result = await engine.purgeObservations({
|
|
240
|
+
projectId: project_id,
|
|
241
|
+
observationIds: observation_ids,
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: 'text', text: JSON.stringify({ ...result, success: true }, null, 2) }],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
return handleToolError(error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
server.tool('memento_mem_list_deleted', 'List soft-deleted observations that can be restored or purged.', {
|
|
252
|
+
project_id: zod_1.z.string().optional(),
|
|
253
|
+
limit: zod_1.z.number().optional().describe('Max results (default: 20)'),
|
|
254
|
+
}, async ({ project_id, limit }) => {
|
|
255
|
+
try {
|
|
256
|
+
const result = await engine.listDeleted({ projectId: project_id, limit });
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
return handleToolError(error);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
// ─── Merge ──────────────────────────────────────────────────
|
|
266
|
+
server.tool('memento_mem_merge', 'Merge related observations into a single synthesized record. Identifies candidates automatically by topic_key or content similarity (Jaccard > 0.85). Use dry_run to preview without executing.', {
|
|
267
|
+
project_id: zod_1.z.string().describe('Project to merge observations in (required)'),
|
|
268
|
+
topic_key: zod_1.z.string().optional().describe('Merge all observations with this topic_key'),
|
|
269
|
+
observation_ids: zod_1.z
|
|
270
|
+
.array(zod_1.z.number())
|
|
271
|
+
.optional()
|
|
272
|
+
.describe('Specific observation IDs to merge (overrides auto-detection)'),
|
|
273
|
+
strategy: zod_1.z
|
|
274
|
+
.enum(['by_topic', 'by_similarity', 'by_ids'])
|
|
275
|
+
.optional()
|
|
276
|
+
.describe('Merge strategy (default: by_topic)'),
|
|
277
|
+
dry_run: zod_1.z.boolean().optional().describe('If true, returns candidates without executing merge'),
|
|
278
|
+
}, async ({ project_id, topic_key, observation_ids, strategy, dry_run }) => {
|
|
279
|
+
try {
|
|
280
|
+
const results = await engine.mergeObservations({
|
|
281
|
+
projectId: project_id,
|
|
282
|
+
topicKey: topic_key,
|
|
283
|
+
observationIds: observation_ids,
|
|
284
|
+
strategy: strategy || 'by_topic',
|
|
285
|
+
dryRun: dry_run,
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: 'text',
|
|
291
|
+
text: JSON.stringify({
|
|
292
|
+
success: true,
|
|
293
|
+
dryRun: dry_run || false,
|
|
294
|
+
mergeCount: results.length,
|
|
295
|
+
results: results.map((r) => ({
|
|
296
|
+
mergedObservationId: r.mergedObservation.id,
|
|
297
|
+
deletedIds: r.deletedIds,
|
|
298
|
+
originalCount: r.originalCount,
|
|
299
|
+
strategy: r.strategy,
|
|
300
|
+
})),
|
|
301
|
+
}, null, 2),
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
return handleToolError(error);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
// ─── Export ──────────────────────────────────────────────────
|
|
311
|
+
server.tool('memento_mem_export', 'Export observations to JSON, XML, or TXT format. Use filters to reduce scope.', {
|
|
312
|
+
format: zod_1.z.enum(['json', 'xml', 'txt']).optional().describe('Export format (default: json)'),
|
|
313
|
+
project_id: zod_1.z.string().optional().describe('Filter by project'),
|
|
314
|
+
type: zod_1.z.enum(['decision', 'bug', 'discovery', 'note']).optional(),
|
|
315
|
+
topic_key: zod_1.z.string().optional(),
|
|
316
|
+
date_from: zod_1.z.string().optional().describe('ISO date string — export from this date'),
|
|
317
|
+
date_to: zod_1.z.string().optional().describe('ISO date string — export until this date'),
|
|
318
|
+
include_deleted: zod_1.z.boolean().optional().describe('Include soft-deleted observations'),
|
|
319
|
+
}, async ({ format, project_id, type, topic_key, date_from, date_to, include_deleted }) => {
|
|
320
|
+
try {
|
|
321
|
+
const result = await engine.exportObservations({
|
|
322
|
+
format: format || 'json',
|
|
323
|
+
projectId: project_id,
|
|
324
|
+
type: type,
|
|
325
|
+
topicKey: topic_key,
|
|
326
|
+
dateFrom: date_from ? new Date(date_from) : undefined,
|
|
327
|
+
dateTo: date_to ? new Date(date_to) : undefined,
|
|
328
|
+
includeDeleted: include_deleted,
|
|
329
|
+
});
|
|
330
|
+
return {
|
|
331
|
+
content: [
|
|
332
|
+
{
|
|
333
|
+
type: 'text',
|
|
334
|
+
text: JSON.stringify({
|
|
335
|
+
success: true,
|
|
336
|
+
format: result.format,
|
|
337
|
+
recordCount: result.recordCount,
|
|
338
|
+
exportedAt: result.exportedAt.toISOString(),
|
|
339
|
+
content: result.content,
|
|
340
|
+
}, null, 2),
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
return handleToolError(error);
|
|
347
|
+
}
|
|
159
348
|
});
|
|
160
|
-
|
|
349
|
+
// ─── Session Tools ──────────────────────────────────────────
|
|
350
|
+
server.tool('memento_mem_session_start', 'Start a new memory session for tracking a coding conversation.', {
|
|
161
351
|
project_id: zod_1.z.string().describe('Project identifier'),
|
|
162
352
|
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
163
353
|
}, async ({ project_id, metadata }) => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
354
|
+
try {
|
|
355
|
+
const session = await engine.createSession({
|
|
356
|
+
projectId: project_id,
|
|
357
|
+
endedAt: null,
|
|
358
|
+
metadata: metadata || {},
|
|
359
|
+
});
|
|
360
|
+
activeSessionId = session.id;
|
|
361
|
+
return {
|
|
362
|
+
content: [
|
|
363
|
+
{
|
|
364
|
+
type: 'text',
|
|
365
|
+
text: JSON.stringify({ id: session.id, uuid: session.uuid, success: true }, null, 2),
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
return handleToolError(error);
|
|
372
|
+
}
|
|
178
373
|
});
|
|
179
|
-
server.tool('
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
374
|
+
server.tool('memento_mem_session_end', 'End current active session.', {}, async () => {
|
|
375
|
+
try {
|
|
376
|
+
if (!activeSessionId)
|
|
377
|
+
throw new Error('No active session');
|
|
378
|
+
const ended = await engine.endSession(activeSessionId);
|
|
379
|
+
activeSessionId = null;
|
|
380
|
+
return {
|
|
381
|
+
content: [
|
|
382
|
+
{
|
|
383
|
+
type: 'text',
|
|
384
|
+
text: JSON.stringify({ id: ended.id, uuid: ended.uuid, endedAt: ended.endedAt, success: true }, null, 2),
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
return handleToolError(error);
|
|
391
|
+
}
|
|
192
392
|
});
|
|
193
|
-
server.tool('
|
|
393
|
+
server.tool('memento_mem_list_sessions', 'List all sessions.', {
|
|
194
394
|
project_id: zod_1.z.string().optional(),
|
|
195
395
|
limit: zod_1.z.number().optional(),
|
|
196
396
|
}, async ({ project_id, limit }) => {
|
|
197
|
-
|
|
198
|
-
projectId: project_id,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
397
|
+
try {
|
|
398
|
+
const result = await engine.search({ projectId: project_id, limit: limit || 20 });
|
|
399
|
+
const uniqueSessions = new Set(result.observations.map((o) => o.sessionId));
|
|
400
|
+
const sessions = await Promise.all(Array.from(uniqueSessions).map((id) => engine.getSession(id)));
|
|
401
|
+
return {
|
|
402
|
+
content: [
|
|
403
|
+
{
|
|
404
|
+
type: 'text',
|
|
405
|
+
text: JSON.stringify({ sessions: sessions.filter(Boolean), total: sessions.length }, null, 2),
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
return handleToolError(error);
|
|
412
|
+
}
|
|
211
413
|
});
|
|
212
|
-
server.tool('
|
|
414
|
+
server.tool('memento_mem_get_session', 'Get a specific session by ID.', {
|
|
213
415
|
id: zod_1.z.number().describe('Session ID'),
|
|
214
416
|
}, async ({ id }) => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
417
|
+
try {
|
|
418
|
+
const s = await engine.getSession(id);
|
|
419
|
+
if (!s)
|
|
420
|
+
throw new Error(`Session ${id} not found`);
|
|
421
|
+
return {
|
|
422
|
+
content: [{ type: 'text', text: JSON.stringify(s, null, 2) }],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
return handleToolError(error);
|
|
427
|
+
}
|
|
221
428
|
});
|
|
222
|
-
|
|
429
|
+
// ─── Utility Tools ──────────────────────────────────────────
|
|
430
|
+
server.tool('memento_mem_timeline', 'Get chronological timeline of observations.', {
|
|
223
431
|
project_id: zod_1.z.string().optional(),
|
|
224
432
|
limit: zod_1.z.number().optional(),
|
|
225
433
|
offset: zod_1.z.number().optional(),
|
|
226
434
|
}, async ({ project_id, limit, offset }) => {
|
|
227
|
-
|
|
228
|
-
projectId: project_id,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
text: JSON.stringify(result, null, 2),
|
|
237
|
-
},
|
|
238
|
-
],
|
|
239
|
-
};
|
|
435
|
+
try {
|
|
436
|
+
const result = await engine.search({ projectId: project_id, limit, offset });
|
|
437
|
+
return {
|
|
438
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
return handleToolError(error);
|
|
443
|
+
}
|
|
240
444
|
});
|
|
241
|
-
server.tool('
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
445
|
+
server.tool('memento_mem_stats', 'Get memory statistics.', {}, async () => {
|
|
446
|
+
try {
|
|
447
|
+
const result = await engine.search({});
|
|
448
|
+
const deleted = await engine.listDeleted({});
|
|
449
|
+
const byType = {};
|
|
450
|
+
const byProject = {};
|
|
451
|
+
for (const o of result.observations) {
|
|
452
|
+
byType[o.type] = (byType[o.type] || 0) + 1;
|
|
453
|
+
byProject[o.projectId] = (byProject[o.projectId] || 0) + 1;
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
content: [
|
|
457
|
+
{
|
|
458
|
+
type: 'text',
|
|
459
|
+
text: JSON.stringify({
|
|
460
|
+
totalObservations: result.total,
|
|
461
|
+
deletedObservations: deleted.total,
|
|
462
|
+
byType,
|
|
463
|
+
byProject,
|
|
464
|
+
activeSessionId,
|
|
465
|
+
}, null, 2),
|
|
466
|
+
},
|
|
467
|
+
],
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
return handleToolError(error);
|
|
248
472
|
}
|
|
249
|
-
return {
|
|
250
|
-
content: [
|
|
251
|
-
{
|
|
252
|
-
type: 'text',
|
|
253
|
-
text: JSON.stringify({
|
|
254
|
-
totalObservations: result.total,
|
|
255
|
-
byType,
|
|
256
|
-
byProject,
|
|
257
|
-
activeSessionId,
|
|
258
|
-
}, null, 2),
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
};
|
|
262
473
|
});
|
|
263
|
-
server.tool('
|
|
474
|
+
server.tool('memento_mem_health', 'Check system health.', {}, async () => {
|
|
264
475
|
try {
|
|
265
476
|
const isHealthy = engine.isHealthy();
|
|
266
477
|
const result = isHealthy ? await engine.search({}) : { total: 0, observations: [] };
|
|
@@ -271,7 +482,7 @@ server.tool('mem_health', 'Check system health.', {}, async () => {
|
|
|
271
482
|
type: 'text',
|
|
272
483
|
text: JSON.stringify({
|
|
273
484
|
status: isHealthy ? 'healthy' : 'unhealthy',
|
|
274
|
-
version: '0.
|
|
485
|
+
version: '1.0.0',
|
|
275
486
|
storage: 'sqlite-persistent',
|
|
276
487
|
databasePath: dbPath,
|
|
277
488
|
projectId: projectId,
|
|
@@ -288,77 +499,99 @@ server.tool('mem_health', 'Check system health.', {}, async () => {
|
|
|
288
499
|
return handleToolError(error);
|
|
289
500
|
}
|
|
290
501
|
});
|
|
291
|
-
server.tool('
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
502
|
+
server.tool('memento_mem_config', 'Get current memento configuration and system status.', {}, async () => {
|
|
503
|
+
try {
|
|
504
|
+
const searchResult = await engine.search({});
|
|
505
|
+
const currentDbPath = engine.getDatabasePath();
|
|
506
|
+
const byType = {};
|
|
507
|
+
for (const o of searchResult.observations) {
|
|
508
|
+
byType[o.type] = (byType[o.type] || 0) + 1;
|
|
509
|
+
}
|
|
510
|
+
const dbStats = getDatabaseStats(currentDbPath);
|
|
511
|
+
return {
|
|
512
|
+
content: [
|
|
513
|
+
{
|
|
514
|
+
type: 'text',
|
|
515
|
+
text: JSON.stringify({
|
|
516
|
+
name: 'memento',
|
|
517
|
+
version: '1.0.0',
|
|
518
|
+
config: {
|
|
519
|
+
storagePath: currentDbPath,
|
|
520
|
+
projectId: projectId,
|
|
521
|
+
projectRoot: process.cwd(),
|
|
522
|
+
hasConfigFile: (0, fs_1.existsSync)((0, path_1.join)(process.cwd(), '.mementorc')),
|
|
523
|
+
},
|
|
524
|
+
storage: {
|
|
525
|
+
type: 'SQLite Persistent',
|
|
526
|
+
method: 'bun:sqlite',
|
|
527
|
+
databasePath: currentDbPath,
|
|
528
|
+
walEnabled: true,
|
|
529
|
+
},
|
|
530
|
+
diskUsage: dbStats,
|
|
531
|
+
statistics: {
|
|
532
|
+
totalObservations: searchResult.total,
|
|
533
|
+
byType,
|
|
534
|
+
activeSession: activeSessionId,
|
|
535
|
+
},
|
|
536
|
+
environment: {
|
|
537
|
+
nodeVersion: process.version,
|
|
538
|
+
platform: process.platform,
|
|
539
|
+
arch: process.arch,
|
|
540
|
+
bunVersion: process.versions?.bun || 'unknown',
|
|
541
|
+
},
|
|
542
|
+
tools: [
|
|
543
|
+
'memento_mem_save',
|
|
544
|
+
'memento_mem_search',
|
|
545
|
+
'memento_mem_get_observation',
|
|
546
|
+
'memento_mem_update',
|
|
547
|
+
'memento_mem_delete',
|
|
548
|
+
'memento_mem_restore',
|
|
549
|
+
'memento_mem_purge',
|
|
550
|
+
'memento_mem_list_deleted',
|
|
551
|
+
'memento_mem_merge',
|
|
552
|
+
'memento_mem_export',
|
|
553
|
+
'memento_mem_session_start',
|
|
554
|
+
'memento_mem_session_end',
|
|
555
|
+
'memento_mem_list_sessions',
|
|
556
|
+
'memento_mem_get_session',
|
|
557
|
+
'memento_mem_timeline',
|
|
558
|
+
'memento_mem_stats',
|
|
559
|
+
'memento_mem_health',
|
|
560
|
+
'memento_mem_config',
|
|
561
|
+
],
|
|
562
|
+
}, null, 2),
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
return handleToolError(error);
|
|
569
|
+
}
|
|
334
570
|
});
|
|
571
|
+
// ─── Helpers ────────────────────────────────────────────────
|
|
335
572
|
function getDatabaseStats(dbPath) {
|
|
336
|
-
const fs = require('fs');
|
|
337
573
|
let totalSize = 0;
|
|
338
574
|
let walSize = 0;
|
|
339
575
|
let shmSize = 0;
|
|
340
576
|
try {
|
|
341
|
-
|
|
342
|
-
totalSize += mainDb.size;
|
|
577
|
+
totalSize += fs.statSync(dbPath).size;
|
|
343
578
|
}
|
|
344
|
-
catch
|
|
345
|
-
|
|
579
|
+
catch {
|
|
580
|
+
/* empty */
|
|
346
581
|
}
|
|
347
582
|
try {
|
|
348
|
-
|
|
349
|
-
walSize = walFile.size;
|
|
583
|
+
walSize = fs.statSync(`${dbPath}-wal`).size;
|
|
350
584
|
totalSize += walSize;
|
|
351
585
|
}
|
|
352
|
-
catch
|
|
353
|
-
|
|
586
|
+
catch {
|
|
587
|
+
/* empty */
|
|
354
588
|
}
|
|
355
589
|
try {
|
|
356
|
-
|
|
357
|
-
shmSize = shmFile.size;
|
|
590
|
+
shmSize = fs.statSync(`${dbPath}-shm`).size;
|
|
358
591
|
totalSize += shmSize;
|
|
359
592
|
}
|
|
360
|
-
catch
|
|
361
|
-
|
|
593
|
+
catch {
|
|
594
|
+
/* empty */
|
|
362
595
|
}
|
|
363
596
|
return {
|
|
364
597
|
totalBytes: totalSize,
|
|
@@ -367,8 +600,6 @@ function getDatabaseStats(dbPath) {
|
|
|
367
600
|
mainDbSizeHuman: formatBytes(totalSize - walSize - shmSize),
|
|
368
601
|
walBytes: walSize,
|
|
369
602
|
walSizeHuman: formatBytes(walSize),
|
|
370
|
-
shmBytes: shmSize,
|
|
371
|
-
shmSizeHuman: formatBytes(shmSize),
|
|
372
603
|
};
|
|
373
604
|
}
|
|
374
605
|
function formatBytes(bytes) {
|
|
@@ -385,38 +616,27 @@ function formatBytes(bytes) {
|
|
|
385
616
|
return `${(bytes / m).toFixed(2)} MB`;
|
|
386
617
|
return `${(bytes / g).toFixed(2)} GB`;
|
|
387
618
|
}
|
|
619
|
+
// ─── Server Startup ─────────────────────────────────────────
|
|
388
620
|
const BANNER = `
|
|
389
|
-
|
|
390
|
-
║
|
|
391
|
-
║
|
|
392
|
-
║
|
|
393
|
-
║
|
|
394
|
-
║
|
|
395
|
-
║
|
|
396
|
-
║
|
|
397
|
-
║
|
|
398
|
-
|
|
399
|
-
║ ║ ║ ║
|
|
400
|
-
║ ║ MEMENTO - Persistent Memory System ║ ║
|
|
401
|
-
║ ║ ║ ║
|
|
402
|
-
║ ║ Version: 0.5.0 ║ ║
|
|
403
|
-
║ ║ Storage: SQLite Persistent ║ ║
|
|
404
|
-
║ ╚═══════════════════════════════════════╝ ║
|
|
405
|
-
╚═════════════════════════════════════════════════════════╝
|
|
621
|
+
╔══════════════════════════════════════════════════╗
|
|
622
|
+
║ ║
|
|
623
|
+
║ MEMENTO — Persistent Memory System ║
|
|
624
|
+
║ ║
|
|
625
|
+
║ Version: 1.0.0 ║
|
|
626
|
+
║ MCP Server: memento ║
|
|
627
|
+
║ Storage: SQLite Persistent ║
|
|
628
|
+
║ Tools: memento_mem_* ║
|
|
629
|
+
║ ║
|
|
630
|
+
╚══════════════════════════════════════════════════╝
|
|
406
631
|
`;
|
|
407
632
|
async function main() {
|
|
408
633
|
console.error(BANNER);
|
|
409
|
-
// Check database health and show warnings if needed
|
|
410
634
|
if (!engine.isHealthy()) {
|
|
411
635
|
const initError = engine.getInitError();
|
|
412
636
|
console.error('\n⚠️ WARNING: Database initialization failed');
|
|
413
637
|
console.error(` Error: ${initError?.message || 'Unknown error'}`);
|
|
414
638
|
console.error(` Database path: ${dbPath}`);
|
|
415
639
|
console.error('\n The server will start, but database operations will fail.');
|
|
416
|
-
console.error(' Please check:');
|
|
417
|
-
console.error(' - Directory permissions');
|
|
418
|
-
console.error(' - Disk space');
|
|
419
|
-
console.error(' - Path configuration in .mementorc\n');
|
|
420
640
|
}
|
|
421
641
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
422
642
|
await server.connect(transport);
|
|
@@ -424,11 +644,11 @@ async function main() {
|
|
|
424
644
|
console.error(` Database: ${dbPath}`);
|
|
425
645
|
console.error(` Project: ${projectId}`);
|
|
426
646
|
console.error(` Health: ${engine.isHealthy() ? '✓ Healthy' : '✗ Unhealthy'}`);
|
|
647
|
+
console.error(` Tools: 18 (memento_mem_*)`);
|
|
427
648
|
console.error(` Ready to accept connections...\n`);
|
|
428
649
|
}
|
|
429
650
|
main().catch((err) => {
|
|
430
651
|
console.error('Fatal error during server startup:', err);
|
|
431
|
-
console.error('\nThe server failed to start. Please check the error above.');
|
|
432
652
|
process.exit(1);
|
|
433
653
|
});
|
|
434
654
|
//# sourceMappingURL=index.js.map
|