shabti 2.11.0 → 2.12.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 +2 -0
- package/package.json +1 -1
- package/src/a2a/agentCard.js +48 -0
- package/src/a2a/server.js +130 -0
- package/src/mcp/server.js +92 -0
package/README.md
CHANGED
|
@@ -210,11 +210,13 @@ Or manually add to your MCP settings:
|
|
|
210
210
|
| --------------- | ----------------------------------------------------------- |
|
|
211
211
|
| `memory_store` | Store a memory entry with optional namespace, tags, and TTL |
|
|
212
212
|
| `memory_search` | Search memories by semantic similarity |
|
|
213
|
+
| `memory_get` | Retrieve a specific memory entry by UUID |
|
|
213
214
|
| `memory_delete` | Delete a memory entry by ID |
|
|
214
215
|
| `memory_list` | List recent memory entries |
|
|
215
216
|
| `memory_export` | Export entries as JSONL |
|
|
216
217
|
| `memory_gc` | Garbage collect expired entries |
|
|
217
218
|
| `memory_status` | Get engine status |
|
|
219
|
+
| `memory_health` | Run health checks on engine components |
|
|
218
220
|
|
|
219
221
|
### MCP Resources
|
|
220
222
|
|
package/package.json
CHANGED
package/src/a2a/agentCard.js
CHANGED
|
@@ -43,6 +43,54 @@ export function buildAgentCard(baseUrl) {
|
|
|
43
43
|
inputModes: ["text"],
|
|
44
44
|
outputModes: ["application/json"],
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
id: "memory_get",
|
|
48
|
+
name: "Get Memory",
|
|
49
|
+
description: "Retrieve a specific memory entry by its ID",
|
|
50
|
+
tags: ["memory", "get", "retrieve", "fetch"],
|
|
51
|
+
inputModes: ["application/json"],
|
|
52
|
+
outputModes: ["application/json"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "memory_delete",
|
|
56
|
+
name: "Delete Memory",
|
|
57
|
+
description: "Delete a specific memory entry by its ID",
|
|
58
|
+
tags: ["memory", "delete", "remove"],
|
|
59
|
+
inputModes: ["application/json"],
|
|
60
|
+
outputModes: ["application/json"],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "memory_list",
|
|
64
|
+
name: "List Memories",
|
|
65
|
+
description: "List stored memory entries with optional filters",
|
|
66
|
+
tags: ["memory", "list", "entries"],
|
|
67
|
+
inputModes: ["text", "application/json"],
|
|
68
|
+
outputModes: ["application/json"],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "memory_export",
|
|
72
|
+
name: "Export Memories",
|
|
73
|
+
description: "Export all memory entries as JSONL data",
|
|
74
|
+
tags: ["memory", "export", "dump", "backup"],
|
|
75
|
+
inputModes: ["text", "application/json"],
|
|
76
|
+
outputModes: ["application/json"],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "memory_gc",
|
|
80
|
+
name: "Garbage Collect",
|
|
81
|
+
description: "Remove expired memory entries and reclaim storage",
|
|
82
|
+
tags: ["memory", "gc", "garbage", "cleanup"],
|
|
83
|
+
inputModes: ["text"],
|
|
84
|
+
outputModes: ["application/json"],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "memory_health",
|
|
88
|
+
name: "Health Check",
|
|
89
|
+
description: "Check the health of the memory engine and its dependencies",
|
|
90
|
+
tags: ["memory", "health", "diagnostics"],
|
|
91
|
+
inputModes: ["text"],
|
|
92
|
+
outputModes: ["application/json"],
|
|
93
|
+
},
|
|
46
94
|
],
|
|
47
95
|
};
|
|
48
96
|
}
|
package/src/a2a/server.js
CHANGED
|
@@ -122,6 +122,112 @@ function handleMemoryStatus(engine) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
async function handleMemoryDelete(engine, parts) {
|
|
126
|
+
let id = null;
|
|
127
|
+
|
|
128
|
+
for (const part of parts) {
|
|
129
|
+
if (part.kind === "data" && part.data?.id) {
|
|
130
|
+
id = part.data.id;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!id) {
|
|
135
|
+
throw Object.assign(new Error("Missing id in message parts"), { code: -32602 });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await engine.delete(id);
|
|
139
|
+
return {
|
|
140
|
+
artifactId: randomUUID(),
|
|
141
|
+
name: "delete_result",
|
|
142
|
+
parts: [{ kind: "data", data: { deleted: true, id } }],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function handleMemoryGet(engine, parts) {
|
|
147
|
+
let id = null;
|
|
148
|
+
|
|
149
|
+
for (const part of parts) {
|
|
150
|
+
if (part.kind === "data" && part.data?.id) {
|
|
151
|
+
id = part.data.id;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!id) {
|
|
156
|
+
throw Object.assign(new Error("Missing id in message parts"), { code: -32602 });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const entry = await engine.get(id);
|
|
160
|
+
return {
|
|
161
|
+
artifactId: randomUUID(),
|
|
162
|
+
name: "get_result",
|
|
163
|
+
parts: [{ kind: "data", data: entry }],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleMemoryList(engine, parts) {
|
|
168
|
+
let opts = {};
|
|
169
|
+
|
|
170
|
+
for (const part of parts) {
|
|
171
|
+
if (part.kind === "data" && part.data) {
|
|
172
|
+
if (part.data.limit) opts.limit = part.data.limit;
|
|
173
|
+
if (part.data.namespace) opts.namespace = part.data.namespace;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const entries = engine.listEntries(opts);
|
|
178
|
+
return {
|
|
179
|
+
artifactId: randomUUID(),
|
|
180
|
+
name: "list_result",
|
|
181
|
+
parts: [{ kind: "data", data: { entries, count: entries.length } }],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function handleMemoryExport(engine) {
|
|
186
|
+
const entries = engine.listEntries();
|
|
187
|
+
const lines = entries.map((e) => JSON.stringify(e));
|
|
188
|
+
return {
|
|
189
|
+
artifactId: randomUUID(),
|
|
190
|
+
name: "export_result",
|
|
191
|
+
parts: [{ kind: "data", data: { entries: entries.length, data: lines.join("\n") } }],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function handleMemoryGc(engine) {
|
|
196
|
+
const removed = await engine.gc();
|
|
197
|
+
return {
|
|
198
|
+
artifactId: randomUUID(),
|
|
199
|
+
name: "gc_result",
|
|
200
|
+
parts: [{ kind: "data", data: { removed } }],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function handleMemoryHealth(engine) {
|
|
205
|
+
const checks = [];
|
|
206
|
+
const status = engine.status();
|
|
207
|
+
const qdrantUrl = status.qdrantUrl.replace(/:\d+$/, ":6333");
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const res = await fetch(`${qdrantUrl}/healthz`, { signal: AbortSignal.timeout(3000) });
|
|
211
|
+
checks.push({
|
|
212
|
+
name: "qdrant",
|
|
213
|
+
status: res.ok ? "ok" : "degraded",
|
|
214
|
+
message: res.ok ? "Reachable" : `HTTP ${res.status}`,
|
|
215
|
+
});
|
|
216
|
+
} catch (err) {
|
|
217
|
+
checks.push({ name: "qdrant", status: "error", message: err.message });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
checks.push({ name: "engine", status: "ok", message: `${status.entryCount} entries` });
|
|
221
|
+
checks.push({ name: "embedding", status: "ok", message: `Model: ${engine.modelId()}` });
|
|
222
|
+
|
|
223
|
+
const healthy = checks.every((c) => c.status === "ok");
|
|
224
|
+
return {
|
|
225
|
+
artifactId: randomUUID(),
|
|
226
|
+
name: "health_result",
|
|
227
|
+
parts: [{ kind: "data", data: { healthy, checks } }],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
125
231
|
// ============================================================
|
|
126
232
|
// Skill Router
|
|
127
233
|
// ============================================================
|
|
@@ -140,6 +246,11 @@ export function resolveSkill(parts) {
|
|
|
140
246
|
const lower = part.text.toLowerCase();
|
|
141
247
|
if (/\b(store|save|remember)\b/.test(lower)) return "memory_store";
|
|
142
248
|
if (/\b(search|find|recall|query)\b/.test(lower)) return "memory_search";
|
|
249
|
+
if (/\b(delete|remove)\b/.test(lower)) return "memory_delete";
|
|
250
|
+
if (/\b(get|retrieve)\b/.test(lower)) return "memory_get";
|
|
251
|
+
if (/\b(list)\b/.test(lower)) return "memory_list";
|
|
252
|
+
if (/\b(export|dump)\b/.test(lower)) return "memory_export";
|
|
253
|
+
if (/\b(garbage|cleanup)\b/.test(lower)) return "memory_gc";
|
|
143
254
|
if (/\b(status|health|stats|info)\b/.test(lower)) return "memory_status";
|
|
144
255
|
}
|
|
145
256
|
}
|
|
@@ -149,6 +260,7 @@ export function resolveSkill(parts) {
|
|
|
149
260
|
if (part.kind === "data" && part.data) {
|
|
150
261
|
if (part.data.content) return "memory_store";
|
|
151
262
|
if (part.data.query) return "memory_search";
|
|
263
|
+
if (part.data.id) return "memory_get";
|
|
152
264
|
}
|
|
153
265
|
}
|
|
154
266
|
|
|
@@ -204,6 +316,24 @@ async function handleMessageSend(engine, taskStore, params) {
|
|
|
204
316
|
case "memory_status":
|
|
205
317
|
artifact = handleMemoryStatus(engine);
|
|
206
318
|
break;
|
|
319
|
+
case "memory_delete":
|
|
320
|
+
artifact = await handleMemoryDelete(engine, message.parts);
|
|
321
|
+
break;
|
|
322
|
+
case "memory_get":
|
|
323
|
+
artifact = await handleMemoryGet(engine, message.parts);
|
|
324
|
+
break;
|
|
325
|
+
case "memory_list":
|
|
326
|
+
artifact = handleMemoryList(engine, message.parts);
|
|
327
|
+
break;
|
|
328
|
+
case "memory_export":
|
|
329
|
+
artifact = handleMemoryExport(engine);
|
|
330
|
+
break;
|
|
331
|
+
case "memory_gc":
|
|
332
|
+
artifact = await handleMemoryGc(engine);
|
|
333
|
+
break;
|
|
334
|
+
case "memory_health":
|
|
335
|
+
artifact = await handleMemoryHealth(engine);
|
|
336
|
+
break;
|
|
207
337
|
default:
|
|
208
338
|
task.status = { state: "failed", timestamp: new Date().toISOString() };
|
|
209
339
|
taskStore.set(task.id, task);
|
package/src/mcp/server.js
CHANGED
|
@@ -103,6 +103,26 @@ const TOOLS = [
|
|
|
103
103
|
properties: {},
|
|
104
104
|
},
|
|
105
105
|
},
|
|
106
|
+
{
|
|
107
|
+
name: "memory_health",
|
|
108
|
+
description:
|
|
109
|
+
"Run health checks on the memory engine (Qdrant connectivity, engine status, embedding model)",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "memory_get",
|
|
117
|
+
description: "Retrieve a specific memory entry by its UUID",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
id: { type: "string", description: "UUID of the memory entry to retrieve" },
|
|
122
|
+
},
|
|
123
|
+
required: ["id"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
106
126
|
];
|
|
107
127
|
|
|
108
128
|
const RESOURCES = [
|
|
@@ -373,6 +393,78 @@ async function handleToolsCall(id, params) {
|
|
|
373
393
|
}
|
|
374
394
|
}
|
|
375
395
|
|
|
396
|
+
if (name === "memory_health") {
|
|
397
|
+
const checks = [];
|
|
398
|
+
const config = loadConfig();
|
|
399
|
+
const qdrantUrl = config.qdrant_url.replace(/:\d+$/, ":6333");
|
|
400
|
+
try {
|
|
401
|
+
const res = await fetch(`${qdrantUrl}/healthz`, {
|
|
402
|
+
signal: AbortSignal.timeout(3000),
|
|
403
|
+
});
|
|
404
|
+
checks.push({
|
|
405
|
+
name: "qdrant",
|
|
406
|
+
status: res.ok ? "ok" : "degraded",
|
|
407
|
+
message: res.ok ? "Reachable" : `HTTP ${res.status}`,
|
|
408
|
+
});
|
|
409
|
+
} catch (err) {
|
|
410
|
+
checks.push({ name: "qdrant", status: "error", message: err.message });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (eng) {
|
|
414
|
+
try {
|
|
415
|
+
const status = eng.status();
|
|
416
|
+
checks.push({
|
|
417
|
+
name: "engine",
|
|
418
|
+
status: "ok",
|
|
419
|
+
message: `${status.entryCount} entries, tier: ${status.tier}`,
|
|
420
|
+
});
|
|
421
|
+
} catch (err) {
|
|
422
|
+
checks.push({ name: "engine", status: "error", message: err.message });
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const modelId = eng.modelId();
|
|
426
|
+
checks.push({ name: "embedding", status: "ok", message: modelId });
|
|
427
|
+
} catch (err) {
|
|
428
|
+
checks.push({ name: "embedding", status: "error", message: err.message });
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
checks.push({ name: "engine", status: "error", message: "Engine not available" });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const healthy = checks.every((c) => c.status === "ok");
|
|
435
|
+
return respond(id, {
|
|
436
|
+
content: [
|
|
437
|
+
{
|
|
438
|
+
type: "text",
|
|
439
|
+
text: JSON.stringify({ healthy, checks }, null, 2),
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (name === "memory_get") {
|
|
446
|
+
if (!eng) {
|
|
447
|
+
return respondError(id, -32603, "Engine not available");
|
|
448
|
+
}
|
|
449
|
+
const entryId = args?.id;
|
|
450
|
+
if (!entryId) {
|
|
451
|
+
return respondError(id, -32602, "Missing required parameter: id");
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
const entry = await eng.get(entryId);
|
|
455
|
+
return respond(id, {
|
|
456
|
+
content: [
|
|
457
|
+
{
|
|
458
|
+
type: "text",
|
|
459
|
+
text: JSON.stringify(entry, null, 2),
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
});
|
|
463
|
+
} catch (err) {
|
|
464
|
+
return respondError(id, -32603, err.message);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
376
468
|
respondError(id, -32601, `Unknown tool: ${name}`);
|
|
377
469
|
}
|
|
378
470
|
|