memoryai-mcp 2.1.0 → 2.2.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 +185 -5
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ function err(e) {
|
|
|
40
40
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
41
41
|
}
|
|
42
42
|
// --- MCP Server ---
|
|
43
|
-
const server = new McpServer({ name: "memoryai", version: "0.
|
|
43
|
+
const server = new McpServer({ name: "memoryai", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
44
44
|
// 1. memory_store
|
|
45
45
|
server.tool("memory_store", "Store information in persistent memory. Use when you learn something important — project context, user preferences, architectural decisions, patterns, bugs, pricing/cost discussions, business plans, financial calculations, credit/billing info, revenue models, partnership details, or ANY information the user might ask about later. When in doubt, STORE — MemoryAI handles dedup automatically, so storing too much is always better than forgetting.", {
|
|
46
46
|
content: z.string().describe("What to remember"),
|
|
@@ -874,12 +874,30 @@ server.tool("context_guard_compact", "Compact session context with DNA protectio
|
|
|
874
874
|
return err(e);
|
|
875
875
|
}
|
|
876
876
|
});
|
|
877
|
-
// context_guard_bootstrap — DNA-first session bootstrap
|
|
878
|
-
server.tool("context_guard_bootstrap", "
|
|
877
|
+
// context_guard_bootstrap — DNA-first session bootstrap (IDE)
|
|
878
|
+
server.tool("context_guard_bootstrap", "Load context from previous sessions at session start. Returns preferences, recent activity, and task-relevant memories. Call once at the beginning of a session to restore context.", {
|
|
879
|
+
task: z.string().describe("Task description for context relevance"),
|
|
880
|
+
limit: z.number().optional().describe("Max memories to include (default: 10)"),
|
|
881
|
+
}, async (args) => {
|
|
882
|
+
try {
|
|
883
|
+
const r = (await api("POST", "/v1/context/guard/bootstrap", {
|
|
884
|
+
task: args.task,
|
|
885
|
+
limit: args.limit || 10,
|
|
886
|
+
}));
|
|
887
|
+
return ok(`Context restored: ${r.memories_restored || r.memories_included || 0} memories (${r.dna_memories || 0} DNA)\n` +
|
|
888
|
+
`Tokens used: ${r.tokens_used}\n\n` +
|
|
889
|
+
r.context_block);
|
|
890
|
+
}
|
|
891
|
+
catch (e) {
|
|
892
|
+
return err(e);
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
// bot_guard_bootstrap — 3-tier wake-up for bots (800 tokens)
|
|
896
|
+
server.tool("bot_guard_bootstrap", "Bot-specific bootstrap — 3-tier wake-up (Identity→Context→Details) with 800 token budget. Use for chatbots, not IDEs.", {
|
|
879
897
|
task: z.string().describe("Task description for the new session"),
|
|
880
898
|
limit: z.number().optional().describe("Max memories to include (default: 10)"),
|
|
881
|
-
mode: z.enum(["default", "deep"]).optional().describe("'default' = 800 token 3-tier
|
|
882
|
-
token_budget: z.number().optional().describe("Token budget
|
|
899
|
+
mode: z.enum(["default", "deep"]).optional().describe("'default' = 800 token 3-tier, 'deep' = full context with L2 chunks"),
|
|
900
|
+
token_budget: z.number().optional().describe("Token budget (default: 800)"),
|
|
883
901
|
}, async (args) => {
|
|
884
902
|
try {
|
|
885
903
|
const r = (await api("POST", "/v1/bot/guard/bootstrap", {
|
|
@@ -1289,6 +1307,168 @@ server.tool("memory_entity_profile", "Get complete profile for a specific entity
|
|
|
1289
1307
|
return err(e);
|
|
1290
1308
|
}
|
|
1291
1309
|
});
|
|
1310
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1311
|
+
// PHASE 5 (2026-05-28) — DNA-aligned tools:
|
|
1312
|
+
// • brain_export / brain_import — DNA #3 vendor neutrality
|
|
1313
|
+
// • benchmark_recall_vs_full — DNA #2 retina (measurable moat)
|
|
1314
|
+
// • benchmark_pricing — public model pricing reference
|
|
1315
|
+
// • trust_agents / trust_chunk — DNA #1.5 trust graph
|
|
1316
|
+
// • twin_respond / twin_status — Cognitive Twin (promax+ tier)
|
|
1317
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1318
|
+
// brain_export
|
|
1319
|
+
server.tool("brain_export", "Export the entire brain to a portable JSON bundle (vendor-neutral). Use when the user wants to back up their brain, migrate to another instance (e.g. lite-build on-prem), or comply with data-portability rights. The bundle is self-contained — chunks, edges, entities, L2 sessions, mood, agents — and includes a sha256 checksum. Returns the bundle JSON.", {
|
|
1320
|
+
scope: z.enum(["full", "dna_only", "since"]).optional().describe("'full'=everything (default), 'dna_only'=just preferences/decisions/identity (lightweight portable identity), 'since'=incremental (requires `since` ISO datetime)"),
|
|
1321
|
+
since: z.string().optional().describe("ISO8601 datetime, only used when scope='since'"),
|
|
1322
|
+
}, async (args) => {
|
|
1323
|
+
try {
|
|
1324
|
+
const r = (await api("POST", "/v1/brain/export", {
|
|
1325
|
+
scope: args.scope || "full",
|
|
1326
|
+
since: args.since,
|
|
1327
|
+
}));
|
|
1328
|
+
const counts = r?.manifest?.counts || {};
|
|
1329
|
+
const summary = `Exported brain bundle (format=${r.format} v${r.version}):\n` +
|
|
1330
|
+
`- chunks: ${counts.chunks ?? 0}\n` +
|
|
1331
|
+
`- memory_edges: ${counts.memory_edges ?? 0}\n` +
|
|
1332
|
+
`- entities: ${counts.entities ?? 0}\n` +
|
|
1333
|
+
`- l2_sessions: ${counts.l2_sessions ?? 0}\n` +
|
|
1334
|
+
`- agents: ${counts.agents ?? 0}\n` +
|
|
1335
|
+
`- checksum: ${r?.manifest?.checksum?.slice(0, 16)}...\n\n` +
|
|
1336
|
+
`Bundle JSON ready (truncated preview):\n\`\`\`json\n${JSON.stringify(r, null, 2).slice(0, 1200)}...\n\`\`\``;
|
|
1337
|
+
return ok(summary);
|
|
1338
|
+
}
|
|
1339
|
+
catch (e) {
|
|
1340
|
+
return err(e);
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
// brain_import
|
|
1344
|
+
server.tool("brain_import", "Import a MemoryAI bundle (from brain_export) into the current tenant. Idempotent — chunks deduped by content_hash; edges/entities upserted. Embeddings are reused if the bundle's embedding_model matches the local one; otherwise dropped (chunks re-embed lazily).", {
|
|
1345
|
+
bundle: z.record(z.string(), z.unknown()).describe("The bundle JSON produced by brain_export (must contain format='memoryai-bundle', version, manifest, etc.)"),
|
|
1346
|
+
keep_embeddings: z.boolean().optional().describe("Reuse bundle embeddings if model matches (default: true)"),
|
|
1347
|
+
}, async (args) => {
|
|
1348
|
+
try {
|
|
1349
|
+
const r = (await api("POST", `/v1/brain/import?keep_embeddings=${args.keep_embeddings === false ? "false" : "true"}`, args.bundle));
|
|
1350
|
+
const rep = r?.report || {};
|
|
1351
|
+
const out = `Brain import complete (format=${rep.bundle_format} v${rep.bundle_version}):\n` +
|
|
1352
|
+
`- chunks: ${rep.chunks?.inserted ?? 0} inserted, ${rep.chunks?.skipped_duplicate ?? 0} skipped (dup), ${rep.chunks?.skipped_invalid ?? 0} invalid\n` +
|
|
1353
|
+
`- memory_edges: ${rep.memory_edges?.upserted ?? 0} upserted (${rep.memory_edges?.skipped ?? 0} skipped)\n` +
|
|
1354
|
+
`- entities: ${rep.entities?.upserted ?? 0} upserted\n` +
|
|
1355
|
+
`- l2_sessions: ${rep.l2_sessions?.inserted ?? 0} inserted\n` +
|
|
1356
|
+
`- agents: ${rep.agents?.upserted ?? 0} upserted\n` +
|
|
1357
|
+
`- embedding_model_match: ${rep.embedding_model_match}\n` +
|
|
1358
|
+
(rep.warnings?.length ? `\nWarnings:\n ${rep.warnings.join("\n ")}` : "");
|
|
1359
|
+
return ok(out);
|
|
1360
|
+
}
|
|
1361
|
+
catch (e) {
|
|
1362
|
+
return err(e);
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
// benchmark_recall_vs_full
|
|
1366
|
+
server.tool("benchmark_recall_vs_full", "Run a public benchmark: smart recall vs full-context dump on the calling brain. DNA #2 — proves the 'retina for AI' moat with measurable numbers (cost, latency, signal density). Available on every tier; safe to share results publicly.", {
|
|
1367
|
+
query: z.string().describe("The query to benchmark (e.g. 'what does the user prefer?')"),
|
|
1368
|
+
model: z.string().optional().describe("Model whose pricing to apply (default: claude-opus-4-6). Affects $cost only."),
|
|
1369
|
+
naive_budget_tokens: z.number().optional().describe("Cap on full-context dump (default: 200K = Claude window)"),
|
|
1370
|
+
smart_top_k: z.number().optional().describe("Top-K chunks for smart mode (default: 8)"),
|
|
1371
|
+
smart_depth: z.enum(["instant", "fast", "deep"]).optional().describe("Smart recall depth (default: deep)"),
|
|
1372
|
+
}, async (args) => {
|
|
1373
|
+
try {
|
|
1374
|
+
const r = (await api("POST", "/v1/benchmark/recall-vs-fullcontext", {
|
|
1375
|
+
query: args.query,
|
|
1376
|
+
model: args.model || "claude-opus-4-6",
|
|
1377
|
+
naive_budget_tokens: args.naive_budget_tokens,
|
|
1378
|
+
smart_top_k: args.smart_top_k,
|
|
1379
|
+
smart_depth: args.smart_depth,
|
|
1380
|
+
}));
|
|
1381
|
+
const out = `Benchmark — query: ${JSON.stringify(r.query)}\n` +
|
|
1382
|
+
`Model: ${r.model} ($${r.price_per_m_tokens_usd}/M tokens)\n\n` +
|
|
1383
|
+
`NAIVE: ${r.naive.chunks_used} chunks · ${r.naive.input_tokens} tok · $${r.naive.estimated_cost_usd} · ${r.naive.latency_ms}ms\n` +
|
|
1384
|
+
`SMART: ${r.smart.chunks_used} chunks · ${r.smart.input_tokens} tok · $${r.smart.estimated_cost_usd} · ${r.smart.latency_ms}ms\n\n` +
|
|
1385
|
+
`→ ${r.headline}`;
|
|
1386
|
+
return ok(out);
|
|
1387
|
+
}
|
|
1388
|
+
catch (e) {
|
|
1389
|
+
return err(e);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
// benchmark_pricing
|
|
1393
|
+
server.tool("benchmark_pricing", "Get the assumed $/1M-input-tokens pricing for each LLM (used by benchmark_recall_vs_full). No auth required; list prices only.", {}, async () => {
|
|
1394
|
+
try {
|
|
1395
|
+
const r = (await api("GET", "/v1/benchmark/pricing"));
|
|
1396
|
+
const lines = Object.entries(r.prices || {}).map(([k, v]) => `- ${k}: $${v}`);
|
|
1397
|
+
return ok(`Model pricing (${r.currency} ${r.unit}, as of ${r.as_of}):\n${lines.join("\n")}\n\nNote: ${r.note}`);
|
|
1398
|
+
}
|
|
1399
|
+
catch (e) {
|
|
1400
|
+
return err(e);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
// trust_agents
|
|
1404
|
+
server.tool("trust_agents", "Get the agent reputation leaderboard (sorted by reputation_score desc). DNA #1.5 — when 20 agents share memory and disagree, this tells you whose claims to trust. Requires team+ plan.", {
|
|
1405
|
+
limit: z.number().optional().describe("Max agents to return (default: 50, max: 500)"),
|
|
1406
|
+
}, async (args) => {
|
|
1407
|
+
try {
|
|
1408
|
+
const r = (await api("GET", `/v1/trust/agents?limit=${args.limit ?? 50}`));
|
|
1409
|
+
if (!r.agents?.length)
|
|
1410
|
+
return ok("No agent reputation snapshots yet. Run /v1/trust/recompute-all to populate.");
|
|
1411
|
+
const lines = r.agents.map((a) => `- ${a.agent_id}: ${a.reputation_score.toFixed(3)} [${a.label}] — V=${a.verified_count}/C=${a.contradicted_count}/N=${a.feedback_n}`);
|
|
1412
|
+
return ok(`Agent reputation (${r.agents.length} agents):\n${lines.join("\n")}`);
|
|
1413
|
+
}
|
|
1414
|
+
catch (e) {
|
|
1415
|
+
return err(e);
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
// trust_chunk
|
|
1419
|
+
server.tool("trust_chunk", "Get per-chunk trust info: which agent claimed it, that agent's reputation, helpful/unhelpful counts. Use after a recall to decide whether to trust a specific result. Available on every paid tier.", {
|
|
1420
|
+
chunk_id: z.number().describe("The chunk ID returned by recall"),
|
|
1421
|
+
}, async (args) => {
|
|
1422
|
+
try {
|
|
1423
|
+
const r = (await api("GET", `/v1/trust/chunks/${args.chunk_id}`));
|
|
1424
|
+
const stats = r.agent_stats || {};
|
|
1425
|
+
const fb = r.feedback || {};
|
|
1426
|
+
return ok(`Chunk #${r.chunk_id} (${r.memory_type})\n` +
|
|
1427
|
+
`Source agent: ${r.source_agent_id ?? "(none)"}\n` +
|
|
1428
|
+
`Agent trust: ${r.agent_trust?.toFixed(3) ?? "(none)"} — V=${stats.verified_count ?? 0}/C=${stats.contradicted_count ?? 0}/total=${stats.claim_count ?? 0}\n` +
|
|
1429
|
+
`Per-chunk trust: ${r.chunk_trust_score?.toFixed(3) ?? "(none)"} (helpful=${fb.helpful ?? 0}, unhelpful=${fb.unhelpful ?? 0})`);
|
|
1430
|
+
}
|
|
1431
|
+
catch (e) {
|
|
1432
|
+
return err(e);
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
// twin_respond
|
|
1436
|
+
server.tool("twin_respond", "Ask the Cognitive Twin to predict how the user would respond to a given query. The twin uses the user's personality + mood + DNA + procedures to simulate their voice. Returns the predicted response, a confidence score 0-1, and the chunk IDs that informed it (provenance trail). Auto-refuses with confidence=0 if the brain has fewer than 5 DNA memories. Requires promax+ plan.", {
|
|
1437
|
+
query: z.string().describe("The free-form question to ask the twin (e.g. 'what database for a chat app?')"),
|
|
1438
|
+
operation: z.enum(["respond", "decide"]).optional().describe("'respond'=free-form answer (default), 'decide'=pick one option from the query"),
|
|
1439
|
+
}, async (args) => {
|
|
1440
|
+
try {
|
|
1441
|
+
const path = args.operation === "decide" ? "/v1/twin/decide" : "/v1/twin/respond";
|
|
1442
|
+
const r = (await api("POST", path, { query: args.query }));
|
|
1443
|
+
const out = `Cognitive Twin (${r.operation}, confidence ${r.confidence}):\n` +
|
|
1444
|
+
`Persona: ${r.persona_summary || "(not synthesized)"}\n` +
|
|
1445
|
+
`Mood: ${r.mood || "unknown"}\n` +
|
|
1446
|
+
`Provenance chunks: ${r.provenance_chunks?.length ?? 0}\n\n` +
|
|
1447
|
+
`Response:\n${r.response}\n\n` +
|
|
1448
|
+
`(reason: ${r.confidence_reason})`;
|
|
1449
|
+
return ok(out);
|
|
1450
|
+
}
|
|
1451
|
+
catch (e) {
|
|
1452
|
+
return err(e);
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1455
|
+
// twin_status
|
|
1456
|
+
server.tool("twin_status", "Check whether the Cognitive Twin is ready for the calling tenant. Cheap — no LLM call. Returns DNA count, personality/mood presence, and a `ready` boolean. Useful before invoking twin_respond.", {}, async () => {
|
|
1457
|
+
try {
|
|
1458
|
+
const r = (await api("GET", "/v1/twin/status"));
|
|
1459
|
+
return ok(`Twin ready: ${r.ready ? "YES" : "NO"} (need ≥${r.min_dna_required} DNA, have ${r.dna_count})\n` +
|
|
1460
|
+
`- has_personality: ${r.has_personality}\n` +
|
|
1461
|
+
`- has_mood: ${r.has_mood}\n` +
|
|
1462
|
+
`- procedures: ${r.procedures_count}\n` +
|
|
1463
|
+
`- active_goals: ${r.active_goals_count}\n` +
|
|
1464
|
+
`- top_entities: ${r.top_entities_count}\n` +
|
|
1465
|
+
(r.persona_summary ? `\nPersona: ${r.persona_summary}\n` : "") +
|
|
1466
|
+
(r.mood ? `Mood: ${r.mood}\n` : ""));
|
|
1467
|
+
}
|
|
1468
|
+
catch (e) {
|
|
1469
|
+
return err(e);
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1292
1472
|
async function main() {
|
|
1293
1473
|
const transport = new StdioServerTransport();
|
|
1294
1474
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoryai-mcp",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "MCP server for MemoryAI —
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "MCP server for MemoryAI v2.0 — One brain. ∞ agents. Forever. Adds Brain Export/Import (vendor-neutral bundles), Public Benchmark (smart recall vs full context), Trust Graph (per-agent reputation), Cognitive Twin (simulate user voice). Plus the v1.5 base: 11 biological behaviors, DNA-protected memories, Multi-Agent Mesh.",
|
|
5
5
|
"homepage": "https://memoryai.dev",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|