shabti 2.1.0 → 2.3.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/README.md +9 -1
- package/package.json +1 -1
- package/src/commands/store.js +2 -0
- package/src/index.js +33 -0
- package/src/mcp/server.js +112 -0
- package/src/repl/slashCommands.js +21 -0
package/README.md
CHANGED
|
@@ -128,7 +128,13 @@ npx shabti-mcp
|
|
|
128
128
|
|
|
129
129
|
### Claude Code configuration
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
Generate the MCP settings JSON:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
shabti mcp-config
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Or manually add to your MCP settings:
|
|
132
138
|
|
|
133
139
|
```json
|
|
134
140
|
{
|
|
@@ -147,6 +153,8 @@ Add to your MCP settings:
|
|
|
147
153
|
| --------------- | -------------------------------------- |
|
|
148
154
|
| `memory_store` | Store a memory entry |
|
|
149
155
|
| `memory_search` | Search memories by semantic similarity |
|
|
156
|
+
| `memory_delete` | Delete a memory entry by ID |
|
|
157
|
+
| `memory_list` | List recent memory entries |
|
|
150
158
|
| `memory_status` | Get engine status |
|
|
151
159
|
|
|
152
160
|
### Available resources
|
package/package.json
CHANGED
package/src/commands/store.js
CHANGED
|
@@ -9,6 +9,7 @@ export function registerStore(program) {
|
|
|
9
9
|
.option("-n, --namespace <ns>", "Namespace for the entry")
|
|
10
10
|
.option("-s, --session <id>", "Session ID")
|
|
11
11
|
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
12
|
+
.option("--ttl <seconds>", "Time-to-live in seconds (auto-expires after this duration)")
|
|
12
13
|
.action(async (content, opts) => {
|
|
13
14
|
try {
|
|
14
15
|
const engine = createEngine();
|
|
@@ -16,6 +17,7 @@ export function registerStore(program) {
|
|
|
16
17
|
if (opts.namespace) options.namespace = opts.namespace;
|
|
17
18
|
if (opts.session) options.sessionId = opts.session;
|
|
18
19
|
if (opts.tags) options.tags = opts.tags.split(",").map((t) => t.trim());
|
|
20
|
+
if (opts.ttl) options.ttlSeconds = parseInt(opts.ttl, 10);
|
|
19
21
|
|
|
20
22
|
// Model versioning check
|
|
21
23
|
const currentModelId = engine.modelId();
|
package/src/index.js
CHANGED
|
@@ -78,6 +78,39 @@ function buildProgram() {
|
|
|
78
78
|
registerSpin(program);
|
|
79
79
|
registerStatus(program);
|
|
80
80
|
registerStore(program);
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command("gc")
|
|
84
|
+
.description("Garbage collect expired memory entries")
|
|
85
|
+
.action(async () => {
|
|
86
|
+
const { createEngine } = await import("./core/engine.js");
|
|
87
|
+
const { success, error: showError } = await import("./utils/style.js");
|
|
88
|
+
try {
|
|
89
|
+
const engine = createEngine();
|
|
90
|
+
const removed = await engine.gc();
|
|
91
|
+
success(`GC complete: ${removed} expired entries removed`);
|
|
92
|
+
await engine.shutdown();
|
|
93
|
+
} catch (err) {
|
|
94
|
+
showError(err.message);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
program
|
|
100
|
+
.command("mcp-config")
|
|
101
|
+
.description("Print MCP server configuration JSON for Claude Code / Cursor")
|
|
102
|
+
.action(() => {
|
|
103
|
+
const config = {
|
|
104
|
+
mcpServers: {
|
|
105
|
+
"shabti-memory": {
|
|
106
|
+
command: "npx",
|
|
107
|
+
args: ["shabti-mcp"],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
console.log(JSON.stringify(config, null, 2));
|
|
112
|
+
});
|
|
113
|
+
|
|
81
114
|
return program;
|
|
82
115
|
}
|
|
83
116
|
|
package/src/mcp/server.js
CHANGED
|
@@ -24,6 +24,10 @@ const TOOLS = [
|
|
|
24
24
|
items: { type: "string" },
|
|
25
25
|
description: "Tags to associate with the memory",
|
|
26
26
|
},
|
|
27
|
+
ttl: {
|
|
28
|
+
type: "integer",
|
|
29
|
+
description: "Time-to-live in seconds (entry auto-expires after this duration)",
|
|
30
|
+
},
|
|
27
31
|
},
|
|
28
32
|
required: ["content"],
|
|
29
33
|
},
|
|
@@ -44,6 +48,39 @@ const TOOLS = [
|
|
|
44
48
|
required: ["query"],
|
|
45
49
|
},
|
|
46
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: "memory_delete",
|
|
53
|
+
description: "Delete a memory entry by ID",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
id: { type: "string", description: "UUID of the memory entry to delete" },
|
|
58
|
+
},
|
|
59
|
+
required: ["id"],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "memory_list",
|
|
64
|
+
description: "List recent memory entries",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
limit: {
|
|
69
|
+
type: "integer",
|
|
70
|
+
description: "Maximum number of entries to return (default: 10)",
|
|
71
|
+
},
|
|
72
|
+
namespace: { type: "string", description: "Filter by namespace" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "memory_gc",
|
|
78
|
+
description: "Garbage collect expired memory entries (removes entries past their TTL)",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
47
84
|
{
|
|
48
85
|
name: "memory_status",
|
|
49
86
|
description: "Get the current status of the memory engine",
|
|
@@ -70,9 +107,12 @@ const RESOURCES = [
|
|
|
70
107
|
];
|
|
71
108
|
|
|
72
109
|
let engine = null;
|
|
110
|
+
let engineInitAttempted = false;
|
|
73
111
|
|
|
74
112
|
function initEngine() {
|
|
75
113
|
if (engine) return engine;
|
|
114
|
+
if (engineInitAttempted) return null;
|
|
115
|
+
engineInitAttempted = true;
|
|
76
116
|
try {
|
|
77
117
|
engine = createEngine();
|
|
78
118
|
} catch {
|
|
@@ -163,6 +203,7 @@ async function handleToolsCall(id, params) {
|
|
|
163
203
|
const opts = {};
|
|
164
204
|
if (args.namespace) opts.namespace = args.namespace;
|
|
165
205
|
if (args.tags) opts.tags = args.tags;
|
|
206
|
+
if (args.ttl) opts.ttlSeconds = args.ttl;
|
|
166
207
|
const result = await eng.store(content, opts);
|
|
167
208
|
return respond(id, {
|
|
168
209
|
content: [
|
|
@@ -213,6 +254,77 @@ async function handleToolsCall(id, params) {
|
|
|
213
254
|
}
|
|
214
255
|
}
|
|
215
256
|
|
|
257
|
+
if (name === "memory_delete") {
|
|
258
|
+
if (!eng) {
|
|
259
|
+
return respondError(id, -32603, "Engine not available");
|
|
260
|
+
}
|
|
261
|
+
const entryId = args?.id;
|
|
262
|
+
if (!entryId) {
|
|
263
|
+
return respondError(id, -32602, "Missing required parameter: id");
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
await eng.delete(entryId);
|
|
267
|
+
return respond(id, {
|
|
268
|
+
content: [
|
|
269
|
+
{
|
|
270
|
+
type: "text",
|
|
271
|
+
text: JSON.stringify({ deleted: true, id: entryId }, null, 2),
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
} catch (err) {
|
|
276
|
+
return respondError(id, -32603, err.message);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (name === "memory_list") {
|
|
281
|
+
if (!eng) {
|
|
282
|
+
return respondError(id, -32603, "Engine not available");
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
const limit = args?.limit || 10;
|
|
286
|
+
const queryObj = { text: "*", limit };
|
|
287
|
+
if (args?.namespace) queryObj.namespace = args.namespace;
|
|
288
|
+
const results = await eng.executeQuery(queryObj);
|
|
289
|
+
const entries = results.map((r) => ({
|
|
290
|
+
id: r.id,
|
|
291
|
+
content: r.content,
|
|
292
|
+
score: r.score,
|
|
293
|
+
namespace: r.namespace,
|
|
294
|
+
createdAt: r.createdAt,
|
|
295
|
+
}));
|
|
296
|
+
return respond(id, {
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
text: JSON.stringify({ entries, count: entries.length }, null, 2),
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
});
|
|
304
|
+
} catch (err) {
|
|
305
|
+
return respondError(id, -32603, err.message);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (name === "memory_gc") {
|
|
310
|
+
if (!eng) {
|
|
311
|
+
return respondError(id, -32603, "Engine not available");
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const removed = await eng.gc();
|
|
315
|
+
return respond(id, {
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: "text",
|
|
319
|
+
text: JSON.stringify({ removed }, null, 2),
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
});
|
|
323
|
+
} catch (err) {
|
|
324
|
+
return respondError(id, -32603, err.message);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
216
328
|
respondError(id, -32601, `Unknown tool: ${name}`);
|
|
217
329
|
}
|
|
218
330
|
|
|
@@ -9,6 +9,7 @@ const COMMANDS = {
|
|
|
9
9
|
"/history": "Show conversation history",
|
|
10
10
|
"/remember": "Store a memory (e.g. /remember Tokyo is the capital of Japan)",
|
|
11
11
|
"/recall": "Search memories (e.g. /recall capital of Japan)",
|
|
12
|
+
"/gc": "Garbage collect expired memory entries",
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -75,6 +76,9 @@ export function handleSlashCommand(cmd, args, session, rl, engine = null) {
|
|
|
75
76
|
case "/recall":
|
|
76
77
|
return handleRecall(args, engine);
|
|
77
78
|
|
|
79
|
+
case "/gc":
|
|
80
|
+
return handleGc(engine);
|
|
81
|
+
|
|
78
82
|
default:
|
|
79
83
|
return false;
|
|
80
84
|
}
|
|
@@ -136,3 +140,20 @@ async function handleRecall(query, engine) {
|
|
|
136
140
|
console.log();
|
|
137
141
|
return true;
|
|
138
142
|
}
|
|
143
|
+
|
|
144
|
+
async function handleGc(engine) {
|
|
145
|
+
if (!engine) {
|
|
146
|
+
console.log();
|
|
147
|
+
warn("Memory engine not available. Start Qdrant to enable memory features.");
|
|
148
|
+
console.log();
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const removed = await engine.gc();
|
|
153
|
+
success(`GC complete: ${removed} expired entries removed`);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
error(`Failed to run GC: ${err.message}`);
|
|
156
|
+
}
|
|
157
|
+
console.log();
|
|
158
|
+
return true;
|
|
159
|
+
}
|