memoryai-mcp 0.9.0 → 2.1.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 +188 -55
- package/dist/index.js +493 -4
- package/dist/kiro-setup.d.ts +7 -0
- package/dist/kiro-setup.js +115 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
# memoryai-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for [MemoryAI](https://memoryai.dev) — a brain for your AI agent.
|
|
3
|
+
MCP server for [MemoryAI](https://memoryai.dev) — a living brain for your AI agent.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Your AI agent gets persistent memory that works like a real brain:
|
|
6
|
+
- Remembers what matters, forgets what doesn't
|
|
7
|
+
- Strengthens memories you use often (Hebbian learning)
|
|
8
|
+
- Consolidates knowledge while idle (Sleep cycles)
|
|
9
|
+
- Protects core identity (DNA memories never fade)
|
|
10
|
+
- Adapts to your emotional state
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
- 🔍 **Deep Search** — Scans memory by meaning, not just keywords.
|
|
9
|
-
- 🧠 **Reasoning** — Connects the dots across memories, synthesizes precise answers. *(Pro)*
|
|
10
|
-
- 📦 **Archive** — Compressed long-term storage. Nothing truly forgotten.
|
|
12
|
+
**Install once. Everything auto from there.**
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
## Quick Start (2 minutes)
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
### 1. Get an API Key
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
|
-
|
|
19
|
+
curl -X POST https://memoryai.dev/v1/admin/provision \
|
|
20
|
+
-H "Content-Type: application/json" \
|
|
21
|
+
-d '{"name": "my-agent", "tos_accepted": true}'
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
Save the `api_key` from the response. You'll need it below.
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
### 2. Pick Your IDE/Tool
|
|
27
|
+
|
|
28
|
+
Choose your platform below. Config once — memory works automatically forever.
|
|
29
|
+
|
|
30
|
+
---
|
|
25
31
|
|
|
26
32
|
## IDE Setup
|
|
27
33
|
|
|
28
|
-
###
|
|
34
|
+
### Claude Code (CLI) — `~/.claude/settings.json`
|
|
29
35
|
|
|
30
36
|
```json
|
|
31
37
|
{
|
|
@@ -42,7 +48,42 @@ npm install -g memoryai-mcp
|
|
|
42
48
|
}
|
|
43
49
|
```
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
Then create `~/.claude/CLAUDE.md` for auto-bootstrap:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
## Memory Protocol
|
|
55
|
+
At the start of every conversation, call `memory_bootstrap` to load context from MemoryAI.
|
|
56
|
+
Before context gets large (>100K tokens), call `memory_compact` to save important context.
|
|
57
|
+
Use `memory_store` to save important decisions, preferences, and facts.
|
|
58
|
+
Use `memory_recall` to search past memories when relevant.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Cursor — `~/.cursor/mcp.json`
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"memoryai": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "memoryai-mcp"],
|
|
69
|
+
"env": {
|
|
70
|
+
"HM_ENDPOINT": "https://memoryai.dev",
|
|
71
|
+
"HM_API_KEY": "hm_sk_your_key_here"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Auto-bootstrap — create `.cursor/rules/memoryai.mdc`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
At the start of every session, call memory_bootstrap to load context.
|
|
82
|
+
After completing tasks, call memory_compact to save context.
|
|
83
|
+
Store important decisions and preferences with memory_store.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### VS Code — `.vscode/mcp.json`
|
|
46
87
|
|
|
47
88
|
```json
|
|
48
89
|
{
|
|
@@ -59,7 +100,7 @@ npm install -g memoryai-mcp
|
|
|
59
100
|
}
|
|
60
101
|
```
|
|
61
102
|
|
|
62
|
-
###
|
|
103
|
+
### Kiro — `.kiro/settings/mcp.json`
|
|
63
104
|
|
|
64
105
|
```json
|
|
65
106
|
{
|
|
@@ -76,7 +117,7 @@ npm install -g memoryai-mcp
|
|
|
76
117
|
}
|
|
77
118
|
```
|
|
78
119
|
|
|
79
|
-
### Windsurf
|
|
120
|
+
### Windsurf — `~/.codeium/windsurf/mcp_config.json`
|
|
80
121
|
|
|
81
122
|
```json
|
|
82
123
|
{
|
|
@@ -93,7 +134,10 @@ npm install -g memoryai-mcp
|
|
|
93
134
|
}
|
|
94
135
|
```
|
|
95
136
|
|
|
96
|
-
###
|
|
137
|
+
### Claude Desktop — `claude_desktop_config.json`
|
|
138
|
+
|
|
139
|
+
macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
140
|
+
Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
97
141
|
|
|
98
142
|
```json
|
|
99
143
|
{
|
|
@@ -110,60 +154,149 @@ npm install -g memoryai-mcp
|
|
|
110
154
|
}
|
|
111
155
|
```
|
|
112
156
|
|
|
113
|
-
###
|
|
157
|
+
### Antigravity — `~/.antigravity/mcp.json`
|
|
114
158
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
memoryai:
|
|
119
|
-
command: npx
|
|
120
|
-
args: ["-y", "memoryai-mcp"]
|
|
121
|
-
env:
|
|
122
|
-
HM_ENDPOINT: "https://memoryai.dev"
|
|
123
|
-
HM_API_KEY: "
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"memoryai": {
|
|
163
|
+
"command": "npx",
|
|
164
|
+
"args": ["-y", "memoryai-mcp"],
|
|
165
|
+
"env": {
|
|
166
|
+
"HM_ENDPOINT": "https://memoryai.dev",
|
|
167
|
+
"HM_API_KEY": "hm_sk_your_key_here"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
124
172
|
```
|
|
125
173
|
|
|
126
|
-
|
|
174
|
+
### Any MCP-compatible tool
|
|
127
175
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"mcpServers": {
|
|
179
|
+
"memoryai": {
|
|
180
|
+
"command": "npx",
|
|
181
|
+
"args": ["-y", "memoryai-mcp"],
|
|
182
|
+
"env": {
|
|
183
|
+
"HM_ENDPOINT": "https://memoryai.dev",
|
|
184
|
+
"HM_API_KEY": "hm_sk_your_key_here"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Auto-Bootstrap (Make It Fully Automatic)
|
|
142
194
|
|
|
143
|
-
|
|
195
|
+
After MCP config, add a system rule so your agent auto-loads memory every session:
|
|
144
196
|
|
|
145
|
-
|
|
197
|
+
| Platform | Rule File | Content |
|
|
198
|
+
|----------|-----------|---------|
|
|
199
|
+
| Claude Code | `~/.claude/CLAUDE.md` | See above |
|
|
200
|
+
| Cursor | `.cursor/rules/memoryai.mdc` | See above |
|
|
201
|
+
| Kiro | `.kiro/rules/memoryai.md` | Same content |
|
|
202
|
+
| Windsurf | `.windsurfrules` | Same content |
|
|
203
|
+
| VS Code | `.github/copilot-instructions.md` | Same content |
|
|
204
|
+
|
|
205
|
+
Rule content (copy-paste for any platform):
|
|
146
206
|
|
|
147
|
-
**Cursor** (`.cursor/rules/memoryai.mdc`):
|
|
148
207
|
```
|
|
149
|
-
|
|
150
|
-
|
|
208
|
+
## Memory Protocol
|
|
209
|
+
At the start of every conversation, call `memory_bootstrap` to load context from MemoryAI.
|
|
210
|
+
Before context gets large, call `memory_compact` to save important context.
|
|
211
|
+
Use `memory_store` to save important decisions, preferences, and facts.
|
|
212
|
+
Use `memory_recall` to search past memories when relevant.
|
|
151
213
|
```
|
|
152
214
|
|
|
153
|
-
|
|
215
|
+
---
|
|
154
216
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
217
|
+
## How It Works
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
Open IDE → MCP auto-connects → Agent reads rules → Calls bootstrap
|
|
221
|
+
→ Loads your identity, preferences, recent work
|
|
222
|
+
→ Works normally (auto-stores important stuff)
|
|
223
|
+
→ Context getting full? Auto-compacts
|
|
224
|
+
→ Close IDE → Sleep workers consolidate overnight
|
|
225
|
+
→ Open IDE next day → Bootstrap loads everything back
|
|
226
|
+
→ Cycle repeats. Memory grows smarter over time.
|
|
159
227
|
```
|
|
160
228
|
|
|
161
|
-
|
|
229
|
+
**You do nothing.** The agent handles everything automatically.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Tools Available
|
|
234
|
+
|
|
235
|
+
| Tool | What It Does |
|
|
236
|
+
|------|-------------|
|
|
237
|
+
| `memory_bootstrap` | Wake up with full context (identity + recent + preferences) |
|
|
238
|
+
| `memory_store` | Save a memory (fact, decision, preference, identity) |
|
|
239
|
+
| `memory_recall` | Search memories by meaning (semantic + graph + FTS) |
|
|
240
|
+
| `memory_compact` | Save conversation context before it's lost |
|
|
241
|
+
| `memory_recover` | Recover session after a break |
|
|
242
|
+
| `memory_health` | Check context pressure (safe/warning/critical) |
|
|
243
|
+
| `memory_explore` | Explore connections between memories |
|
|
244
|
+
| `memory_clusters` | View topic clusters in your knowledge graph |
|
|
245
|
+
| `learn` | Store action + result + lesson learned |
|
|
246
|
+
| `entity_list` | List tracked entities (files, people, packages) |
|
|
247
|
+
| `reasoning_store` | Deep reasoning memory (Pro+) |
|
|
248
|
+
| `reasoning_recall` | Recall reasoned insights (Pro+) |
|
|
249
|
+
| `snapshot_create` | Backup memory state |
|
|
250
|
+
| `snapshot_restore` | Restore from backup |
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Context Guard (Built-in)
|
|
255
|
+
|
|
256
|
+
Context Guard monitors your session and prevents context loss:
|
|
257
|
+
|
|
258
|
+
| State | Meaning | Agent Action |
|
|
259
|
+
|-------|---------|-------------|
|
|
260
|
+
| SAFE | Context < 40% full | Continue normally |
|
|
261
|
+
| COMPACT_SOON | Context 40-55% full | Prepare to compact |
|
|
262
|
+
| COMPACT_NOW | Context > 55% full | Must compact immediately |
|
|
263
|
+
|
|
264
|
+
The agent handles this automatically when rules are configured. No manual intervention needed.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## What Gets Remembered (DNA System)
|
|
269
|
+
|
|
270
|
+
| Memory Type | Example | Persistence |
|
|
271
|
+
|-------------|---------|-------------|
|
|
272
|
+
| `preference` | "I prefer Python over Java" | **Forever** (DNA-protected) |
|
|
273
|
+
| `decision` | "Chose PostgreSQL for this project" | **Forever** (DNA-protected) |
|
|
274
|
+
| `identity` | "Senior backend engineer, 10 years" | **Forever** (DNA-protected) |
|
|
275
|
+
| `fact` | "API endpoint is /v1/users" | Decays if unused |
|
|
276
|
+
| `goal` | "Launch v2.0 by June" | Decays if unused |
|
|
277
|
+
|
|
278
|
+
DNA memories (preference/decision/identity) **never decay, never get deleted, never get overwritten** by any background process. They define who you are.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Pricing
|
|
283
|
+
|
|
284
|
+
| Plan | Features | Price |
|
|
285
|
+
|------|----------|-------|
|
|
286
|
+
| Free | Basic store/recall, 100 memories | Free |
|
|
287
|
+
| Pro | Full brain (reasoning, consolidation, personality) | Paid |
|
|
288
|
+
| ProMax | Multi-agent mesh, advanced features | Paid |
|
|
289
|
+
| God | Everything + deep graph traversal | Internal |
|
|
290
|
+
|
|
291
|
+
Get started free: https://memoryai.dev
|
|
292
|
+
|
|
293
|
+
---
|
|
162
294
|
|
|
163
295
|
## Links
|
|
164
296
|
|
|
165
297
|
- Website: https://memoryai.dev
|
|
166
|
-
- Python SDK:
|
|
298
|
+
- Python SDK: `pip install hmc-memory`
|
|
299
|
+
- npm MCP: `npx memoryai-mcp`
|
|
167
300
|
- GitHub: https://github.com/memoryai-dev/memoryai
|
|
168
301
|
|
|
169
302
|
## License
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,10 @@ import { z } from "zod";
|
|
|
11
11
|
const API_URL = process.env.HM_ENDPOINT || "http://localhost:8420";
|
|
12
12
|
const 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
|
+
const CG_CONTEXT_CAP = parseInt(process.env.HM_CONTEXT_CAP || "0", 10); // IDE's context window (0 = let server detect)
|
|
16
|
+
const CG_COMPACT_PCT = parseInt(process.env.HM_COMPACT_AT || "0", 10); // % to warn (e.g. 30 = 30%)
|
|
17
|
+
const CG_CRITICAL_PCT = parseInt(process.env.HM_CRITICAL_AT || "0", 10); // % to force compact (e.g. 50 = 50%)
|
|
14
18
|
// --- HTTP helper ---
|
|
15
19
|
async function api(method, path, body) {
|
|
16
20
|
const resp = await fetch(`${API_URL}${path}`, {
|
|
@@ -36,19 +40,21 @@ function err(e) {
|
|
|
36
40
|
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
37
41
|
}
|
|
38
42
|
// --- MCP Server ---
|
|
39
|
-
const server = new McpServer({ name: "memoryai", version: "0.
|
|
43
|
+
const server = new McpServer({ name: "memoryai", version: "0.9.0" }, { capabilities: { tools: {} } });
|
|
40
44
|
// 1. memory_store
|
|
41
|
-
server.tool("memory_store", "Store information in persistent memory. Use when you learn something important — project context, user preferences, architectural decisions, patterns, or
|
|
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.", {
|
|
42
46
|
content: z.string().describe("What to remember"),
|
|
43
47
|
source: z.string().optional().describe("Source context (e.g. file path, conversation)"),
|
|
44
48
|
tags: z.array(z.string()).optional().describe("Categories: preferences, architecture, bugs, patterns, decisions"),
|
|
45
49
|
priority: z.enum(["hot", "warm", "cold"]).optional().describe("Memory priority (default: warm)"),
|
|
46
|
-
memory_type: z.enum(["fact", "decision", "preference", "error", "goal", "episodic", "identity"]).optional().describe("Memory type. 'preference', 'decision', 'identity' are DNA-protected — never decay, 1.5x recall boost. Default: fact"),
|
|
50
|
+
memory_type: z.enum(["fact", "decision", "preference", "error", "goal", "episodic", "identity", "pitfall", "life_event", "procedure"]).optional().describe("Memory type. 'preference', 'decision', 'identity', 'procedure' are DNA-protected — never decay, 1.5x recall boost. 'pitfall' for failure memories. 'procedure' for learned workflows/steps. Default: fact"),
|
|
47
51
|
retention: z.enum(["auto", "forever", "6m", "1y"]).optional().describe("Retention policy. 'forever' = never deleted. Default: auto"),
|
|
48
52
|
content_type: z.enum(["conversation", "code", "decision", "preference", "architecture", "lesson_learned", "todo", "entity", "pattern", "environment", "bug_fix", "action_log"]).optional().describe("Content type — helps with filtering and recall accuracy"),
|
|
49
53
|
metadata: z.record(z.string(), z.unknown()).optional().describe("Additional metadata (JSONB)"),
|
|
50
54
|
zone: z.enum(["critical", "important", "standard", "ephemeral"]).optional().describe("Memory zone (default: standard). critical=never evict, ephemeral=auto-expire"),
|
|
51
55
|
importance: z.number().min(0).max(1).optional().describe("Importance score 0.0-1.0 (default: 0.5). Higher = slower decay, prioritized in recall"),
|
|
56
|
+
project_id: z.string().optional().describe("Scope memory to a project/workspace. DNA memories (preference/decision/identity/pitfall) are always cross-project visible."),
|
|
57
|
+
thread_id: z.string().optional().describe("Scope memory to a conversation thread. Memories without thread_id are visible in all threads. Use for parallel topics (e.g. 'relationship', 'career')."),
|
|
52
58
|
}, async (args) => {
|
|
53
59
|
try {
|
|
54
60
|
const r = (await api("POST", "/v1/store", {
|
|
@@ -62,6 +68,8 @@ server.tool("memory_store", "Store information in persistent memory. Use when yo
|
|
|
62
68
|
metadata: args.metadata,
|
|
63
69
|
zone: args.zone || "standard",
|
|
64
70
|
importance: args.importance ?? 0.5,
|
|
71
|
+
project_id: args.project_id,
|
|
72
|
+
thread_id: args.thread_id,
|
|
65
73
|
}));
|
|
66
74
|
let msg = `Stored (id=${r.id}, type=${args.memory_type || "fact"})`;
|
|
67
75
|
if (r.deduplicated) {
|
|
@@ -81,12 +89,14 @@ server.tool("memory_store", "Store information in persistent memory. Use when yo
|
|
|
81
89
|
// 2. memory_recall
|
|
82
90
|
server.tool("memory_recall", "Search persistent memory for relevant context. Use before starting work to check what you already know about the project or task.", {
|
|
83
91
|
query: z.string().describe("What to search for"),
|
|
84
|
-
depth: z.enum(["fast", "deep", "exhaustive"]).optional().describe("Search depth (default
|
|
92
|
+
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"),
|
|
85
93
|
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
86
94
|
min_score: z.number().optional().describe("Minimum relevance score 0-1 (default: 0)"),
|
|
87
95
|
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
88
96
|
max_tokens: z.number().optional().describe("Token budget limit — results truncated to fit within this budget"),
|
|
89
97
|
priority_min: z.enum(["critical", "important", "standard", "ephemeral"]).optional().describe("Minimum zone priority filter (default: all zones)"),
|
|
98
|
+
project_id: z.string().optional().describe("Scope recall to a project/workspace. DNA memories are always visible cross-project."),
|
|
99
|
+
thread_id: z.string().optional().describe("Scope recall to a conversation thread. Memories without thread_id are always visible."),
|
|
90
100
|
}, async (args) => {
|
|
91
101
|
try {
|
|
92
102
|
const body = {
|
|
@@ -100,6 +110,10 @@ server.tool("memory_recall", "Search persistent memory for relevant context. Use
|
|
|
100
110
|
body.max_tokens = args.max_tokens;
|
|
101
111
|
if (args.priority_min)
|
|
102
112
|
body.priority_min = args.priority_min;
|
|
113
|
+
if (args.project_id)
|
|
114
|
+
body.project_id = args.project_id;
|
|
115
|
+
if (args.thread_id)
|
|
116
|
+
body.thread_id = args.thread_id;
|
|
103
117
|
const r = (await api("POST", "/v1/recall", body));
|
|
104
118
|
if (!r.results?.length)
|
|
105
119
|
return ok("No relevant memories found.");
|
|
@@ -800,6 +814,481 @@ server.tool("session_handoff_status", "Check current session handoff status —
|
|
|
800
814
|
return err(e);
|
|
801
815
|
}
|
|
802
816
|
});
|
|
817
|
+
// ─── Context Guard v6 Tools ─────────────────────────────────────────
|
|
818
|
+
// context_guard_check — universal guard check with DNA count
|
|
819
|
+
server.tool("context_guard_check", "Check context window health using Context Guard v6 — dynamic thresholds, DNA memory count, bootstrap readiness. Replaces memory_health with richer data.", {
|
|
820
|
+
estimated_tokens: z.number().describe("Current token count in context window"),
|
|
821
|
+
max_tokens: z.number().optional().describe("Max context window size (uses HM_CONTEXT_CAP env if omitted)"),
|
|
822
|
+
model: z.string().optional().describe("Model name for auto-detecting context window size (e.g. claude-sonnet-4-6)"),
|
|
823
|
+
}, async (args) => {
|
|
824
|
+
try {
|
|
825
|
+
// Use env var HM_CONTEXT_CAP as default if max_tokens not provided
|
|
826
|
+
const maxTokens = args.max_tokens || CG_CONTEXT_CAP || 0;
|
|
827
|
+
const payload = {
|
|
828
|
+
estimated_tokens: args.estimated_tokens,
|
|
829
|
+
max_tokens: maxTokens,
|
|
830
|
+
model: args.model || null,
|
|
831
|
+
};
|
|
832
|
+
// Send per-IDE threshold overrides if configured via env vars
|
|
833
|
+
if (CG_COMPACT_PCT > 0)
|
|
834
|
+
payload.compact_pct = CG_COMPACT_PCT / 100;
|
|
835
|
+
if (CG_CRITICAL_PCT > 0)
|
|
836
|
+
payload.critical_pct = CG_CRITICAL_PCT / 100;
|
|
837
|
+
const r = (await api("POST", "/v1/context/guard/check", payload));
|
|
838
|
+
const pct = r.usage_percent;
|
|
839
|
+
const barLen = 20;
|
|
840
|
+
const filled = Math.round(pct / 100 * barLen);
|
|
841
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barLen - filled);
|
|
842
|
+
return ok(`Context Guard v6:\n` +
|
|
843
|
+
`[${bar}] ${pct.toFixed(1)}%\n` +
|
|
844
|
+
`Recommendation: ${r.recommendation.toUpperCase()}${r.should_compact ? " — compact now" : ""}\n` +
|
|
845
|
+
`Urgency: ${r.urgency}\n` +
|
|
846
|
+
`Thresholds: compact=${r.compact_at_tokens.toLocaleString()}, critical=${r.critical_at_tokens.toLocaleString()}\n` +
|
|
847
|
+
`DNA memories: ${r.dna_memories} | Hot: ${r.hot_memories} | Stale: ${r.stale_memories}\n` +
|
|
848
|
+
`Bootstrap ready: ${r.bootstrap_ready ? "yes" : "no"}\n` +
|
|
849
|
+
(r.last_compact_minutes_ago != null ? `Last compact: ${r.last_compact_minutes_ago.toFixed(0)} min ago` : "No compacts yet"));
|
|
850
|
+
}
|
|
851
|
+
catch (e) {
|
|
852
|
+
return err(e);
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
// context_guard_compact — compact with DNA protection
|
|
856
|
+
server.tool("context_guard_compact", "Compact session context with DNA protection — DNA memories are never overwritten. IMPORTANT: Send a REAL summary of the conversation (>500 chars) including topics discussed, decisions made, key numbers/facts, and current status. Do NOT send just a status string like 'context guard - 132%'. If you send useless content, the server will use its internal buffer as fallback, but a good summary from you produces better memories.", {
|
|
857
|
+
content: z.string().describe("Conversation summary — include topics, decisions, key facts, numbers. Must be >500 chars of real content."),
|
|
858
|
+
task_context: z.string().optional().describe("Task description for tagging"),
|
|
859
|
+
blocking: z.boolean().optional().describe("Wait for result (true) or return task_id (false, default)"),
|
|
860
|
+
}, async (args) => {
|
|
861
|
+
try {
|
|
862
|
+
const r = (await api("POST", "/v1/context/guard/compact", {
|
|
863
|
+
content: args.content,
|
|
864
|
+
task_context: args.task_context || null,
|
|
865
|
+
blocking: args.blocking || false,
|
|
866
|
+
}));
|
|
867
|
+
if (r.status === "queued") {
|
|
868
|
+
return ok(`Compact queued (task_id=${r.task_id}). Poll with guard_status.`);
|
|
869
|
+
}
|
|
870
|
+
return ok(`Compact ${r.status}: ${r.chunks_created} chunks stored, ${r.chunks_deduplicated} deduplicated.\n` +
|
|
871
|
+
r.message);
|
|
872
|
+
}
|
|
873
|
+
catch (e) {
|
|
874
|
+
return err(e);
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
// context_guard_bootstrap — DNA-first session bootstrap
|
|
878
|
+
server.tool("context_guard_bootstrap", "Bootstrap a new session with DNA-first context — identity/preferences first, then recent activity, then task-relevant memories. For BOT clients: uses 3-tier wake-up (800 tokens). For IDE: flat layout (~4000 tokens).", {
|
|
879
|
+
task: z.string().describe("Task description for the new session"),
|
|
880
|
+
limit: z.number().optional().describe("Max memories to include (default: 10)"),
|
|
881
|
+
mode: z.enum(["default", "deep"]).optional().describe("'default' = 800 token 3-tier wake-up, 'deep' = full context with L2 chunks"),
|
|
882
|
+
token_budget: z.number().optional().describe("Token budget for bootstrap (default: 800 for bot, 4000 for IDE)"),
|
|
883
|
+
}, async (args) => {
|
|
884
|
+
try {
|
|
885
|
+
const r = (await api("POST", "/v1/bot/guard/bootstrap", {
|
|
886
|
+
task: args.task,
|
|
887
|
+
limit: args.limit || 10,
|
|
888
|
+
mode: args.mode || "default",
|
|
889
|
+
token_budget: args.token_budget,
|
|
890
|
+
}));
|
|
891
|
+
return ok(`Bootstrap complete: ${r.memories_included} memories\n` +
|
|
892
|
+
`Tokens used: ${r.tokens_used}\n` +
|
|
893
|
+
`L2 sessions: ${r.l2_sessions_included || 0}\n\n` +
|
|
894
|
+
r.context_block);
|
|
895
|
+
}
|
|
896
|
+
catch (e) {
|
|
897
|
+
return err(e);
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
// bot_session_message — Rolling 3-session tracking (60 msg raw context)
|
|
901
|
+
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.", {
|
|
902
|
+
message: z.object({
|
|
903
|
+
role: z.enum(["user", "assistant"]).describe("Message role"),
|
|
904
|
+
content: z.string().describe("Message content"),
|
|
905
|
+
}).describe("The message to track"),
|
|
906
|
+
rotation_size: z.number().optional().describe("Messages per session before rotation (default: 20, range: 5-50)"),
|
|
907
|
+
}, async (args) => {
|
|
908
|
+
try {
|
|
909
|
+
const r = (await api("POST", "/v1/bot/session/message", {
|
|
910
|
+
message: args.message,
|
|
911
|
+
rotation_size: args.rotation_size || 20,
|
|
912
|
+
}));
|
|
913
|
+
if (r.rotate) {
|
|
914
|
+
let output = `🔄 SESSION ROTATED\n` +
|
|
915
|
+
`New session: ${r.session_id} (msg ${r.message_count})\n` +
|
|
916
|
+
`Context: ${r.context_message_count} messages raw in LLM\n`;
|
|
917
|
+
if (r.should_compress) {
|
|
918
|
+
output += `\n⚠️ COMPRESS: session ${r.compress_session_id} (${r.compress_message_count} msgs)\n` +
|
|
919
|
+
`Action: Call bot_session_compress with session_id="${r.compress_session_id}"`;
|
|
920
|
+
}
|
|
921
|
+
return ok(output);
|
|
922
|
+
}
|
|
923
|
+
return ok(`Session ${r.session_id}: ${r.message_count}/20 messages | context: ${r.context_message_count} msgs`);
|
|
924
|
+
}
|
|
925
|
+
catch (e) {
|
|
926
|
+
return err(e);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
// bot_guard_check — Bot-specific guard with spawn signal
|
|
930
|
+
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.", {
|
|
931
|
+
estimated_tokens: z.number().describe("Current token count in context window"),
|
|
932
|
+
max_tokens: z.number().optional().describe("Max context window size (default: 200000)"),
|
|
933
|
+
model: z.string().optional().describe("Model name for auto-detecting context window size"),
|
|
934
|
+
compress_threshold: z.number().optional().describe("Custom spawn threshold in tokens (default: 70% of max_tokens)"),
|
|
935
|
+
}, async (args) => {
|
|
936
|
+
try {
|
|
937
|
+
const payload = {
|
|
938
|
+
estimated_tokens: args.estimated_tokens,
|
|
939
|
+
max_tokens: args.max_tokens || CG_CONTEXT_CAP || 200000,
|
|
940
|
+
model: args.model || null,
|
|
941
|
+
};
|
|
942
|
+
if (args.compress_threshold)
|
|
943
|
+
payload.compress_threshold = args.compress_threshold;
|
|
944
|
+
const r = (await api("POST", "/v1/bot/guard/check", payload));
|
|
945
|
+
const pct = r.usage_percent;
|
|
946
|
+
const barLen = 20;
|
|
947
|
+
const filled = Math.round(pct / 100 * barLen);
|
|
948
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barLen - filled);
|
|
949
|
+
let output = `Bot Guard:\n` +
|
|
950
|
+
`[${bar}] ${pct.toFixed(1)}%\n` +
|
|
951
|
+
`Recommendation: ${r.recommendation.toUpperCase()}${r.should_compact ? " — compact now" : ""}\n` +
|
|
952
|
+
`Urgency: ${r.urgency}\n` +
|
|
953
|
+
`Spawn threshold: ${r.compress_threshold.toLocaleString()} tokens\n` +
|
|
954
|
+
`DNA memories: ${r.dna_memories} | Bootstrap ready: ${r.bootstrap_ready ? "yes" : "no"}\n`;
|
|
955
|
+
if (r.should_spawn_new_session) {
|
|
956
|
+
output += `\n⚠️ SPAWN NEW SESSION: ${r.spawn_reason}\n`;
|
|
957
|
+
output += `Action: Start new session → when new session reaches 20K tokens → compress old session via /v1/bot/session/compress`;
|
|
958
|
+
}
|
|
959
|
+
return ok(output);
|
|
960
|
+
}
|
|
961
|
+
catch (e) {
|
|
962
|
+
return err(e);
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
// ── Self-Thinking Tools ──────────────────────────────────────────────
|
|
966
|
+
// brain_thoughts — Get current active thoughts
|
|
967
|
+
server.tool("brain_thoughts", "Get the brain's current active thoughts — what it's thinking about autonomously.", {
|
|
968
|
+
limit: z.number().optional().describe("Max thoughts to return (default: 10)"),
|
|
969
|
+
}, async (args) => {
|
|
970
|
+
try {
|
|
971
|
+
const r = (await api("GET", `/v1/brain/thoughts?limit=${args.limit || 10}`));
|
|
972
|
+
if (!r.thoughts || r.thoughts.length === 0)
|
|
973
|
+
return ok("Brain has no active thoughts right now.");
|
|
974
|
+
const lines = r.thoughts.map((t) => `[${t.thought_type}] ${t.content} (confidence: ${t.confidence}, urgency: ${t.urgency})`);
|
|
975
|
+
return ok(`Active thoughts (${r.count}):\n${lines.join("\n")}`);
|
|
976
|
+
}
|
|
977
|
+
catch (e) {
|
|
978
|
+
return err(e);
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
// brain_think_about — Request brain to think about a topic
|
|
982
|
+
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.", {
|
|
983
|
+
topic: z.string().describe("What should the brain think about?"),
|
|
984
|
+
}, async (args) => {
|
|
985
|
+
try {
|
|
986
|
+
const r = (await api("POST", "/v1/brain/think-about", { topic: args.topic }));
|
|
987
|
+
return ok(`Queued for thinking: "${args.topic}"\nQueue size: ${r.queue_size}`);
|
|
988
|
+
}
|
|
989
|
+
catch (e) {
|
|
990
|
+
return err(e);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
// brain_hypotheses — Get active hypotheses
|
|
994
|
+
server.tool("brain_hypotheses", "Get hypotheses the brain is currently testing — predictions about user behavior patterns.", {
|
|
995
|
+
limit: z.number().optional().describe("Max hypotheses to return (default: 10)"),
|
|
996
|
+
}, async (args) => {
|
|
997
|
+
try {
|
|
998
|
+
const r = (await api("GET", `/v1/brain/hypotheses?limit=${args.limit || 10}`));
|
|
999
|
+
if (!r.hypotheses || r.hypotheses.length === 0)
|
|
1000
|
+
return ok("No active hypotheses being tested.");
|
|
1001
|
+
const lines = r.hypotheses.map((h) => `[${h.status}] ${h.hypothesis} (confidence: ${h.confidence})`);
|
|
1002
|
+
return ok(`Hypotheses (${r.count}):\n${lines.join("\n")}`);
|
|
1003
|
+
}
|
|
1004
|
+
catch (e) {
|
|
1005
|
+
return err(e);
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
// brain_feedback — Rate a thought
|
|
1009
|
+
server.tool("brain_feedback", "Rate a thought as useful or not — helps the brain learn what's worth thinking about.", {
|
|
1010
|
+
thought_id: z.number().describe("ID of the thought to rate"),
|
|
1011
|
+
useful: z.boolean().describe("Was this thought useful?"),
|
|
1012
|
+
}, async (args) => {
|
|
1013
|
+
try {
|
|
1014
|
+
await api("POST", "/v1/brain/thoughts/feedback", {
|
|
1015
|
+
thought_id: args.thought_id,
|
|
1016
|
+
useful: args.useful,
|
|
1017
|
+
});
|
|
1018
|
+
return ok(`Feedback recorded: thought #${args.thought_id} marked as ${args.useful ? "useful" : "not useful"}`);
|
|
1019
|
+
}
|
|
1020
|
+
catch (e) {
|
|
1021
|
+
return err(e);
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
// brain_thinking_stats — Budget and efficiency
|
|
1025
|
+
server.tool("brain_thinking_stats", "Get thinking system statistics — token budget, efficiency, queue size, and meta-cognition report.", {}, async () => {
|
|
1026
|
+
try {
|
|
1027
|
+
const r = (await api("GET", "/v1/brain/thinking-stats"));
|
|
1028
|
+
return ok(`Budget: ${r.budget.remaining_tokens} tokens remaining (limit: ${r.budget.limit_per_hour}/hr)\n` +
|
|
1029
|
+
`Efficiency: ${(r.budget.efficiency * 100).toFixed(1)}%\n` +
|
|
1030
|
+
`Queue size: ${r.queue_size}\n` +
|
|
1031
|
+
`Total thoughts: ${r.meta.total_thoughts} (${r.meta.useful_thoughts} useful)\n` +
|
|
1032
|
+
`Interval: ${r.meta.recommended_interval_seconds}s\n` +
|
|
1033
|
+
`Best types: ${r.meta.best_types.join(", ") || "none yet"}\n` +
|
|
1034
|
+
`Suppressed: ${r.meta.suppressed_types.join(", ") || "none"}`);
|
|
1035
|
+
}
|
|
1036
|
+
catch (e) {
|
|
1037
|
+
return err(e);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
// ── Session Settings Tools ──────────────────────────────────────────
|
|
1041
|
+
// memory_auto_extract — LLM-based fact extraction from conversation
|
|
1042
|
+
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.", {
|
|
1043
|
+
conversation: z.string().describe("The conversation text to extract facts from (include both user and assistant messages)"),
|
|
1044
|
+
source: z.string().optional().describe("Source context (e.g. 'discord chat', 'slack thread')"),
|
|
1045
|
+
store: z.boolean().optional().describe("Whether to store extracted facts (default: true). Set false to preview what would be extracted."),
|
|
1046
|
+
}, async (args) => {
|
|
1047
|
+
try {
|
|
1048
|
+
const r = (await api("POST", "/v1/memory/auto-extract", {
|
|
1049
|
+
conversation: args.conversation,
|
|
1050
|
+
source: args.source || "auto-extract",
|
|
1051
|
+
store: args.store !== false,
|
|
1052
|
+
}));
|
|
1053
|
+
if (!r.facts?.length)
|
|
1054
|
+
return ok("No extractable facts found in conversation.");
|
|
1055
|
+
const factList = r.facts
|
|
1056
|
+
.map((f, i) => `${i + 1}. [${f.memory_type || 'fact'}] ${f.content}`)
|
|
1057
|
+
.join("\n");
|
|
1058
|
+
return ok(`Extracted ${r.facts.length} facts (added: ${r.added}, updated: ${r.updated}, skipped: ${r.skipped}):\n\n${factList}`);
|
|
1059
|
+
}
|
|
1060
|
+
catch (e) {
|
|
1061
|
+
return err(e);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
// ── IDE Upgrade Tools ──────────────────────────────────────────────
|
|
1065
|
+
// memory_pitfall_check — Check pitfalls before risky actions
|
|
1066
|
+
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.", {
|
|
1067
|
+
intent: z.string().describe("What you're about to do (e.g. 'deploy to production', 'delete user table')"),
|
|
1068
|
+
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
1069
|
+
limit: z.number().optional().describe("Max results (default 5)"),
|
|
1070
|
+
}, async (args) => {
|
|
1071
|
+
try {
|
|
1072
|
+
const r = (await api("POST", "/v1/bot/pitfall/check", {
|
|
1073
|
+
intent: args.intent,
|
|
1074
|
+
tags: args.tags,
|
|
1075
|
+
limit: args.limit || 5,
|
|
1076
|
+
}));
|
|
1077
|
+
if (!r.has_pitfalls)
|
|
1078
|
+
return ok("No known pitfalls for this action. Proceed safely.");
|
|
1079
|
+
const list = r.pitfalls
|
|
1080
|
+
.map((p, i) => `${i + 1}. [score: ${p.score}] ${p.content}`)
|
|
1081
|
+
.join("\n");
|
|
1082
|
+
return ok(`⚠️ ${r.pitfalls.length} pitfall(s) found:\n\n${list}\n\nReview before proceeding.`);
|
|
1083
|
+
}
|
|
1084
|
+
catch (e) {
|
|
1085
|
+
return err(e);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
// memory_plan_save — Save current plan/state for session resumption
|
|
1089
|
+
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.", {
|
|
1090
|
+
session_id: z.string().optional().describe("Session identifier (default: 'default')"),
|
|
1091
|
+
state: z.record(z.string(), z.unknown()).describe("State to save: {plan: [...], cursor: 3, active_goal: '...', last_action: '...', files_read: [...]}"),
|
|
1092
|
+
}, async (args) => {
|
|
1093
|
+
try {
|
|
1094
|
+
const r = (await api("POST", "/v1/bot/state/save", {
|
|
1095
|
+
session_id: args.session_id || "default",
|
|
1096
|
+
state: args.state,
|
|
1097
|
+
}));
|
|
1098
|
+
return ok(`State saved for session '${r.session_id}'. Will be restored on next bootstrap.`);
|
|
1099
|
+
}
|
|
1100
|
+
catch (e) {
|
|
1101
|
+
return err(e);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
// memory_plan_resume — Restore saved state from previous session
|
|
1105
|
+
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.", {
|
|
1106
|
+
session_id: z.string().optional().describe("Session identifier (default: 'default')"),
|
|
1107
|
+
}, async (args) => {
|
|
1108
|
+
try {
|
|
1109
|
+
const r = (await api("GET", `/v1/bot/state/restore?session_id=${args.session_id || "default"}`));
|
|
1110
|
+
if (r.status === "not_found")
|
|
1111
|
+
return ok("No saved state found for this session. Starting fresh.");
|
|
1112
|
+
return ok(`State restored (saved at ${r.saved_at}):\n\n${JSON.stringify(r.state, null, 2)}`);
|
|
1113
|
+
}
|
|
1114
|
+
catch (e) {
|
|
1115
|
+
return err(e);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
// memory_goal_track — Create/update/query goals
|
|
1119
|
+
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).", {
|
|
1120
|
+
action: z.enum(["create", "update", "list"]).describe("Action to perform"),
|
|
1121
|
+
title: z.string().optional().describe("Goal title (for create)"),
|
|
1122
|
+
progress: z.number().optional().describe("Progress 0.0-1.0 (for update)"),
|
|
1123
|
+
goal_id: z.number().optional().describe("Goal ID (for update)"),
|
|
1124
|
+
status: z.enum(["active", "achieved", "abandoned"]).optional().describe("New status (for update)"),
|
|
1125
|
+
}, async (args) => {
|
|
1126
|
+
try {
|
|
1127
|
+
if (args.action === "create") {
|
|
1128
|
+
const r = (await api("POST", "/v1/store", {
|
|
1129
|
+
content: args.title,
|
|
1130
|
+
memory_type: "goal",
|
|
1131
|
+
zone: "important",
|
|
1132
|
+
tags: ["goal", "active"],
|
|
1133
|
+
}));
|
|
1134
|
+
return ok(`Goal created: "${args.title}" (id: ${r.id}). DNA-protected while active.`);
|
|
1135
|
+
}
|
|
1136
|
+
else if (args.action === "list") {
|
|
1137
|
+
const r = (await api("POST", "/v1/recall", {
|
|
1138
|
+
query: "active goals and objectives",
|
|
1139
|
+
memory_type: "goal",
|
|
1140
|
+
depth: "deep",
|
|
1141
|
+
limit: 10,
|
|
1142
|
+
}));
|
|
1143
|
+
if (!r.results?.length)
|
|
1144
|
+
return ok("No active goals found.");
|
|
1145
|
+
const list = r.results.map((g, i) => `${i + 1}. ${g.content}`).join("\n");
|
|
1146
|
+
return ok(`Active goals:\n\n${list}`);
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
return ok("Goal update: use memory_store with memory_type='goal' to update goal content.");
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
catch (e) {
|
|
1153
|
+
return err(e);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
// memory_thought_log — Query what the brain has been thinking about
|
|
1157
|
+
server.tool("memory_thought_log", "See what the brain has been thinking about autonomously. Returns recent thoughts, hypotheses, and insights generated during idle time.", {
|
|
1158
|
+
limit: z.number().optional().describe("Max thoughts to return (default 5)"),
|
|
1159
|
+
}, async (args) => {
|
|
1160
|
+
try {
|
|
1161
|
+
const r = (await api("GET", `/v1/brain/thoughts?limit=${args.limit || 5}`));
|
|
1162
|
+
if (!r.thoughts?.length)
|
|
1163
|
+
return ok("No recent thoughts. The brain thinks during idle periods.");
|
|
1164
|
+
const list = r.thoughts
|
|
1165
|
+
.map((t, i) => `${i + 1}. [${t.thought_type}] ${t.content} (urgency: ${t.urgency})`)
|
|
1166
|
+
.join("\n");
|
|
1167
|
+
return ok(`Recent brain thoughts:\n\n${list}`);
|
|
1168
|
+
}
|
|
1169
|
+
catch (e) {
|
|
1170
|
+
return err(e);
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
// memory_feedback — Report recall quality for self-improvement
|
|
1174
|
+
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.", {
|
|
1175
|
+
query: z.string().describe("The recall query that was made"),
|
|
1176
|
+
chunk_ids: z.array(z.number()).describe("IDs of chunks that were returned"),
|
|
1177
|
+
helpful: z.boolean().describe("Were the results helpful for your task?"),
|
|
1178
|
+
action_succeeded: z.boolean().optional().describe("Did the action using these memories succeed? (default: true)"),
|
|
1179
|
+
}, async (args) => {
|
|
1180
|
+
try {
|
|
1181
|
+
const r = (await api("POST", "/v1/bot/feedback", {
|
|
1182
|
+
query: args.query,
|
|
1183
|
+
chunk_ids: args.chunk_ids,
|
|
1184
|
+
helpful: args.helpful,
|
|
1185
|
+
action_succeeded: args.action_succeeded !== false,
|
|
1186
|
+
}));
|
|
1187
|
+
return ok(r.message || "Feedback recorded. Brain will adapt over time.");
|
|
1188
|
+
}
|
|
1189
|
+
catch (e) {
|
|
1190
|
+
return err(e);
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
// memory_predict — Predictive recall (push intent, get predicted memories)
|
|
1194
|
+
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.", {
|
|
1195
|
+
intent: z.string().describe("What you/user are about to do"),
|
|
1196
|
+
context: z.string().optional().describe("Current conversation context (helps prediction accuracy)"),
|
|
1197
|
+
limit: z.number().optional().describe("Max predictions (default 5)"),
|
|
1198
|
+
}, async (args) => {
|
|
1199
|
+
try {
|
|
1200
|
+
const r = (await api("POST", "/v1/bot/predict", {
|
|
1201
|
+
intent: args.intent,
|
|
1202
|
+
context: args.context || "",
|
|
1203
|
+
limit: args.limit || 5,
|
|
1204
|
+
}));
|
|
1205
|
+
if (!r.predictions?.length)
|
|
1206
|
+
return ok("No relevant predictions for this intent.");
|
|
1207
|
+
const list = r.predictions
|
|
1208
|
+
.map((p, i) => `${i + 1}. [${p.memory_type || 'memory'}] ${p.content}\n (score: ${p.score}, reason: ${p.reason})`)
|
|
1209
|
+
.join("\n\n");
|
|
1210
|
+
return ok(`Predicted ${r.count} relevant memories:\n\n${list}`);
|
|
1211
|
+
}
|
|
1212
|
+
catch (e) {
|
|
1213
|
+
return err(e);
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
// memory_changelog — What changed since last session
|
|
1217
|
+
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.", {
|
|
1218
|
+
since: z.string().describe("ISO datetime — show changes after this time (e.g. '2026-05-20T10:00:00Z')"),
|
|
1219
|
+
project_id: z.string().optional().describe("Filter to specific project"),
|
|
1220
|
+
limit: z.number().optional().describe("Max changes to return (default 50)"),
|
|
1221
|
+
}, async (args) => {
|
|
1222
|
+
try {
|
|
1223
|
+
const r = (await api("POST", "/v1/memory/changelog", {
|
|
1224
|
+
since: args.since,
|
|
1225
|
+
project_id: args.project_id,
|
|
1226
|
+
limit: args.limit || 50,
|
|
1227
|
+
}));
|
|
1228
|
+
if (!r.changes?.length)
|
|
1229
|
+
return ok("No changes since last session. Memory is up to date.");
|
|
1230
|
+
const list = r.changes
|
|
1231
|
+
.map((c, i) => `${i + 1}. [${c.type}] ${c.content}${c.source ? ` (source: ${c.source})` : ""}`)
|
|
1232
|
+
.join("\n");
|
|
1233
|
+
return ok(`${r.count} changes since ${args.since}:\n\n${list}`);
|
|
1234
|
+
}
|
|
1235
|
+
catch (e) {
|
|
1236
|
+
return err(e);
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
// memory_cognitive_profile — Complete self-model (metacognition)
|
|
1240
|
+
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 () => {
|
|
1241
|
+
try {
|
|
1242
|
+
const r = (await api("GET", "/v1/personality/cognitive-profile"));
|
|
1243
|
+
let out = `## Cognitive Profile\n\n`;
|
|
1244
|
+
if (r.persona)
|
|
1245
|
+
out += `**Persona:** ${r.persona}\n\n`;
|
|
1246
|
+
if (r.mood)
|
|
1247
|
+
out += `**Mood:** ${r.mood.current} (trend: ${r.mood.trend})\n\n`;
|
|
1248
|
+
if (r.active_goals?.length)
|
|
1249
|
+
out += `**Active Goals:**\n${r.active_goals.map((g) => `- ${g}`).join("\n")}\n\n`;
|
|
1250
|
+
if (r.top_entities?.length)
|
|
1251
|
+
out += `**Top Entities:** ${r.top_entities.map((e) => e.name || e).join(", ")}\n\n`;
|
|
1252
|
+
if (r.procedures?.length)
|
|
1253
|
+
out += `**Procedures:**\n${r.procedures.map((p) => `- ${p.slice(0, 100)}`).join("\n")}\n\n`;
|
|
1254
|
+
if (r.recent_topics?.length)
|
|
1255
|
+
out += `**Recent Topics:**\n${r.recent_topics.map((t) => `- ${t}`).join("\n")}\n`;
|
|
1256
|
+
return ok(out.trim());
|
|
1257
|
+
}
|
|
1258
|
+
catch (e) {
|
|
1259
|
+
return err(e);
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
// memory_entity_profile — Get everything known about an entity
|
|
1263
|
+
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.", {
|
|
1264
|
+
name: z.string().describe("Entity name to look up (e.g. 'Sarah', 'React', 'AuthService')"),
|
|
1265
|
+
}, async (args) => {
|
|
1266
|
+
try {
|
|
1267
|
+
const r = (await api("GET", `/v1/entities/${encodeURIComponent(args.name)}/profile`));
|
|
1268
|
+
if (!r.stats && !r.memories?.length)
|
|
1269
|
+
return ok(`No information found about "${args.name}".`);
|
|
1270
|
+
let out = `## Entity: ${args.name}\n\n`;
|
|
1271
|
+
if (r.stats) {
|
|
1272
|
+
out += `**Stats:** mentioned ${r.stats.frequency}x, recalled ${r.stats.recall_count}x`;
|
|
1273
|
+
if (r.stats.first_seen)
|
|
1274
|
+
out += `, first seen ${r.stats.first_seen.slice(0, 10)}`;
|
|
1275
|
+
out += `\n\n`;
|
|
1276
|
+
}
|
|
1277
|
+
if (r.memories?.length) {
|
|
1278
|
+
out += `**Linked Memories (${r.memory_count}):**\n`;
|
|
1279
|
+
out += r.memories.map((m) => `- [${m.memory_type}] ${m.content}`).join("\n");
|
|
1280
|
+
out += `\n\n`;
|
|
1281
|
+
}
|
|
1282
|
+
if (r.relationships?.length) {
|
|
1283
|
+
out += `**Relationships (${r.relationship_count}):**\n`;
|
|
1284
|
+
out += r.relationships.map((rel) => `- ${rel.source} → ${rel.relationship} → ${rel.target}`).join("\n");
|
|
1285
|
+
}
|
|
1286
|
+
return ok(out.trim());
|
|
1287
|
+
}
|
|
1288
|
+
catch (e) {
|
|
1289
|
+
return err(e);
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
803
1292
|
async function main() {
|
|
804
1293
|
const transport = new StdioServerTransport();
|
|
805
1294
|
await server.connect(transport);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* memoryai-kiro-setup
|
|
4
|
+
* Zero-dependency setup script that creates .kiro/settings/mcp.json
|
|
5
|
+
* and .kiro/steering/memoryai.md in the current project directory.
|
|
6
|
+
*/
|
|
7
|
+
import { createInterface } from "node:readline";
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
+
function ask(question, fallback) {
|
|
12
|
+
const suffix = fallback ? ` [${fallback}]` : "";
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
15
|
+
resolve(answer.trim() || fallback || "");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function writeIfMissing(filePath, content, label) {
|
|
20
|
+
if (existsSync(filePath)) {
|
|
21
|
+
console.log(` skip ${label} (already exists)`);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(filePath, content, "utf-8");
|
|
27
|
+
console.log(` create ${label}`);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const MCP_CONFIG = (apiKey, endpoint) => JSON.stringify({
|
|
31
|
+
mcpServers: {
|
|
32
|
+
memoryai: {
|
|
33
|
+
command: "npx",
|
|
34
|
+
args: ["-y", "memoryai-mcp"],
|
|
35
|
+
env: {
|
|
36
|
+
HM_API_KEY: apiKey,
|
|
37
|
+
HM_ENDPOINT: endpoint,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}, null, 2) + "\n";
|
|
42
|
+
const STEERING = `---
|
|
43
|
+
inclusion: always
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# MemoryAI — Persistent Memory Instructions
|
|
47
|
+
|
|
48
|
+
You have access to MemoryAI tools via MCP. Use them to maintain long-term memory across sessions.
|
|
49
|
+
|
|
50
|
+
## Session Start
|
|
51
|
+
|
|
52
|
+
Call \`memory_bootstrap\` at the beginning of every session to load prior context.
|
|
53
|
+
If bootstrap returns nothing, call \`memory_recover\` to check for recent session state.
|
|
54
|
+
|
|
55
|
+
## During Work
|
|
56
|
+
|
|
57
|
+
- Before answering questions about past decisions, architecture, or preferences: call \`memory_recall\` with a relevant query.
|
|
58
|
+
- After making a significant decision, completing a task, or learning something about the codebase: call \`memory_store\` with appropriate \`memory_type\`:
|
|
59
|
+
- \`decision\` — architectural or technical decisions (DNA-protected, never decays)
|
|
60
|
+
- \`preference\` — user preferences and conventions (DNA-protected, never decays)
|
|
61
|
+
- \`fact\` — codebase facts, API details, configs
|
|
62
|
+
- \`error\` — lessons learned from mistakes
|
|
63
|
+
- \`goal\` — current objectives and milestones
|
|
64
|
+
- After learning from a mistake or unexpected result: call \`learn\` with action, result, and lesson fields.
|
|
65
|
+
|
|
66
|
+
## Entity Tracking
|
|
67
|
+
|
|
68
|
+
When you create, modify, or reference important files, packages, or people:
|
|
69
|
+
1. Call \`entity_list\` to check if already tracked
|
|
70
|
+
2. If not tracked, call \`memory_store\` with \`memory_type=entity\`
|
|
71
|
+
|
|
72
|
+
## Memory Health
|
|
73
|
+
|
|
74
|
+
If a session is running long, call \`memory_health\` to check working memory usage.
|
|
75
|
+
If above 80%, call \`memory_compact\` proactively to consolidate context.
|
|
76
|
+
|
|
77
|
+
## Session End
|
|
78
|
+
|
|
79
|
+
When wrapping up or when the agent is about to stop:
|
|
80
|
+
1. Call \`memory_compact\` to consolidate the session's context into durable memories
|
|
81
|
+
2. Call \`memory_store\` with a brief summary of what was accomplished
|
|
82
|
+
|
|
83
|
+
## Rules
|
|
84
|
+
|
|
85
|
+
- Recall only when past context is actually needed — not on every message
|
|
86
|
+
- Store important outcomes after completing tasks, not after every interaction
|
|
87
|
+
- Present memories naturally — integrate recalled info into responses, don't show raw API output
|
|
88
|
+
- Use \`zone: "critical"\` for decisions that must never be forgotten
|
|
89
|
+
- Use \`retention: "forever"\` for permanent project knowledge
|
|
90
|
+
`;
|
|
91
|
+
async function main() {
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
console.log(`\nMemoryAI Kiro Setup`);
|
|
94
|
+
console.log(`Project: ${cwd}\n`);
|
|
95
|
+
const apiKey = process.env.HM_API_KEY || (await ask("MemoryAI API key (hm_sk_...)"));
|
|
96
|
+
if (!apiKey) {
|
|
97
|
+
console.error("Error: API key is required. Set HM_API_KEY or enter it above.");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const endpoint = await ask("Endpoint", process.env.HM_ENDPOINT || "https://memoryai.dev");
|
|
101
|
+
console.log("");
|
|
102
|
+
writeIfMissing(join(cwd, ".kiro", "settings", "mcp.json"), MCP_CONFIG(apiKey, endpoint), ".kiro/settings/mcp.json");
|
|
103
|
+
writeIfMissing(join(cwd, ".kiro", "steering", "memoryai.md"), STEERING, ".kiro/steering/memoryai.md");
|
|
104
|
+
console.log(`
|
|
105
|
+
Done. Next steps:
|
|
106
|
+
1. Restart Kiro
|
|
107
|
+
2. Ask: "What do you remember about this project?"
|
|
108
|
+
3. The agent should call memory_bootstrap automatically
|
|
109
|
+
`);
|
|
110
|
+
rl.close();
|
|
111
|
+
}
|
|
112
|
+
main().catch((err) => {
|
|
113
|
+
console.error(err);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoryai-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "MCP server for MemoryAI
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "MCP server for MemoryAI — Long-term memory for AI agents. Works with Claude Code, Cursor, Windsurf, VS Code, Kiro.",
|
|
5
5
|
"homepage": "https://memoryai.dev",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"bin": {
|
|
13
|
-
"memoryai-mcp": "dist/index.js"
|
|
13
|
+
"memoryai-mcp": "dist/index.js",
|
|
14
|
+
"memoryai-kiro-setup": "dist/kiro-setup.js"
|
|
14
15
|
},
|
|
15
16
|
"scripts": {
|
|
16
17
|
"build": "tsc",
|