rag-memory-epf-mcp 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +39 -4
- package/dist/src/tools/rag-tools.js +1 -0
- package/docs/UPDATING.md +35 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ Place this `.mcp.json` in each project folder with its own `DB_FILE_PATH`. Each
|
|
|
64
64
|
### Document Pipeline (9)
|
|
65
65
|
| Tool | Description | Annotation |
|
|
66
66
|
|------|------------|------------|
|
|
67
|
-
| `storeDocument` | Store documents with metadata | idempotent |
|
|
67
|
+
| `storeDocument` | Store documents with metadata. Replacing an existing document reports what it destroyed: `{ replaced, deletedChunks }` | idempotent |
|
|
68
68
|
| `chunkDocument` | Create text chunks with configurable parameters | — |
|
|
69
69
|
| `embedChunks` | Generate 1024-dim embeddings + auto-link entities | idempotent |
|
|
70
70
|
| `embedAllEntities` | Batch embed all entities (32 parallel) | idempotent |
|
|
@@ -72,7 +72,7 @@ Place this `.mcp.json` in each project folder with its own `DB_FILE_PATH`. Each
|
|
|
72
72
|
| `linkEntitiesToDocument` | Link entities to chunks where they actually appear (text-matched) | idempotent |
|
|
73
73
|
| `deleteDocuments` | Remove documents and associated data | destructive |
|
|
74
74
|
| `listDocuments` | View all stored documents | readOnly |
|
|
75
|
-
| `syncDocumentFromFile` | One-call server-side sync: reads file + delete/store/chunk/embed/link, content stays off model context. Atomic (embed-first transaction swap) + `content_hash` dedup (skips unchanged files) | idempotent |
|
|
75
|
+
| `syncDocumentFromFile` | One-call server-side sync: reads file + delete/store/chunk/embed/link, content stays off model context. Atomic (embed-first transaction swap) + `content_hash` dedup (skips unchanged files). `excludePattern` strips regions before indexing, and the hash follows the stripped text so changing the pattern re-indexes | idempotent |
|
|
76
76
|
|
|
77
77
|
### Search & Retrieval (9)
|
|
78
78
|
| Tool | Description | Annotation |
|
package/dist/index.d.ts
CHANGED
|
@@ -227,6 +227,7 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
227
227
|
syncDocumentFromFile(filePath: string, documentId: string, options?: {
|
|
228
228
|
metadata?: Record<string, any>;
|
|
229
229
|
content?: string;
|
|
230
|
+
excludePattern?: string | string[];
|
|
230
231
|
entityNames?: string[];
|
|
231
232
|
chunkParams?: {
|
|
232
233
|
maxTokens?: number;
|
|
@@ -252,6 +253,8 @@ export declare class RAGKnowledgeGraphManager {
|
|
|
252
253
|
storeDocument(id: string, content: string, metadata?: Record<string, any>): Promise<{
|
|
253
254
|
id: string;
|
|
254
255
|
stored: boolean;
|
|
256
|
+
replaced: boolean;
|
|
257
|
+
deletedChunks: number;
|
|
255
258
|
}>;
|
|
256
259
|
chunkDocument(documentId: string, options?: {
|
|
257
260
|
maxTokens?: number;
|
package/dist/index.js
CHANGED
|
@@ -146,6 +146,28 @@ function safeRowid(value) {
|
|
|
146
146
|
}
|
|
147
147
|
return n;
|
|
148
148
|
}
|
|
149
|
+
// Remove regions the caller does not want indexed. Compiled with `s` because the intended use is
|
|
150
|
+
// spanning a marked block (`<!-- SECRET -->…<!-- /SECRET -->`) and JS has no inline (?s) flag —
|
|
151
|
+
// without it every such pattern would silently match nothing.
|
|
152
|
+
// A malformed pattern throws rather than degrading to "no exclusion": indexing is a disclosure
|
|
153
|
+
// path, so believing you excluded something you did not is worse than a failed sync.
|
|
154
|
+
function applyExcludePatterns(text, pattern) {
|
|
155
|
+
if (pattern === undefined)
|
|
156
|
+
return text;
|
|
157
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
158
|
+
let out = text;
|
|
159
|
+
for (const p of patterns) {
|
|
160
|
+
let re;
|
|
161
|
+
try {
|
|
162
|
+
re = new RegExp(p, 'gs');
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
throw new Error(`excludePattern is not a valid regular expression: ${JSON.stringify(p)} (${e.message})`);
|
|
166
|
+
}
|
|
167
|
+
out = out.replace(re, '');
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
149
171
|
// Enhanced RAG-enabled Knowledge Graph Manager
|
|
150
172
|
export class RAGKnowledgeGraphManager {
|
|
151
173
|
db = null;
|
|
@@ -1935,7 +1957,11 @@ export class RAGKnowledgeGraphManager {
|
|
|
1935
1957
|
const zero = { reusedChunks: 0, newlyEmbeddedChunks: 0, queuedChunks: 0, deletedChunks: 0, chunkerTransitioned: false };
|
|
1936
1958
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
1937
1959
|
// r6-3: CAS 재시작 = 처음부터 — 파일 읽기·hash·metadata 도 attempt 안에서 재계산한다.
|
|
1938
|
-
|
|
1960
|
+
// Strip excluded regions before anything else looks at the text. Everything downstream —
|
|
1961
|
+
// content_hash, bytes, chunking — then describes what was actually indexed, so changing the
|
|
1962
|
+
// pattern alone still invalidates the dedup gate below. Hashing the raw file instead would
|
|
1963
|
+
// report `unchanged` for a different exclusion, which is the silent-wrong case.
|
|
1964
|
+
const content = applyExcludePatterns(options.content !== undefined ? options.content : fsSync.readFileSync(filePath, 'utf-8'), options.excludePattern);
|
|
1939
1965
|
const bytes = Buffer.byteLength(content, 'utf-8');
|
|
1940
1966
|
const today = new Date().toISOString().slice(0, 10);
|
|
1941
1967
|
const contentHash = shaHex(content);
|
|
@@ -2104,15 +2130,18 @@ export class RAGKnowledgeGraphManager {
|
|
|
2104
2130
|
if (!this.db)
|
|
2105
2131
|
throw new Error('Database not initialized');
|
|
2106
2132
|
console.error(`📄 Storing document: ${id}`);
|
|
2133
|
+
// Decide `replaced` from the document row, not from the chunk count: a document stored but
|
|
2134
|
+
// never chunked still gets overwritten here, and reporting that as a fresh write would be a lie.
|
|
2135
|
+
const existed = this.db.prepare(`SELECT 1 FROM documents WHERE id = ?`).get(id) !== undefined;
|
|
2107
2136
|
// Clean up existing document
|
|
2108
|
-
await this.cleanupDocument(id);
|
|
2137
|
+
const cleaned = await this.cleanupDocument(id);
|
|
2109
2138
|
// Store document
|
|
2110
2139
|
this.db.prepare(`
|
|
2111
2140
|
INSERT OR REPLACE INTO documents (id, content, metadata)
|
|
2112
2141
|
VALUES (?, ?, ?)
|
|
2113
2142
|
`).run(id, content, JSON.stringify(metadata));
|
|
2114
2143
|
console.error(`✅ Document stored: ${id}`);
|
|
2115
|
-
return { id, stored: true };
|
|
2144
|
+
return { id, stored: true, replaced: existed, deletedChunks: cleaned.deletedChunks };
|
|
2116
2145
|
}
|
|
2117
2146
|
async chunkDocument(documentId, options = {}) {
|
|
2118
2147
|
if (!this.db)
|
|
@@ -2430,9 +2459,13 @@ export class RAGKnowledgeGraphManager {
|
|
|
2430
2459
|
console.error(`✅ Entities linked: ${linkedCount} entities linked to document`);
|
|
2431
2460
|
return { documentId, linkedEntities: linkedCount };
|
|
2432
2461
|
}
|
|
2462
|
+
// Report what was destroyed. The counts were already computed here and thrown away, so a caller
|
|
2463
|
+
// that replaces a document could not tell from the return value that anything was deleted
|
|
2464
|
+
// (2026-08-05 field report from a deployed project: "{stored:true} came back and I did not know
|
|
2465
|
+
// what I had just wiped"). Silent destruction is the defect; the numbers are free.
|
|
2433
2466
|
async cleanupDocument(documentId) {
|
|
2434
2467
|
if (!this.db)
|
|
2435
|
-
return;
|
|
2468
|
+
return { deletedChunks: 0, deletedAssociations: 0, deletedVectors: 0 };
|
|
2436
2469
|
console.error(`🧹 Cleaning up document: ${documentId}`);
|
|
2437
2470
|
// Get existing chunks
|
|
2438
2471
|
const existingChunks = this.db.prepare(`
|
|
@@ -2460,6 +2493,7 @@ export class RAGKnowledgeGraphManager {
|
|
|
2460
2493
|
console.error(` ├─ Deleted ${deletedVectors} vector embeddings`);
|
|
2461
2494
|
console.error(` └─ Deleted ${metadata.changes} chunk metadata records`);
|
|
2462
2495
|
}
|
|
2496
|
+
return { deletedChunks: existingChunks.length, deletedAssociations, deletedVectors };
|
|
2463
2497
|
}
|
|
2464
2498
|
async deleteDocument(documentId) {
|
|
2465
2499
|
if (!this.db)
|
|
@@ -3839,6 +3873,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3839
3873
|
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.syncDocumentFromFile(validatedArgs.path, validatedArgs.documentId, {
|
|
3840
3874
|
metadata: validatedArgs.metadata,
|
|
3841
3875
|
content: validatedArgs.content,
|
|
3876
|
+
excludePattern: validatedArgs.excludePattern,
|
|
3842
3877
|
entityNames: validatedArgs.entityNames,
|
|
3843
3878
|
chunkParams: validatedArgs.chunkParams,
|
|
3844
3879
|
}), null, 2) }] };
|
|
@@ -594,6 +594,7 @@ const syncDocumentFromFileSchema = {
|
|
|
594
594
|
documentId: z.string().describe('RAG document ID to (re)create from the file'),
|
|
595
595
|
metadata: z.record(z.any()).optional().describe('Metadata merged into the stored document'),
|
|
596
596
|
content: z.string().optional().describe('Optional content override; stored instead of reading the file'),
|
|
597
|
+
excludePattern: z.union([z.string(), z.array(z.string())]).optional().describe('Regular expression(s) whose matches are stripped before indexing. Applied to the file (or to `content`) first, so hash, byte count and chunking all describe what was actually indexed. Compiled with the dotAll flag, so a pattern may span lines to drop a marked block. An invalid expression fails the call rather than indexing the whole file.'),
|
|
597
598
|
entityNames: z.array(z.string()).optional().describe('Optional entities to explicitly link'),
|
|
598
599
|
chunkParams: z.record(z.any()).optional().describe('Optional chunking parameters { maxTokens }. overlap: omit or 0 only (rejected otherwise since v5.0.0)'),
|
|
599
600
|
};
|
package/docs/UPDATING.md
CHANGED
|
@@ -108,6 +108,41 @@ path and holder pid (e.g. `.download-<key>.lock`). Verify the holder process
|
|
|
108
108
|
is genuinely gone or hung (`ps -p <pid>`), then remove the lock file manually;
|
|
109
109
|
the next start becomes a clean download owner.
|
|
110
110
|
|
|
111
|
+
## v5.1.0 (schema v14, unchanged): destructive-replace reporting + `excludePattern`
|
|
112
|
+
|
|
113
|
+
**What changes on upgrade**: nothing you have to do. No migration, no schema
|
|
114
|
+
change, no re-embedding. Both changes are additive — existing calls keep their
|
|
115
|
+
arguments and keep working, and the new response fields are extra keys.
|
|
116
|
+
|
|
117
|
+
**`storeDocument` now says what it destroyed.** It has always deleted the
|
|
118
|
+
previous document's chunks, vectors and entity links before writing, but the
|
|
119
|
+
response was `{ id, stored: true }`, so a caller replacing a document could not
|
|
120
|
+
tell from the return value that anything was removed. It now returns
|
|
121
|
+
`{ id, stored, replaced, deletedChunks }`, matching what `syncDocumentFromFile`
|
|
122
|
+
already reported. `replaced` is decided by the document row, not the chunk
|
|
123
|
+
count — a document that was stored but never chunked still gets overwritten,
|
|
124
|
+
and reporting that as a fresh write would be wrong.
|
|
125
|
+
|
|
126
|
+
**`syncDocumentFromFile` accepts `excludePattern`** (string or array of
|
|
127
|
+
strings): regions matching these regular expressions are stripped before
|
|
128
|
+
indexing. Previously the only way to leave part of a file out was to read it
|
|
129
|
+
yourself and pass the whole edited text through `content`, which defeats the
|
|
130
|
+
point of a tool that reads server-side to keep content off the model context.
|
|
131
|
+
|
|
132
|
+
Three properties worth knowing:
|
|
133
|
+
|
|
134
|
+
1. **The exclusion happens first**, before hashing and chunking, so
|
|
135
|
+
`content_hash`, the reported `bytes` and the chunk boundaries all describe
|
|
136
|
+
what was actually indexed. Changing only the pattern therefore invalidates
|
|
137
|
+
the dedup gate and re-indexes; it does not silently return `unchanged`.
|
|
138
|
+
2. **Patterns are compiled with the dotAll flag**, so one pattern can span
|
|
139
|
+
lines to drop a marked block (`<!-- SECRET -->[\s\S]*?<!-- /SECRET -->`).
|
|
140
|
+
JavaScript has no inline `(?s)`, so without this every such pattern would
|
|
141
|
+
quietly match nothing.
|
|
142
|
+
3. **An invalid expression fails the call.** Degrading to "no exclusion" would
|
|
143
|
+
index the whole file while the caller believes it was filtered, and an index
|
|
144
|
+
is a disclosure path — a failed sync is the safer error.
|
|
145
|
+
|
|
111
146
|
## v5.0.0 (schema v14): chunker c1 + vector reuse
|
|
112
147
|
|
|
113
148
|
**Breaking**: `chunkParams.overlap` is rejected on BOTH public paths
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rag-memory-epf-mcp",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=24"
|
|
6
6
|
},
|
|
7
|
-
"description": "Project-local RAG memory MCP server
|
|
7
|
+
"description": "Project-local RAG memory MCP server — knowledge graph + multilingual vector + FTS5 in a single SQLite file. Per-project isolation, 38 MCP tools, codepoint-safe chunking (Korean/CJK/emoji).",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"mcp",
|
|
10
10
|
"model-context-protocol",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"prepare": "npm run build",
|
|
46
46
|
"watch": "tsc --watch",
|
|
47
47
|
"verify:invariants": "node test/chunk-invariants.test.mjs",
|
|
48
|
-
"verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs && node test/migration12.test.mjs && node test/model-cache.test.mjs && node test/embedding-gate.test.mjs && node test/lazy-boot.test.mjs && node test/reconciliation.test.mjs && node test/backfill.test.mjs && node test/fts-query.test.mjs && node test/search-contracts.test.mjs && node test/tool-contracts.test.mjs && node test/bounded-exit.test.mjs && node test/observation-schema.test.mjs && node test/observation-migration.test.mjs && node test/observation-lifecycle.test.mjs && node test/observation-contracts.test.mjs && node test/observation-search.test.mjs && node test/observation-cascade.test.mjs && node test/observation-realdata.test.mjs && node test/chunker-c.test.mjs && node test/migration14.test.mjs && node test/chunk-params-validation.test.mjs && node test/vector-reuse.test.mjs && node test/entity-range-linking.test.mjs && node test/stats-chunking.test.mjs && node test/migration14-realdata.test.mjs && node test/migration14-realdata-sync.test.mjs && node test/search-summaries-off.test.mjs",
|
|
48
|
+
"verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs && node test/migration12.test.mjs && node test/model-cache.test.mjs && node test/embedding-gate.test.mjs && node test/lazy-boot.test.mjs && node test/reconciliation.test.mjs && node test/backfill.test.mjs && node test/fts-query.test.mjs && node test/search-contracts.test.mjs && node test/tool-contracts.test.mjs && node test/bounded-exit.test.mjs && node test/observation-schema.test.mjs && node test/observation-migration.test.mjs && node test/observation-lifecycle.test.mjs && node test/observation-contracts.test.mjs && node test/observation-search.test.mjs && node test/observation-cascade.test.mjs && node test/observation-realdata.test.mjs && node test/chunker-c.test.mjs && node test/migration14.test.mjs && node test/chunk-params-validation.test.mjs && node test/vector-reuse.test.mjs && node test/entity-range-linking.test.mjs && node test/stats-chunking.test.mjs && node test/migration14-realdata.test.mjs && node test/migration14-realdata-sync.test.mjs && node test/search-summaries-off.test.mjs && node test/document-return-contracts.test.mjs",
|
|
49
49
|
"test": "npm run build && npm run verify:invariants && npm run verify:engine",
|
|
50
50
|
"prepublishOnly": "npm run build && npm run verify:invariants && npm run verify:engine"
|
|
51
51
|
},
|