rag-memory-epf-mcp 3.5.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -4
- package/dist/index.d.ts +96 -9
- package/dist/index.js +1324 -270
- package/dist/src/backfillCoordinator.d.ts +59 -0
- package/dist/src/backfillCoordinator.js +552 -0
- package/dist/src/backup/preflight.d.ts +5 -0
- package/dist/src/backup/preflight.js +185 -0
- package/dist/src/embeddingGate.d.ts +68 -0
- package/dist/src/embeddingGate.js +227 -0
- package/dist/src/migrations/migrations.d.ts +2 -0
- package/dist/src/migrations/migrations.js +181 -0
- package/dist/src/modelCache.d.ts +33 -0
- package/dist/src/modelCache.js +235 -0
- package/dist/src/observations/history.d.ts +8 -0
- package/dist/src/observations/history.js +64 -0
- package/dist/src/observations/lifecycle.d.ts +45 -0
- package/dist/src/observations/lifecycle.js +101 -0
- package/dist/src/observations/projection.d.ts +3 -0
- package/dist/src/observations/projection.js +31 -0
- package/dist/src/observations/schema.d.ts +1 -0
- package/dist/src/observations/schema.js +115 -0
- package/dist/src/tools/graph-query-tools.js +29 -5
- package/dist/src/tools/knowledge-graph-tools.d.ts +14 -0
- package/dist/src/tools/knowledge-graph-tools.js +244 -5
- package/dist/src/tools/tool-registry.d.ts +7 -0
- package/dist/src/tools/tool-registry.js +43 -3
- package/dist/src/tools/types.d.ts +1 -0
- package/docs/UPDATING.md +178 -0
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -13,7 +13,8 @@ A **project-local RAG memory** MCP server — knowledge graph + multilingual vec
|
|
|
13
13
|
- **3-signal hybrid search** — vector similarity (bge-m3, 1024-dim) + FTS5 BM25 keyword matching + knowledge graph re-ranking, combined via Reciprocal Rank Fusion
|
|
14
14
|
- **100+ languages** — Korean, Chinese, Japanese, Arabic, and more. Cross-lingual search works out of the box.
|
|
15
15
|
- **Graph-aware scoring** — per-entity geometric decay (0.5^i) with hard cap prevents any single document from dominating results
|
|
16
|
-
- **
|
|
16
|
+
- **38 MCP tools** — knowledge graph CRUD, observation lifecycle (correct / retract / history), document pipeline, hybrid search, multi-hop traversal, graph analytics (centrality / community detection / structure), export/import, temporal queries
|
|
17
|
+
- **Observations that hold their history** — corrections supersede instead of overwrite, search returns only current facts, and every revision keeps its provenance
|
|
17
18
|
- **Codepoint-safe chunking** — chunk offsets are Unicode codepoints, language-neutral across SQL `substr`, Python slicing, and JS `[...str]` iteration. Korean/CJK/emoji documents stay aligned. Verified by a publish-time invariant test.
|
|
18
19
|
- **SQLite optimized** — WAL mode, 32MB cache, 256MB mmap, FTS5 triggers, 7 indexes
|
|
19
20
|
- **MCP SDK 1.27.1** — Tool Annotations (readOnly/destructive/idempotent), latest protocol 2025-11-25
|
|
@@ -36,7 +37,7 @@ A **project-local RAG memory** MCP server — knowledge graph + multilingual vec
|
|
|
36
37
|
|
|
37
38
|
Place this `.mcp.json` in each project folder with its own `DB_FILE_PATH`. Each project maintains completely isolated memory.
|
|
38
39
|
|
|
39
|
-
## Tools (
|
|
40
|
+
## Tools (38)
|
|
40
41
|
|
|
41
42
|
### Knowledge Graph (7)
|
|
42
43
|
| Tool | Description | Annotation |
|
|
@@ -47,7 +48,18 @@ Place this `.mcp.json` in each project folder with its own `DB_FILE_PATH`. Each
|
|
|
47
48
|
| `updateRelations` | Update relationship confidence and metadata | idempotent |
|
|
48
49
|
| `deleteEntities` | Remove entities and relationships | destructive |
|
|
49
50
|
| `deleteRelations` | Remove specific relationships | destructive |
|
|
50
|
-
| `deleteObservations` |
|
|
51
|
+
| `deleteObservations` | **Deprecated** soft-retract shim — see Observation Lifecycle | destructive |
|
|
52
|
+
|
|
53
|
+
### Observation Lifecycle (7)
|
|
54
|
+
| Tool | Description | Annotation |
|
|
55
|
+
|------|------------|------------|
|
|
56
|
+
| `correctObservation` | Supersede a revision with corrected text, keeping the old one | |
|
|
57
|
+
| `retractObservation` | `active` → `retracted` (hidden from search, kept in history) | |
|
|
58
|
+
| `restoreObservation` | `retracted` → `active` | |
|
|
59
|
+
| `approveObservation` | `provisional` → `active` | |
|
|
60
|
+
| `declineObservation` | `provisional` → `retracted` (reason required) | |
|
|
61
|
+
| `purgeObservation` | Physically delete a revision and its successors (`confirm='PURGE'`) | destructive |
|
|
62
|
+
| `getObservationHistory` | Every revision, status, provenance and event | read-only |
|
|
51
63
|
|
|
52
64
|
### Document Pipeline (9)
|
|
53
65
|
| Tool | Description | Annotation |
|
|
@@ -121,7 +133,8 @@ storeDocument(id, content, metadata)
|
|
|
121
133
|
│ │ ├── chunks (sqlite-vec, 1024-dim) │ │
|
|
122
134
|
│ │ ├── entity_embeddings (sqlite-vec) │ │
|
|
123
135
|
│ │ ├── entities_fts + chunks_fts (FTS5) │ │
|
|
124
|
-
│ │
|
|
136
|
+
│ │ ├── observation lifecycle (4 tables) │ │
|
|
137
|
+
│ │ └── 13 migrations (auto-applied) │ │
|
|
125
138
|
│ └────────────────────────────────────────┘ │
|
|
126
139
|
│ │
|
|
127
140
|
│ bge-m3 (ONNX, 100+ langs) │
|
|
@@ -134,9 +147,58 @@ storeDocument(id, content, metadata)
|
|
|
134
147
|
|----------|---------|-------------|
|
|
135
148
|
| `DB_FILE_PATH` | `rag-memory.db` (server dir) | Path to project-local SQLite database |
|
|
136
149
|
| `EMBEDDING_MODEL` | `Xenova/bge-m3` | HuggingFace model ID for embeddings |
|
|
150
|
+
| `RAG_MEMORY_EMBEDDINGS` | `lazy` | Boot mode: `lazy` (connect instantly, model loads in background), `eager` (wait for model + reconciliation, pre-3.6 behavior), `off` (never load the model — FTS5-only, zero download) |
|
|
151
|
+
| `RAG_MEMORY_MODEL_CACHE_DIR` | OS user cache | Version-independent model cache location (see `docs/UPDATING.md`) |
|
|
152
|
+
| `RAG_MEMORY_TRUST_LEGACY_VECTORS` | unset | Set `1` to grandfather pre-existing vectors under a **custom** `EMBEDDING_MODEL` (default model configs grandfather automatically) |
|
|
137
153
|
|
|
138
154
|
## Changelog
|
|
139
155
|
|
|
156
|
+
### v4.0.0
|
|
157
|
+
|
|
158
|
+
**Observation lifecycle (schema v13).** Observations used to be a JSON array of strings on the
|
|
159
|
+
entity row. A correction overwrote a string, so the fact that it *was* a correction disappeared —
|
|
160
|
+
and if you deleted the wrong duplicate, nothing recorded that either. Observations now have stable
|
|
161
|
+
ids, provenance, and a status, and `entities.observations` becomes a projection synthesised from
|
|
162
|
+
the `active` revisions.
|
|
163
|
+
|
|
164
|
+
- **Corrections keep the previous revision.** `correctObservation(observation_id, content, change_kind, reason)`
|
|
165
|
+
marks the old revision `superseded` and inserts a new one that inherits its position in the array,
|
|
166
|
+
so a correction does not reorder anything.
|
|
167
|
+
- **Search returns `active` revisions only.** A retracted or superseded fact stops coming back from
|
|
168
|
+
`openNodes` / `searchNodes` / `readGraph` / `getNeighbors` without being destroyed.
|
|
169
|
+
- **`getObservationHistory({entity_name | observation_id | root_id})` is the only history surface.**
|
|
170
|
+
It always returns `{ roots: [...] }`, one root per logical observation, revisions oldest-first.
|
|
171
|
+
- **State transitions are a table, not a guess**: `retract` / `restore` / `approve` / `decline`.
|
|
172
|
+
`superseded` is terminal. Anything outside the table is rejected.
|
|
173
|
+
- **Provenance**: `addObservations` and `createEntities` accept `sources: [{source_kind, source_ref, source_hash?}]`.
|
|
174
|
+
Repeated content from a *new* source adds evidence to the existing revision instead of a duplicate.
|
|
175
|
+
Unknown provenance is zero source rows — the engine does not invent one.
|
|
176
|
+
- **`observation_ids`**: `addObservations` and `createEntities` return ids aligned 1:1 with the input,
|
|
177
|
+
`null` where no revision was created (dedup or source-only).
|
|
178
|
+
- **`purgeObservation(observation_id, 'PURGE')`** physically deletes, as a suffix purge from the
|
|
179
|
+
target to the newest revision of that root. It is separate, explicit, and almost never what you want.
|
|
180
|
+
- **⚠️ BREAKING**: `deleteObservations` is deprecated. It now performs a soft **retract** instead of
|
|
181
|
+
a delete, and a batch where any item matches two or more active revisions **aborts with zero
|
|
182
|
+
mutations** — v3.6 deleted every duplicate and carried on, but a machine cannot tell which
|
|
183
|
+
revision was meant. Use `retractObservation(observation_id)` to say which one.
|
|
184
|
+
- Migration to v13 writes a recovery point first (`<db>.v12.bak`) using the SQLite Online Backup API,
|
|
185
|
+
and verifies it — `quick_check` plus FTS5's own `integrity-check`, because a snapshot can be
|
|
186
|
+
structurally valid and still have a broken full-text index. **An existing one is never
|
|
187
|
+
overwritten**: the next attempt writes `.bak.1`, then `.bak.2`. Every file in that rotation was
|
|
188
|
+
taken before any schema change, so each is a valid pre-migration snapshot on its own and nothing
|
|
189
|
+
has to prove which matches the live database. Slots are bounded and a full set refuses to migrate.
|
|
190
|
+
Conversion runs in one transaction with two gates: `PRAGMA foreign_key_check`, then a byte-exact
|
|
191
|
+
comparison of the rebuilt projection against the original array. `foreign_keys` is checked at boot
|
|
192
|
+
and the server refuses to migrate without it. Restore and slot-exhaustion runbook:
|
|
193
|
+
`docs/UPDATING.md`.
|
|
194
|
+
|
|
195
|
+
### v3.6.0
|
|
196
|
+
- **Lite install / lazy boot**: the MCP server connects immediately — FTS5 search, knowledge graph and CRUD work from the first second, while the bge-m3 model (~1.2GB) loads or downloads in the background. Hybrid search switches on automatically. Requires Node **>= 24**.
|
|
197
|
+
- **Version-independent model cache** with a cross-process download lock: engine version bumps no longer re-download the model, and concurrent servers on one machine never corrupt a download. Cleaning the npx cache no longer deletes the model.
|
|
198
|
+
- **Embedding provenance + automatic backfill**: every vector records its input hash and model profile; anything missing or stale (including rows written while the model was unavailable) is re-embedded automatically with a per-target retry cap. Fixes a long-standing defect where `deleteObservations` left stale entity vectors behind.
|
|
199
|
+
- **Search state transparency**: responses report `search_mode` (`hybrid` / `hybrid-partial` / `fts-only`), model state, provenance coverage and a `degradation_reason`; `searchNodes` gains a lexical FTS fallback so entities never disappear from search while embeddings catch up. `getKnowledgeGraphStats` gains a `server` block (version, node, states, coverage) for update-reliability checks.
|
|
200
|
+
- **⚠️ BREAKING**: (1) `hybridSearch` now returns an envelope `{results, search_mode, model_state, coverage, degradation_reason?}` instead of a bare array (per-item `search_mode` removed). (2) `deleteObservations` returns `{results: [{entityName, deleted, embedding_status}], total_deleted}` instead of a success string. Error responses now set `isError: true`. Migration/rollback and fleet-rollout guidance: `docs/UPDATING.md`.
|
|
201
|
+
|
|
140
202
|
### v3.5.0
|
|
141
203
|
- **Atomic `syncDocumentFromFile`**: embeddings are computed before any DB write, then applied in a single synchronous transaction, so a failed embedding (e.g. model still loading) leaves the existing document fully intact instead of a half-deleted or partially-embedded state.
|
|
142
204
|
- **`content_hash` dedup**: unchanged files short-circuit the delete/chunk/embed pipeline (`skipped: true`), with a completeness gate that still re-processes a partially-embedded document.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { type SourceInput } from './src/observations/lifecycle.js';
|
|
3
|
+
import { EmbeddingGate } from './src/embeddingGate.js';
|
|
4
|
+
import type { EmbedPriority } from './src/embeddingGate.js';
|
|
5
|
+
import { BackfillCoordinator } from './src/backfillCoordinator.js';
|
|
6
|
+
export declare function compileFtsLiteralQuery(q: string): string | null;
|
|
2
7
|
interface Entity {
|
|
3
8
|
name: string;
|
|
4
9
|
entityType: string;
|
|
@@ -44,15 +49,30 @@ interface DetailedContext {
|
|
|
44
49
|
export declare class RAGKnowledgeGraphManager {
|
|
45
50
|
private db;
|
|
46
51
|
private encoding;
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
gate: EmbeddingGate;
|
|
53
|
+
embeddingsMode: 'lazy' | 'eager' | 'off';
|
|
54
|
+
currentProfileId: number;
|
|
55
|
+
grandfatherAllowed: boolean;
|
|
56
|
+
coordinator: BackfillCoordinator | null;
|
|
49
57
|
private embeddingCache;
|
|
50
58
|
private readonly EMBEDDING_CACHE_MAX;
|
|
51
59
|
private dictionaryCache;
|
|
52
60
|
initialize(opts?: {
|
|
53
61
|
skipModel?: boolean;
|
|
62
|
+
gate?: EmbeddingGate;
|
|
63
|
+
__testForceFkOff?: boolean;
|
|
54
64
|
}): Promise<void>;
|
|
55
|
-
private
|
|
65
|
+
private ensureCurrentProfile;
|
|
66
|
+
private buildRealLoader;
|
|
67
|
+
startReconciliation(): Promise<void>;
|
|
68
|
+
hashWithBuilderVersion(text: string): string;
|
|
69
|
+
entityInputHash(entityId: string): string | null;
|
|
70
|
+
tryEmbedEntity(entityId: string, priority?: EmbedPriority): Promise<'embedded' | 'queued' | 'disabled'>;
|
|
71
|
+
private mutateEntityAndInvalidate;
|
|
72
|
+
private invalidateDerivedForEntity;
|
|
73
|
+
invalidateEntityVector(entityId: string): void;
|
|
74
|
+
reembedChunkByRowid(rowid: number): Promise<boolean>;
|
|
75
|
+
shutdownAll(): Promise<void>;
|
|
56
76
|
runMigrations(): Promise<{
|
|
57
77
|
applied: number;
|
|
58
78
|
currentVersion: number;
|
|
@@ -63,20 +83,53 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
63
83
|
}>;
|
|
64
84
|
cleanup(): void;
|
|
65
85
|
private _timestampObservation;
|
|
66
|
-
createEntities(entities: Entity
|
|
86
|
+
createEntities(entities: Array<Entity & {
|
|
87
|
+
status?: 'active' | 'provisional';
|
|
88
|
+
sources?: SourceInput[];
|
|
89
|
+
}>): Promise<Array<Entity & {
|
|
90
|
+
created?: boolean;
|
|
91
|
+
observation_ids?: (string | null)[];
|
|
92
|
+
}>>;
|
|
67
93
|
createRelations(relations: Relation[]): Promise<Relation[]>;
|
|
68
94
|
addObservations(observations: {
|
|
69
95
|
entityName: string;
|
|
70
96
|
contents: string[];
|
|
71
|
-
|
|
97
|
+
status?: 'active' | 'provisional';
|
|
98
|
+
sources?: SourceInput[];
|
|
99
|
+
}[]): Promise<Array<{
|
|
72
100
|
entityName: string;
|
|
101
|
+
observation_ids: (string | null)[];
|
|
73
102
|
addedObservations: string[];
|
|
74
|
-
|
|
103
|
+
embedding_status?: string;
|
|
104
|
+
}>>;
|
|
105
|
+
correctObservation(observationId: string, content: string, changeKind?: 'correction' | 'world_change', reason?: string): Promise<string>;
|
|
106
|
+
private _transition;
|
|
107
|
+
retractObservation(observationId: string, reason?: string): Promise<void>;
|
|
108
|
+
restoreObservation(observationId: string, reason?: string): Promise<void>;
|
|
109
|
+
approveObservation(observationId: string, reason?: string): Promise<void>;
|
|
110
|
+
declineObservation(observationId: string, reason: string): Promise<void>;
|
|
111
|
+
purgeObservation(observationId: string, confirm: string): Promise<{
|
|
112
|
+
purged: number;
|
|
113
|
+
}>;
|
|
114
|
+
getObservationHistory(sel: {
|
|
115
|
+
entity_name?: string;
|
|
116
|
+
observation_id?: string;
|
|
117
|
+
root_id?: string;
|
|
118
|
+
}): Promise<{
|
|
119
|
+
roots: any[];
|
|
120
|
+
}>;
|
|
75
121
|
deleteEntities(entityNames: string[]): Promise<void>;
|
|
76
122
|
deleteObservations(deletions: {
|
|
77
123
|
entityName: string;
|
|
78
124
|
observations: string[];
|
|
79
|
-
}[]): Promise<
|
|
125
|
+
}[]): Promise<{
|
|
126
|
+
results: Array<{
|
|
127
|
+
entityName: string;
|
|
128
|
+
deleted: number;
|
|
129
|
+
embedding_status: string;
|
|
130
|
+
}>;
|
|
131
|
+
total_deleted: number;
|
|
132
|
+
}>;
|
|
80
133
|
deleteRelations(relations: Relation[]): Promise<void>;
|
|
81
134
|
updateRelations(updates: {
|
|
82
135
|
from: string;
|
|
@@ -108,7 +161,17 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
108
161
|
path: string[];
|
|
109
162
|
}>;
|
|
110
163
|
}>;
|
|
111
|
-
searchNodes(query: string, limit?: number, since?: string, until?: string): Promise<KnowledgeGraph
|
|
164
|
+
searchNodes(query: string, limit?: number, since?: string, until?: string): Promise<KnowledgeGraph & {
|
|
165
|
+
search_mode?: string;
|
|
166
|
+
model_state?: string;
|
|
167
|
+
coverage?: {
|
|
168
|
+
entity_pct: number;
|
|
169
|
+
};
|
|
170
|
+
degradation_reason?: string;
|
|
171
|
+
warning?: string;
|
|
172
|
+
}>;
|
|
173
|
+
private searchNodesFts;
|
|
174
|
+
private relationsAmong;
|
|
112
175
|
openNodes(names: string[]): Promise<KnowledgeGraph>;
|
|
113
176
|
private generateEntityEmbeddingText;
|
|
114
177
|
private buildEntityEmbeddingText;
|
|
@@ -241,6 +304,10 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
241
304
|
entities: any[];
|
|
242
305
|
relations: any[];
|
|
243
306
|
documents: any[];
|
|
307
|
+
observation_roots: any[];
|
|
308
|
+
entity_observations: any[];
|
|
309
|
+
observation_sources: any[];
|
|
310
|
+
observation_events: any[];
|
|
244
311
|
metadata: {
|
|
245
312
|
exportedAt: string;
|
|
246
313
|
version: string;
|
|
@@ -253,6 +320,10 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
253
320
|
entities?: any[];
|
|
254
321
|
relations?: any[];
|
|
255
322
|
documents?: any[];
|
|
323
|
+
observation_roots?: any[];
|
|
324
|
+
entity_observations?: any[];
|
|
325
|
+
observation_sources?: any[];
|
|
326
|
+
observation_events?: any[];
|
|
256
327
|
}, options?: {
|
|
257
328
|
merge?: boolean;
|
|
258
329
|
}): Promise<{
|
|
@@ -266,8 +337,24 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
266
337
|
relations: number;
|
|
267
338
|
documents: number;
|
|
268
339
|
};
|
|
340
|
+
observation_order_remap: Array<{
|
|
341
|
+
root_id: string;
|
|
342
|
+
entity_id: string;
|
|
343
|
+
from: number;
|
|
344
|
+
to: number;
|
|
345
|
+
}>;
|
|
346
|
+
}>;
|
|
347
|
+
hybridSearch(query: string, limit?: number, useGraph?: boolean): Promise<{
|
|
348
|
+
results: EnhancedSearchResult[];
|
|
349
|
+
search_mode: 'hybrid' | 'hybrid-partial' | 'fts-only';
|
|
350
|
+
model_state: string;
|
|
351
|
+
coverage: {
|
|
352
|
+
chunk_pct: number;
|
|
353
|
+
graph_coverage_pct: number;
|
|
354
|
+
};
|
|
355
|
+
degradation_reason?: string;
|
|
269
356
|
}>;
|
|
270
|
-
|
|
357
|
+
degradationReason(): 'disabled' | 'model_not_ready' | 'reconciling' | 'reconciliation_failed' | undefined;
|
|
271
358
|
getDetailedContext(chunkId: string, includeSurrounding?: boolean): Promise<DetailedContext>;
|
|
272
359
|
getKnowledgeGraphStats(): Promise<any>;
|
|
273
360
|
private _buildGraphologyGraph;
|