@j0hanz/memory-mcp 0.1.0 → 0.1.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 CHANGED
@@ -2,34 +2,37 @@
2
2
 
3
3
  <!-- markdownlint-disable MD033 -->
4
4
 
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D24-339933?style=flat-square&logo=nodedotjs&logoColor=white)](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [![TypeScript](https://img.shields.io/badge/TypeScript-5.9%2B-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://github.com/j0hanz/memory-mcp/blob/master/package.json)
5
+ [![npm version](https://img.shields.io/npm/v/@j0hanz/memory-mcp?style=flat-square&logo=npm&logoColor=white&color=CB3837)](https://www.npmjs.com/package/@j0hanz/memory-mcp) [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D24-339933?style=flat-square&logo=nodedotjs&logoColor=white)](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [![TypeScript](https://img.shields.io/badge/TypeScript-5.9%2B-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://github.com/j0hanz/memory-mcp/blob/master/package.json)
6
6
 
7
7
  [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D) [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D&quality=insiders)
8
8
 
9
+ [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=memory-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqMGhhbnovbWVtb3J5LW1jcEBsYXRlc3QiXX0=)
10
+
9
11
  A SQLite-backed MCP server for persistent memory storage, full-text retrieval, and relationship graph traversal.
10
12
 
11
13
  ## Overview
12
14
 
13
- Memory MCP provides a local, persistent memory layer for MCP-enabled assistants. It supports SHA-256-addressed memory items, FTS5-powered search, graph relationships, BFS recall, an `internal://instructions` resource, and a `get-help` prompt.
15
+ Memory MCP provides a local, persistent memory layer for MCP-enabled assistants. It stores SHA-256-addressed memory items in SQLite with FTS5-powered full-text search, a directed relationship graph, BFS recall traversal, and token-budget-aware context retrieval all accessible over stdio transport with no external dependencies.
14
16
 
15
17
  ## Key Features
16
18
 
17
- - 12 MCP tools for CRUD, batch operations, search, recall, relationships, and stats.
18
- - Full-text search over content and tags via SQLite FTS5.
19
- - Graph recall with BFS traversal and bounded frontier size.
20
- - Strict Zod input validation with typed output envelopes.
21
- - Resource support with URI-template completion for memory hashes.
22
- - stdio transport with clean shutdown handling (`SIGINT`, `SIGTERM`).
19
+ - **13 MCP tools** for CRUD, batch operations, FTS5 search, BFS graph recall, token-budget context retrieval, relationships, and stats.
20
+ - **Full-text search** over content and tags via SQLite FTS5 with importance and type filters.
21
+ - **Graph recall** with BFS traversal, bounded frontier, and MCP progress notifications per hop.
22
+ - **Token-budget retrieval** (`retrieve_context`) selects memories that fit a caller-specified token budget — no manual pagination needed.
23
+ - **Strict Zod input validation** with typed output envelopes and SHA-256 hash addressing.
24
+ - **Resource support** with `internal://instructions` (Markdown guide) and `memory://memories/{hash}` URI template with hash auto-completion.
25
+ - **stdio transport** with clean shutdown handling (`SIGINT`, `SIGTERM`) and no HTTP endpoints.
23
26
 
24
27
  ## Requirements
25
28
 
26
29
  - Node.js `>=24`.
27
- - SQLite with FTS5 support (required at startup).
30
+ - SQLite with FTS5 support (verified at startup).
28
31
  - Any MCP client that supports stdio command servers.
29
32
 
30
33
  ## Quick Start
31
34
 
32
- Use the npm package directly with `npx`:
35
+ Use the npm package directly with `npx` — no installation required:
33
36
 
34
37
  ```json
35
38
  {
@@ -43,7 +46,13 @@ Use the npm package directly with `npx`:
43
46
  ```
44
47
 
45
48
  > [!TIP]
46
- > The server uses stdio transport only; no HTTP endpoint is exposed.
49
+ > The server uses stdio transport only; no HTTP endpoint is exposed. Stdout must not be polluted by custom logging.
50
+
51
+ Or run with Docker:
52
+
53
+ ```bash
54
+ docker run --rm -i ghcr.io/j0hanz/memory-mcp:latest
55
+ ```
47
56
 
48
57
  ## Client Configuration
49
58
 
@@ -65,6 +74,45 @@ Workspace file `.vscode/mcp.json`:
65
74
  }
66
75
  ```
67
76
 
77
+ CLI:
78
+
79
+ ```bash
80
+ code --add-mcp '{"name":"memory-mcp","command":"npx","args":["-y","@j0hanz/memory-mcp@latest"]}'
81
+ ```
82
+
83
+ </details>
84
+
85
+ <details>
86
+ <summary><b>Install in VS Code Insiders</b></summary>
87
+
88
+ [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D&quality=insiders)
89
+
90
+ CLI:
91
+
92
+ ```bash
93
+ code-insiders --add-mcp '{"name":"memory-mcp","command":"npx","args":["-y","@j0hanz/memory-mcp@latest"]}'
94
+ ```
95
+
96
+ </details>
97
+
98
+ <details>
99
+ <summary><b>Install in Cursor</b></summary>
100
+
101
+ [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=memory-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqMGhhbnovbWVtb3J5LW1jcEBsYXRlc3QiXX0=)
102
+
103
+ `~/.cursor/mcp.json`:
104
+
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "memory-mcp": {
109
+ "command": "npx",
110
+ "args": ["-y", "@j0hanz/memory-mcp@latest"]
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
68
116
  </details>
69
117
 
70
118
  <details>
@@ -92,9 +140,9 @@ claude mcp add memory-mcp -- npx -y @j0hanz/memory-mcp@latest
92
140
  </details>
93
141
 
94
142
  <details>
95
- <summary><b>Install in Cursor</b></summary>
143
+ <summary><b>Install in Windsurf</b></summary>
96
144
 
97
- `~/.cursor/mcp.json`:
145
+ MCP config:
98
146
 
99
147
  ```json
100
148
  {
@@ -109,6 +157,41 @@ claude mcp add memory-mcp -- npx -y @j0hanz/memory-mcp@latest
109
157
 
110
158
  </details>
111
159
 
160
+ <details>
161
+ <summary><b>Run with Docker</b></summary>
162
+
163
+ ```bash
164
+ # Pull and run (stdio mode)
165
+ docker run --rm -i \
166
+ -e MEMORY_DB_PATH=/data/memory.db \
167
+ -v memory-data:/data \
168
+ ghcr.io/j0hanz/memory-mcp:latest
169
+ ```
170
+
171
+ MCP client config:
172
+
173
+ ```json
174
+ {
175
+ "mcpServers": {
176
+ "memory-mcp": {
177
+ "command": "docker",
178
+ "args": [
179
+ "run",
180
+ "--rm",
181
+ "-i",
182
+ "-e",
183
+ "MEMORY_DB_PATH=/data/memory.db",
184
+ "-v",
185
+ "memory-data:/data",
186
+ "ghcr.io/j0hanz/memory-mcp:latest"
187
+ ]
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ </details>
194
+
112
195
  ## MCP Surface
113
196
 
114
197
  ### Tools Summary
@@ -116,210 +199,286 @@ claude mcp add memory-mcp -- npx -y @j0hanz/memory-mcp@latest
116
199
  | Tool | Category | Notes |
117
200
  | --------------------- | -------- | --------------------------------------- |
118
201
  | `store_memory` | Write | Idempotent by content+sorted tags hash |
119
- | `store_memories` | Write | Batch (1-50), transaction-wrapped |
202
+ | `store_memories` | Write | Batch (150), transaction-wrapped |
120
203
  | `get_memory` | Read | Hash lookup |
121
204
  | `update_memory` | Write | Returns `old_hash` + `new_hash` |
122
205
  | `delete_memory` | Write | Cascades relationship deletion |
123
- | `delete_memories` | Write | Batch (1-50), transaction-wrapped |
124
- | `search_memories` | Read | FTS5 query + cursor pagination |
125
- | `create_relationship` | Write | Idempotent edge creation |
206
+ | `delete_memories` | Write | Batch (150), transaction-wrapped |
207
+ | `search_memories` | Read | FTS5 + importance/type filters + cursor |
208
+ | `create_relationship` | Write | Idempotent directed edge creation |
126
209
  | `delete_relationship` | Write | Deletes exact directed edge |
127
210
  | `get_relationships` | Read | Direction filter + linked memory fields |
128
- | `recall` | Read | Search + BFS traversal (`depth` 0-3) |
211
+ | `recall` | Read | FTS5 seed + BFS traversal (depth 03) |
212
+ | `retrieve_context` | Read | Token-budget-aware context retrieval |
129
213
  | `memory_stats` | Read | Store aggregates and type breakdown |
130
214
 
215
+ ---
216
+
131
217
  ### `store_memory`
132
218
 
133
- Purpose: Store one memory and return its SHA-256 hash.
219
+ Store a new memory with content, tags, and optional type/importance. Idempotent — storing the same content+tags returns the existing hash with `created: false`.
134
220
 
135
221
  | Name | Type | Required | Default | Description |
136
222
  | ------------- | ---------- | -------- | --------- | ---------------------------------------------------------------------------------- |
137
- | `content` | `string` | Yes | — | Memory content (1-100000 chars) |
138
- | `tags` | `string[]` | Yes | — | 1-100 tags, each max 50, no whitespace |
223
+ | `content` | `string` | Yes | — | Memory content (1100000 chars) |
224
+ | `tags` | `string[]` | Yes | — | 1100 tags, each max 50 chars, no whitespace |
139
225
  | `memory_type` | enum | No | `general` | `general`, `fact`, `plan`, `decision`, `reflection`, `lesson`, `error`, `gradient` |
140
- | `importance` | `integer` | No | `0` | Priority 0-10 |
226
+ | `importance` | `integer` | No | `0` | Priority 010 |
227
+
228
+ Returns: `{ ok, result: { hash, created } }`
141
229
 
142
- Returns: `{ ok, result: { hash, created } }`.
230
+ ---
143
231
 
144
232
  ### `store_memories`
145
233
 
146
- Purpose: Store multiple memories in one call (max 50 items).
234
+ Store multiple memories in one transaction (max 50 items).
235
+
236
+ | Name | Type | Required | Description |
237
+ | ------- | ------------------------ | -------- | -------------------------------------------------------------------------------------- |
238
+ | `items` | `Array<StoreMemoryItem>` | Yes | 1–50 items, each with `content`, `tags`, optional `memory_type`, optional `importance` |
147
239
 
148
- | Name | Type | Required | Default | Description |
149
- | ------- | ------------------------ | -------- | ------- | --------------------------------------------------------------------------------------------- |
150
- | `items` | `Array<StoreMemoryItem>` | Yes | — | 1-50 memory items, each with `content`, `tags`, optional `memory_type`, optional `importance` |
240
+ Returns: `{ ok, result: { items, succeeded, failed } }`
151
241
 
152
- Returns: `{ ok, result: { items, succeeded, failed } }`.
242
+ ---
153
243
 
154
244
  ### `get_memory`
155
245
 
156
- Purpose: Retrieve one memory by hash.
246
+ Retrieve one memory by its SHA-256 hash.
157
247
 
158
- | Name | Type | Required | Default | Description |
159
- | ------ | -------- | -------- | ------- | ----------------------------- |
160
- | `hash` | `string` | Yes | — | 64-char lowercase SHA-256 hex |
248
+ | Name | Type | Required | Description |
249
+ | ------ | -------- | -------- | ----------------------------- |
250
+ | `hash` | `string` | Yes | 64-char lowercase SHA-256 hex |
161
251
 
162
- Returns: `{ ok, result: Memory }` or `{ ok: false, error }` (`E_NOT_FOUND`).
252
+ Returns: `{ ok, result: Memory }` or `{ ok: false, error }` on `E_NOT_FOUND`.
253
+
254
+ ---
163
255
 
164
256
  ### `update_memory`
165
257
 
166
- Purpose: Update content and optionally tags for an existing memory.
258
+ Update content and optionally tags for an existing memory. Returns both hashes.
167
259
 
168
260
  | Name | Type | Required | Default | Description |
169
261
  | --------- | ---------- | -------- | ------------- | -------------------- |
170
262
  | `hash` | `string` | Yes | — | Existing memory hash |
171
263
  | `content` | `string` | Yes | — | Replacement content |
172
- | `tags` | `string[]` | No | existing tags | Replacement tags |
264
+ | `tags` | `string[]` | No | Existing tags | Replacement tags |
265
+
266
+ Returns: `{ ok, result: { old_hash, new_hash } }`
173
267
 
174
- Returns: `{ ok, result: { old_hash, new_hash } }`.
268
+ ---
175
269
 
176
270
  ### `delete_memory`
177
271
 
178
- Purpose: Delete one memory by hash.
272
+ Delete one memory by hash. Cascades to related relationship rows.
273
+
274
+ | Name | Type | Required | Description |
275
+ | ------ | -------- | -------- | ----------- |
276
+ | `hash` | `string` | Yes | Memory hash |
179
277
 
180
- | Name | Type | Required | Default | Description |
181
- | ------ | -------- | -------- | ------- | ----------- |
182
- | `hash` | `string` | Yes | — | Memory hash |
278
+ Returns: `{ ok, result: { hash, deleted } }`
183
279
 
184
- Returns: `{ ok, result: { hash, deleted } }`.
280
+ ---
185
281
 
186
282
  ### `delete_memories`
187
283
 
188
- Purpose: Delete multiple memories by hash.
284
+ Delete multiple memories by hash in one transaction.
189
285
 
190
- | Name | Type | Required | Default | Description |
191
- | -------- | ---------- | -------- | ------- | ------------------ |
192
- | `hashes` | `string[]` | Yes | — | 1-50 memory hashes |
286
+ | Name | Type | Required | Description |
287
+ | -------- | ---------- | -------- | ------------------ |
288
+ | `hashes` | `string[]` | Yes | 150 memory hashes |
193
289
 
194
- Returns: `{ ok, result: { items, succeeded, failed } }`.
290
+ Returns: `{ ok, result: { items, succeeded, failed } }`
291
+
292
+ ---
195
293
 
196
294
  ### `search_memories`
197
295
 
198
- Purpose: FTS5 search over content and tags with cursor pagination.
296
+ Full-text search over memory content and tags using FTS5. Supports importance and type filters with cursor pagination.
297
+
298
+ | Name | Type | Required | Default | Description |
299
+ | ---------------- | --------- | -------- | ------- | --------------------------------------------------------- |
300
+ | `query` | `string` | Yes | — | Search text (1–1000 chars) |
301
+ | `limit` | `integer` | No | `20` | Results per page (1–100) |
302
+ | `cursor` | `string` | No | — | Pagination cursor from previous response |
303
+ | `min_importance` | `integer` | No | — | Only return memories with importance >= this value (0–10) |
304
+ | `max_importance` | `integer` | No | — | Only return memories with importance <= this value (0–10) |
305
+ | `memory_type` | enum | No | — | Filter by memory type |
199
306
 
200
- | Name | Type | Required | Default | Description |
201
- | -------- | --------- | -------- | ------- | --------------------------- |
202
- | `query` | `string` | Yes | — | Search text (1-1000 chars) |
203
- | `limit` | `integer` | No | `20` | Result cap per page (1-100) |
204
- | `cursor` | `string` | No | — | Pagination cursor |
307
+ Returns: `{ ok, result: { memories, total_returned, nextCursor? } }`
205
308
 
206
- Returns: `{ ok, result: { memories, total_returned, nextCursor? } }`.
309
+ ---
207
310
 
208
311
  ### `create_relationship`
209
312
 
210
- Purpose: Create a directed relationship between two memories.
313
+ Create a directed relationship edge between two memories. Idempotent.
314
+
315
+ Suggested `relation_type` values: `related_to`, `causes`, `depends_on`, `parent_of`, `child_of`, `supersedes`, `contradicts`, `supports`, `references`.
211
316
 
212
- | Name | Type | Required | Default | Description |
213
- | --------------- | -------- | -------- | ------- | -------------------------------------- |
214
- | `from_hash` | `string` | Yes | — | Source memory hash |
215
- | `to_hash` | `string` | Yes | — | Target memory hash |
216
- | `relation_type` | `string` | Yes | — | Edge label (1-50 chars, no whitespace) |
317
+ | Name | Type | Required | Description |
318
+ | --------------- | -------- | -------- | ------------------------------------------------- |
319
+ | `from_hash` | `string` | Yes | Source memory hash |
320
+ | `to_hash` | `string` | Yes | Target memory hash |
321
+ | `relation_type` | `string` | Yes | Edge label (150 chars, no whitespace, free-form) |
217
322
 
218
- Returns: `{ ok, result: { created } }`.
323
+ Returns: `{ ok, result: { created } }`
324
+
325
+ ---
219
326
 
220
327
  ### `delete_relationship`
221
328
 
222
- Purpose: Delete one directed relationship edge.
329
+ Delete one directed relationship edge.
330
+
331
+ | Name | Type | Required | Description |
332
+ | --------------- | -------- | -------- | ----------------- |
333
+ | `from_hash` | `string` | Yes | Source hash |
334
+ | `to_hash` | `string` | Yes | Target hash |
335
+ | `relation_type` | `string` | Yes | Relationship type |
223
336
 
224
- | Name | Type | Required | Default | Description |
225
- | --------------- | -------- | -------- | ------- | ----------------- |
226
- | `from_hash` | `string` | Yes | — | Source hash |
227
- | `to_hash` | `string` | Yes | — | Target hash |
228
- | `relation_type` | `string` | Yes | — | Relationship type |
337
+ Returns: `{ ok, result: { deleted } }` or `{ ok: false, error }` on `E_NOT_FOUND`.
229
338
 
230
- Returns: `{ ok, result: { deleted } }` or `{ ok: false, error }` (`E_NOT_FOUND`).
339
+ ---
231
340
 
232
341
  ### `get_relationships`
233
342
 
234
- Purpose: Retrieve relationships for a memory, optionally filtered by direction.
343
+ Retrieve relationships for a memory, with optional direction filter.
235
344
 
236
345
  | Name | Type | Required | Default | Description |
237
346
  | ----------- | -------- | -------- | ------- | --------------------------------- |
238
347
  | `hash` | `string` | Yes | — | Memory hash |
239
348
  | `direction` | enum | No | `both` | `outgoing`, `incoming`, or `both` |
240
349
 
241
- Returns: `{ ok, result: { relationships, count } }`.
350
+ Returns: `{ ok, result: { relationships, count } }`
351
+
352
+ Each relationship includes `from_hash`, `to_hash`, `relation_type`, `created_at`, `linked_hash`, `linked_content`, and `linked_tags`.
353
+
354
+ ---
242
355
 
243
356
  ### `recall`
244
357
 
245
- Purpose: Search memories, then traverse connected graph edges up to `depth` hops.
358
+ Search memories by full-text query, then traverse the relationship graph up to `depth` hops via BFS. Emits MCP progress notifications per hop.
246
359
 
247
- | Name | Type | Required | Default | Description |
248
- | -------- | --------- | -------- | ------- | ------------------------ |
249
- | `query` | `string` | Yes | — | Seed search query |
250
- | `depth` | `integer` | No | `1` | BFS hops (0-3) |
251
- | `limit` | `integer` | No | `10` | Seed memory count (1-50) |
252
- | `cursor` | `string` | No | — | Pagination cursor |
360
+ | Name | Type | Required | Default | Description |
361
+ | ---------------- | --------- | -------- | ------- | ---------------------------------------------------------- |
362
+ | `query` | `string` | Yes | — | Seed search query (1–1000 chars) |
363
+ | `depth` | `integer` | No | `1` | BFS hops (03) |
364
+ | `limit` | `integer` | No | `10` | Seed memory count (150) |
365
+ | `cursor` | `string` | No | — | Pagination cursor from previous response |
366
+ | `min_importance` | `integer` | No | — | Seed filter: only memories with importance >= value (0–10) |
367
+ | `max_importance` | `integer` | No | — | Seed filter: only memories with importance <= value (0–10) |
368
+ | `memory_type` | enum | No | — | Seed filter: only memories of this type |
253
369
 
254
- Returns: `{ ok, result: { memories, graph, depth_reached, nextCursor? } }`.
370
+ Returns: `{ ok, result: { memories, graph, depth_reached, aborted?, nextCursor? } }`
255
371
 
256
- Each item in the `graph` array uses this shape:
372
+ Each item in `graph` uses the shape:
257
373
 
258
374
  ```json
259
375
  { "from_hash": "...", "to_hash": "...", "relation_type": "..." }
260
376
  ```
261
377
 
378
+ > [!NOTE]
379
+ > `aborted: true` indicates the traversal hit a safety limit (`RECALL_MAX_FRONTIER_SIZE`, `RECALL_MAX_EDGE_ROWS`, or `RECALL_MAX_VISITED_NODES`). Partial results are still returned.
380
+
381
+ ---
382
+
383
+ ### `retrieve_context`
384
+
385
+ Search memories and return relevance-ranked results that fit within a caller-specified token budget. Eliminates manual pagination and token counting for context window management.
386
+
387
+ | Name | Type | Required | Default | Description |
388
+ | -------------- | --------- | -------- | ----------- | ------------------------------------------------------------------------------------------ |
389
+ | `query` | `string` | Yes | — | Search query (1–1000 chars) |
390
+ | `token_budget` | `integer` | No | `4000` | Maximum estimated tokens to return (100–200000) |
391
+ | `strategy` | enum | No | `relevance` | Sort order: `relevance` (FTS rank), `importance` (highest first), `recency` (newest first) |
392
+
393
+ Returns: `{ ok, result: { memories, estimated_tokens, truncated } }`
394
+
395
+ > [!TIP]
396
+ > Token estimation is approximate (content length ÷ 4). `truncated: true` means the budget was reached before all candidates were included.
397
+
398
+ ---
399
+
262
400
  ### `memory_stats`
263
401
 
264
- Purpose: Return aggregate memory and relationship stats.
402
+ Return aggregate memory and relationship stats. Takes no input.
265
403
 
266
- | Name | Type | Required | Default | Description |
267
- | -------- | ---- | -------- | ------- | ------------------ |
268
- | _(none)_ | — | — | — | Empty input object |
404
+ Returns:
269
405
 
270
- Returns: `{ ok, result: { memories, relationships, by_type } }`.
406
+ ```json
407
+ {
408
+ "ok": true,
409
+ "result": {
410
+ "memories": {
411
+ "total": 0,
412
+ "oldest": null,
413
+ "newest": null,
414
+ "avg_importance": null
415
+ },
416
+ "relationships": { "total": 0 },
417
+ "by_type": {}
418
+ }
419
+ }
420
+ ```
421
+
422
+ ---
271
423
 
272
424
  ### Resources
273
425
 
274
- | URI | Type | Description |
275
- | -------------------------- | --------------- | ----------------------------------------------------- |
276
- | `internal://instructions` | Static resource | Markdown usage guide for all tools |
277
- | `memory://memories/{hash}` | URI template | Returns one memory as JSON; hash completion supported |
426
+ | URI | MIME | Description |
427
+ | -------------------------- | ------------------ | ----------------------------------------------------- |
428
+ | `internal://instructions` | `text/markdown` | Markdown usage guide for all tools and workflows |
429
+ | `memory://memories/{hash}` | `application/json` | Returns one memory as JSON; hash completion supported |
278
430
 
279
431
  ### Prompts
280
432
 
281
- | Name | Arguments | Purpose |
282
- | ---------- | --------- | -------------------------------------- |
283
- | `get-help` | none | Returns memory tool usage instructions |
433
+ | Name | Arguments | Purpose |
434
+ | ---------- | --------- | --------------------------------------------- |
435
+ | `get-help` | none | Returns full usage instructions for all tools |
284
436
 
285
437
  ## Configuration
286
438
 
287
439
  ### Environment Variables
288
440
 
289
- | Variable | Description | Default | Required |
290
- | ---------------- | ------------------------- | ----------- | -------- |
291
- | `MEMORY_DB_PATH` | SQLite database file path | `memory.db` | No |
441
+ | Variable | Description | Default | Required |
442
+ | -------------------------- | ------------------------------------------------------- | ----------- | -------- |
443
+ | `MEMORY_DB_PATH` | SQLite database file path | `memory.db` | No |
444
+ | `RECALL_MAX_FRONTIER_SIZE` | Max BFS frontier nodes per hop (100–50000) | `1000` | No |
445
+ | `RECALL_MAX_EDGE_ROWS` | Max relationship rows fetched per traversal (100–50000) | `5000` | No |
446
+ | `RECALL_MAX_VISITED_NODES` | Max visited nodes across entire traversal (100–50000) | `5000` | No |
292
447
 
293
448
  > [!IMPORTANT]
294
449
  > If `MEMORY_DB_PATH` is relative (including the default `memory.db`), it resolves from the process working directory.
295
450
 
451
+ > [!TIP]
452
+ > Add `memory.db` to your `.gitignore` to keep the database out of version control — it contains local session data and should not be shared or committed.
453
+
296
454
  ### Limits and Constraints
297
455
 
298
- | Item | Value |
299
- | ----------------------- | ------------------------------------------------ |
300
- | Content length | 1-100000 chars |
301
- | Tag count | 1-100 per memory |
302
- | Tag length | 1-50 chars, no whitespace |
303
- | Hash format | 64-char lowercase hex SHA-256 |
304
- | Search query length | 1-1000 chars |
305
- | `search_memories.limit` | 1-100 (default 20) |
306
- | `recall.depth` | 0-3 (default 1) |
307
- | `recall.limit` | 1-50 (default 10) |
308
- | Batch size | 1-50 items (`store_memories`, `delete_memories`) |
309
- | Recall frontier guard | Max 1000 nodes per hop |
310
- | SQLite busy timeout | 5000 ms |
456
+ | Item | Value |
457
+ | ------------------------------- | ------------------------------------------------- |
458
+ | Content length | 1100000 chars |
459
+ | Tag count | 1100 per memory |
460
+ | Tag length | 150 chars, no whitespace |
461
+ | Hash format | 64-char lowercase hex SHA-256 |
462
+ | Search query length | 11000 chars |
463
+ | `search_memories.limit` | 1100 (default 20) |
464
+ | `recall.depth` | 03 (default 1) |
465
+ | `recall.limit` | 150 (default 10) |
466
+ | `retrieve_context.token_budget` | 100–200000 (default 4000) |
467
+ | Batch size | 1–50 items (`store_memories`, `delete_memories`) |
468
+ | Recall frontier guard | `RECALL_MAX_FRONTIER_SIZE` (default 1000 per hop) |
469
+ | SQLite busy timeout | 5000 ms |
311
470
 
312
471
  > [!NOTE]
313
472
  > Cursor values are base64url-encoded offsets. Treat them as opaque tokens.
314
473
 
315
474
  ## Security
316
475
 
317
- - Transport is stdio-only (`StdioServerTransport`), with no HTTP endpoints.
318
- - Fatal process errors are written to `stderr` in the entrypoint.
319
- - Inputs are validated with strict Zod schemas and bounded field constraints.
320
- - Hashes are validated against lowercase SHA-256 hex format.
321
- - Search input is tokenized to alphanumeric terms before FTS `MATCH` execution (non-alphanumeric characters act as delimiters).
322
- - SQLite foreign keys are enabled; relationship rows cascade on memory delete.
476
+ - Transport is stdio-only (`StdioServerTransport`) no HTTP endpoints.
477
+ - Fatal process errors are written to `stderr`; stdout must remain clean for the MCP protocol.
478
+ - All inputs are validated with strict Zod schemas and bounded field constraints before any database access.
479
+ - Hashes are validated against a lowercase 64-char SHA-256 hex regex.
480
+ - Search input is tokenized to alphanumeric terms before FTS `MATCH` execution (non-alphanumeric characters act as delimiters, preventing FTS injection).
481
+ - SQLite foreign keys are enabled; relationship rows cascade-delete when a memory is removed.
323
482
 
324
483
  ## Development
325
484
 
@@ -340,7 +499,9 @@ Core scripts:
340
499
  | `test` | `npm run test` | Full build + tests via task runner |
341
500
  | `test:fast` | `npm run test:fast` | Run TS tests directly with Node test runner |
342
501
  | `lint` | `npm run lint` | ESLint checks |
502
+ | `lint:fix` | `npm run lint:fix` | ESLint auto-fix |
343
503
  | `type-check` | `npm run type-check` | Strict TypeScript checks |
504
+ | `format` | `npm run format` | Prettier format |
344
505
  | `inspector` | `npm run inspector` | Build and open MCP Inspector against stdio server |
345
506
 
346
507
  Inspect with MCP Inspector:
@@ -349,15 +510,45 @@ Inspect with MCP Inspector:
349
510
  npx @modelcontextprotocol/inspector node dist/index.js
350
511
  ```
351
512
 
513
+ ## Build & Release
514
+
515
+ GitHub Actions release workflow (`.github/workflows/release.yml`) handles versioning, validation, and publishing via a single `workflow_dispatch` trigger:
516
+
517
+ ```
518
+ workflow_dispatch (patch / minor / major / custom)
519
+
520
+
521
+ release — bump package.json + server.json → lint → type-check → test → build → tag → GitHub Release
522
+
523
+ ├──► publish-npm ──► publish-mcp (npm Trusted Publishing OIDC → MCP Registry)
524
+
525
+ └──► publish-docker (GHCR, linux/amd64 + linux/arm64)
526
+ ```
527
+
528
+ Trigger a release:
529
+
530
+ ```bash
531
+ gh workflow run release.yml -f bump=patch
532
+ ```
533
+
534
+ Or use the GitHub UI: **Actions → Release → Run workflow**.
535
+
536
+ > [!NOTE]
537
+ > npm publishing uses OIDC Trusted Publishing — no `NPM_TOKEN` secret required. MCP Registry uses GitHub OIDC. Docker uses the built-in `GITHUB_TOKEN`.
538
+
352
539
  ## Troubleshooting
353
540
 
354
- - If startup fails with FTS5 errors, use Node.js 24+ with SQLite FTS5 support.
355
- - If a request fails with `E_INVALID_CURSOR`, retry without the cursor.
356
- - If stdio clients fail to connect, ensure no custom stdout logging is added to the server process.
357
- - If memory or relationship lookups fail, confirm hashes exist via `search_memories` first.
541
+ | Symptom | Cause | Fix |
542
+ | ----------------------------- | ---------------------------- | -------------------------------------------------------- |
543
+ | Startup fails with FTS5 error | Node.js build without FTS5 | Use Node.js 24+ with SQLite FTS5 support |
544
+ | `E_NOT_FOUND` on `get_memory` | Hash doesn't exist | Verify via `search_memories` first |
545
+ | `E_INVALID_CURSOR` | Stale or malformed cursor | Retry the request without the `cursor` parameter |
546
+ | MCP client can't connect | Custom stdout logging added | Ensure nothing writes to stdout in the server process |
547
+ | `aborted: true` in recall | Traversal hit a safety limit | Reduce `depth`, or tune `RECALL_MAX_*` env vars |
548
+ | Database locked errors | High concurrent write load | SQLite busy timeout is 5000 ms; reduce concurrent writes |
358
549
 
359
550
  ## License
360
551
 
361
- - **MIT**
552
+ MIT
362
553
 
363
554
  <!-- markdownlint-enable MD033 -->
@@ -1,36 +1,36 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="0.8" stroke-linecap="round"
2
- stroke-linejoin="round" role="img" aria-label="icon">
3
- <style>
4
- path,
5
- circle,
6
- rect,
7
- line,
8
- polyline,
9
- polygon {
10
- stroke: #111111;
11
- }
12
-
13
- @media (prefers-color-scheme: dark) {
14
-
15
- path,
16
- circle,
17
- rect,
18
- line,
19
- polyline,
20
- polygon {
21
- stroke: #eeeeee;
22
- }
23
- }
24
- </style>
25
-
26
- <rect x="5" y="2" width="14" height="20" rx="2" />
27
-
28
- <path d="M9 7H15" />
29
- <path d="M9 12H15" />
30
- <path d="M9 17H15" />
31
-
32
- <path d="M3 6H5" />
33
- <path d="M3 18H5" />
34
- <path d="M19 6H21" />
35
- <path d="M19 18H21" />
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="0.8" stroke-linecap="round"
2
+ stroke-linejoin="round" role="img" aria-label="icon">
3
+ <style>
4
+ path,
5
+ circle,
6
+ rect,
7
+ line,
8
+ polyline,
9
+ polygon {
10
+ stroke: #111111;
11
+ }
12
+
13
+ @media (prefers-color-scheme: dark) {
14
+
15
+ path,
16
+ circle,
17
+ rect,
18
+ line,
19
+ polyline,
20
+ polygon {
21
+ stroke: #eeeeee;
22
+ }
23
+ }
24
+ </style>
25
+
26
+ <rect x="5" y="2" width="14" height="20" rx="2" />
27
+
28
+ <path d="M9 7H15" />
29
+ <path d="M9 12H15" />
30
+ <path d="M9 17H15" />
31
+
32
+ <path d="M3 6H5" />
33
+ <path d="M3 18H5" />
34
+ <path d="M19 6H21" />
35
+ <path d="M19 18H21" />
36
36
  </svg>
package/dist/index.js CHANGED
File without changes
@@ -1,14 +1,14 @@
1
- # Memory INSTRUCTIONS
1
+ # MEMORY-MCP INSTRUCTIONS
2
2
 
3
- > Available as resource `internal://instructions`. Load when unsure about tool usage.
3
+ These instructions are available as a resource `internal://instructions` or prompt `get-help`. Load them when unsure about tool usage.
4
4
 
5
5
  ---
6
6
 
7
7
  ## CORE CAPABILITY
8
8
 
9
9
  - Domain: SQLite-backed memory store with FTS5 search and knowledge graph for AI assistants.
10
- - Primary Resources: Memory (content+tags+hash), Relationship (directed edges).
11
- - Tools: `search_memories` `get_memory` `recall` `get_relationships` `memory_stats` (READ); `store_memory` `store_memories` `update_memory` `delete_memory` `delete_memories` `create_relationship` `delete_relationship` (WRITE).
10
+ - Primary Resources: Memories (content+tags+hash), Relationships (directed edges).
11
+ - Tools: `search_memories`, `get_memory`, `recall`, `get_relationships`, `memory_stats`, `retrieve_context` (READ); `store_memory`, `store_memories`, `update_memory`, `delete_memory`, `delete_memories`, `create_relationship`, `delete_relationship` (WRITE).
12
12
 
13
13
  ---
14
14
 
@@ -21,37 +21,39 @@
21
21
  ## RESOURCES & RESOURCE LINKS
22
22
 
23
23
  - `internal://instructions`: This document.
24
- - `memory://memories/{hash}`: Retrieve a single memory by SHA-256 hash (JSON). Supports hash autocompletion.
24
+ - `memory://memories/{hash}`: Retrieve a single memory by its SHA-256 hash.
25
+ - If a tool response includes a `resourceUri` or `resource_link`, call `resources/read` with the URI to fetch the full payload.
25
26
 
26
27
  ---
27
28
 
28
29
  ## THE "GOLDEN PATH" WORKFLOWS (CRITICAL)
29
30
 
30
- ### WORKFLOW A: Recall & Exploration
31
+ ### WORKFLOW A: RECALL & EXPLORATION
31
32
 
32
- 1. Call `search_memories` with `{ query }` to find memories by content/tags.
33
- 2. Call `recall` with `{ query, depth }` to traverse graph connections from search hits.
34
- 3. Call `get_memory` with `{ hash }` for exact retrieval using a hash from previous results.
35
- NOTE: Never guess hashes. Always search first.
33
+ - Call `search_memories` with `{ query }` to find memories by content/tags.
34
+ - Call `recall` with `{ query, depth }` to traverse graph connections from hits.
35
+ - Call `get_memory` with `{ hash }` for exact retrieval using hash from results.
36
+ NOTE: Never guess hashes. Always search first.
36
37
 
37
- ### WORKFLOW B: Knowledge Storage
38
+ ### WORKFLOW B: KNOWLEDGE MANAGEMENT (STORAGE)
38
39
 
39
- 1. Call `store_memory` to persist a single memory with content, tags, and optional type/importance.
40
- 2. Call `store_memories` for batch storage (up to 50 items, atomic transaction).
41
- 3. Call `create_relationship` to link memories via `{ from_hash, to_hash, relation_type }`.
42
- NOTE: Both hashes must exist before creating a relationship.
40
+ - Call `store_memory` or `store_memories` (batch ≤50) to persist content with tags.
41
+ - Call `create_relationship` to link memories via `{ from_hash, to_hash, relation_type }`.
42
+ - Call `update_memory` with `{ hash, content }` to revise returns new hash.
43
+ NOTE: `update_memory` changes the hash because hash depends on content.
43
44
 
44
- ### WORKFLOW C: Knowledge Management
45
+ ### WORKFLOW C: CONTEXT RETRIEVAL (RAG)
45
46
 
46
- 1. Call `update_memory` with `{ hash, content }` to revise returns a **new hash** (hash changes on content/tag update).
47
- 2. Call `delete_memory` or `delete_memories` to remove items. Deleting a memory cascades to its relationships.
48
- 3. Call `delete_relationship` to remove a specific edge.
49
- NOTE: Confirm destructive actions with the user first.
47
+ - Call `retrieve_context` with `{ query, token_budget }` to get relevant memories fitting a token limit.
48
+ - Use `strategy='importance'` for high-priority items or `strategy='recency'` for latest updates.
49
+ NOTE: Output is truncated to fit `token_budget`.
50
50
 
51
- ### WORKFLOW D: Diagnostics & Graph Inspection
51
+ ### WORKFLOW D: CLEANUP & MAINTENANCE
52
52
 
53
- 1. Call `memory_stats` (no input) to get counts, timestamps, and type breakdown.
54
- 2. Call `get_relationships` with `{ hash, direction }` to inspect a memory's connections.
53
+ - Call `delete_memory` or `delete_memories` to remove obsolete items.
54
+ - Call `delete_relationship` to remove specific edges.
55
+ - Call `memory_stats` to monitor database size and health.
56
+ NOTE: Always confirm destructive actions (delete) with the user first.
55
57
 
56
58
  ---
57
59
 
@@ -59,86 +61,63 @@
59
61
 
60
62
  `store_memory` / `store_memories`
61
63
 
62
- - Idempotent: storing the same content+tags returns the existing hash with `created: false`.
63
- - Hash is computed from `SHA-256(content + sorted_tags)` changing tags produces a different hash.
64
- - `importance`: integer 0–10 (default 0). `memory_type`: one of `general`, `fact`, `plan`, `decision`, `reflection`, `lesson`, `error`, `gradient` (default `general`).
65
- - Batch: `store_memories` accepts 1–50 items. All succeed or the transaction rolls back.
66
-
67
- `search_memories`
68
-
69
- - Uses FTS5 full-text search over content and tags. Results ranked by relevance.
70
- - `limit`: 1–100 (default 20). Returns `nextCursor` for pagination.
71
- - Query is sanitized to alphanumeric tokens — special characters are stripped.
72
-
73
- `recall`
74
-
75
- - Seeds from FTS5 search, then performs BFS traversal up to `depth` hops (0–3, default 1).
76
- - `depth: 0` returns only search results with no graph traversal.
77
- - `limit`: 1–50 (default 10) controls the seed count, not the total.
78
- - Returns all discovered `memories` and the `graph` edges connecting them.
79
- - BFS frontier capped at 1,000 nodes per hop to prevent unbounded memory usage.
64
+ - Purpose: Persist memory content.
65
+ - Input: `tags` (array, no whitespace, max 50 chars each), `importance` (0-10).
66
+ - Output: Returns `hash` and `created` boolean.
67
+ - Gotcha: Idempotent. Storing identical content+tags returns existing hash with `created: false`.
80
68
 
81
69
  `update_memory`
82
70
 
83
- - `hash` must exist (`E_NOT_FOUND` otherwise). New `content` required; `tags` optional (kept if omitted).
84
- - Returns `{ old_hash, new_hash }`. **The hash changes** — update references accordingly.
85
- - Emits a `resources/updated` notification for the old hash URI.
71
+ - Purpose: Modify memory content.
72
+ - Input: Requires `hash` and new `content`. `tags` optional.
73
+ - Output: Returns `old_hash` and `new_hash`.
74
+ - Nuance: Changing content changes the hash. The old memory is effectively replaced.
86
75
 
87
- `get_relationships`
76
+ `search_memories`
88
77
 
89
- - `direction`: `outgoing` | `incoming` | `both` (default `both`).
90
- - Each relationship includes the linked memory's content and tags inline.
91
- - Returns `E_NOT_FOUND` if the source hash does not exist.
78
+ - Purpose: FTS5 full-text search over content and tags.
79
+ - Input: `query`, `limit` (default 20), `cursor` for pagination.
80
+ - Limits: Max 100 results per call.
92
81
 
93
- `delete_memory` / `delete_memories`
82
+ `recall`
94
83
 
95
- - Deleting a memory cascades to all its relationships (foreign key ON DELETE CASCADE).
96
- - Batch: `delete_memories` accepts 1–50 hashes. Atomic transaction.
97
- - Items not found are returned with `deleted: false` (not an error in batch mode).
84
+ - Purpose: Graph traversal starting from search hits.
85
+ - Input: `depth` (0-3, default 1). 0 = search only. Higher depth = more hops.
86
+ - Gotcha: High depth may return large graphs. Use cautiously.
98
87
 
99
- `create_relationship` / `delete_relationship`
88
+ `retrieve_context`
100
89
 
101
- - `create_relationship`: Idempotent re-creating returns `created: false`.
102
- - `relation_type`: no whitespace, max 50 chars (e.g., `related_to`, `causes`, `depends_on`).
103
- - `delete_relationship`: Returns `E_NOT_FOUND` if the exact edge does not exist.
90
+ - Purpose: Get memories optimized for LLM context window.
91
+ - Input: `token_budget` (default 4000).
92
+ - Output: `memories` array and `truncated` boolean.
104
93
 
105
- `memory_stats`
94
+ `create_relationship`
106
95
 
107
- - No input. Returns total memories, total relationships, oldest/newest timestamps, average importance, and per-type breakdown.
96
+ - Purpose: Link two memories.
97
+ - Input: `from_hash`, `to_hash`, `relation_type` (e.g., related_to, causes).
98
+ - Gotcha: Both hashes must exist.
108
99
 
109
100
  ---
110
101
 
111
102
  ## CROSS-FEATURE RELATIONSHIPS
112
103
 
113
- - Use `search_memories` results to obtain hashes for `get_memory`, `update_memory`, `delete_memory`, and `create_relationship`.
114
- - Use `recall` to discover connected memories and graph edges in a single call — combines FTS5 search with BFS traversal.
115
- - `update_memory` changes the hash any relationships referencing the old hash are updated via CASCADE.
116
- - `memory://memories/{hash}` resource provides the same data as `get_memory` but through the MCP resource protocol.
104
+ - Use `store_memory` to get hashes, then `create_relationship` to link them.
105
+ - Use `search_memories` to find entry points for `recall` traversals.
106
+ - `retrieve_context` uses the same ranking as `search_memories` but limits by token count.
117
107
 
118
108
  ---
119
109
 
120
110
  ## CONSTRAINTS & LIMITATIONS
121
111
 
122
- - Transport: stdio only.
123
- - Requires Node.js 24 with built-in `node:sqlite` and FTS5 support.
124
- - Database path: set via `MEMORY_DB_PATH` env var (default: `memory.db`).
125
- - SQLite busy timeout: 5,000 ms.
126
- - Content: max 100,000 characters per memory.
127
- - Tags: 1–100 per memory, each max 50 chars, no whitespace.
128
- - Hash: 64 lowercase hex characters (SHA-256).
129
- - Search query: 1–1,000 characters.
130
- - Batch operations: max 50 items per call.
131
- - Pagination cursors are base64url-encoded offsets — do not construct manually.
112
+ - Content size: Max 100,000 characters per memory.
113
+ - Batch size: Max 50 items for `store_memories` and `delete_memories`.
114
+ - Tags: Max 100 tags per memory, no whitespace.
115
+ - Relationships: Directed edges only.
132
116
 
133
117
  ---
134
118
 
135
119
  ## ERROR HANDLING STRATEGY
136
120
 
137
- - `E_NOT_FOUND`: Hash or relationship missing. → Call `search_memories` or `recall` to find valid hashes.
138
- - `E_DUPLICATE`: Entry already exists. → Safe to ignore for idempotent operations.
139
- - `E_CONSTRAINT`: Foreign key or uniqueness violation. → Verify both hashes exist before creating relationships.
140
- - `E_INVALID_CURSOR`: Malformed pagination cursor. → Drop the cursor and restart from the first page.
141
- - `E_TIMEOUT`: SQLite busy timeout exceeded (5s). → Retry after a brief delay; reduce batch size if persistent.
142
- - `E_UNKNOWN`: Unexpected error. → Check the error message for details; retry once.
143
-
144
- ---
121
+ - `E_NOT_FOUND`: Memory hash or relationship not found. → Search/recall to find valid hashes.
122
+ - `E_INVALID_CURSOR`: Pagination cursor invalid or expired. → Restart search without cursor.
123
+ - `E_UNKNOWN`: Unexpected internal error. → Check input format and retry.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/memory-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "mcpName": "io.github.j0hanz/memory-mcp",
5
5
  "author": "Johanz",
6
6
  "license": "MIT",