memorandum-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 (52) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +237 -0
  3. package/README.ru.md +237 -0
  4. package/dist/config.d.ts +36 -0
  5. package/dist/config.js +63 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/document-store.d.ts +145 -0
  8. package/dist/document-store.js +682 -0
  9. package/dist/document-store.js.map +1 -0
  10. package/dist/document-tools.d.ts +10 -0
  11. package/dist/document-tools.js +101 -0
  12. package/dist/document-tools.js.map +1 -0
  13. package/dist/document-types.d.ts +147 -0
  14. package/dist/document-types.js +125 -0
  15. package/dist/document-types.js.map +1 -0
  16. package/dist/embedder.d.ts +55 -0
  17. package/dist/embedder.js +152 -0
  18. package/dist/embedder.js.map +1 -0
  19. package/dist/embedding-queue.d.ts +66 -0
  20. package/dist/embedding-queue.js +152 -0
  21. package/dist/embedding-queue.js.map +1 -0
  22. package/dist/errors.d.ts +26 -0
  23. package/dist/errors.js +46 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/index.d.ts +9 -0
  26. package/dist/index.js +147 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/logger.d.ts +12 -0
  29. package/dist/logger.js +22 -0
  30. package/dist/logger.js.map +1 -0
  31. package/dist/semantic-index.d.ts +126 -0
  32. package/dist/semantic-index.js +427 -0
  33. package/dist/semantic-index.js.map +1 -0
  34. package/dist/semantic-tools.d.ts +10 -0
  35. package/dist/semantic-tools.js +80 -0
  36. package/dist/semantic-tools.js.map +1 -0
  37. package/dist/semantic-types.d.ts +161 -0
  38. package/dist/semantic-types.js +101 -0
  39. package/dist/semantic-types.js.map +1 -0
  40. package/dist/store.d.ts +130 -0
  41. package/dist/store.js +389 -0
  42. package/dist/store.js.map +1 -0
  43. package/dist/tools.d.ts +15 -0
  44. package/dist/tools.js +104 -0
  45. package/dist/tools.js.map +1 -0
  46. package/dist/types.d.ts +97 -0
  47. package/dist/types.js +88 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/vector-store.d.ts +85 -0
  50. package/dist/vector-store.js +241 -0
  51. package/dist/vector-store.js.map +1 -0
  52. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # memorandum-mcp
2
+
3
+ [README on Russian](README.ru.md)
4
+
5
+ MCP server with persistent memory for AI agents. Two memory types:
6
+
7
+ - **Facts** — short key-value records with LRU eviction and optional TTL
8
+ - **Documents** — structured storage with metadata, tags, topics, and semantic search
9
+
10
+ ## Features
11
+
12
+ - LRU cache with configurable size and per-entry TTL
13
+ - Namespace isolation for facts
14
+ - Document storage with YAML index and Markdown frontmatter
15
+ - Binary document support (base64 encoding, SHA-256 integrity)
16
+ - File import with automatic MIME type detection
17
+ - Semantic search via local HuggingFace embedding model (multilingual-e5-small)
18
+ - Debounced batch embedding queue with failure tracking and retry
19
+ - Atomic file writes (temp file + rename) for crash safety
20
+ - Autosave with configurable interval
21
+ - Export/import for facts backup and migration
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install memorandum-mcp
27
+ ```
28
+
29
+ Or run directly with npx:
30
+
31
+ ```bash
32
+ npx memorandum-mcp
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ Create `.memorandum/config.yaml` in your working directory:
38
+
39
+ ```yaml
40
+ max_entries: 2048
41
+ autosave_interval_seconds: 60
42
+ storage_dir: .memorandum/
43
+ ```
44
+
45
+ ### All options
46
+
47
+ | Option | Default | Description |
48
+ |--------|---------|-------------|
49
+ | `max_entries` | `1000` | Maximum facts in LRU cache |
50
+ | `autosave_interval_seconds` | `300` | Autosave interval (0 to disable) |
51
+ | `storage_dir` | `.memorandum` | Base storage directory |
52
+ | `max_document_size` | `16777216` | Max document body size in bytes (16 MiB) |
53
+ | `semantic_enabled` | `true` | Enable semantic search |
54
+ | `semantic_model` | `Xenova/multilingual-e5-small` | HuggingFace embedding model |
55
+ | `semantic_model_dtype` | `q8` | Model quantization type |
56
+ | `semantic_debounce_seconds` | `10` | Delay before batch embedding |
57
+ | `semantic_max_queue_size` | `200` | Queue size before forced batch |
58
+ | `semantic_max_retries` | `3` | Max retries per failed embedding |
59
+
60
+ ### Environment variables
61
+
62
+ | Variable | Description |
63
+ |----------|-------------|
64
+ | `MEMORANDUM_STORAGE_DIR` | Override storage directory path |
65
+ | `MEMORANDUM_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` |
66
+
67
+ ## Storage layout
68
+
69
+ ```
70
+ .memorandum/
71
+ config.yaml # Configuration
72
+ .gitignore # Auto-generated, excludes cache/
73
+ facts/
74
+ facts.json # LRU cache dump
75
+ documents/
76
+ _index.yaml # Document index
77
+ doc-001.md # Inline document (with YAML frontmatter)
78
+ doc-002.yaml # Binary document sidecar
79
+ blobs/
80
+ doc-002.png # Binary blob
81
+ cache/
82
+ vector-index.json # Semantic search index
83
+ ```
84
+
85
+ ## MCP Tools
86
+
87
+ ### Facts (3 tools)
88
+
89
+ #### `memory_write`
90
+
91
+ Write or update a fact.
92
+
93
+ ```json
94
+ {
95
+ "key": "server-web-01",
96
+ "value": { "ip": "192.168.1.10", "role": "webserver" },
97
+ "namespace": "servers",
98
+ "ttl_seconds": 3600
99
+ }
100
+ ```
101
+
102
+ #### `memory_read`
103
+
104
+ Read a fact by key and namespace.
105
+
106
+ ```json
107
+ { "key": "server-web-01", "namespace": "servers" }
108
+ ```
109
+
110
+ #### `memory_manage`
111
+
112
+ Manage facts with actions: `list`, `search`, `namespaces`, `delete`, `delete_namespace`, `export`, `import`, `sync`.
113
+
114
+ ```json
115
+ { "action": "list", "namespace": "servers", "pattern": "web-*", "include_values": true }
116
+ ```
117
+
118
+ ```json
119
+ { "action": "search", "query": "webserver", "limit": 5 }
120
+ ```
121
+
122
+ ```json
123
+ { "action": "sync" }
124
+ ```
125
+
126
+ ### Documents (4 tools)
127
+
128
+ #### `document_write`
129
+
130
+ Create or update a document.
131
+
132
+ ```json
133
+ {
134
+ "title": "Server inventory",
135
+ "body": "# Servers\n\n- web-01: 192.168.1.10\n- db-01: 192.168.1.20",
136
+ "topic": "infrastructure",
137
+ "tags": ["servers", "inventory"],
138
+ "content_type": "text/markdown"
139
+ }
140
+ ```
141
+
142
+ Update by providing `id`:
143
+
144
+ ```json
145
+ { "id": "doc-001", "tags": ["servers", "inventory", "updated"] }
146
+ ```
147
+
148
+ Import a local file:
149
+
150
+ ```json
151
+ { "file_path": "/path/to/document.md" }
152
+ ```
153
+
154
+ #### `document_read`
155
+
156
+ ```json
157
+ { "id": "doc-001", "include_body": true }
158
+ ```
159
+
160
+ #### `document_list`
161
+
162
+ ```json
163
+ { "tag": "servers", "search": "inventory", "limit": 10 }
164
+ ```
165
+
166
+ #### `document_delete`
167
+
168
+ ```json
169
+ { "id": "doc-001" }
170
+ ```
171
+
172
+ ### Semantic Search (2 tools)
173
+
174
+ #### `semantic_search`
175
+
176
+ Search by meaning across facts and documents.
177
+
178
+ ```json
179
+ {
180
+ "query": "web server configuration",
181
+ "source": "all",
182
+ "limit": 10,
183
+ "threshold": 0.3
184
+ }
185
+ ```
186
+
187
+ #### `semantic_reindex`
188
+
189
+ Rebuild the vector index.
190
+
191
+ ```json
192
+ { "source": "all" }
193
+ ```
194
+
195
+ ## Usage with Claude Desktop
196
+
197
+ Add to your `claude_desktop_config.json`:
198
+
199
+ ```json
200
+ {
201
+ "mcpServers": {
202
+ "memorandum": {
203
+ "command": "npx",
204
+ "args": ["memorandum-mcp"]
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ ## Usage with Claude Code
211
+
212
+ Add to your `.claude/settings.json` or project settings:
213
+
214
+ ```json
215
+ {
216
+ "mcpServers": {
217
+ "memorandum": {
218
+ "command": "npx",
219
+ "args": ["memorandum-mcp"]
220
+ }
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## Development
226
+
227
+ ```bash
228
+ git clone https://github.com/lionsoftware/memorandum.git
229
+ cd memorandum
230
+ npm install
231
+ npm run build
232
+ npm test
233
+ ```
234
+
235
+ ## License
236
+
237
+ [GPL-3.0](LICENSE)
package/README.ru.md ADDED
@@ -0,0 +1,237 @@
1
+ # memorandum-mcp
2
+
3
+ [README in English](README.md)
4
+
5
+ MCP-сервер с персистентной памятью для AI-агентов. Два типа памяти:
6
+
7
+ - **Факты** — короткие key-value записи с LRU-вытеснением и опциональным TTL
8
+ - **Документы** — структурированное хранилище с метаданными, тегами, топиками и семантическим поиском
9
+
10
+ ## Возможности
11
+
12
+ - LRU-кэш с настраиваемым размером и TTL для каждой записи
13
+ - Изоляция фактов по пространствам имён (namespaces)
14
+ - Хранение документов с YAML-индексом и Markdown-фронтматтером
15
+ - Поддержка бинарных документов (base64, проверка целостности SHA-256)
16
+ - Импорт файлов с автоматическим определением MIME-типа
17
+ - Семантический поиск через локальную модель HuggingFace (multilingual-e5-small)
18
+ - Очередь пакетного эмбеддинга с debounce, отслеживанием ошибок и повторными попытками
19
+ - Атомарная запись файлов (временный файл + rename) для защиты от сбоев
20
+ - Автосохранение с настраиваемым интервалом
21
+ - Экспорт/импорт фактов для резервного копирования и миграции
22
+
23
+ ## Установка
24
+
25
+ ```bash
26
+ npm install memorandum-mcp
27
+ ```
28
+
29
+ Или запуск напрямую через npx:
30
+
31
+ ```bash
32
+ npx memorandum-mcp
33
+ ```
34
+
35
+ ## Конфигурация
36
+
37
+ Создайте `.memorandum/config.yaml` в рабочей директории:
38
+
39
+ ```yaml
40
+ max_entries: 2048
41
+ autosave_interval_seconds: 60
42
+ storage_dir: .memorandum/
43
+ ```
44
+
45
+ ### Все параметры
46
+
47
+ | Параметр | По умолчанию | Описание |
48
+ |----------|-------------|----------|
49
+ | `max_entries` | `1000` | Максимум фактов в LRU-кэше |
50
+ | `autosave_interval_seconds` | `300` | Интервал автосохранения (0 — отключить) |
51
+ | `storage_dir` | `.memorandum` | Базовая директория хранения |
52
+ | `max_document_size` | `16777216` | Максимальный размер документа в байтах (16 МиБ) |
53
+ | `semantic_enabled` | `true` | Включить семантический поиск |
54
+ | `semantic_model` | `Xenova/multilingual-e5-small` | Модель эмбеддингов HuggingFace |
55
+ | `semantic_model_dtype` | `q8` | Тип квантизации модели |
56
+ | `semantic_debounce_seconds` | `10` | Задержка перед пакетным эмбеддингом |
57
+ | `semantic_max_queue_size` | `200` | Размер очереди для принудительного батча |
58
+ | `semantic_max_retries` | `3` | Максимум повторных попыток на запись |
59
+
60
+ ### Переменные окружения
61
+
62
+ | Переменная | Описание |
63
+ |------------|----------|
64
+ | `MEMORANDUM_STORAGE_DIR` | Переопределить путь к директории хранения |
65
+ | `MEMORANDUM_LOG_LEVEL` | Уровень логирования: `debug`, `info`, `warn`, `error` |
66
+
67
+ ## Структура хранилища
68
+
69
+ ```
70
+ .memorandum/
71
+ config.yaml # Конфигурация
72
+ .gitignore # Автоматически создаётся, исключает cache/
73
+ facts/
74
+ facts.json # Дамп LRU-кэша
75
+ documents/
76
+ _index.yaml # Индекс документов
77
+ doc-001.md # Текстовый документ (с YAML-фронтматтером)
78
+ doc-002.yaml # Сайдкар бинарного документа
79
+ blobs/
80
+ doc-002.png # Бинарный блоб
81
+ cache/
82
+ vector-index.json # Индекс семантического поиска
83
+ ```
84
+
85
+ ## MCP-инструменты
86
+
87
+ ### Факты (3 инструмента)
88
+
89
+ #### `memory_write`
90
+
91
+ Записать или обновить факт.
92
+
93
+ ```json
94
+ {
95
+ "key": "server-web-01",
96
+ "value": { "ip": "192.168.1.10", "role": "webserver" },
97
+ "namespace": "servers",
98
+ "ttl_seconds": 3600
99
+ }
100
+ ```
101
+
102
+ #### `memory_read`
103
+
104
+ Прочитать факт по ключу и пространству имён.
105
+
106
+ ```json
107
+ { "key": "server-web-01", "namespace": "servers" }
108
+ ```
109
+
110
+ #### `memory_manage`
111
+
112
+ Управление фактами. Действия: `list`, `search`, `namespaces`, `delete`, `delete_namespace`, `export`, `import`, `sync`.
113
+
114
+ ```json
115
+ { "action": "list", "namespace": "servers", "pattern": "web-*", "include_values": true }
116
+ ```
117
+
118
+ ```json
119
+ { "action": "search", "query": "webserver", "limit": 5 }
120
+ ```
121
+
122
+ ```json
123
+ { "action": "sync" }
124
+ ```
125
+
126
+ ### Документы (4 инструмента)
127
+
128
+ #### `document_write`
129
+
130
+ Создать или обновить документ.
131
+
132
+ ```json
133
+ {
134
+ "title": "Инвентарь серверов",
135
+ "body": "# Серверы\n\n- web-01: 192.168.1.10\n- db-01: 192.168.1.20",
136
+ "topic": "infrastructure",
137
+ "tags": ["servers", "inventory"],
138
+ "content_type": "text/markdown"
139
+ }
140
+ ```
141
+
142
+ Обновление по `id`:
143
+
144
+ ```json
145
+ { "id": "doc-001", "tags": ["servers", "inventory", "updated"] }
146
+ ```
147
+
148
+ Импорт локального файла:
149
+
150
+ ```json
151
+ { "file_path": "/path/to/document.md" }
152
+ ```
153
+
154
+ #### `document_read`
155
+
156
+ ```json
157
+ { "id": "doc-001", "include_body": true }
158
+ ```
159
+
160
+ #### `document_list`
161
+
162
+ ```json
163
+ { "tag": "servers", "search": "inventory", "limit": 10 }
164
+ ```
165
+
166
+ #### `document_delete`
167
+
168
+ ```json
169
+ { "id": "doc-001" }
170
+ ```
171
+
172
+ ### Семантический поиск (2 инструмента)
173
+
174
+ #### `semantic_search`
175
+
176
+ Поиск по смыслу среди фактов и документов.
177
+
178
+ ```json
179
+ {
180
+ "query": "конфигурация веб-сервера",
181
+ "source": "all",
182
+ "limit": 10,
183
+ "threshold": 0.3
184
+ }
185
+ ```
186
+
187
+ #### `semantic_reindex`
188
+
189
+ Перестроить векторный индекс.
190
+
191
+ ```json
192
+ { "source": "all" }
193
+ ```
194
+
195
+ ## Использование с Claude Desktop
196
+
197
+ Добавьте в `claude_desktop_config.json`:
198
+
199
+ ```json
200
+ {
201
+ "mcpServers": {
202
+ "memorandum": {
203
+ "command": "npx",
204
+ "args": ["memorandum-mcp"]
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ ## Использование с Claude Code
211
+
212
+ Добавьте в `.claude/settings.json` или настройки проекта:
213
+
214
+ ```json
215
+ {
216
+ "mcpServers": {
217
+ "memorandum": {
218
+ "command": "npx",
219
+ "args": ["memorandum-mcp"]
220
+ }
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## Разработка
226
+
227
+ ```bash
228
+ git clone https://github.com/lionsoftware/memorandum.git
229
+ cd memorandum
230
+ npm install
231
+ npm run build
232
+ npm test
233
+ ```
234
+
235
+ ## Лицензия
236
+
237
+ [GPL-3.0](LICENSE)
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Configuration schema and loader for Memorandum MCP server.
3
+ *
4
+ * Config file: {storage_dir}/config.yaml
5
+ */
6
+ import { z } from 'zod';
7
+ /** Zod schema defining all configuration options with defaults. */
8
+ export declare const ConfigSchema: z.ZodObject<{
9
+ max_entries: z.ZodDefault<z.ZodNumber>;
10
+ autosave_interval_seconds: z.ZodDefault<z.ZodNumber>;
11
+ storage_dir: z.ZodDefault<z.ZodString>;
12
+ max_document_size: z.ZodDefault<z.ZodNumber>;
13
+ semantic_model: z.ZodDefault<z.ZodString>;
14
+ semantic_model_dtype: z.ZodDefault<z.ZodString>;
15
+ semantic_enabled: z.ZodDefault<z.ZodBoolean>;
16
+ semantic_debounce_seconds: z.ZodDefault<z.ZodNumber>;
17
+ semantic_max_queue_size: z.ZodDefault<z.ZodNumber>;
18
+ semantic_max_retries: z.ZodDefault<z.ZodNumber>;
19
+ }, z.core.$strip>;
20
+ /** Inferred configuration type from {@link ConfigSchema}. */
21
+ export type Config = z.infer<typeof ConfigSchema>;
22
+ /** Derived paths from config. */
23
+ export interface ResolvedPaths {
24
+ storageDir: string;
25
+ factsPath: string;
26
+ documentsPath: string;
27
+ cachePath: string;
28
+ configPath: string;
29
+ }
30
+ /** Resolve all storage paths from the base storage_dir. */
31
+ export declare function resolvePaths(storageDir: string): ResolvedPaths;
32
+ /** Load config from YAML file, falling back to defaults. */
33
+ export declare function loadConfig(storageDir?: string): {
34
+ config: Config;
35
+ paths: ResolvedPaths;
36
+ };
package/dist/config.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Configuration schema and loader for Memorandum MCP server.
3
+ *
4
+ * Config file: {storage_dir}/config.yaml
5
+ */
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { z } from 'zod';
9
+ import { parse as yamlParse } from 'yaml';
10
+ /** Zod schema defining all configuration options with defaults. */
11
+ export const ConfigSchema = z.object({
12
+ /** Maximum number of facts in memory (LRU eviction threshold). */
13
+ max_entries: z.number().int().positive().default(1000),
14
+ /** Autosave interval in seconds (0 to disable). */
15
+ autosave_interval_seconds: z.number().int().nonnegative().default(300),
16
+ /** Base storage directory. */
17
+ storage_dir: z.string().default('.memorandum'),
18
+ /** Maximum document body size in bytes (default: 16 MiB). */
19
+ max_document_size: z.number().int().positive().default(16 * 1024 * 1024),
20
+ /** Embedding model ID for semantic search. */
21
+ semantic_model: z.string().default('Xenova/multilingual-e5-small'),
22
+ /** Model quantization dtype (e.g. q8, fp32). */
23
+ semantic_model_dtype: z.string().default('q8'),
24
+ /** Enable or disable semantic search. */
25
+ semantic_enabled: z.boolean().default(true),
26
+ /** Debounce delay in seconds before batch embedding (0 = immediate). */
27
+ semantic_debounce_seconds: z.number().int().nonnegative().default(10),
28
+ /** Maximum pending items in embedding queue before forced batch. */
29
+ semantic_max_queue_size: z.number().int().positive().default(200),
30
+ /** Maximum embedding retry attempts per item before marking as failed. */
31
+ semantic_max_retries: z.number().int().positive().default(3),
32
+ });
33
+ /** Resolve all storage paths from the base storage_dir. */
34
+ export function resolvePaths(storageDir) {
35
+ return {
36
+ storageDir,
37
+ factsPath: join(storageDir, 'facts', 'facts.json'),
38
+ documentsPath: join(storageDir, 'documents'),
39
+ cachePath: join(storageDir, 'cache'),
40
+ configPath: join(storageDir, 'config.yaml'),
41
+ };
42
+ }
43
+ /** Load config from YAML file, falling back to defaults. */
44
+ export function loadConfig(storageDir) {
45
+ const baseDir = storageDir ?? '.memorandum';
46
+ const configPath = join(baseDir, 'config.yaml');
47
+ let rawConfig = {};
48
+ if (existsSync(configPath)) {
49
+ const content = readFileSync(configPath, 'utf-8');
50
+ const parsed = yamlParse(content);
51
+ if (typeof parsed === 'object' && parsed !== null) {
52
+ rawConfig = parsed;
53
+ }
54
+ }
55
+ // Ensure storage_dir is set
56
+ if (!rawConfig.storage_dir) {
57
+ rawConfig.storage_dir = baseDir;
58
+ }
59
+ const config = ConfigSchema.parse(rawConfig);
60
+ const paths = resolvePaths(config.storage_dir);
61
+ return { config, paths };
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}