@j0hanz/memory-mcp 0.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.
Files changed (128) hide show
  1. package/README.md +363 -0
  2. package/dist/assets/logo.svg +36 -0
  3. package/dist/completions/index.d.ts +3 -0
  4. package/dist/completions/index.d.ts.map +1 -0
  5. package/dist/completions/index.js +17 -0
  6. package/dist/completions/index.js.map +1 -0
  7. package/dist/db/index.d.ts +6 -0
  8. package/dist/db/index.d.ts.map +1 -0
  9. package/dist/db/index.js +102 -0
  10. package/dist/db/index.js.map +1 -0
  11. package/dist/db/typed.d.ts +15 -0
  12. package/dist/db/typed.d.ts.map +1 -0
  13. package/dist/db/typed.js +24 -0
  14. package/dist/db/typed.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +60 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/instructions.md +144 -0
  20. package/dist/lib/errors.d.ts +5 -0
  21. package/dist/lib/errors.d.ts.map +1 -0
  22. package/dist/lib/errors.js +12 -0
  23. package/dist/lib/errors.js.map +1 -0
  24. package/dist/lib/hash.d.ts +2 -0
  25. package/dist/lib/hash.d.ts.map +1 -0
  26. package/dist/lib/hash.js +11 -0
  27. package/dist/lib/hash.js.map +1 -0
  28. package/dist/lib/pagination.d.ts +8 -0
  29. package/dist/lib/pagination.d.ts.map +1 -0
  30. package/dist/lib/pagination.js +40 -0
  31. package/dist/lib/pagination.js.map +1 -0
  32. package/dist/lib/search.d.ts +14 -0
  33. package/dist/lib/search.d.ts.map +1 -0
  34. package/dist/lib/search.js +36 -0
  35. package/dist/lib/search.js.map +1 -0
  36. package/dist/lib/tool-response.d.ts +8 -0
  37. package/dist/lib/tool-response.d.ts.map +1 -0
  38. package/dist/lib/tool-response.js +21 -0
  39. package/dist/lib/tool-response.js.map +1 -0
  40. package/dist/lib/types.d.ts +75 -0
  41. package/dist/lib/types.d.ts.map +1 -0
  42. package/dist/lib/types.js +25 -0
  43. package/dist/lib/types.js.map +1 -0
  44. package/dist/prompts/index.d.ts +3 -0
  45. package/dist/prompts/index.d.ts.map +1 -0
  46. package/dist/prompts/index.js +46 -0
  47. package/dist/prompts/index.js.map +1 -0
  48. package/dist/resources/index.d.ts +4 -0
  49. package/dist/resources/index.d.ts.map +1 -0
  50. package/dist/resources/index.js +89 -0
  51. package/dist/resources/index.js.map +1 -0
  52. package/dist/schemas/index.d.ts +3 -0
  53. package/dist/schemas/index.d.ts.map +1 -0
  54. package/dist/schemas/index.js +4 -0
  55. package/dist/schemas/index.js.map +1 -0
  56. package/dist/schemas/inputs.d.ts +128 -0
  57. package/dist/schemas/inputs.d.ts.map +1 -0
  58. package/dist/schemas/inputs.js +176 -0
  59. package/dist/schemas/inputs.js.map +1 -0
  60. package/dist/schemas/outputs.d.ts +202 -0
  61. package/dist/schemas/outputs.d.ts.map +1 -0
  62. package/dist/schemas/outputs.js +104 -0
  63. package/dist/schemas/outputs.js.map +1 -0
  64. package/dist/server.d.ts +4 -0
  65. package/dist/server.d.ts.map +1 -0
  66. package/dist/server.js +73 -0
  67. package/dist/server.js.map +1 -0
  68. package/dist/tools/create-relationship.d.ts +4 -0
  69. package/dist/tools/create-relationship.d.ts.map +1 -0
  70. package/dist/tools/create-relationship.js +49 -0
  71. package/dist/tools/create-relationship.js.map +1 -0
  72. package/dist/tools/delete-memories.d.ts +4 -0
  73. package/dist/tools/delete-memories.d.ts.map +1 -0
  74. package/dist/tools/delete-memories.js +55 -0
  75. package/dist/tools/delete-memories.js.map +1 -0
  76. package/dist/tools/delete-memory.d.ts +4 -0
  77. package/dist/tools/delete-memory.d.ts.map +1 -0
  78. package/dist/tools/delete-memory.js +35 -0
  79. package/dist/tools/delete-memory.js.map +1 -0
  80. package/dist/tools/delete-relationship.d.ts +4 -0
  81. package/dist/tools/delete-relationship.d.ts.map +1 -0
  82. package/dist/tools/delete-relationship.js +35 -0
  83. package/dist/tools/delete-relationship.js.map +1 -0
  84. package/dist/tools/get-memory.d.ts +4 -0
  85. package/dist/tools/get-memory.d.ts.map +1 -0
  86. package/dist/tools/get-memory.js +30 -0
  87. package/dist/tools/get-memory.js.map +1 -0
  88. package/dist/tools/get-relationships.d.ts +4 -0
  89. package/dist/tools/get-relationships.d.ts.map +1 -0
  90. package/dist/tools/get-relationships.js +57 -0
  91. package/dist/tools/get-relationships.js.map +1 -0
  92. package/dist/tools/helpers.d.ts +13 -0
  93. package/dist/tools/helpers.d.ts.map +1 -0
  94. package/dist/tools/helpers.js +49 -0
  95. package/dist/tools/helpers.js.map +1 -0
  96. package/dist/tools/index.d.ts +15 -0
  97. package/dist/tools/index.d.ts.map +1 -0
  98. package/dist/tools/index.js +29 -0
  99. package/dist/tools/index.js.map +1 -0
  100. package/dist/tools/memory-stats.d.ts +4 -0
  101. package/dist/tools/memory-stats.d.ts.map +1 -0
  102. package/dist/tools/memory-stats.js +60 -0
  103. package/dist/tools/memory-stats.js.map +1 -0
  104. package/dist/tools/recall.d.ts +4 -0
  105. package/dist/tools/recall.d.ts.map +1 -0
  106. package/dist/tools/recall.js +192 -0
  107. package/dist/tools/recall.js.map +1 -0
  108. package/dist/tools/retrieve-context.d.ts +4 -0
  109. package/dist/tools/retrieve-context.d.ts.map +1 -0
  110. package/dist/tools/retrieve-context.js +75 -0
  111. package/dist/tools/retrieve-context.js.map +1 -0
  112. package/dist/tools/search-memories.d.ts +4 -0
  113. package/dist/tools/search-memories.d.ts.map +1 -0
  114. package/dist/tools/search-memories.js +62 -0
  115. package/dist/tools/search-memories.js.map +1 -0
  116. package/dist/tools/store-memories.d.ts +4 -0
  117. package/dist/tools/store-memories.d.ts.map +1 -0
  118. package/dist/tools/store-memories.js +52 -0
  119. package/dist/tools/store-memories.js.map +1 -0
  120. package/dist/tools/store-memory.d.ts +4 -0
  121. package/dist/tools/store-memory.d.ts.map +1 -0
  122. package/dist/tools/store-memory.js +35 -0
  123. package/dist/tools/store-memory.js.map +1 -0
  124. package/dist/tools/update-memory.d.ts +4 -0
  125. package/dist/tools/update-memory.d.ts.map +1 -0
  126. package/dist/tools/update-memory.js +49 -0
  127. package/dist/tools/update-memory.js.map +1 -0
  128. package/package.json +84 -0
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # Memory MCP
2
+
3
+ <!-- markdownlint-disable MD033 -->
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)
6
+
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
+
9
+ A SQLite-backed MCP server for persistent memory storage, full-text retrieval, and relationship graph traversal.
10
+
11
+ ## Overview
12
+
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.
14
+
15
+ ## Key Features
16
+
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`).
23
+
24
+ ## Requirements
25
+
26
+ - Node.js `>=24`.
27
+ - SQLite with FTS5 support (required at startup).
28
+ - Any MCP client that supports stdio command servers.
29
+
30
+ ## Quick Start
31
+
32
+ Use the npm package directly with `npx`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "memory-mcp": {
38
+ "command": "npx",
39
+ "args": ["-y", "@j0hanz/memory-mcp@latest"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ > [!TIP]
46
+ > The server uses stdio transport only; no HTTP endpoint is exposed.
47
+
48
+ ## Client Configuration
49
+
50
+ <details>
51
+ <summary><b>Install in VS Code</b></summary>
52
+
53
+ [![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)
54
+
55
+ Workspace file `.vscode/mcp.json`:
56
+
57
+ ```json
58
+ {
59
+ "servers": {
60
+ "memory-mcp": {
61
+ "command": "npx",
62
+ "args": ["-y", "@j0hanz/memory-mcp@latest"]
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ </details>
69
+
70
+ <details>
71
+ <summary><b>Install in Claude Desktop / Claude Code</b></summary>
72
+
73
+ `claude_desktop_config.json`:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "memory-mcp": {
79
+ "command": "npx",
80
+ "args": ["-y", "@j0hanz/memory-mcp@latest"]
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ CLI:
87
+
88
+ ```bash
89
+ claude mcp add memory-mcp -- npx -y @j0hanz/memory-mcp@latest
90
+ ```
91
+
92
+ </details>
93
+
94
+ <details>
95
+ <summary><b>Install in Cursor</b></summary>
96
+
97
+ `~/.cursor/mcp.json`:
98
+
99
+ ```json
100
+ {
101
+ "mcpServers": {
102
+ "memory-mcp": {
103
+ "command": "npx",
104
+ "args": ["-y", "@j0hanz/memory-mcp@latest"]
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ </details>
111
+
112
+ ## MCP Surface
113
+
114
+ ### Tools Summary
115
+
116
+ | Tool | Category | Notes |
117
+ | --------------------- | -------- | --------------------------------------- |
118
+ | `store_memory` | Write | Idempotent by content+sorted tags hash |
119
+ | `store_memories` | Write | Batch (1-50), transaction-wrapped |
120
+ | `get_memory` | Read | Hash lookup |
121
+ | `update_memory` | Write | Returns `old_hash` + `new_hash` |
122
+ | `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 |
126
+ | `delete_relationship` | Write | Deletes exact directed edge |
127
+ | `get_relationships` | Read | Direction filter + linked memory fields |
128
+ | `recall` | Read | Search + BFS traversal (`depth` 0-3) |
129
+ | `memory_stats` | Read | Store aggregates and type breakdown |
130
+
131
+ ### `store_memory`
132
+
133
+ Purpose: Store one memory and return its SHA-256 hash.
134
+
135
+ | Name | Type | Required | Default | Description |
136
+ | ------------- | ---------- | -------- | --------- | ---------------------------------------------------------------------------------- |
137
+ | `content` | `string` | Yes | — | Memory content (1-100000 chars) |
138
+ | `tags` | `string[]` | Yes | — | 1-100 tags, each max 50, no whitespace |
139
+ | `memory_type` | enum | No | `general` | `general`, `fact`, `plan`, `decision`, `reflection`, `lesson`, `error`, `gradient` |
140
+ | `importance` | `integer` | No | `0` | Priority 0-10 |
141
+
142
+ Returns: `{ ok, result: { hash, created } }`.
143
+
144
+ ### `store_memories`
145
+
146
+ Purpose: Store multiple memories in one call (max 50 items).
147
+
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` |
151
+
152
+ Returns: `{ ok, result: { items, succeeded, failed } }`.
153
+
154
+ ### `get_memory`
155
+
156
+ Purpose: Retrieve one memory by hash.
157
+
158
+ | Name | Type | Required | Default | Description |
159
+ | ------ | -------- | -------- | ------- | ----------------------------- |
160
+ | `hash` | `string` | Yes | — | 64-char lowercase SHA-256 hex |
161
+
162
+ Returns: `{ ok, result: Memory }` or `{ ok: false, error }` (`E_NOT_FOUND`).
163
+
164
+ ### `update_memory`
165
+
166
+ Purpose: Update content and optionally tags for an existing memory.
167
+
168
+ | Name | Type | Required | Default | Description |
169
+ | --------- | ---------- | -------- | ------------- | -------------------- |
170
+ | `hash` | `string` | Yes | — | Existing memory hash |
171
+ | `content` | `string` | Yes | — | Replacement content |
172
+ | `tags` | `string[]` | No | existing tags | Replacement tags |
173
+
174
+ Returns: `{ ok, result: { old_hash, new_hash } }`.
175
+
176
+ ### `delete_memory`
177
+
178
+ Purpose: Delete one memory by hash.
179
+
180
+ | Name | Type | Required | Default | Description |
181
+ | ------ | -------- | -------- | ------- | ----------- |
182
+ | `hash` | `string` | Yes | — | Memory hash |
183
+
184
+ Returns: `{ ok, result: { hash, deleted } }`.
185
+
186
+ ### `delete_memories`
187
+
188
+ Purpose: Delete multiple memories by hash.
189
+
190
+ | Name | Type | Required | Default | Description |
191
+ | -------- | ---------- | -------- | ------- | ------------------ |
192
+ | `hashes` | `string[]` | Yes | — | 1-50 memory hashes |
193
+
194
+ Returns: `{ ok, result: { items, succeeded, failed } }`.
195
+
196
+ ### `search_memories`
197
+
198
+ Purpose: FTS5 search over content and tags with cursor pagination.
199
+
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 |
205
+
206
+ Returns: `{ ok, result: { memories, total_returned, nextCursor? } }`.
207
+
208
+ ### `create_relationship`
209
+
210
+ Purpose: Create a directed relationship between two memories.
211
+
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) |
217
+
218
+ Returns: `{ ok, result: { created } }`.
219
+
220
+ ### `delete_relationship`
221
+
222
+ Purpose: Delete one directed relationship edge.
223
+
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 |
229
+
230
+ Returns: `{ ok, result: { deleted } }` or `{ ok: false, error }` (`E_NOT_FOUND`).
231
+
232
+ ### `get_relationships`
233
+
234
+ Purpose: Retrieve relationships for a memory, optionally filtered by direction.
235
+
236
+ | Name | Type | Required | Default | Description |
237
+ | ----------- | -------- | -------- | ------- | --------------------------------- |
238
+ | `hash` | `string` | Yes | — | Memory hash |
239
+ | `direction` | enum | No | `both` | `outgoing`, `incoming`, or `both` |
240
+
241
+ Returns: `{ ok, result: { relationships, count } }`.
242
+
243
+ ### `recall`
244
+
245
+ Purpose: Search memories, then traverse connected graph edges up to `depth` hops.
246
+
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 |
253
+
254
+ Returns: `{ ok, result: { memories, graph, depth_reached, nextCursor? } }`.
255
+
256
+ Each item in the `graph` array uses this shape:
257
+
258
+ ```json
259
+ { "from_hash": "...", "to_hash": "...", "relation_type": "..." }
260
+ ```
261
+
262
+ ### `memory_stats`
263
+
264
+ Purpose: Return aggregate memory and relationship stats.
265
+
266
+ | Name | Type | Required | Default | Description |
267
+ | -------- | ---- | -------- | ------- | ------------------ |
268
+ | _(none)_ | — | — | — | Empty input object |
269
+
270
+ Returns: `{ ok, result: { memories, relationships, by_type } }`.
271
+
272
+ ### Resources
273
+
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 |
278
+
279
+ ### Prompts
280
+
281
+ | Name | Arguments | Purpose |
282
+ | ---------- | --------- | -------------------------------------- |
283
+ | `get-help` | none | Returns memory tool usage instructions |
284
+
285
+ ## Configuration
286
+
287
+ ### Environment Variables
288
+
289
+ | Variable | Description | Default | Required |
290
+ | ---------------- | ------------------------- | ----------- | -------- |
291
+ | `MEMORY_DB_PATH` | SQLite database file path | `memory.db` | No |
292
+
293
+ > [!IMPORTANT]
294
+ > If `MEMORY_DB_PATH` is relative (including the default `memory.db`), it resolves from the process working directory.
295
+
296
+ ### Limits and Constraints
297
+
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 |
311
+
312
+ > [!NOTE]
313
+ > Cursor values are base64url-encoded offsets. Treat them as opaque tokens.
314
+
315
+ ## Security
316
+
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.
323
+
324
+ ## Development
325
+
326
+ Install dependencies:
327
+
328
+ ```bash
329
+ npm install
330
+ ```
331
+
332
+ Core scripts:
333
+
334
+ | Script | Command | Purpose |
335
+ | ------------ | -------------------- | -------------------------------------------------------------------- |
336
+ | `build` | `npm run build` | Clean, compile, validate instructions, copy assets, chmod executable |
337
+ | `dev` | `npm run dev` | TypeScript watch mode |
338
+ | `dev:run` | `npm run dev:run` | Run built server with `.env` and file watch |
339
+ | `start` | `npm run start` | Start built server |
340
+ | `test` | `npm run test` | Full build + tests via task runner |
341
+ | `test:fast` | `npm run test:fast` | Run TS tests directly with Node test runner |
342
+ | `lint` | `npm run lint` | ESLint checks |
343
+ | `type-check` | `npm run type-check` | Strict TypeScript checks |
344
+ | `inspector` | `npm run inspector` | Build and open MCP Inspector against stdio server |
345
+
346
+ Inspect with MCP Inspector:
347
+
348
+ ```bash
349
+ npx @modelcontextprotocol/inspector node dist/index.js
350
+ ```
351
+
352
+ ## Troubleshooting
353
+
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.
358
+
359
+ ## License
360
+
361
+ - **MIT**
362
+
363
+ <!-- markdownlint-enable MD033 -->
@@ -0,0 +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" />
36
+ </svg>
@@ -0,0 +1,3 @@
1
+ import type { TypedDb } from '../db/typed.js';
2
+ export declare function createHashCompletionCallback(db: TypedDb): (value: string) => string[];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/completions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQ9C,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,OAAO,GACV,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAQ7B"}
@@ -0,0 +1,17 @@
1
+ const HASH_MAX_LENGTH = 64;
2
+ const HASH_COMPLETION_LIMIT = 101;
3
+ const HASH_COMPLETION_SQL = `SELECT hash FROM memories WHERE hash LIKE ? ESCAPE '\\' ORDER BY hash LIMIT ${HASH_COMPLETION_LIMIT}`;
4
+ // Returns a completion callback for the `hash` URI variable.
5
+ export function createHashCompletionCallback(db) {
6
+ return (value) => {
7
+ const escapedPrefix = escapeLikePattern(value.slice(0, HASH_MAX_LENGTH));
8
+ const rows = db
9
+ .prepare(HASH_COMPLETION_SQL)
10
+ .all(`${escapedPrefix}%`);
11
+ return rows.map((r) => r.hash);
12
+ };
13
+ }
14
+ function escapeLikePattern(value) {
15
+ return value.replace(/[%_\\]/g, '\\$&');
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/completions/index.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,mBAAmB,GAAG,+EAA+E,qBAAqB,EAAE,CAAC;AAEnI,6DAA6D;AAC7D,MAAM,UAAU,4BAA4B,CAC1C,EAAW;IAEX,OAAO,CAAC,KAAa,EAAY,EAAE;QACjC,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,EAAE;aACZ,OAAO,CAAU,mBAAmB,CAAC;aACrC,GAAG,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { DatabaseSync, type SQLTagStore } from 'node:sqlite';
2
+ import { type TypedDb } from './typed.js';
3
+ export declare function initDatabase(path: string): DatabaseSync;
4
+ export declare function initTypedDatabase(path: string): TypedDb;
5
+ export declare function createStatementCache(db: DatabaseSync): SQLTagStore;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AA2FzD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAWvD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,WAAW,CAElE"}
@@ -0,0 +1,102 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { DatabaseSync } from 'node:sqlite';
4
+ import { createTypedDb } from './typed.js';
5
+ const SQLITE_TIMEOUT_MS = 5000;
6
+ const STATEMENT_CACHE_SIZE = 1000;
7
+ const FTS5_CHECK_SQL = 'CREATE VIRTUAL TABLE IF NOT EXISTS __fts5_check USING fts5(x); DROP TABLE __fts5_check;';
8
+ const FTS5_REQUIRED_MESSAGE = 'SQLite FTS5 extension is not available. memory-mcp requires a SQLite build with FTS5 support.';
9
+ const SCHEMA_SQL = `
10
+ CREATE TABLE IF NOT EXISTS memories (
11
+ hash TEXT PRIMARY KEY NOT NULL,
12
+ content TEXT NOT NULL,
13
+ tags TEXT NOT NULL DEFAULT '[]',
14
+ memory_type TEXT NOT NULL DEFAULT 'general',
15
+ importance INTEGER NOT NULL DEFAULT 0,
16
+ created_at TEXT NOT NULL,
17
+ updated_at TEXT NOT NULL
18
+ ) STRICT;
19
+
20
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
21
+ content,
22
+ tags,
23
+ content='memories',
24
+ content_rowid='rowid'
25
+ );
26
+
27
+ CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
28
+ INSERT INTO memories_fts(rowid, content, tags)
29
+ VALUES (new.rowid, new.content, new.tags);
30
+ END;
31
+
32
+ CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
33
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
34
+ VALUES ('delete', old.rowid, old.content, old.tags);
35
+ INSERT INTO memories_fts(rowid, content, tags)
36
+ VALUES (new.rowid, new.content, new.tags);
37
+ END;
38
+
39
+ CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
40
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
41
+ VALUES ('delete', old.rowid, old.content, old.tags);
42
+ END;
43
+
44
+ CREATE TABLE IF NOT EXISTS relationships (
45
+ from_hash TEXT NOT NULL REFERENCES memories(hash) ON DELETE CASCADE,
46
+ to_hash TEXT NOT NULL REFERENCES memories(hash) ON DELETE CASCADE,
47
+ relation_type TEXT NOT NULL,
48
+ created_at TEXT NOT NULL,
49
+ PRIMARY KEY (from_hash, to_hash, relation_type)
50
+ ) STRICT;
51
+
52
+ CREATE INDEX IF NOT EXISTS idx_memories_importance
53
+ ON memories(importance DESC);
54
+
55
+ CREATE INDEX IF NOT EXISTS idx_memories_created
56
+ ON memories(created_at DESC);
57
+
58
+ CREATE INDEX IF NOT EXISTS idx_relationships_from
59
+ ON relationships(from_hash);
60
+
61
+ CREATE INDEX IF NOT EXISTS idx_relationships_to
62
+ ON relationships(to_hash);
63
+ `;
64
+ function assertFts5Available(db) {
65
+ try {
66
+ db.exec(FTS5_CHECK_SQL);
67
+ }
68
+ catch {
69
+ throw new Error(FTS5_REQUIRED_MESSAGE);
70
+ }
71
+ }
72
+ function ensureParentDir(path) {
73
+ const isInMemoryPath = path === ':memory:';
74
+ if (isInMemoryPath) {
75
+ return;
76
+ }
77
+ mkdirSync(dirname(path), { recursive: true });
78
+ }
79
+ function configureDatabase(db) {
80
+ // Enable defensive mode (SQLite v3.39+ / Node 24.12+: prevents deliberate DB corruption)
81
+ db.exec('PRAGMA defensive = ON');
82
+ // Verify FTS5 support
83
+ assertFts5Available(db);
84
+ db.exec(SCHEMA_SQL);
85
+ }
86
+ export function initDatabase(path) {
87
+ ensureParentDir(path);
88
+ const db = new DatabaseSync(path, {
89
+ enableForeignKeyConstraints: true,
90
+ timeout: SQLITE_TIMEOUT_MS,
91
+ });
92
+ configureDatabase(db);
93
+ return db;
94
+ }
95
+ export function initTypedDatabase(path) {
96
+ const db = initDatabase(path);
97
+ return createTypedDb(db);
98
+ }
99
+ export function createStatementCache(db) {
100
+ return db.createTagStore(STATEMENT_CACHE_SIZE);
101
+ }
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAoB,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAC;AAEzD,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAClC,MAAM,cAAc,GAClB,yFAAyF,CAAC;AAC5F,MAAM,qBAAqB,GACzB,+FAA+F,CAAC;AAElG,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDlB,CAAC;AAEF,SAAS,mBAAmB,CAAC,EAAgB;IAC3C,IAAI,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,cAAc,GAAG,IAAI,KAAK,UAAU,CAAC;IAC3C,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;IACT,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,EAAgB;IACzC,yFAAyF;IACzF,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAEjC,sBAAsB;IACtB,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAExB,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,eAAe,CAAC,IAAI,CAAC,CAAC;IAEtB,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE;QAChC,2BAA2B,EAAE,IAAI;QACjC,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;IAEH,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAEtB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,aAAa,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAgB;IACnD,OAAO,EAAE,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { DatabaseSync, SQLInputValue, StatementResultingChanges } from 'node:sqlite';
2
+ export interface TypedStatement<T> {
3
+ all(...params: SQLInputValue[]): T[];
4
+ get(...params: SQLInputValue[]): T | undefined;
5
+ run(...params: SQLInputValue[]): StatementResultingChanges;
6
+ }
7
+ export declare class TypedDb {
8
+ private readonly db;
9
+ constructor(db: DatabaseSync);
10
+ prepare<T>(sql: string): TypedStatement<T>;
11
+ exec(sql: string): void;
12
+ close(): void;
13
+ }
14
+ export declare function createTypedDb(db: DatabaseSync): TypedDb;
15
+ //# sourceMappingURL=typed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed.d.ts","sourceRoot":"","sources":["../../src/db/typed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC;IACrC,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;IAC/C,GAAG,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,yBAAyB,CAAC;CAC5D;AAED,qBAAa,OAAO;IACN,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAE7C,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAS1C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvB,KAAK,IAAI,IAAI;CAGd;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,CAEvD"}
@@ -0,0 +1,24 @@
1
+ export class TypedDb {
2
+ db;
3
+ constructor(db) {
4
+ this.db = db;
5
+ }
6
+ prepare(sql) {
7
+ const stmt = this.db.prepare(sql);
8
+ return {
9
+ all: (...params) => stmt.all(...params),
10
+ get: (...params) => stmt.get(...params),
11
+ run: (...params) => stmt.run(...params),
12
+ };
13
+ }
14
+ exec(sql) {
15
+ this.db.exec(sql);
16
+ }
17
+ close() {
18
+ this.db.close();
19
+ }
20
+ }
21
+ export function createTypedDb(db) {
22
+ return new TypedDb(db);
23
+ }
24
+ //# sourceMappingURL=typed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed.js","sourceRoot":"","sources":["../../src/db/typed.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,OAAO;IACW;IAA7B,YAA6B,EAAgB;QAAhB,OAAE,GAAF,EAAE,CAAc;IAAG,CAAC;IAEjD,OAAO,CAAI,GAAW;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO;YACL,GAAG,EAAE,CAAC,GAAG,MAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAQ;YAC/D,GAAG,EAAE,CAAC,GAAG,MAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAkB;YACzE,GAAG,EAAE,CAAC,GAAG,MAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAW;QACd,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,EAAgB;IAC5C,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import process from 'node:process';
4
+ import { initTypedDatabase } from './db/index.js';
5
+ import { createServer } from './server.js';
6
+ const MEMORY_DB_PATH = process.env['MEMORY_DB_PATH'] ?? 'memory.db';
7
+ const SHUTDOWN_SIGNALS = ['SIGINT', 'SIGTERM'];
8
+ const SHUTDOWN_TIMEOUT_MS = 3000;
9
+ const FORCED_EXIT_CODE = 1;
10
+ const CLEAN_EXIT_CODE = 0;
11
+ function formatFatalError(err) {
12
+ return err instanceof Error ? err.message : String(err);
13
+ }
14
+ function registerShutdownHandlers(shutdown) {
15
+ for (const signal of SHUTDOWN_SIGNALS) {
16
+ process.on(signal, shutdown);
17
+ }
18
+ }
19
+ function scheduleForcedShutdown() {
20
+ const timer = setTimeout(() => {
21
+ process.stderr.write('Shutdown timed out, forcing exit.\n');
22
+ process.exit(FORCED_EXIT_CODE);
23
+ }, SHUTDOWN_TIMEOUT_MS);
24
+ timer.unref();
25
+ return timer;
26
+ }
27
+ async function runShutdown(server, db) {
28
+ const timer = scheduleForcedShutdown();
29
+ try {
30
+ await server.close();
31
+ }
32
+ catch {
33
+ // ignore close errors
34
+ }
35
+ db.close();
36
+ clearTimeout(timer);
37
+ process.exit(CLEAN_EXIT_CODE);
38
+ }
39
+ function createShutdownHandler(server, db) {
40
+ let isShuttingDown = false;
41
+ return () => {
42
+ if (isShuttingDown)
43
+ return;
44
+ isShuttingDown = true;
45
+ void runShutdown(server, db);
46
+ };
47
+ }
48
+ async function main() {
49
+ const db = initTypedDatabase(MEMORY_DB_PATH);
50
+ const server = createServer(db);
51
+ const transport = new StdioServerTransport();
52
+ const shutdown = createShutdownHandler(server, db);
53
+ registerShutdownHandlers(shutdown);
54
+ await server.connect(transport);
55
+ }
56
+ main().catch((err) => {
57
+ process.stderr.write(`Fatal error: ${formatFatalError(err)}\n`);
58
+ process.exit(FORCED_EXIT_CODE);
59
+ });
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,WAAW,CAAC;AACpE,MAAM,gBAAgB,GAAqB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AACjE,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACjC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,SAAS,gBAAgB,CAAC,GAAY;IACpC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,wBAAwB,CAAC,QAAoB;IACpD,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACtC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC,EAAE,mBAAmB,CAAC,CAAC;IACxB,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,MAAuC,EACvC,EAAwC;IAExC,MAAM,KAAK,GAAG,sBAAsB,EAAE,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IACD,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,YAAY,CAAC,KAAK,CAAC,CAAC;IACpB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAAuC,EACvC,EAAwC;IAExC,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,OAAO,GAAG,EAAE;QACV,IAAI,cAAc;YAAE,OAAO;QAC3B,cAAc,GAAG,IAAI,CAAC;QACtB,KAAK,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnD,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAEnC,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC"}