@lotargo/memory_plugin 1.4.621 → 1.5.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 +352 -366
- package/mcp-server/admin/auth.js +31 -4
- package/mcp-server/cli/direct_commands.js +313 -0
- package/mcp-server/cli/handlers/cloud_actions.js +138 -0
- package/mcp-server/cli/handlers/diagnostics_actions.js +107 -0
- package/mcp-server/cli/handlers/engine_actions.js +214 -0
- package/mcp-server/cli/handlers/prompt_actions.js +24 -0
- package/mcp-server/cli/handlers/storage_actions.js +749 -0
- package/mcp-server/cli/quick_stats.js +39 -0
- package/mcp-server/cli/ui.js +565 -0
- package/mcp-server/cli.js +324 -2085
- package/mcp-server/config/auth_store.js +56 -9
- package/mcp-server/config/config_manager.js +1 -0
- package/mcp-server/db/database.js +14 -1
- package/mcp-server/db/migrations.js +28 -0
- package/mcp-server/fact_format.js +244 -177
- package/mcp-server/identity.js +152 -0
- package/mcp-server/index.js +42 -679
- package/mcp-server/memory.js +50 -63
- package/mcp-server/prompt_manager.js +1 -1
- package/mcp-server/tools/helpers.js +39 -0
- package/mcp-server/tools/identity_tools.js +277 -0
- package/mcp-server/tools/index.js +9 -0
- package/mcp-server/tools/memory_tools.js +506 -0
- package/mcp-server/tools/rag_tools.js +235 -0
- package/opencode-plugin/index.js +460 -48
- package/package.json +7 -3
- package/skills/using-memory/SKILL.md +31 -14
- package/mcp-server/benchmarks/fetch_real_corpus.js +0 -351
- package/mcp-server/benchmarks/gpu_profile_benchmark.js +0 -170
- package/mcp-server/benchmarks/quality_evaluator.js +0 -600
- package/mcp-server/benchmarks/run_benchmarks.js +0 -347
- package/mcp-server/benchmarks/stress_ingestion.js +0 -195
- package/mcp-server/benchmarks/test_dual_layer.js +0 -140
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
|
+
import { optStr, defBool, defNum } from "./helpers.js";
|
|
3
|
+
|
|
4
|
+
export function registerRagTools(server) {
|
|
5
|
+
server.registerTool(
|
|
6
|
+
"ingest_document",
|
|
7
|
+
{
|
|
8
|
+
description:
|
|
9
|
+
"Ingest a document into the RAG knowledge base. " +
|
|
10
|
+
"Accepts local file paths, web URLs, or raw Markdown/text content. " +
|
|
11
|
+
"For type='file' the file is read from disk and indexed with a code-block wrapper. " +
|
|
12
|
+
"For type='url' the page is fetched and its content is indexed (not just the URL). " +
|
|
13
|
+
"Processes document through 3-tier hierarchy chunking (Big/Medium/Small), " +
|
|
14
|
+
"computes dense vectors, and extracts GraphRAG code symbols.",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
content: z
|
|
17
|
+
.string()
|
|
18
|
+
.describe(
|
|
19
|
+
"Raw text content, file path, or web URL. For type='file' this can be the file path (reads from disk) or the file content directly"
|
|
20
|
+
),
|
|
21
|
+
type: z
|
|
22
|
+
.enum(["text", "file", "url"])
|
|
23
|
+
.nullish()
|
|
24
|
+
.transform((v) => v || "text")
|
|
25
|
+
.describe("Input content type: 'text' (raw content), 'file' (reads from disk, wraps in code block), or 'url' (fetches page content)"),
|
|
26
|
+
title: optStr().describe("Document title"),
|
|
27
|
+
path: optStr().describe("Original document file path"),
|
|
28
|
+
generateEmbeddings: defBool(true).describe("Compute dense vector embeddings"),
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
async ({ content, type, title, path, generateEmbeddings }) => {
|
|
32
|
+
const { ingestDocument } = await import("../ingest/pipeline.js");
|
|
33
|
+
const result = await ingestDocument({
|
|
34
|
+
content,
|
|
35
|
+
type,
|
|
36
|
+
title: title || null,
|
|
37
|
+
path: path || null,
|
|
38
|
+
generateEmbeddings,
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: JSON.stringify(
|
|
45
|
+
{
|
|
46
|
+
status: "success",
|
|
47
|
+
docId: result.docId,
|
|
48
|
+
title: result.title,
|
|
49
|
+
sectionsCount: result.sectionsCount,
|
|
50
|
+
microChunksCount: result.microChunksCount,
|
|
51
|
+
deduplicated: result.deduplicated,
|
|
52
|
+
},
|
|
53
|
+
null,
|
|
54
|
+
2
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
server.registerTool(
|
|
63
|
+
"query_knowledge_base",
|
|
64
|
+
{
|
|
65
|
+
description:
|
|
66
|
+
"Perform hybrid search (RSF/RRF BM25 full-text + dense vector similarity) across the RAG knowledge base. " +
|
|
67
|
+
"Returns top-ranked candidate document sections with breadcrumbs, GraphRAG defined code symbols, and relevance scores.",
|
|
68
|
+
inputSchema: z.object({
|
|
69
|
+
query: z.string().describe("Search query in natural language or symbol name"),
|
|
70
|
+
limit: defNum(5).describe("Maximum number of sections to return"),
|
|
71
|
+
instruction: optStr().describe(
|
|
72
|
+
"Optional task-specific retrieval instruction shaping embedding focus (e.g. 'Retrieve code snippets', 'Find user preferences'). " +
|
|
73
|
+
"Recommended when using E5/BGE models for domain-specific queries."
|
|
74
|
+
),
|
|
75
|
+
generateEmbeddings: defBool(true).describe("Use vector search alongside BM25"),
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
async ({ query, limit, instruction, generateEmbeddings }) => {
|
|
79
|
+
const { hybridQuery } = await import("../retrieval/retriever.js");
|
|
80
|
+
const { getConfig } = await import("../config/config_manager.js");
|
|
81
|
+
const activeConfig = getConfig();
|
|
82
|
+
|
|
83
|
+
const results = await hybridQuery({
|
|
84
|
+
query,
|
|
85
|
+
limit,
|
|
86
|
+
generateEmbeddings,
|
|
87
|
+
instruction: instruction || null,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!results || results.length === 0) {
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: `[Active Model: ${activeConfig.embeddingModel}]\nNo matching knowledge found for query.`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const headerNote = `[Active Model: ${activeConfig.embeddingModel} | Fusion: ${activeConfig.fusionAlgorithm.toUpperCase()}]\n\n`;
|
|
102
|
+
|
|
103
|
+
const formatted = results
|
|
104
|
+
.map((r, i) => {
|
|
105
|
+
let header = `### [${i + 1}] ${r.doc_title || "Untitled"}`;
|
|
106
|
+
if (r.heading) header += ` > ${r.heading}`;
|
|
107
|
+
if (r.breadcrumbs) header += ` (${r.breadcrumbs})`;
|
|
108
|
+
let body = `Score: ${(r.score || 0).toFixed(4)}\n`;
|
|
109
|
+
if (r.defined_symbols && r.defined_symbols.length > 0) {
|
|
110
|
+
body += `Defined Symbols: ${r.defined_symbols.join(", ")}\n`;
|
|
111
|
+
}
|
|
112
|
+
body += `\n${r.snippet || r.full_section_content || ""}`;
|
|
113
|
+
return `${header}\n${body}`;
|
|
114
|
+
})
|
|
115
|
+
.join("\n\n---\n\n");
|
|
116
|
+
|
|
117
|
+
return { content: [{ type: "text", text: headerNote + formatted }] };
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
server.registerTool(
|
|
122
|
+
"manage_knowledge_base",
|
|
123
|
+
{
|
|
124
|
+
description:
|
|
125
|
+
"Manage the RAG knowledge base: inspect stats, list documents, read full raw document, delete documents, or export/import snapshots.",
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
action: z.enum(["stats", "list", "read_document", "delete", "export_snapshot", "import_snapshot"]).describe("Management action"),
|
|
128
|
+
docId: optStr().describe("Document ID, title, or path (required for read_document and delete)"),
|
|
129
|
+
snapshotPath: optStr().describe("File path for snapshot export/import"),
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
async ({ action, docId, snapshotPath }) => {
|
|
133
|
+
const { getDatabase } = await import("../db/database.js");
|
|
134
|
+
const db = await getDatabase();
|
|
135
|
+
|
|
136
|
+
if (action === "stats") {
|
|
137
|
+
const docCountRow = await db.prepare("SELECT COUNT(*) as cnt FROM documents").get();
|
|
138
|
+
const docCount = docCountRow ? docCountRow.cnt : 0;
|
|
139
|
+
const secCountRow = await db.prepare("SELECT COUNT(*) as cnt FROM sections").get();
|
|
140
|
+
const secCount = secCountRow ? secCountRow.cnt : 0;
|
|
141
|
+
const chunkCountRow = await db.prepare("SELECT COUNT(*) as cnt FROM micro_chunks").get();
|
|
142
|
+
const chunkCount = chunkCountRow ? chunkCountRow.cnt : 0;
|
|
143
|
+
const edgeCountRow = await db.prepare("SELECT COUNT(*) as cnt FROM graph_edges").get();
|
|
144
|
+
const edgeCount = edgeCountRow ? edgeCountRow.cnt : 0;
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: JSON.stringify(
|
|
150
|
+
{
|
|
151
|
+
documents: docCount,
|
|
152
|
+
sections: secCount,
|
|
153
|
+
micro_chunks: chunkCount,
|
|
154
|
+
graph_edges: edgeCount,
|
|
155
|
+
},
|
|
156
|
+
null,
|
|
157
|
+
2
|
|
158
|
+
),
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (action === "list") {
|
|
165
|
+
const docs = await db.prepare("SELECT id, title, path, blob_hash, created_at FROM documents ORDER BY created_at DESC").all();
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: JSON.stringify(docs, null, 2) }],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (action === "read_document") {
|
|
172
|
+
if (!docId) throw new Error("docId parameter is required for read_document action");
|
|
173
|
+
const doc = await db
|
|
174
|
+
.prepare("SELECT id, title, path, blob_hash, created_at FROM documents WHERE id = ? OR path = ? OR title = ?")
|
|
175
|
+
.get(docId, docId, docId);
|
|
176
|
+
if (!doc) {
|
|
177
|
+
throw new Error(`Document not found in knowledge base for docId: ${docId}`);
|
|
178
|
+
}
|
|
179
|
+
const { readBlob } = await import("../storage/blob_store.js");
|
|
180
|
+
const rawContent = await readBlob(doc.blob_hash);
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: JSON.stringify(
|
|
186
|
+
{
|
|
187
|
+
id: doc.id,
|
|
188
|
+
title: doc.title,
|
|
189
|
+
path: doc.path,
|
|
190
|
+
created_at: doc.created_at,
|
|
191
|
+
content: rawContent,
|
|
192
|
+
},
|
|
193
|
+
null,
|
|
194
|
+
2
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (action === "delete") {
|
|
202
|
+
if (!docId) throw new Error("docId parameter is required for delete action");
|
|
203
|
+
const { deleteDocument } = await import("../ingest/pipeline.js");
|
|
204
|
+
const result = await deleteDocument(docId, db);
|
|
205
|
+
return {
|
|
206
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (action === "export_snapshot") {
|
|
211
|
+
const { exportSnapshot } = await import("../admin/snapshot.js");
|
|
212
|
+
const result = await exportSnapshot({ customDb: db, outputPath: snapshotPath || null });
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: snapshotPath ? `Snapshot exported successfully to ${snapshotPath}` : JSON.stringify(result, null, 2),
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (action === "import_snapshot") {
|
|
224
|
+
if (!snapshotPath) throw new Error("snapshotPath parameter is required for import_snapshot action");
|
|
225
|
+
const { importSnapshot } = await import("../admin/snapshot.js");
|
|
226
|
+
const result = await importSnapshot({ customDb: db, snapshotPathOrData: snapshotPath });
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw new Error(`Unknown action: ${action}`);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|