memoryai-mcp 2.2.0 → 2.3.1
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 +362 -304
- package/dist/claude-setup.d.ts +19 -0
- package/dist/claude-setup.js +216 -0
- package/dist/index.js +342 -77
- package/dist/kiro-setup.d.ts +11 -2
- package/dist/kiro-setup.js +143 -60
- package/package.json +46 -45
package/dist/index.js
CHANGED
|
@@ -8,13 +8,46 @@
|
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
9
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
const API_URL = process.env.HM_ENDPOINT || "http://localhost:8420";
|
|
12
|
-
const API_KEY = process.env.HM_API_KEY || "";
|
|
11
|
+
const API_URL = process.env.MEMORYAI_ENDPOINT || process.env.HM_ENDPOINT || "http://localhost:8420";
|
|
12
|
+
const API_KEY = process.env.MEMORYAI_API_KEY || process.env.HM_API_KEY || "";
|
|
13
13
|
const REQUEST_TIMEOUT_MS = 30_000; // P2 #6: 30s default timeout for API requests
|
|
14
|
-
// Context Guard — per-IDE settings via env vars
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
// Context Guard — per-IDE settings via env vars.
|
|
15
|
+
// HM_COMPACT_AT and HM_CRITICAL_AT are now ABSOLUTE token counts (e.g. "100000",
|
|
16
|
+
// "150000"). The legacy meaning ("30" = 30%) is detected automatically: any
|
|
17
|
+
// value < 1000 is treated as a deprecated percentage and converted to absolute
|
|
18
|
+
// using HM_CONTEXT_CAP if present, otherwise the value is sent as-is and the
|
|
19
|
+
// server interprets it as a fraction (backward-compat path).
|
|
20
|
+
//
|
|
21
|
+
// HM_CONTEXT_CAP itself is no longer required — users set the two thresholds
|
|
22
|
+
// directly. When present it is forwarded as max_tokens so the server can clamp.
|
|
23
|
+
const CG_CONTEXT_CAP = parseInt(process.env.MEMORYAI_CONTEXT_CAP || process.env.HM_CONTEXT_CAP || "0", 10);
|
|
24
|
+
const CG_COMPACT_RAW = parseInt(process.env.MEMORYAI_COMPACT_AT || process.env.HM_COMPACT_AT || "0", 10);
|
|
25
|
+
const CG_CRITICAL_RAW = parseInt(process.env.MEMORYAI_CRITICAL_AT || process.env.HM_CRITICAL_AT || "0", 10);
|
|
26
|
+
// Heuristic: small numbers are legacy percentages; large numbers are absolute tokens.
|
|
27
|
+
// Threshold "<= 100" is generous enough to catch any sensible % (max 95%) and
|
|
28
|
+
// well below any sensible absolute count (min would be ~10K tokens).
|
|
29
|
+
function _isLegacyPct(v) { return v > 0 && v <= 100; }
|
|
30
|
+
// Resolved absolute thresholds. 0 means "not configured — use server defaults".
|
|
31
|
+
const CG_COMPACT_AT_TOKENS = (() => {
|
|
32
|
+
if (CG_COMPACT_RAW <= 0)
|
|
33
|
+
return 0;
|
|
34
|
+
if (_isLegacyPct(CG_COMPACT_RAW) && CG_CONTEXT_CAP > 0) {
|
|
35
|
+
return Math.round((CG_COMPACT_RAW / 100) * CG_CONTEXT_CAP);
|
|
36
|
+
}
|
|
37
|
+
return _isLegacyPct(CG_COMPACT_RAW) ? 0 : CG_COMPACT_RAW;
|
|
38
|
+
})();
|
|
39
|
+
const CG_CRITICAL_AT_TOKENS = (() => {
|
|
40
|
+
if (CG_CRITICAL_RAW <= 0)
|
|
41
|
+
return 0;
|
|
42
|
+
if (_isLegacyPct(CG_CRITICAL_RAW) && CG_CONTEXT_CAP > 0) {
|
|
43
|
+
return Math.round((CG_CRITICAL_RAW / 100) * CG_CONTEXT_CAP);
|
|
44
|
+
}
|
|
45
|
+
return _isLegacyPct(CG_CRITICAL_RAW) ? 0 : CG_CRITICAL_RAW;
|
|
46
|
+
})();
|
|
47
|
+
// Legacy decimal % path for the rare case where user keeps "30/50" without
|
|
48
|
+
// HM_CONTEXT_CAP — server still accepts compact_pct/critical_pct as decimals.
|
|
49
|
+
const CG_COMPACT_PCT = _isLegacyPct(CG_COMPACT_RAW) && CG_CONTEXT_CAP <= 0 ? CG_COMPACT_RAW : 0;
|
|
50
|
+
const CG_CRITICAL_PCT = _isLegacyPct(CG_CRITICAL_RAW) && CG_CONTEXT_CAP <= 0 ? CG_CRITICAL_RAW : 0;
|
|
18
51
|
// --- HTTP helper ---
|
|
19
52
|
async function api(method, path, body) {
|
|
20
53
|
const resp = await fetch(`${API_URL}${path}`, {
|
|
@@ -40,9 +73,9 @@ function err(e) {
|
|
|
40
73
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
41
74
|
}
|
|
42
75
|
// --- MCP Server ---
|
|
43
|
-
const server = new McpServer({ name: "memoryai", version: "
|
|
76
|
+
const server = new McpServer({ name: "memoryai", version: "2.3.0" }, { capabilities: { tools: {} } });
|
|
44
77
|
// 1. memory_store
|
|
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,
|
|
78
|
+
server.tool("memory_store", "[CORE] Store information in persistent memory. Use when you learn something important — project context, user preferences, architectural decisions, patterns, bugs, pricing/cost discussions, business plans, or ANY information the user might ask about later. When in doubt, STORE — dedup is automatic.", {
|
|
46
79
|
content: z.string().describe("What to remember"),
|
|
47
80
|
source: z.string().optional().describe("Source context (e.g. file path, conversation)"),
|
|
48
81
|
tags: z.array(z.string()).optional().describe("Categories: preferences, architecture, bugs, patterns, decisions"),
|
|
@@ -87,7 +120,7 @@ server.tool("memory_store", "Store information in persistent memory. Use when yo
|
|
|
87
120
|
}
|
|
88
121
|
});
|
|
89
122
|
// 2. memory_recall
|
|
90
|
-
server.tool("memory_recall", "Search persistent memory for relevant context. Use
|
|
123
|
+
server.tool("memory_recall", "[CORE] Search persistent memory for relevant context. Use BEFORE starting work to check what you already know about the project, user preferences, or past decisions.", {
|
|
91
124
|
query: z.string().describe("What to search for"),
|
|
92
125
|
depth: z.enum(["fast", "instant", "deep", "exhaustive"]).optional().describe("Search depth. 'instant'=vector only (~50ms), 'fast'=FTS only, 'deep'=full fusion (default), 'exhaustive'=deep+more results"),
|
|
93
126
|
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
@@ -129,7 +162,7 @@ server.tool("memory_recall", "Search persistent memory for relevant context. Use
|
|
|
129
162
|
}
|
|
130
163
|
});
|
|
131
164
|
// 3. memory_stats
|
|
132
|
-
server.tool("memory_stats", "Get memory usage statistics — chunk count, storage size, monthly usage.", {}, async () => {
|
|
165
|
+
server.tool("memory_stats", "Advanced: Get memory usage statistics — chunk count, storage size, monthly usage.", {}, async () => {
|
|
133
166
|
try {
|
|
134
167
|
const r = (await api("GET", "/v1/stats"));
|
|
135
168
|
return ok(`Memory Stats:\n` +
|
|
@@ -143,7 +176,7 @@ server.tool("memory_stats", "Get memory usage statistics — chunk count, storag
|
|
|
143
176
|
}
|
|
144
177
|
});
|
|
145
178
|
// 4. memory_compact
|
|
146
|
-
server.tool("memory_compact", "Compact long text into memory chunks for long-term storage. Use at end of session or when context is getting large.", {
|
|
179
|
+
server.tool("memory_compact", "Advanced: Compact long text into memory chunks for long-term storage. Use at end of session or when context is getting large.", {
|
|
147
180
|
content: z.string().describe("Text content to compact"),
|
|
148
181
|
task_context: z.string().optional().describe("Brief description of the task/session"),
|
|
149
182
|
content_type: z.enum(["conversation", "code"]).optional().describe("Content type"),
|
|
@@ -186,7 +219,7 @@ server.tool("memory_compact", "Compact long text into memory chunks for long-ter
|
|
|
186
219
|
}
|
|
187
220
|
});
|
|
188
221
|
// 5. context_check
|
|
189
|
-
server.tool("context_check", "Check current context token usage and urgency level.", {
|
|
222
|
+
server.tool("context_check", "Advanced: Check current context token usage and urgency level.", {
|
|
190
223
|
content_type: z.enum(["conversation", "code"]).optional().describe("Check conversation or code context"),
|
|
191
224
|
}, async (args) => {
|
|
192
225
|
try {
|
|
@@ -203,7 +236,7 @@ server.tool("context_check", "Check current context token usage and urgency leve
|
|
|
203
236
|
}
|
|
204
237
|
});
|
|
205
238
|
// 6. context_restore
|
|
206
|
-
server.tool("context_restore", "Restore context for a task by recalling the most relevant memory chunks.", {
|
|
239
|
+
server.tool("context_restore", "Advanced: Restore context for a task by recalling the most relevant memory chunks.", {
|
|
207
240
|
task_description: z.string().describe("Description of the task to restore context for"),
|
|
208
241
|
limit: z.number().optional().describe("Max chunks to restore (default: 5)"),
|
|
209
242
|
}, async (args) => {
|
|
@@ -224,7 +257,7 @@ server.tool("context_restore", "Restore context for a task by recalling the most
|
|
|
224
257
|
}
|
|
225
258
|
});
|
|
226
259
|
// 7. project_index
|
|
227
|
-
server.tool("project_index", "Index a code project's file tree and key files into memory. IDE use only.", {
|
|
260
|
+
server.tool("project_index", "Advanced: Index a code project's file tree and key files into memory. IDE use only.", {
|
|
228
261
|
file_tree: z.array(z.string()).describe("List of file paths in the project"),
|
|
229
262
|
key_files: z.record(z.string(), z.string()).optional().describe("Map of file_path → file_content for important files"),
|
|
230
263
|
git_info: z.record(z.string(), z.unknown()).optional().describe("Git metadata (branch, remote, last commit)"),
|
|
@@ -251,7 +284,7 @@ server.tool("project_index", "Index a code project's file tree and key files int
|
|
|
251
284
|
// 8. collective_contribute
|
|
252
285
|
server.tool(
|
|
253
286
|
"collective_contribute",
|
|
254
|
-
"Contribute knowledge to the collective memory pool. Requires opt-in. Content is anonymized before storage. Use for sharing bug fixes, patterns, decisions that could help other developers.",
|
|
287
|
+
"Advanced: Contribute knowledge to the collective memory pool. Requires opt-in. Content is anonymized before storage. Use for sharing bug fixes, patterns, decisions that could help other developers.",
|
|
255
288
|
{
|
|
256
289
|
content: z.string().describe("Knowledge to contribute (will be anonymized)"),
|
|
257
290
|
content_type: z.enum(["bug_fix", "pattern", "decision", "migration", "tip", "performance", "security"]).optional().describe("Type of knowledge (default: bug_fix)"),
|
|
@@ -281,7 +314,7 @@ server.tool(
|
|
|
281
314
|
// 9. collective_recall
|
|
282
315
|
server.tool(
|
|
283
316
|
"collective_recall",
|
|
284
|
-
"Search the collective knowledge pool for solutions, patterns, and tips from the developer community. Free for all users.",
|
|
317
|
+
"Advanced: Search the collective knowledge pool for solutions, patterns, and tips from the developer community. Free for all users.",
|
|
285
318
|
{
|
|
286
319
|
query: z.string().describe("What to search for"),
|
|
287
320
|
depth: z.enum(["fast", "deep"]).optional().describe("Search depth (default: deep)"),
|
|
@@ -314,7 +347,7 @@ server.tool(
|
|
|
314
347
|
// 10. collective_confirm
|
|
315
348
|
server.tool(
|
|
316
349
|
"collective_confirm",
|
|
317
|
-
"Confirm whether a collective solution worked or not. Helps improve confidence scores.",
|
|
350
|
+
"Advanced: Confirm whether a collective solution worked or not. Helps improve confidence scores.",
|
|
318
351
|
{
|
|
319
352
|
collective_chunk_id: z.number().describe("ID of the collective chunk to confirm"),
|
|
320
353
|
worked: z.boolean().describe("Did the solution work? true/false"),
|
|
@@ -337,7 +370,7 @@ server.tool(
|
|
|
337
370
|
// 11. collective_stats
|
|
338
371
|
server.tool(
|
|
339
372
|
"collective_stats",
|
|
340
|
-
"Get statistics about the collective knowledge pool.",
|
|
373
|
+
"Advanced: Get statistics about the collective knowledge pool.",
|
|
341
374
|
{},
|
|
342
375
|
async () => {
|
|
343
376
|
try {
|
|
@@ -357,7 +390,7 @@ server.tool(
|
|
|
357
390
|
);
|
|
358
391
|
*/
|
|
359
392
|
// 12. l2_store
|
|
360
|
-
server.tool("reasoning_store", "Store content in a reasoning bank. Reasoning banks hold context for deep analysis. Requires Pro plan or higher.", {
|
|
393
|
+
server.tool("reasoning_store", "Advanced: Store content in a reasoning bank. Reasoning banks hold context for deep analysis. Requires Pro plan or higher.", {
|
|
361
394
|
bank_name: z.string().describe("Name of the L2 bank"),
|
|
362
395
|
content: z.string().describe("Content to store in the bank"),
|
|
363
396
|
token_count: z.number().optional().describe("Approximate token count of the content"),
|
|
@@ -375,7 +408,7 @@ server.tool("reasoning_store", "Store content in a reasoning bank. Reasoning ban
|
|
|
375
408
|
}
|
|
376
409
|
});
|
|
377
410
|
// 13. l2_recall
|
|
378
|
-
server.tool("reasoning_recall", "Recall from reasoning banks via deep analysis. Requires Pro plan or higher.", {
|
|
411
|
+
server.tool("reasoning_recall", "Advanced: Recall from reasoning banks via deep analysis. Requires Pro plan or higher.", {
|
|
379
412
|
query: z.string().describe("Question to answer using L2 bank context"),
|
|
380
413
|
bank_names: z.array(z.string()).optional().describe("Specific banks to search (default: all)"),
|
|
381
414
|
}, async (args) => {
|
|
@@ -393,7 +426,7 @@ server.tool("reasoning_recall", "Recall from reasoning banks via deep analysis.
|
|
|
393
426
|
}
|
|
394
427
|
});
|
|
395
428
|
// 14. l2_compress
|
|
396
|
-
server.tool("reasoning_compress", "Compress old reasoning bank entries into a digest. Reduces storage while preserving key information.", {
|
|
429
|
+
server.tool("reasoning_compress", "Advanced: Compress old reasoning bank entries into a digest. Reduces storage while preserving key information.", {
|
|
397
430
|
bank_name: z.string().describe("Name of the L2 bank to compress"),
|
|
398
431
|
}, async (args) => {
|
|
399
432
|
try {
|
|
@@ -409,7 +442,7 @@ server.tool("reasoning_compress", "Compress old reasoning bank entries into a di
|
|
|
409
442
|
}
|
|
410
443
|
});
|
|
411
444
|
// 15. l2_stats
|
|
412
|
-
server.tool("reasoning_stats", "Get reasoning layer statistics — bank sizes, usage, and plan limits.", {}, async () => {
|
|
445
|
+
server.tool("reasoning_stats", "Advanced: Get reasoning layer statistics — bank sizes, usage, and plan limits.", {}, async () => {
|
|
413
446
|
try {
|
|
414
447
|
const r = (await api("GET", "/v1/l2/stats"));
|
|
415
448
|
const bankList = (r.banks || [])
|
|
@@ -425,7 +458,7 @@ server.tool("reasoning_stats", "Get reasoning layer statistics — bank sizes, u
|
|
|
425
458
|
}
|
|
426
459
|
});
|
|
427
460
|
// 16. entity_list
|
|
428
|
-
server.tool("entity_list", "List tracked entities (files, URLs, people, packages, code symbols) extracted from stored memories.", {
|
|
461
|
+
server.tool("entity_list", "Advanced: List tracked entities (files, URLs, people, packages, code symbols) extracted from stored memories.", {
|
|
429
462
|
entity_type: z.string().optional().describe("Filter by type: file, url, person, package, code_symbol"),
|
|
430
463
|
limit: z.number().optional().describe("Max results (default: 50)"),
|
|
431
464
|
}, async (args) => {
|
|
@@ -449,7 +482,7 @@ server.tool("entity_list", "List tracked entities (files, URLs, people, packages
|
|
|
449
482
|
}
|
|
450
483
|
});
|
|
451
484
|
// 17. entity_search
|
|
452
|
-
server.tool("entity_search", "Find memory chunks linked to a specific entity by name.", {
|
|
485
|
+
server.tool("entity_search", "Advanced: Find memory chunks linked to a specific entity by name.", {
|
|
453
486
|
name: z.string().describe("Entity name to search for"),
|
|
454
487
|
limit: z.number().optional().describe("Max chunk IDs to return (default: 20)"),
|
|
455
488
|
}, async (args) => {
|
|
@@ -465,7 +498,7 @@ server.tool("entity_search", "Find memory chunks linked to a specific entity by
|
|
|
465
498
|
}
|
|
466
499
|
});
|
|
467
500
|
// 18. learn
|
|
468
|
-
server.tool("learn", "Store an action, its result, and lesson learned. Use after completing tasks, fixing bugs, or making decisions to build action memory.", {
|
|
501
|
+
server.tool("learn", "Advanced: Store an action, its result, and lesson learned. Use after completing tasks, fixing bugs, or making decisions to build action memory.", {
|
|
469
502
|
action: z.string().describe("What was done"),
|
|
470
503
|
result: z.string().describe("What happened"),
|
|
471
504
|
outcome: z.enum(["success", "failure", "partial"]).optional().describe("Outcome (default: success)"),
|
|
@@ -491,7 +524,7 @@ server.tool("learn", "Store an action, its result, and lesson learned. Use after
|
|
|
491
524
|
// 12. collective_shards
|
|
492
525
|
server.tool(
|
|
493
526
|
"collective_shards",
|
|
494
|
-
"List all domain shards in the collective knowledge pool with chunk counts.",
|
|
527
|
+
"Advanced: List all domain shards in the collective knowledge pool with chunk counts.",
|
|
495
528
|
{},
|
|
496
529
|
async () => {
|
|
497
530
|
try {
|
|
@@ -508,7 +541,7 @@ server.tool(
|
|
|
508
541
|
// 13. collective_synthesize
|
|
509
542
|
server.tool(
|
|
510
543
|
"collective_synthesize",
|
|
511
|
-
"Trigger knowledge card synthesis from collective chunks. Groups similar chunks and creates merged knowledge cards. Admin operation.",
|
|
544
|
+
"Advanced: Trigger knowledge card synthesis from collective chunks. Groups similar chunks and creates merged knowledge cards. Admin operation.",
|
|
512
545
|
{},
|
|
513
546
|
async () => {
|
|
514
547
|
try {
|
|
@@ -530,7 +563,7 @@ server.tool(
|
|
|
530
563
|
// 14. pool_stats
|
|
531
564
|
server.tool(
|
|
532
565
|
"pool_stats",
|
|
533
|
-
"Get content pool dedup statistics — pool size, ref count, dedup ratio, and estimated savings.",
|
|
566
|
+
"Advanced: Get content pool dedup statistics — pool size, ref count, dedup ratio, and estimated savings.",
|
|
534
567
|
{},
|
|
535
568
|
async () => {
|
|
536
569
|
try {
|
|
@@ -549,7 +582,7 @@ server.tool(
|
|
|
549
582
|
*/
|
|
550
583
|
// --- Start ---
|
|
551
584
|
// 16. memory_recover
|
|
552
|
-
server.tool("memory_recover", "Recover session context from recent memory. Use when resuming work after a break to quickly understand what was happening — active files, pending tasks, timeline, and key references.", {
|
|
585
|
+
server.tool("memory_recover", "Advanced: Recover session context from recent memory. Use when resuming work after a break to quickly understand what was happening — active files, pending tasks, timeline, and key references.", {
|
|
553
586
|
task_context: z.string().optional().describe("Hint about what you were working on"),
|
|
554
587
|
time_range_hours: z.number().optional().describe("Look back N hours (default: 24)"),
|
|
555
588
|
max_tokens: z.number().optional().describe("Token budget for response (default: 8000)"),
|
|
@@ -579,7 +612,7 @@ server.tool("memory_recover", "Recover session context from recent memory. Use w
|
|
|
579
612
|
}
|
|
580
613
|
});
|
|
581
614
|
// 17. memory_health
|
|
582
|
-
server.tool("memory_health", "Check context window health — usage percentage, compaction recommendation, and memory freshness stats.", {
|
|
615
|
+
server.tool("memory_health", "Advanced: Check context window health — usage percentage, compaction recommendation, and memory freshness stats.", {
|
|
583
616
|
current_tokens: z.number().describe("Current token count in context window"),
|
|
584
617
|
max_tokens: z.number().optional().describe("Max context window size (default: 200000)"),
|
|
585
618
|
}, async (args) => {
|
|
@@ -604,7 +637,7 @@ server.tool("memory_health", "Check context window health — usage percentage,
|
|
|
604
637
|
}
|
|
605
638
|
});
|
|
606
639
|
// 18. memory_health_detailed
|
|
607
|
-
server.tool("memory_health_detailed", "Get detailed memory health — chunk distribution, stale chunk count, entity stats, and actionable recommendations.", {}, async () => {
|
|
640
|
+
server.tool("memory_health_detailed", "Advanced: Get detailed memory health — chunk distribution, stale chunk count, entity stats, and actionable recommendations.", {}, async () => {
|
|
608
641
|
try {
|
|
609
642
|
const r = (await api("GET", "/v1/health/detailed"));
|
|
610
643
|
const recs = (r.recommendations || []).map((rec) => ` ⚠ ${rec}`).join("\n");
|
|
@@ -622,7 +655,7 @@ server.tool("memory_health_detailed", "Get detailed memory health — chunk dist
|
|
|
622
655
|
}
|
|
623
656
|
});
|
|
624
657
|
// 19. snapshot_create
|
|
625
|
-
server.tool("snapshot_create", "Create a snapshot backup of all current memory chunks. Use before risky operations.", {}, async () => {
|
|
658
|
+
server.tool("snapshot_create", "Advanced: Create a snapshot backup of all current memory chunks. Use before risky operations.", {}, async () => {
|
|
626
659
|
try {
|
|
627
660
|
const r = (await api("POST", "/v1/snapshots/create"));
|
|
628
661
|
return ok(`Snapshot created (id=${r.snapshot_id}): ${r.chunks_count} chunks, ${r.size_bytes} bytes`);
|
|
@@ -632,7 +665,7 @@ server.tool("snapshot_create", "Create a snapshot backup of all current memory c
|
|
|
632
665
|
}
|
|
633
666
|
});
|
|
634
667
|
// 19. snapshot_list
|
|
635
|
-
server.tool("snapshot_list", "List all available memory snapshots.", {}, async () => {
|
|
668
|
+
server.tool("snapshot_list", "Advanced: List all available memory snapshots.", {}, async () => {
|
|
636
669
|
try {
|
|
637
670
|
const r = (await api("GET", "/v1/snapshots"));
|
|
638
671
|
if (!r.snapshots?.length)
|
|
@@ -647,7 +680,7 @@ server.tool("snapshot_list", "List all available memory snapshots.", {}, async (
|
|
|
647
680
|
}
|
|
648
681
|
});
|
|
649
682
|
// 20. snapshot_restore
|
|
650
|
-
server.tool("snapshot_restore", "Restore memory from a snapshot. Current chunks will be soft-deleted and replaced.", {
|
|
683
|
+
server.tool("snapshot_restore", "Advanced: Restore memory from a snapshot. Current chunks will be soft-deleted and replaced.", {
|
|
651
684
|
snapshot_id: z.string().describe("Snapshot ID to restore from"),
|
|
652
685
|
}, async (args) => {
|
|
653
686
|
try {
|
|
@@ -659,7 +692,7 @@ server.tool("snapshot_restore", "Restore memory from a snapshot. Current chunks
|
|
|
659
692
|
}
|
|
660
693
|
});
|
|
661
694
|
// 21. memory_bootstrap
|
|
662
|
-
server.tool("memory_bootstrap", "
|
|
695
|
+
server.tool("memory_bootstrap", "[CORE] Load context at session start — call this FIRST in every new session. Returns preferences, recent activity, project context, and key entities as a ready-to-use block.", {
|
|
663
696
|
task_description: z.string().optional().describe("What you're about to work on"),
|
|
664
697
|
project_name: z.string().optional().describe("Project name to focus on"),
|
|
665
698
|
max_tokens: z.number().optional().describe("Token budget for context block (default: 4000)"),
|
|
@@ -685,7 +718,7 @@ server.tool("memory_bootstrap", "Get a ready-to-use context block at session sta
|
|
|
685
718
|
}
|
|
686
719
|
});
|
|
687
720
|
// 22. memory_explore
|
|
688
|
-
server.tool("memory_explore", "Explore memory connections — find chunks related to a specific memory. Reveals hidden connections and associations.", {
|
|
721
|
+
server.tool("memory_explore", "Advanced: Explore memory connections — find chunks related to a specific memory. Reveals hidden connections and associations.", {
|
|
689
722
|
chunk_id: z.number().describe("ID of the memory chunk to explore from"),
|
|
690
723
|
limit: z.number().optional().describe("Max neighbors to return (default: 10)"),
|
|
691
724
|
}, async (args) => {
|
|
@@ -707,7 +740,7 @@ server.tool("memory_explore", "Explore memory connections — find chunks relate
|
|
|
707
740
|
}
|
|
708
741
|
});
|
|
709
742
|
// 23. memory_clusters
|
|
710
|
-
server.tool("memory_clusters", "View memory topic clusters — groups of related memories organized by topic. Useful for understanding what topics are stored.", {
|
|
743
|
+
server.tool("memory_clusters", "Advanced: View memory topic clusters — groups of related memories organized by topic. Useful for understanding what topics are stored.", {
|
|
711
744
|
limit: z.number().optional().describe("Max clusters to return (default: 20)"),
|
|
712
745
|
}, async (args) => {
|
|
713
746
|
try {
|
|
@@ -724,7 +757,7 @@ server.tool("memory_clusters", "View memory topic clusters — groups of related
|
|
|
724
757
|
}
|
|
725
758
|
});
|
|
726
759
|
// 24. session_handoff_start
|
|
727
|
-
server.tool("session_handoff_start", "Start a session handoff — save old session conversation to MemoryAI server for new session to read. Use when context window is filling up and you need to switch sessions without losing context.", {
|
|
760
|
+
server.tool("session_handoff_start", "Advanced: Start a session handoff — save old session conversation to MemoryAI server for new session to read. Use when context window is filling up and you need to switch sessions without losing context.", {
|
|
728
761
|
conversation: z.array(z.object({
|
|
729
762
|
role: z.string().describe("Message role: user, assistant, system"),
|
|
730
763
|
content: z.string().describe("Message content"),
|
|
@@ -744,7 +777,7 @@ server.tool("session_handoff_start", "Start a session handoff — save old sessi
|
|
|
744
777
|
}
|
|
745
778
|
});
|
|
746
779
|
// 25. session_handoff_restore
|
|
747
|
-
server.tool("session_handoff_restore", "Restore old session conversation + related MemoryAI memories for a new session. Call this at the start of a new session to pick up where the old session left off — zero context loss.", {
|
|
780
|
+
server.tool("session_handoff_restore", "Advanced: Restore old session conversation + related MemoryAI memories for a new session. Call this at the start of a new session to pick up where the old session left off — zero context loss.", {
|
|
748
781
|
handoff_id: z.string().optional().describe("Specific handoff ID, or omit for latest"),
|
|
749
782
|
include_memories: z.boolean().optional().describe("Also include related MemoryAI memories (default: true)"),
|
|
750
783
|
memory_limit: z.number().optional().describe("Max related memories to include (default: 5)"),
|
|
@@ -775,7 +808,7 @@ server.tool("session_handoff_restore", "Restore old session conversation + relat
|
|
|
775
808
|
}
|
|
776
809
|
});
|
|
777
810
|
// 26. session_handoff_complete
|
|
778
|
-
server.tool("session_handoff_complete", "Complete a session handoff — archive old session conversation into long-term MemoryAI storage. Call this when the new session has enough context (e.g., after working for a while).", {
|
|
811
|
+
server.tool("session_handoff_complete", "Advanced: Complete a session handoff — archive old session conversation into long-term MemoryAI storage. Call this when the new session has enough context (e.g., after working for a while).", {
|
|
779
812
|
handoff_id: z.string().optional().describe("Specific handoff ID, or omit for latest"),
|
|
780
813
|
archive_to_memory: z.boolean().optional().describe("Store old conversation as MemoryAI chunks (default: true)"),
|
|
781
814
|
}, async (args) => {
|
|
@@ -795,7 +828,7 @@ server.tool("session_handoff_complete", "Complete a session handoff — archive
|
|
|
795
828
|
}
|
|
796
829
|
});
|
|
797
830
|
// 27. session_handoff_status
|
|
798
|
-
server.tool("session_handoff_status", "Check current session handoff status — whether there's a pending handoff and its state.", {}, async () => {
|
|
831
|
+
server.tool("session_handoff_status", "Advanced: Check current session handoff status — whether there's a pending handoff and its state.", {}, async () => {
|
|
799
832
|
try {
|
|
800
833
|
const r = (await api("GET", "/v1/session/handoff/status"));
|
|
801
834
|
if (!r.handoff_id)
|
|
@@ -816,7 +849,7 @@ server.tool("session_handoff_status", "Check current session handoff status —
|
|
|
816
849
|
});
|
|
817
850
|
// ─── Context Guard v6 Tools ─────────────────────────────────────────
|
|
818
851
|
// context_guard_check — universal guard check with DNA count
|
|
819
|
-
server.tool("context_guard_check", "Check context
|
|
852
|
+
server.tool("context_guard_check", "[CORE] Check context pressure — returns recommendation (safe/compact_soon/compact_now). Call every ~15 messages to monitor context window health.", {
|
|
820
853
|
estimated_tokens: z.number().describe("Current token count in context window"),
|
|
821
854
|
max_tokens: z.number().optional().describe("Max context window size (uses HM_CONTEXT_CAP env if omitted)"),
|
|
822
855
|
model: z.string().optional().describe("Model name for auto-detecting context window size (e.g. claude-sonnet-4-6)"),
|
|
@@ -829,21 +862,34 @@ server.tool("context_guard_check", "Check context window health using Context Gu
|
|
|
829
862
|
max_tokens: maxTokens,
|
|
830
863
|
model: args.model || null,
|
|
831
864
|
};
|
|
832
|
-
//
|
|
865
|
+
// Per-IDE threshold overrides. Absolute (CG_*_AT_TOKENS) is preferred —
|
|
866
|
+
// server treats it as the authoritative trigger. Decimal % is the
|
|
867
|
+
// backward-compat path for users whose env still says "30/50".
|
|
868
|
+
if (CG_COMPACT_AT_TOKENS > 0)
|
|
869
|
+
payload.compact_at_tokens = CG_COMPACT_AT_TOKENS;
|
|
870
|
+
if (CG_CRITICAL_AT_TOKENS > 0)
|
|
871
|
+
payload.critical_at_tokens = CG_CRITICAL_AT_TOKENS;
|
|
833
872
|
if (CG_COMPACT_PCT > 0)
|
|
834
873
|
payload.compact_pct = CG_COMPACT_PCT / 100;
|
|
835
874
|
if (CG_CRITICAL_PCT > 0)
|
|
836
875
|
payload.critical_pct = CG_CRITICAL_PCT / 100;
|
|
837
876
|
const r = (await api("POST", "/v1/context/guard/check", payload));
|
|
838
|
-
|
|
877
|
+
// Render against the user's configured critical threshold so the bar is
|
|
878
|
+
// anchored to the absolute trigger point (HM_CRITICAL_AT), not a soft
|
|
879
|
+
// model-window percentage. When critical_at_tokens is missing, fall back
|
|
880
|
+
// to the legacy usage_percent rendering.
|
|
881
|
+
const tokens = args.estimated_tokens;
|
|
882
|
+
const critical = r.critical_at_tokens || (CG_CRITICAL_AT_TOKENS > 0 ? CG_CRITICAL_AT_TOKENS : 0);
|
|
883
|
+
const compact = r.compact_at_tokens || (CG_COMPACT_AT_TOKENS > 0 ? CG_COMPACT_AT_TOKENS : 0);
|
|
839
884
|
const barLen = 20;
|
|
840
|
-
const
|
|
885
|
+
const ratio = critical > 0 ? Math.min(1, tokens / critical) : (r.usage_percent / 100);
|
|
886
|
+
const filled = Math.max(0, Math.min(barLen, Math.round(ratio * barLen)));
|
|
841
887
|
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barLen - filled);
|
|
842
888
|
return ok(`Context Guard v6:\n` +
|
|
843
|
-
`[${bar}] ${
|
|
844
|
-
`Recommendation: ${r.recommendation.toUpperCase()}${r.should_compact ? "
|
|
889
|
+
`[${bar}] ${tokens.toLocaleString()} / ${critical ? critical.toLocaleString() + " tokens" : (r.usage_percent.toFixed(1) + "%")}\n` +
|
|
890
|
+
`Recommendation: ${r.recommendation.toUpperCase()}${r.should_compact ? " \u2014 compact now" : ""}\n` +
|
|
845
891
|
`Urgency: ${r.urgency}\n` +
|
|
846
|
-
`Thresholds: compact=${r.compact_at_tokens.toLocaleString()}, critical=${r.critical_at_tokens.toLocaleString()}\n` +
|
|
892
|
+
`Thresholds: compact=${(compact || r.compact_at_tokens).toLocaleString()}, critical=${(critical || r.critical_at_tokens).toLocaleString()}\n` +
|
|
847
893
|
`DNA memories: ${r.dna_memories} | Hot: ${r.hot_memories} | Stale: ${r.stale_memories}\n` +
|
|
848
894
|
`Bootstrap ready: ${r.bootstrap_ready ? "yes" : "no"}\n` +
|
|
849
895
|
(r.last_compact_minutes_ago != null ? `Last compact: ${r.last_compact_minutes_ago.toFixed(0)} min ago` : "No compacts yet"));
|
|
@@ -853,7 +899,7 @@ server.tool("context_guard_check", "Check context window health using Context Gu
|
|
|
853
899
|
}
|
|
854
900
|
});
|
|
855
901
|
// context_guard_compact — compact with DNA protection
|
|
856
|
-
server.tool("context_guard_compact", "
|
|
902
|
+
server.tool("context_guard_compact", "[CORE] Save context to long-term memory when context_guard_check says 'compact_now'. Send a REAL summary (>500 chars) of topics discussed, decisions made, key facts, and current status. DNA memories are never overwritten.", {
|
|
857
903
|
content: z.string().describe("Conversation summary — include topics, decisions, key facts, numbers. Must be >500 chars of real content."),
|
|
858
904
|
task_context: z.string().optional().describe("Task description for tagging"),
|
|
859
905
|
blocking: z.boolean().optional().describe("Wait for result (true) or return task_id (false, default)"),
|
|
@@ -874,8 +920,58 @@ server.tool("context_guard_compact", "Compact session context with DNA protectio
|
|
|
874
920
|
return err(e);
|
|
875
921
|
}
|
|
876
922
|
});
|
|
923
|
+
// ide_turn_check — server-authoritative context guard (zero guessing)
|
|
924
|
+
// The agent cannot read its host IDE's actual context window from inside an
|
|
925
|
+
// MCP tool. Instead of asking it to estimate tokens, this tool only asks for
|
|
926
|
+
// turn_count + max_tokens and the server does the math. Returns a ready-to-
|
|
927
|
+
// follow action_prompt — agent reads it verbatim, no interpretation needed.
|
|
928
|
+
server.tool("ide_turn_check", "[CORE] Server-authoritative context check for IDE hooks. Pass turn_count + max_tokens; server computes pressure and returns an action_prompt the agent should follow verbatim. Use this instead of context_guard_check from IDE auto-capture hooks.", {
|
|
929
|
+
turn_count: z.number().describe("Number of assistant turns in this conversation."),
|
|
930
|
+
max_tokens: z.number().optional().describe("Actual IDE context window in tokens — NOT the underlying model claim. Default 200000."),
|
|
931
|
+
avg_tokens_per_turn: z.number().optional().describe("Calibration knob, default 4000."),
|
|
932
|
+
skip_below_turns: z.number().optional().describe("Skip the check below this many turns (default 20)."),
|
|
933
|
+
model: z.string().optional().describe("Optional model hint when max_tokens not set."),
|
|
934
|
+
}, async (args) => {
|
|
935
|
+
try {
|
|
936
|
+
const payload = {
|
|
937
|
+
turn_count: args.turn_count,
|
|
938
|
+
max_tokens: args.max_tokens ?? CG_CONTEXT_CAP ?? 200_000,
|
|
939
|
+
avg_tokens_per_turn: args.avg_tokens_per_turn ?? 8000,
|
|
940
|
+
skip_below_turns: args.skip_below_turns ?? 10,
|
|
941
|
+
model: args.model ?? null,
|
|
942
|
+
};
|
|
943
|
+
// Per-IDE threshold overrides. Absolute first (preferred), % fallback.
|
|
944
|
+
if (CG_COMPACT_AT_TOKENS > 0)
|
|
945
|
+
payload.compact_at_tokens = CG_COMPACT_AT_TOKENS;
|
|
946
|
+
if (CG_CRITICAL_AT_TOKENS > 0)
|
|
947
|
+
payload.critical_at_tokens = CG_CRITICAL_AT_TOKENS;
|
|
948
|
+
if (CG_COMPACT_PCT > 0)
|
|
949
|
+
payload.compact_pct = CG_COMPACT_PCT / 100;
|
|
950
|
+
if (CG_CRITICAL_PCT > 0)
|
|
951
|
+
payload.critical_pct = CG_CRITICAL_PCT / 100;
|
|
952
|
+
const r = (await api("POST", "/v1/ide/guard/turn-check", payload));
|
|
953
|
+
if (r.skipped) {
|
|
954
|
+
return ok(`Turn check skipped (turn ${args.turn_count} below threshold). Recommendation: SAFE.`);
|
|
955
|
+
}
|
|
956
|
+
const tokens = r.estimated_tokens;
|
|
957
|
+
const critical = r.critical_at_tokens || (CG_CRITICAL_AT_TOKENS > 0 ? CG_CRITICAL_AT_TOKENS : 0);
|
|
958
|
+
const barLen = 20;
|
|
959
|
+
const ratio = critical > 0 ? Math.min(1, tokens / critical) : (r.usage_percent / 100);
|
|
960
|
+
const filled = Math.max(0, Math.min(barLen, Math.round(ratio * barLen)));
|
|
961
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barLen - filled);
|
|
962
|
+
return ok(`IDE Turn Check (server-authoritative):\n` +
|
|
963
|
+
`[${bar}] ${tokens.toLocaleString()} / ${critical ? critical.toLocaleString() + " tokens" : (r.usage_percent.toFixed(1) + "%")}\n` +
|
|
964
|
+
`Recommendation: ${r.recommendation.toUpperCase()}\n` +
|
|
965
|
+
`Urgency: ${r.urgency}\n` +
|
|
966
|
+
`Thresholds: compact=${r.compact_at_tokens.toLocaleString()}, critical=${r.critical_at_tokens.toLocaleString()}\n` +
|
|
967
|
+
(r.action_prompt ? `\nACTION FOR AGENT (follow verbatim):\n${r.action_prompt}` : "No action needed."));
|
|
968
|
+
}
|
|
969
|
+
catch (e) {
|
|
970
|
+
return err(e);
|
|
971
|
+
}
|
|
972
|
+
});
|
|
877
973
|
// 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.", {
|
|
974
|
+
server.tool("context_guard_bootstrap", "Advanced: 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
975
|
task: z.string().describe("Task description for context relevance"),
|
|
880
976
|
limit: z.number().optional().describe("Max memories to include (default: 10)"),
|
|
881
977
|
}, async (args) => {
|
|
@@ -893,7 +989,7 @@ server.tool("context_guard_bootstrap", "Load context from previous sessions at s
|
|
|
893
989
|
}
|
|
894
990
|
});
|
|
895
991
|
// 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.", {
|
|
992
|
+
server.tool("bot_guard_bootstrap", "Advanced: Bot-specific bootstrap — 3-tier wake-up (Identity→Context→Details) with 800 token budget. Use for chatbots, not IDEs.", {
|
|
897
993
|
task: z.string().describe("Task description for the new session"),
|
|
898
994
|
limit: z.number().optional().describe("Max memories to include (default: 10)"),
|
|
899
995
|
mode: z.enum(["default", "deep"]).optional().describe("'default' = 800 token 3-tier, 'deep' = full context with L2 chunks"),
|
|
@@ -916,7 +1012,7 @@ server.tool("bot_guard_bootstrap", "Bot-specific bootstrap — 3-tier wake-up (I
|
|
|
916
1012
|
}
|
|
917
1013
|
});
|
|
918
1014
|
// bot_session_message — Rolling 3-session tracking (60 msg raw context)
|
|
919
|
-
server.tool("bot_session_message", "Track a message in the rolling session (rolling 3: keeps 60 messages raw in LLM context). Call on EVERY message (user + assistant). Returns rotate=true when session hits 20 messages. When should_compress=true, compress the oldest session via bot_session_compress.", {
|
|
1015
|
+
server.tool("bot_session_message", "Advanced: Track a message in the rolling session (rolling 3: keeps 60 messages raw in LLM context). Call on EVERY message (user + assistant). Returns rotate=true when session hits 20 messages. When should_compress=true, compress the oldest session via bot_session_compress.", {
|
|
920
1016
|
message: z.object({
|
|
921
1017
|
role: z.enum(["user", "assistant"]).describe("Message role"),
|
|
922
1018
|
content: z.string().describe("Message content"),
|
|
@@ -945,7 +1041,7 @@ server.tool("bot_session_message", "Track a message in the rolling session (roll
|
|
|
945
1041
|
}
|
|
946
1042
|
});
|
|
947
1043
|
// bot_guard_check — Bot-specific guard with spawn signal
|
|
948
|
-
server.tool("bot_guard_check", "Bot context guard — checks context pressure AND returns spawn signal. When should_spawn_new_session=true, bot should spawn a new session and compress the old one later. Use this instead of context_guard_check for bot/chatbot clients.", {
|
|
1044
|
+
server.tool("bot_guard_check", "Advanced: Bot context guard — checks context pressure AND returns spawn signal. When should_spawn_new_session=true, bot should spawn a new session and compress the old one later. Use this instead of context_guard_check for bot/chatbot clients.", {
|
|
949
1045
|
estimated_tokens: z.number().describe("Current token count in context window"),
|
|
950
1046
|
max_tokens: z.number().optional().describe("Max context window size (default: 200000)"),
|
|
951
1047
|
model: z.string().optional().describe("Model name for auto-detecting context window size"),
|
|
@@ -982,7 +1078,7 @@ server.tool("bot_guard_check", "Bot context guard — checks context pressure AN
|
|
|
982
1078
|
});
|
|
983
1079
|
// ── Self-Thinking Tools ──────────────────────────────────────────────
|
|
984
1080
|
// brain_thoughts — Get current active thoughts
|
|
985
|
-
server.tool("brain_thoughts", "Get the brain's current active thoughts — what it's thinking about autonomously.", {
|
|
1081
|
+
server.tool("brain_thoughts", "Advanced: Get the brain's current active thoughts — what it's thinking about autonomously.", {
|
|
986
1082
|
limit: z.number().optional().describe("Max thoughts to return (default: 10)"),
|
|
987
1083
|
}, async (args) => {
|
|
988
1084
|
try {
|
|
@@ -997,7 +1093,7 @@ server.tool("brain_thoughts", "Get the brain's current active thoughts — what
|
|
|
997
1093
|
}
|
|
998
1094
|
});
|
|
999
1095
|
// brain_think_about — Request brain to think about a topic
|
|
1000
|
-
server.tool("brain_think_about", "Request the brain to think about a specific topic. The brain will deliberate on it in its next thinking cycle.", {
|
|
1096
|
+
server.tool("brain_think_about", "Advanced: Request the brain to think about a specific topic. The brain will deliberate on it in its next thinking cycle.", {
|
|
1001
1097
|
topic: z.string().describe("What should the brain think about?"),
|
|
1002
1098
|
}, async (args) => {
|
|
1003
1099
|
try {
|
|
@@ -1009,7 +1105,7 @@ server.tool("brain_think_about", "Request the brain to think about a specific to
|
|
|
1009
1105
|
}
|
|
1010
1106
|
});
|
|
1011
1107
|
// brain_hypotheses — Get active hypotheses
|
|
1012
|
-
server.tool("brain_hypotheses", "Get hypotheses the brain is currently testing — predictions about user behavior patterns.", {
|
|
1108
|
+
server.tool("brain_hypotheses", "Advanced: Get hypotheses the brain is currently testing — predictions about user behavior patterns.", {
|
|
1013
1109
|
limit: z.number().optional().describe("Max hypotheses to return (default: 10)"),
|
|
1014
1110
|
}, async (args) => {
|
|
1015
1111
|
try {
|
|
@@ -1024,7 +1120,7 @@ server.tool("brain_hypotheses", "Get hypotheses the brain is currently testing
|
|
|
1024
1120
|
}
|
|
1025
1121
|
});
|
|
1026
1122
|
// brain_feedback — Rate a thought
|
|
1027
|
-
server.tool("brain_feedback", "Rate a thought as useful or not — helps the brain learn what's worth thinking about.", {
|
|
1123
|
+
server.tool("brain_feedback", "Advanced: Rate a thought as useful or not — helps the brain learn what's worth thinking about.", {
|
|
1028
1124
|
thought_id: z.number().describe("ID of the thought to rate"),
|
|
1029
1125
|
useful: z.boolean().describe("Was this thought useful?"),
|
|
1030
1126
|
}, async (args) => {
|
|
@@ -1040,7 +1136,7 @@ server.tool("brain_feedback", "Rate a thought as useful or not — helps the bra
|
|
|
1040
1136
|
}
|
|
1041
1137
|
});
|
|
1042
1138
|
// brain_thinking_stats — Budget and efficiency
|
|
1043
|
-
server.tool("brain_thinking_stats", "Get thinking system statistics — token budget, efficiency, queue size, and meta-cognition report.", {}, async () => {
|
|
1139
|
+
server.tool("brain_thinking_stats", "Advanced: Get thinking system statistics — token budget, efficiency, queue size, and meta-cognition report.", {}, async () => {
|
|
1044
1140
|
try {
|
|
1045
1141
|
const r = (await api("GET", "/v1/brain/thinking-stats"));
|
|
1046
1142
|
return ok(`Budget: ${r.budget.remaining_tokens} tokens remaining (limit: ${r.budget.limit_per_hour}/hr)\n` +
|
|
@@ -1057,7 +1153,7 @@ server.tool("brain_thinking_stats", "Get thinking system statistics — token bu
|
|
|
1057
1153
|
});
|
|
1058
1154
|
// ── Session Settings Tools ──────────────────────────────────────────
|
|
1059
1155
|
// memory_auto_extract — LLM-based fact extraction from conversation
|
|
1060
|
-
server.tool("memory_auto_extract", "CRITICAL: Call this at the END of every conversation session to extract and store important facts automatically. Uses LLM analysis to identify pricing, decisions, plans, technical details, and anything worth remembering. This is MORE reliable than manual memory_store because it catches things you might forget to store. ALWAYS call this before the conversation ends — especially after discussions about money, pricing, plans, decisions, or business.", {
|
|
1156
|
+
server.tool("memory_auto_extract", "Advanced: CRITICAL: Call this at the END of every conversation session to extract and store important facts automatically. Uses LLM analysis to identify pricing, decisions, plans, technical details, and anything worth remembering. This is MORE reliable than manual memory_store because it catches things you might forget to store. ALWAYS call this before the conversation ends — especially after discussions about money, pricing, plans, decisions, or business.", {
|
|
1061
1157
|
conversation: z.string().describe("The conversation text to extract facts from (include both user and assistant messages)"),
|
|
1062
1158
|
source: z.string().optional().describe("Source context (e.g. 'discord chat', 'slack thread')"),
|
|
1063
1159
|
store: z.boolean().optional().describe("Whether to store extracted facts (default: true). Set false to preview what would be extracted."),
|
|
@@ -1081,7 +1177,7 @@ server.tool("memory_auto_extract", "CRITICAL: Call this at the END of every conv
|
|
|
1081
1177
|
});
|
|
1082
1178
|
// ── IDE Upgrade Tools ──────────────────────────────────────────────
|
|
1083
1179
|
// memory_pitfall_check — Check pitfalls before risky actions
|
|
1084
|
-
server.tool("memory_pitfall_check", "IMPORTANT: Call this BEFORE executing risky actions (deploy, rm, git push, database changes). Returns known pitfalls (past failures + lessons) so you can avoid repeating mistakes. Pitfalls are DNA-protected and never expire.", {
|
|
1180
|
+
server.tool("memory_pitfall_check", "Advanced: IMPORTANT: Call this BEFORE executing risky actions (deploy, rm, git push, database changes). Returns known pitfalls (past failures + lessons) so you can avoid repeating mistakes. Pitfalls are DNA-protected and never expire.", {
|
|
1085
1181
|
intent: z.string().describe("What you're about to do (e.g. 'deploy to production', 'delete user table')"),
|
|
1086
1182
|
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
1087
1183
|
limit: z.number().optional().describe("Max results (default 5)"),
|
|
@@ -1104,7 +1200,7 @@ server.tool("memory_pitfall_check", "IMPORTANT: Call this BEFORE executing risky
|
|
|
1104
1200
|
}
|
|
1105
1201
|
});
|
|
1106
1202
|
// memory_plan_save — Save current plan/state for session resumption
|
|
1107
|
-
server.tool("memory_plan_save", "Save your current work state (plan steps, cursor position, active goals) so you can resume exactly where you left off in the next session. Call before session ends or when switching tasks.", {
|
|
1203
|
+
server.tool("memory_plan_save", "Advanced: Save your current work state (plan steps, cursor position, active goals) so you can resume exactly where you left off in the next session. Call before session ends or when switching tasks.", {
|
|
1108
1204
|
session_id: z.string().optional().describe("Session identifier (default: 'default')"),
|
|
1109
1205
|
state: z.record(z.string(), z.unknown()).describe("State to save: {plan: [...], cursor: 3, active_goal: '...', last_action: '...', files_read: [...]}"),
|
|
1110
1206
|
}, async (args) => {
|
|
@@ -1120,7 +1216,7 @@ server.tool("memory_plan_save", "Save your current work state (plan steps, curso
|
|
|
1120
1216
|
}
|
|
1121
1217
|
});
|
|
1122
1218
|
// memory_plan_resume — Restore saved state from previous session
|
|
1123
|
-
server.tool("memory_plan_resume", "Restore your work state from a previous session. Returns plan steps, cursor position, active goals — everything needed to continue where you left off.", {
|
|
1219
|
+
server.tool("memory_plan_resume", "Advanced: Restore your work state from a previous session. Returns plan steps, cursor position, active goals — everything needed to continue where you left off.", {
|
|
1124
1220
|
session_id: z.string().optional().describe("Session identifier (default: 'default')"),
|
|
1125
1221
|
}, async (args) => {
|
|
1126
1222
|
try {
|
|
@@ -1134,7 +1230,7 @@ server.tool("memory_plan_resume", "Restore your work state from a previous sessi
|
|
|
1134
1230
|
}
|
|
1135
1231
|
});
|
|
1136
1232
|
// memory_goal_track — Create/update/query goals
|
|
1137
|
-
server.tool("memory_goal_track", "Track goals across sessions. Create new goals, update progress, or query active goals. Goals with status='active' are DNA-protected (never decay).", {
|
|
1233
|
+
server.tool("memory_goal_track", "Advanced: Track goals across sessions. Create new goals, update progress, or query active goals. Goals with status='active' are DNA-protected (never decay).", {
|
|
1138
1234
|
action: z.enum(["create", "update", "list"]).describe("Action to perform"),
|
|
1139
1235
|
title: z.string().optional().describe("Goal title (for create)"),
|
|
1140
1236
|
progress: z.number().optional().describe("Progress 0.0-1.0 (for update)"),
|
|
@@ -1172,7 +1268,7 @@ server.tool("memory_goal_track", "Track goals across sessions. Create new goals,
|
|
|
1172
1268
|
}
|
|
1173
1269
|
});
|
|
1174
1270
|
// memory_thought_log — Query what the brain has been thinking about
|
|
1175
|
-
server.tool("memory_thought_log", "See what the brain has been thinking about autonomously. Returns recent thoughts, hypotheses, and insights generated during idle time.", {
|
|
1271
|
+
server.tool("memory_thought_log", "Advanced: See what the brain has been thinking about autonomously. Returns recent thoughts, hypotheses, and insights generated during idle time.", {
|
|
1176
1272
|
limit: z.number().optional().describe("Max thoughts to return (default 5)"),
|
|
1177
1273
|
}, async (args) => {
|
|
1178
1274
|
try {
|
|
@@ -1189,7 +1285,7 @@ server.tool("memory_thought_log", "See what the brain has been thinking about au
|
|
|
1189
1285
|
}
|
|
1190
1286
|
});
|
|
1191
1287
|
// memory_feedback — Report recall quality for self-improvement
|
|
1192
|
-
server.tool("memory_feedback", "Report whether recall results were helpful. This feeds the neuroplasticity system — over time, the brain learns what works for YOUR specific patterns and improves recall quality.", {
|
|
1288
|
+
server.tool("memory_feedback", "Advanced: Report whether recall results were helpful. This feeds the neuroplasticity system — over time, the brain learns what works for YOUR specific patterns and improves recall quality.", {
|
|
1193
1289
|
query: z.string().describe("The recall query that was made"),
|
|
1194
1290
|
chunk_ids: z.array(z.number()).describe("IDs of chunks that were returned"),
|
|
1195
1291
|
helpful: z.boolean().describe("Were the results helpful for your task?"),
|
|
@@ -1209,7 +1305,7 @@ server.tool("memory_feedback", "Report whether recall results were helpful. This
|
|
|
1209
1305
|
}
|
|
1210
1306
|
});
|
|
1211
1307
|
// memory_predict — Predictive recall (push intent, get predicted memories)
|
|
1212
|
-
server.tool("memory_predict", "Predictive recall — tell the brain what you're about to do and get relevant memories pre-loaded. Call this when you can anticipate what context will be needed next.", {
|
|
1308
|
+
server.tool("memory_predict", "Advanced: Predictive recall — tell the brain what you're about to do and get relevant memories pre-loaded. Call this when you can anticipate what context will be needed next.", {
|
|
1213
1309
|
intent: z.string().describe("What you/user are about to do"),
|
|
1214
1310
|
context: z.string().optional().describe("Current conversation context (helps prediction accuracy)"),
|
|
1215
1311
|
limit: z.number().optional().describe("Max predictions (default 5)"),
|
|
@@ -1232,7 +1328,7 @@ server.tool("memory_predict", "Predictive recall — tell the brain what you're
|
|
|
1232
1328
|
}
|
|
1233
1329
|
});
|
|
1234
1330
|
// memory_changelog — What changed since last session
|
|
1235
|
-
server.tool("memory_changelog", "See what changed in your memory since your last session. Shows new memories, updates, invalidations, and insights from overnight consolidation. Call at session start after bootstrap to understand what the brain learned while you were away.", {
|
|
1331
|
+
server.tool("memory_changelog", "Advanced: See what changed in your memory since your last session. Shows new memories, updates, invalidations, and insights from overnight consolidation. Call at session start after bootstrap to understand what the brain learned while you were away.", {
|
|
1236
1332
|
since: z.string().describe("ISO datetime — show changes after this time (e.g. '2026-05-20T10:00:00Z')"),
|
|
1237
1333
|
project_id: z.string().optional().describe("Filter to specific project"),
|
|
1238
1334
|
limit: z.number().optional().describe("Max changes to return (default 50)"),
|
|
@@ -1255,7 +1351,7 @@ server.tool("memory_changelog", "See what changed in your memory since your last
|
|
|
1255
1351
|
}
|
|
1256
1352
|
});
|
|
1257
1353
|
// memory_cognitive_profile — Complete self-model (metacognition)
|
|
1258
|
-
server.tool("memory_cognitive_profile", "Get the brain's complete self-model: who the user is, their mood, active goals, top entities (people/places), learned procedures, and recent topics. Use for complete context awareness. No LLM cost — pure aggregation (~50ms).", {}, async () => {
|
|
1354
|
+
server.tool("memory_cognitive_profile", "Advanced: Get the brain's complete self-model: who the user is, their mood, active goals, top entities (people/places), learned procedures, and recent topics. Use for complete context awareness. No LLM cost — pure aggregation (~50ms).", {}, async () => {
|
|
1259
1355
|
try {
|
|
1260
1356
|
const r = (await api("GET", "/v1/personality/cognitive-profile"));
|
|
1261
1357
|
let out = `## Cognitive Profile\n\n`;
|
|
@@ -1278,7 +1374,7 @@ server.tool("memory_cognitive_profile", "Get the brain's complete self-model: wh
|
|
|
1278
1374
|
}
|
|
1279
1375
|
});
|
|
1280
1376
|
// memory_entity_profile — Get everything known about an entity
|
|
1281
|
-
server.tool("memory_entity_profile", "Get complete profile for a specific entity (person, place, concept). Returns: frequency stats, linked memories, and relationships. Use when you need context about a specific person or topic the user has discussed.", {
|
|
1377
|
+
server.tool("memory_entity_profile", "Advanced: Get complete profile for a specific entity (person, place, concept). Returns: frequency stats, linked memories, and relationships. Use when you need context about a specific person or topic the user has discussed.", {
|
|
1282
1378
|
name: z.string().describe("Entity name to look up (e.g. 'Sarah', 'React', 'AuthService')"),
|
|
1283
1379
|
}, async (args) => {
|
|
1284
1380
|
try {
|
|
@@ -1316,7 +1412,7 @@ server.tool("memory_entity_profile", "Get complete profile for a specific entity
|
|
|
1316
1412
|
// • twin_respond / twin_status — Cognitive Twin (promax+ tier)
|
|
1317
1413
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1318
1414
|
// 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.", {
|
|
1415
|
+
server.tool("brain_export", "Advanced: 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
1416
|
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
1417
|
since: z.string().optional().describe("ISO8601 datetime, only used when scope='since'"),
|
|
1322
1418
|
}, async (args) => {
|
|
@@ -1341,7 +1437,7 @@ server.tool("brain_export", "Export the entire brain to a portable JSON bundle (
|
|
|
1341
1437
|
}
|
|
1342
1438
|
});
|
|
1343
1439
|
// 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).", {
|
|
1440
|
+
server.tool("brain_import", "Advanced: 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
1441
|
bundle: z.record(z.string(), z.unknown()).describe("The bundle JSON produced by brain_export (must contain format='memoryai-bundle', version, manifest, etc.)"),
|
|
1346
1442
|
keep_embeddings: z.boolean().optional().describe("Reuse bundle embeddings if model matches (default: true)"),
|
|
1347
1443
|
}, async (args) => {
|
|
@@ -1363,7 +1459,7 @@ server.tool("brain_import", "Import a MemoryAI bundle (from brain_export) into t
|
|
|
1363
1459
|
}
|
|
1364
1460
|
});
|
|
1365
1461
|
// 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.", {
|
|
1462
|
+
server.tool("benchmark_recall_vs_full", "Advanced: 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
1463
|
query: z.string().describe("The query to benchmark (e.g. 'what does the user prefer?')"),
|
|
1368
1464
|
model: z.string().optional().describe("Model whose pricing to apply (default: claude-opus-4-6). Affects $cost only."),
|
|
1369
1465
|
naive_budget_tokens: z.number().optional().describe("Cap on full-context dump (default: 200K = Claude window)"),
|
|
@@ -1390,7 +1486,7 @@ server.tool("benchmark_recall_vs_full", "Run a public benchmark: smart recall vs
|
|
|
1390
1486
|
}
|
|
1391
1487
|
});
|
|
1392
1488
|
// 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 () => {
|
|
1489
|
+
server.tool("benchmark_pricing", "Advanced: Get the assumed $/1M-input-tokens pricing for each LLM (used by benchmark_recall_vs_full). No auth required; list prices only.", {}, async () => {
|
|
1394
1490
|
try {
|
|
1395
1491
|
const r = (await api("GET", "/v1/benchmark/pricing"));
|
|
1396
1492
|
const lines = Object.entries(r.prices || {}).map(([k, v]) => `- ${k}: $${v}`);
|
|
@@ -1401,7 +1497,7 @@ server.tool("benchmark_pricing", "Get the assumed $/1M-input-tokens pricing for
|
|
|
1401
1497
|
}
|
|
1402
1498
|
});
|
|
1403
1499
|
// 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.", {
|
|
1500
|
+
server.tool("trust_agents", "Advanced: 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
1501
|
limit: z.number().optional().describe("Max agents to return (default: 50, max: 500)"),
|
|
1406
1502
|
}, async (args) => {
|
|
1407
1503
|
try {
|
|
@@ -1416,7 +1512,7 @@ server.tool("trust_agents", "Get the agent reputation leaderboard (sorted by rep
|
|
|
1416
1512
|
}
|
|
1417
1513
|
});
|
|
1418
1514
|
// 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.", {
|
|
1515
|
+
server.tool("trust_chunk", "Advanced: 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
1516
|
chunk_id: z.number().describe("The chunk ID returned by recall"),
|
|
1421
1517
|
}, async (args) => {
|
|
1422
1518
|
try {
|
|
@@ -1433,7 +1529,7 @@ server.tool("trust_chunk", "Get per-chunk trust info: which agent claimed it, th
|
|
|
1433
1529
|
}
|
|
1434
1530
|
});
|
|
1435
1531
|
// 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.", {
|
|
1532
|
+
server.tool("twin_respond", "Advanced: 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
1533
|
query: z.string().describe("The free-form question to ask the twin (e.g. 'what database for a chat app?')"),
|
|
1438
1534
|
operation: z.enum(["respond", "decide"]).optional().describe("'respond'=free-form answer (default), 'decide'=pick one option from the query"),
|
|
1439
1535
|
}, async (args) => {
|
|
@@ -1453,7 +1549,7 @@ server.tool("twin_respond", "Ask the Cognitive Twin to predict how the user woul
|
|
|
1453
1549
|
}
|
|
1454
1550
|
});
|
|
1455
1551
|
// 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 () => {
|
|
1552
|
+
server.tool("twin_status", "Advanced: 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
1553
|
try {
|
|
1458
1554
|
const r = (await api("GET", "/v1/twin/status"));
|
|
1459
1555
|
return ok(`Twin ready: ${r.ready ? "YES" : "NO"} (need ≥${r.min_dna_required} DNA, have ${r.dna_count})\n` +
|
|
@@ -1469,6 +1565,175 @@ server.tool("twin_status", "Check whether the Cognitive Twin is ready for the ca
|
|
|
1469
1565
|
return err(e);
|
|
1470
1566
|
}
|
|
1471
1567
|
});
|
|
1568
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1569
|
+
// v2.3 (billion-dollar foundation, 2026-05-29) — DNA-aligned tools:
|
|
1570
|
+
// • inherit_* — DNA #3 brain belongs to user, transferable when they die
|
|
1571
|
+
// • fed_* — DNA #1 ∞ agents share brain across organizations
|
|
1572
|
+
// • l2_inject — DNA #2 retina endpoint (compose context within budget)
|
|
1573
|
+
// • l2_status — cache + availability summary
|
|
1574
|
+
// • spec_info — public protocol contract (CC BY 4.0)
|
|
1575
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1576
|
+
// inherit_assign
|
|
1577
|
+
server.tool("inherit_assign", "Advanced: Assign an heir who inherits your brain when you trigger inheritance. DNA #3 — brain belongs to the user and is transferable. Idempotent on (owner, heir).", {
|
|
1578
|
+
heir_tenant_id: z.string().describe("Heir's tenant UUID"),
|
|
1579
|
+
share: z.number().optional().describe("Share percentage 0-100 (default: 100 if single heir)"),
|
|
1580
|
+
scope: z.enum(["all", "dna_only", "tagged"]).optional().describe("What to transfer (default: all)"),
|
|
1581
|
+
tag_filter: z.array(z.string()).optional().describe("Only transfer chunks with these tags (when scope=tagged)"),
|
|
1582
|
+
message: z.string().optional().describe("Optional letter to the heir, surfaced in their inbox"),
|
|
1583
|
+
}, async (args) => {
|
|
1584
|
+
try {
|
|
1585
|
+
const r = (await api("POST", "/v1/inheritance/heirs", args));
|
|
1586
|
+
return ok(`Heir assigned: ${r.heir_tenant_id} (share ${r.share}%, scope ${r.scope}). ${r.message_set ? "Message stored." : ""}`);
|
|
1587
|
+
}
|
|
1588
|
+
catch (e) {
|
|
1589
|
+
return err(e);
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
// inherit_list
|
|
1593
|
+
server.tool("inherit_list", "Advanced: List your assigned heirs and their share/scope. DNA #3 — your brain inheritance plan, viewable any time.", {}, async () => {
|
|
1594
|
+
try {
|
|
1595
|
+
const r = (await api("GET", "/v1/inheritance/heirs"));
|
|
1596
|
+
const heirs = r.heirs || [];
|
|
1597
|
+
if (heirs.length === 0)
|
|
1598
|
+
return ok("No heirs assigned yet.");
|
|
1599
|
+
const lines = heirs.map((h) => `- ${h.heir_tenant_id} · share=${h.share}% · scope=${h.scope} · status=${h.status} · added=${h.created_at?.slice(0, 10)}`);
|
|
1600
|
+
return ok(`Heirs (${heirs.length}):\n${lines.join("\n")}`);
|
|
1601
|
+
}
|
|
1602
|
+
catch (e) {
|
|
1603
|
+
return err(e);
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
// inherit_revoke
|
|
1607
|
+
server.tool("inherit_revoke", "Advanced: Revoke a previously assigned heir. Their access to your brain is removed; nothing is exported.", {
|
|
1608
|
+
heir_tenant_id: z.string().describe("Heir's tenant UUID to revoke"),
|
|
1609
|
+
}, async (args) => {
|
|
1610
|
+
try {
|
|
1611
|
+
await api("DELETE", `/v1/inheritance/heirs/${encodeURIComponent(args.heir_tenant_id)}`);
|
|
1612
|
+
return ok(`Heir revoked: ${args.heir_tenant_id}`);
|
|
1613
|
+
}
|
|
1614
|
+
catch (e) {
|
|
1615
|
+
return err(e);
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
// inherit_trigger
|
|
1619
|
+
server.tool("inherit_trigger", "Advanced: Trigger inheritance — exports your brain bundle and imports it into every accepted heir. Owner-only (heir-triggered inheritance is deferred to P2.3.full). DNA #3 — your brain becomes their brain.", {}, async () => {
|
|
1620
|
+
try {
|
|
1621
|
+
const r = (await api("POST", "/v1/inheritance/trigger", {}));
|
|
1622
|
+
const transfers = r.transfers || [];
|
|
1623
|
+
const summary = transfers.map((t) => `- ${t.heir_tenant_id}: ${t.status} (${t.chunks_inserted ?? 0} chunks transferred)`);
|
|
1624
|
+
return ok(`Inheritance triggered (${transfers.length} heirs):\n${summary.join("\n")}`);
|
|
1625
|
+
}
|
|
1626
|
+
catch (e) {
|
|
1627
|
+
return err(e);
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
// fed_grant
|
|
1631
|
+
server.tool("fed_grant", "Advanced: Grant a federated reader access to your brain. DNA #1 — multi-agent / cross-organization recall without copying. Optional tag filter, expiry, and DNA exclusion (DNA excluded by default for safety). Requires team+ plan.", {
|
|
1632
|
+
grantee_tenant_id: z.string().describe("Grantee's tenant UUID"),
|
|
1633
|
+
permission: z.enum(["read", "read_write"]).optional().describe("Permission (default: read)"),
|
|
1634
|
+
tag_filter: z.array(z.string()).optional().describe("Only expose chunks matching these tags"),
|
|
1635
|
+
include_dna: z.boolean().optional().describe("Allow grantee to recall DNA chunks (default: false)"),
|
|
1636
|
+
expires_at: z.string().optional().describe("ISO datetime expiry (default: no expiry)"),
|
|
1637
|
+
}, async (args) => {
|
|
1638
|
+
try {
|
|
1639
|
+
const r = (await api("POST", "/v1/federation/grants", args));
|
|
1640
|
+
return ok(`Federation grant created: ${r.grant_id} (grantee=${r.grantee_tenant_id}, permission=${r.permission}, expires=${r.expires_at || "never"})`);
|
|
1641
|
+
}
|
|
1642
|
+
catch (e) {
|
|
1643
|
+
return err(e);
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
// fed_revoke
|
|
1647
|
+
server.tool("fed_revoke", "Advanced: Revoke a federation grant. Only the grantor may revoke. Existing in-flight recalls remain audited.", {
|
|
1648
|
+
grant_id: z.string().describe("Grant UUID to revoke"),
|
|
1649
|
+
}, async (args) => {
|
|
1650
|
+
try {
|
|
1651
|
+
await api("DELETE", `/v1/federation/grants/${encodeURIComponent(args.grant_id)}`);
|
|
1652
|
+
return ok(`Federation grant revoked: ${args.grant_id}`);
|
|
1653
|
+
}
|
|
1654
|
+
catch (e) {
|
|
1655
|
+
return err(e);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
// fed_inbox
|
|
1659
|
+
server.tool("fed_inbox", "Advanced: List federation grants others gave you (your federated inbox). Each grant lets you recall against another tenant's brain via fed_recall.", {}, async () => {
|
|
1660
|
+
try {
|
|
1661
|
+
const r = (await api("GET", "/v1/federation/inbox"));
|
|
1662
|
+
const grants = r.grants || [];
|
|
1663
|
+
if (grants.length === 0)
|
|
1664
|
+
return ok("Federation inbox empty.");
|
|
1665
|
+
const lines = grants.map((g) => `- grant=${g.grant_id} · grantor=${g.grantor_tenant_id} · perm=${g.permission} · tags=${(g.tag_filter || []).join(",") || "(any)"} · expires=${g.expires_at || "never"}`);
|
|
1666
|
+
return ok(`Federated grants you can use (${grants.length}):\n${lines.join("\n")}`);
|
|
1667
|
+
}
|
|
1668
|
+
catch (e) {
|
|
1669
|
+
return err(e);
|
|
1670
|
+
}
|
|
1671
|
+
});
|
|
1672
|
+
// fed_recall
|
|
1673
|
+
server.tool("fed_recall", "Advanced: Recall against a federated brain you have access to. Server enforces tag_filter, DNA exclusion, expiry, and audits every call back to the grantor.", {
|
|
1674
|
+
grant_id: z.string().describe("Grant UUID from fed_inbox"),
|
|
1675
|
+
query: z.string().describe("Recall query"),
|
|
1676
|
+
}, async (args) => {
|
|
1677
|
+
try {
|
|
1678
|
+
const r = (await api("POST", "/v1/federation/recall", args));
|
|
1679
|
+
const results = r.results || [];
|
|
1680
|
+
if (results.length === 0)
|
|
1681
|
+
return ok("No federated results.");
|
|
1682
|
+
const lines = results.slice(0, 10).map((m) => `[${m.score?.toFixed(2)}] ${m.content?.slice(0, 200)}`);
|
|
1683
|
+
return ok(`Federated recall (${results.length} results, audited to grantor):\n${lines.join("\n\n")}`);
|
|
1684
|
+
}
|
|
1685
|
+
catch (e) {
|
|
1686
|
+
return err(e);
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
// l2_inject — DNA #2 retina endpoint
|
|
1690
|
+
server.tool("l2_inject", "Compose a context block for your prompt within a token budget. DNA #2 — top model thinks with 100K context, L2 holds 100M memory; this tool is the bridge. Pipeline: L4 cache → DNA → pre-reasoned L2 → L3 4-way recall → compress within budget. Latency p95 < 200ms (cache hit), < 800ms (miss). Returns a markdown block ready to paste into your system prompt.", {
|
|
1691
|
+
query: z.string().describe("What you're about to ask the top model"),
|
|
1692
|
+
context_budget_tokens: z.number().optional().describe("Max tokens in returned context (default: 2000)"),
|
|
1693
|
+
model_hint: z.string().optional().describe("Model name (e.g. 'claude-opus-4-7') for token-counting accuracy"),
|
|
1694
|
+
freshness_priority: z.enum(["balanced", "recent", "deep"]).optional().describe("Bias toward freshness vs depth (default: balanced)"),
|
|
1695
|
+
bypass_cache: z.boolean().optional().describe("Skip L4 cache lookup (default: false)"),
|
|
1696
|
+
}, async (args) => {
|
|
1697
|
+
try {
|
|
1698
|
+
const r = (await api("POST", "/v1/l2/inject", args));
|
|
1699
|
+
const out = `L2 inject — ${r.tokens_used}/${r.tokens_budget} tokens, ${r.sources?.length ?? 0} sources, cache_hit=${r.cache_hit}, ${r.latency_ms}ms\n\n${r.context_block}`;
|
|
1700
|
+
return ok(out);
|
|
1701
|
+
}
|
|
1702
|
+
catch (e) {
|
|
1703
|
+
return err(e);
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
// l2_status — DNA #2
|
|
1707
|
+
server.tool("l2_status", "Check L2 readiness: cached entries, pre-reasoned chunks, DNA chunks. Cheap — no LLM call. Useful before deciding whether to call l2_inject vs raw recall.", {}, async () => {
|
|
1708
|
+
try {
|
|
1709
|
+
const r = (await api("GET", "/v1/l2/status"));
|
|
1710
|
+
return ok(`L2 status:\n` +
|
|
1711
|
+
`- cached_entries: ${r.cached_entries}\n` +
|
|
1712
|
+
`- pre_reasoned_chunks: ${r.pre_reasoned_chunks}\n` +
|
|
1713
|
+
`- dna_chunks: ${r.dna_chunks}`);
|
|
1714
|
+
}
|
|
1715
|
+
catch (e) {
|
|
1716
|
+
return err(e);
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
// spec_info — DNA #3 protocol
|
|
1720
|
+
server.tool("spec_info", "Advanced: Get the MemoryAI Protocol v1 contract (machine-readable summary). DNA #3 — vendor-neutral standard. Use this when interoperating with other MemoryAI-compatible implementations.", {}, async () => {
|
|
1721
|
+
try {
|
|
1722
|
+
const r = (await api("GET", "/v1/spec/info"));
|
|
1723
|
+
const lines = [
|
|
1724
|
+
`Protocol: ${r.format} v${r.version}`,
|
|
1725
|
+
`License: ${r.license}`,
|
|
1726
|
+
`Conformance levels: ${(r.conformance_levels || []).join(", ")}`,
|
|
1727
|
+
`DNA types: ${(r.dna_memory_types || []).join(", ")}`,
|
|
1728
|
+
`Embedding model: ${r.embedding_model} (${r.embedding_dim} dims)`,
|
|
1729
|
+
`Bundle endpoints: ${(r.endpoints || []).join(", ")}`,
|
|
1730
|
+
];
|
|
1731
|
+
return ok(lines.join("\n"));
|
|
1732
|
+
}
|
|
1733
|
+
catch (e) {
|
|
1734
|
+
return err(e);
|
|
1735
|
+
}
|
|
1736
|
+
});
|
|
1472
1737
|
async function main() {
|
|
1473
1738
|
const transport = new StdioServerTransport();
|
|
1474
1739
|
await server.connect(transport);
|