@grec0/memory-bank-mcp 0.0.2 → 0.0.4
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 +74 -5
- package/dist/common/chunker.js +168 -24
- package/dist/common/fileScanner.js +94 -10
- package/dist/common/indexManager.js +97 -25
- package/dist/common/logger.js +54 -0
- package/dist/common/projectKnowledgeService.js +627 -0
- package/dist/common/vectorStore.js +77 -21
- package/dist/index.js +76 -8
- package/dist/tools/analyzeCoverage.js +1 -1
- package/dist/tools/generateProjectDocs.js +133 -0
- package/dist/tools/getProjectDocs.js +126 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/searchMemory.js +2 -2
- package/package.json +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Vector store for Memory Bank using LanceDB
|
|
3
3
|
* Manages storage and retrieval of code embeddings
|
|
4
|
+
* Uses snake_case for field names for LanceDB SQL compatibility
|
|
4
5
|
*/
|
|
5
6
|
import * as lancedb from "@lancedb/lancedb";
|
|
6
7
|
import * as fs from "fs";
|
|
@@ -130,7 +131,8 @@ export class VectorStore {
|
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
133
|
try {
|
|
133
|
-
|
|
134
|
+
// Use snake_case field name for LanceDB SQL compatibility
|
|
135
|
+
await this.table.delete(`file_path = '${filePath}'`);
|
|
134
136
|
console.error(`Deleted all chunks from file: ${filePath}`);
|
|
135
137
|
}
|
|
136
138
|
catch (error) {
|
|
@@ -152,15 +154,18 @@ export class VectorStore {
|
|
|
152
154
|
try {
|
|
153
155
|
// Start with vector search
|
|
154
156
|
let query = this.table.search(queryVector).limit(topK);
|
|
155
|
-
// Apply filters if specified
|
|
157
|
+
// Apply filters if specified (using snake_case field names)
|
|
156
158
|
if (options.filterByFile) {
|
|
157
|
-
query = query.where(`
|
|
159
|
+
query = query.where(`file_path LIKE '%${options.filterByFile}%'`);
|
|
158
160
|
}
|
|
159
161
|
if (options.filterByLanguage) {
|
|
160
162
|
query = query.where(`language = '${options.filterByLanguage}'`);
|
|
161
163
|
}
|
|
162
164
|
if (options.filterByType) {
|
|
163
|
-
query = query.where(`
|
|
165
|
+
query = query.where(`chunk_type = '${options.filterByType}'`);
|
|
166
|
+
}
|
|
167
|
+
if (options.filterByProject) {
|
|
168
|
+
query = query.where(`project_id = '${options.filterByProject}'`);
|
|
164
169
|
}
|
|
165
170
|
// Execute search
|
|
166
171
|
const results = await query.toArray();
|
|
@@ -174,16 +179,17 @@ export class VectorStore {
|
|
|
174
179
|
chunk: {
|
|
175
180
|
id: result.id,
|
|
176
181
|
vector: result.vector,
|
|
177
|
-
|
|
182
|
+
file_path: result.file_path,
|
|
178
183
|
content: result.content,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
start_line: result.start_line,
|
|
185
|
+
end_line: result.end_line,
|
|
186
|
+
chunk_type: result.chunk_type,
|
|
182
187
|
name: result.name,
|
|
183
188
|
language: result.language,
|
|
184
|
-
|
|
189
|
+
file_hash: result.file_hash,
|
|
185
190
|
timestamp: result.timestamp,
|
|
186
191
|
context: result.context,
|
|
192
|
+
project_id: result.project_id,
|
|
187
193
|
},
|
|
188
194
|
score,
|
|
189
195
|
distance,
|
|
@@ -207,21 +213,23 @@ export class VectorStore {
|
|
|
207
213
|
}
|
|
208
214
|
try {
|
|
209
215
|
const results = await this.table
|
|
210
|
-
.
|
|
216
|
+
.query()
|
|
217
|
+
.where(`file_path = '${filePath}'`)
|
|
211
218
|
.toArray();
|
|
212
219
|
return results.map((r) => ({
|
|
213
220
|
id: r.id,
|
|
214
221
|
vector: r.vector,
|
|
215
|
-
|
|
222
|
+
file_path: r.file_path,
|
|
216
223
|
content: r.content,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
start_line: r.start_line,
|
|
225
|
+
end_line: r.end_line,
|
|
226
|
+
chunk_type: r.chunk_type,
|
|
220
227
|
name: r.name,
|
|
221
228
|
language: r.language,
|
|
222
|
-
|
|
229
|
+
file_hash: r.file_hash,
|
|
223
230
|
timestamp: r.timestamp,
|
|
224
231
|
context: r.context,
|
|
232
|
+
project_id: r.project_id,
|
|
225
233
|
}));
|
|
226
234
|
}
|
|
227
235
|
catch (error) {
|
|
@@ -229,6 +237,56 @@ export class VectorStore {
|
|
|
229
237
|
return [];
|
|
230
238
|
}
|
|
231
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Gets all chunks, optionally filtered by project
|
|
242
|
+
*/
|
|
243
|
+
async getAllChunks(projectId) {
|
|
244
|
+
await this.ensureInitialized();
|
|
245
|
+
if (!this.table) {
|
|
246
|
+
console.error("getAllChunks: No table exists");
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
let query = this.table.query();
|
|
251
|
+
// Apply project filter using snake_case field name
|
|
252
|
+
if (projectId) {
|
|
253
|
+
query = query.where(`project_id = '${projectId}'`);
|
|
254
|
+
console.error(`getAllChunks: Filtering by project_id='${projectId}'`);
|
|
255
|
+
}
|
|
256
|
+
const results = await query.toArray();
|
|
257
|
+
console.error(`getAllChunks: Got ${results.length} results`);
|
|
258
|
+
// Debug: Check first result's content
|
|
259
|
+
if (results.length > 0) {
|
|
260
|
+
const first = results[0];
|
|
261
|
+
console.error(`getAllChunks: First result file_path=${first.file_path}, content length=${first.content?.length || 0}`);
|
|
262
|
+
}
|
|
263
|
+
return results.map((r) => ({
|
|
264
|
+
id: r.id,
|
|
265
|
+
vector: r.vector,
|
|
266
|
+
file_path: r.file_path,
|
|
267
|
+
content: r.content,
|
|
268
|
+
start_line: r.start_line,
|
|
269
|
+
end_line: r.end_line,
|
|
270
|
+
chunk_type: r.chunk_type,
|
|
271
|
+
name: r.name,
|
|
272
|
+
language: r.language,
|
|
273
|
+
file_hash: r.file_hash,
|
|
274
|
+
timestamp: r.timestamp,
|
|
275
|
+
context: r.context,
|
|
276
|
+
project_id: r.project_id,
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
console.error(`Error getting all chunks: ${error}`);
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Gets chunks by project ID
|
|
286
|
+
*/
|
|
287
|
+
async getChunksByProject(projectId) {
|
|
288
|
+
return this.getAllChunks(projectId);
|
|
289
|
+
}
|
|
232
290
|
/**
|
|
233
291
|
* Gets statistics about the vector store
|
|
234
292
|
*/
|
|
@@ -243,16 +301,15 @@ export class VectorStore {
|
|
|
243
301
|
};
|
|
244
302
|
}
|
|
245
303
|
try {
|
|
246
|
-
// Use query().toArray() instead of direct toArray()
|
|
247
304
|
const allChunks = await this.table.query().toArray();
|
|
248
305
|
const uniqueFiles = new Set();
|
|
249
306
|
const languageCounts = {};
|
|
250
307
|
const typeCounts = {};
|
|
251
308
|
let latestTimestamp = 0;
|
|
252
309
|
for (const chunk of allChunks) {
|
|
253
|
-
uniqueFiles.add(chunk.
|
|
310
|
+
uniqueFiles.add(chunk.file_path);
|
|
254
311
|
languageCounts[chunk.language] = (languageCounts[chunk.language] || 0) + 1;
|
|
255
|
-
typeCounts[chunk.
|
|
312
|
+
typeCounts[chunk.chunk_type] = (typeCounts[chunk.chunk_type] || 0) + 1;
|
|
256
313
|
if (chunk.timestamp > latestTimestamp) {
|
|
257
314
|
latestTimestamp = chunk.timestamp;
|
|
258
315
|
}
|
|
@@ -307,12 +364,11 @@ export class VectorStore {
|
|
|
307
364
|
return new Map();
|
|
308
365
|
}
|
|
309
366
|
try {
|
|
310
|
-
// Use query().toArray() instead of direct toArray()
|
|
311
367
|
const allChunks = await this.table.query().toArray();
|
|
312
368
|
const fileHashes = new Map();
|
|
313
369
|
for (const chunk of allChunks) {
|
|
314
|
-
if (!fileHashes.has(chunk.
|
|
315
|
-
fileHashes.set(chunk.
|
|
370
|
+
if (!fileHashes.has(chunk.file_path)) {
|
|
371
|
+
fileHashes.set(chunk.file_path, chunk.file_hash);
|
|
316
372
|
}
|
|
317
373
|
}
|
|
318
374
|
return fileHashes;
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { z } from "zod";
|
|
|
10
10
|
import { createEmbeddingService } from "./common/embeddingService.js";
|
|
11
11
|
import { createVectorStore } from "./common/vectorStore.js";
|
|
12
12
|
import { createIndexManager } from "./common/indexManager.js";
|
|
13
|
+
import { createProjectKnowledgeService } from "./common/projectKnowledgeService.js";
|
|
13
14
|
// Import tools
|
|
14
15
|
import { indexCode } from "./tools/indexCode.js";
|
|
15
16
|
import { searchMemory } from "./tools/searchMemory.js";
|
|
@@ -17,11 +18,14 @@ import { readFile } from "./tools/readFile.js";
|
|
|
17
18
|
import { writeFile } from "./tools/writeFile.js";
|
|
18
19
|
import { getStats } from "./tools/getStats.js";
|
|
19
20
|
import { analyzeCoverage } from "./tools/analyzeCoverage.js";
|
|
21
|
+
import { generateProjectDocs, generateProjectDocsToolDefinition } from "./tools/generateProjectDocs.js";
|
|
22
|
+
import { getProjectDocs, getProjectDocsToolDefinition } from "./tools/getProjectDocs.js";
|
|
20
23
|
import { VERSION } from "./common/version.js";
|
|
21
24
|
// Global services
|
|
22
25
|
let embeddingService;
|
|
23
26
|
let vectorStore;
|
|
24
27
|
let indexManager;
|
|
28
|
+
let projectKnowledgeService;
|
|
25
29
|
let workspaceRoot;
|
|
26
30
|
// Create the MCP Server
|
|
27
31
|
const server = new McpServer({
|
|
@@ -67,8 +71,8 @@ server.tool("memorybank_search", "Busca código relevante mediante búsqueda sem
|
|
|
67
71
|
minScore: z
|
|
68
72
|
.number()
|
|
69
73
|
.optional()
|
|
70
|
-
.default(0.
|
|
71
|
-
.describe("Puntuación mínima de similitud (0-1).
|
|
74
|
+
.default(0.4)
|
|
75
|
+
.describe("Puntuación mínima de similitud (0-1). por defecto usa 0.4 y basado en el resultado ajusta el valor"),
|
|
72
76
|
filterByFile: z
|
|
73
77
|
.string()
|
|
74
78
|
.optional()
|
|
@@ -184,6 +188,47 @@ server.tool("memorybank_analyze_coverage", "Analiza la cobertura de indexación
|
|
|
184
188
|
};
|
|
185
189
|
}
|
|
186
190
|
});
|
|
191
|
+
// Tool: Generate Project Docs
|
|
192
|
+
server.tool(generateProjectDocsToolDefinition.name, generateProjectDocsToolDefinition.description, {
|
|
193
|
+
projectId: z
|
|
194
|
+
.string()
|
|
195
|
+
.optional()
|
|
196
|
+
.describe("ID del proyecto (opcional, usa 'default' si no se especifica)"),
|
|
197
|
+
force: z
|
|
198
|
+
.boolean()
|
|
199
|
+
.optional()
|
|
200
|
+
.default(false)
|
|
201
|
+
.describe("Forzar regeneración de todos los documentos aunque no hayan cambiado"),
|
|
202
|
+
}, async (args) => {
|
|
203
|
+
const result = await generateProjectDocs({
|
|
204
|
+
projectId: args.projectId,
|
|
205
|
+
force: args.force,
|
|
206
|
+
}, projectKnowledgeService, vectorStore);
|
|
207
|
+
return {
|
|
208
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
// Tool: Get Project Docs
|
|
212
|
+
server.tool(getProjectDocsToolDefinition.name, getProjectDocsToolDefinition.description, {
|
|
213
|
+
document: z
|
|
214
|
+
.string()
|
|
215
|
+
.optional()
|
|
216
|
+
.default("summary")
|
|
217
|
+
.describe("Documento específico a recuperar: projectBrief, productContext, systemPatterns, techContext, activeContext, progress, all, summary"),
|
|
218
|
+
format: z
|
|
219
|
+
.enum(["full", "summary"])
|
|
220
|
+
.optional()
|
|
221
|
+
.default("full")
|
|
222
|
+
.describe("Formato de salida: 'full' devuelve contenido completo, 'summary' devuelve resumen de todos los docs"),
|
|
223
|
+
}, async (args) => {
|
|
224
|
+
const result = await getProjectDocs({
|
|
225
|
+
document: args.document,
|
|
226
|
+
format: args.format,
|
|
227
|
+
}, projectKnowledgeService);
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
230
|
+
};
|
|
231
|
+
});
|
|
187
232
|
/**
|
|
188
233
|
* Validates and initializes environment
|
|
189
234
|
*/
|
|
@@ -208,6 +253,12 @@ async function validateEnvironment() {
|
|
|
208
253
|
const embeddingModel = process.env.MEMORYBANK_EMBEDDING_MODEL || "text-embedding-3-small";
|
|
209
254
|
const embeddingDimensions = process.env.MEMORYBANK_EMBEDDING_DIMENSIONS || "1536";
|
|
210
255
|
console.error(`✓ Embedding model: ${embeddingModel} (${embeddingDimensions} dimensions)`);
|
|
256
|
+
// Project Knowledge Layer configuration
|
|
257
|
+
const reasoningModel = process.env.MEMORYBANK_REASONING_MODEL || "gpt-5-mini";
|
|
258
|
+
const reasoningEffort = process.env.MEMORYBANK_REASONING_EFFORT || "medium";
|
|
259
|
+
const autoUpdateDocs = process.env.MEMORYBANK_AUTO_UPDATE_DOCS === "true";
|
|
260
|
+
console.error(`✓ Reasoning model: ${reasoningModel} (effort: ${reasoningEffort})`);
|
|
261
|
+
console.error(`✓ Auto-update docs: ${autoUpdateDocs}`);
|
|
211
262
|
// Initialize services
|
|
212
263
|
console.error("\nInitializing services...");
|
|
213
264
|
try {
|
|
@@ -218,6 +269,19 @@ async function validateEnvironment() {
|
|
|
218
269
|
console.error("✓ Vector store initialized");
|
|
219
270
|
indexManager = createIndexManager(embeddingService, vectorStore);
|
|
220
271
|
console.error("✓ Index manager initialized");
|
|
272
|
+
// Initialize Project Knowledge Service
|
|
273
|
+
try {
|
|
274
|
+
projectKnowledgeService = createProjectKnowledgeService();
|
|
275
|
+
console.error("✓ Project Knowledge service initialized");
|
|
276
|
+
// Connect to Index Manager for auto-update hooks
|
|
277
|
+
indexManager.setProjectKnowledgeService(projectKnowledgeService);
|
|
278
|
+
indexManager.setAutoUpdateDocs(autoUpdateDocs);
|
|
279
|
+
console.error("✓ Project Knowledge service connected to Index Manager");
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error(`⚠ Warning: Project Knowledge service not available: ${error}`);
|
|
283
|
+
console.error(" Project documentation features will be disabled.");
|
|
284
|
+
}
|
|
221
285
|
}
|
|
222
286
|
catch (error) {
|
|
223
287
|
console.error(`ERROR: Failed to initialize services: ${error}`);
|
|
@@ -241,12 +305,16 @@ async function startStdioServer() {
|
|
|
241
305
|
await server.connect(transport);
|
|
242
306
|
console.error("\n=== MCP Server Ready ===");
|
|
243
307
|
console.error("Available tools:");
|
|
244
|
-
console.error("
|
|
245
|
-
console.error("
|
|
246
|
-
console.error("
|
|
247
|
-
console.error("
|
|
248
|
-
console.error("
|
|
249
|
-
console.error("
|
|
308
|
+
console.error(" Core Memory Bank:");
|
|
309
|
+
console.error(" - memorybank_index_code: Indexar código semánticamente");
|
|
310
|
+
console.error(" - memorybank_search: Buscar código por similitud semántica");
|
|
311
|
+
console.error(" - memorybank_read_file: Leer archivos del workspace");
|
|
312
|
+
console.error(" - memorybank_write_file: Escribir archivos y reindexar");
|
|
313
|
+
console.error(" - memorybank_get_stats: Obtener estadísticas del índice");
|
|
314
|
+
console.error(" - memorybank_analyze_coverage: Analizar cobertura de indexación");
|
|
315
|
+
console.error(" Project Knowledge Layer:");
|
|
316
|
+
console.error(" - memorybank_generate_project_docs: Generar documentación con IA");
|
|
317
|
+
console.error(" - memorybank_get_project_docs: Leer documentación del proyecto");
|
|
250
318
|
console.error("");
|
|
251
319
|
console.error("Ready to accept requests...\n");
|
|
252
320
|
}
|
|
@@ -256,7 +256,7 @@ export async function analyzeCoverage(indexManager, vectorStore, workspaceRoot)
|
|
|
256
256
|
if (indexed) {
|
|
257
257
|
// Check if file hash matches
|
|
258
258
|
const chunks = await vectorStore.getChunksByFile(file.path);
|
|
259
|
-
if (chunks.length > 0 && chunks[0].
|
|
259
|
+
if (chunks.length > 0 && chunks[0].file_hash !== file.hash) {
|
|
260
260
|
pendingFiles.add(file.path);
|
|
261
261
|
}
|
|
262
262
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tool for generating project documentation
|
|
3
|
+
* Uses the Project Knowledge Service to create structured markdown docs
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generates project documentation using AI reasoning
|
|
7
|
+
*/
|
|
8
|
+
export async function generateProjectDocs(params, projectKnowledgeService, vectorStore) {
|
|
9
|
+
try {
|
|
10
|
+
console.error("\n=== Generating Project Documentation ===");
|
|
11
|
+
console.error(`Project ID: ${params.projectId || "default"}`);
|
|
12
|
+
console.error(`Force regeneration: ${params.force || false}`);
|
|
13
|
+
// Get all chunks from the vector store
|
|
14
|
+
const chunks = await vectorStore.getAllChunks(params.projectId);
|
|
15
|
+
if (chunks.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
success: false,
|
|
18
|
+
message: "No indexed code found. Please run memorybank_index_code first to index your project.",
|
|
19
|
+
result: {
|
|
20
|
+
success: false,
|
|
21
|
+
documentsGenerated: [],
|
|
22
|
+
documentsUpdated: [],
|
|
23
|
+
documentsSkipped: [],
|
|
24
|
+
totalReasoningTokens: 0,
|
|
25
|
+
totalOutputTokens: 0,
|
|
26
|
+
errors: ["No chunks available for documentation generation"],
|
|
27
|
+
},
|
|
28
|
+
tokenUsage: {
|
|
29
|
+
reasoningTokens: 0,
|
|
30
|
+
outputTokens: 0,
|
|
31
|
+
estimatedCost: "$0.00",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
console.error(`Found ${chunks.length} code chunks to analyze`);
|
|
36
|
+
// Generate documents
|
|
37
|
+
const result = await projectKnowledgeService.generateAllDocuments(chunks, params.force || false);
|
|
38
|
+
// Calculate estimated cost (approximate rates for gpt-5-mini)
|
|
39
|
+
// Reasoning tokens are typically more expensive
|
|
40
|
+
const reasoningCostPer1K = 0.003; // $0.003 per 1K reasoning tokens
|
|
41
|
+
const outputCostPer1K = 0.012; // $0.012 per 1K output tokens
|
|
42
|
+
const reasoningCost = (result.totalReasoningTokens / 1000) * reasoningCostPer1K;
|
|
43
|
+
const outputCost = (result.totalOutputTokens / 1000) * outputCostPer1K;
|
|
44
|
+
const totalCost = reasoningCost + outputCost;
|
|
45
|
+
// Build response message
|
|
46
|
+
let message = "";
|
|
47
|
+
if (result.documentsGenerated.length > 0) {
|
|
48
|
+
message += `Generated ${result.documentsGenerated.length} new document(s): ${result.documentsGenerated.join(", ")}. `;
|
|
49
|
+
}
|
|
50
|
+
if (result.documentsUpdated.length > 0) {
|
|
51
|
+
message += `Updated ${result.documentsUpdated.length} document(s): ${result.documentsUpdated.join(", ")}. `;
|
|
52
|
+
}
|
|
53
|
+
if (result.documentsSkipped.length > 0) {
|
|
54
|
+
message += `Skipped ${result.documentsSkipped.length} unchanged document(s). `;
|
|
55
|
+
}
|
|
56
|
+
if (result.errors.length > 0) {
|
|
57
|
+
message += `Errors: ${result.errors.join("; ")}`;
|
|
58
|
+
}
|
|
59
|
+
if (!message) {
|
|
60
|
+
message = "All documents are up to date.";
|
|
61
|
+
}
|
|
62
|
+
console.error(`\nGeneration complete:`);
|
|
63
|
+
console.error(` - Generated: ${result.documentsGenerated.length}`);
|
|
64
|
+
console.error(` - Updated: ${result.documentsUpdated.length}`);
|
|
65
|
+
console.error(` - Skipped: ${result.documentsSkipped.length}`);
|
|
66
|
+
console.error(` - Reasoning tokens: ${result.totalReasoningTokens}`);
|
|
67
|
+
console.error(` - Output tokens: ${result.totalOutputTokens}`);
|
|
68
|
+
console.error(` - Estimated cost: $${totalCost.toFixed(4)}`);
|
|
69
|
+
return {
|
|
70
|
+
success: result.success,
|
|
71
|
+
message,
|
|
72
|
+
result,
|
|
73
|
+
tokenUsage: {
|
|
74
|
+
reasoningTokens: result.totalReasoningTokens,
|
|
75
|
+
outputTokens: result.totalOutputTokens,
|
|
76
|
+
estimatedCost: `$${totalCost.toFixed(4)}`,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error(`Error generating project docs: ${error.message}`);
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
message: `Failed to generate project documentation: ${error.message}`,
|
|
85
|
+
result: {
|
|
86
|
+
success: false,
|
|
87
|
+
documentsGenerated: [],
|
|
88
|
+
documentsUpdated: [],
|
|
89
|
+
documentsSkipped: [],
|
|
90
|
+
totalReasoningTokens: 0,
|
|
91
|
+
totalOutputTokens: 0,
|
|
92
|
+
errors: [error.message],
|
|
93
|
+
},
|
|
94
|
+
tokenUsage: {
|
|
95
|
+
reasoningTokens: 0,
|
|
96
|
+
outputTokens: 0,
|
|
97
|
+
estimatedCost: "$0.00",
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Tool definition for MCP
|
|
104
|
+
*/
|
|
105
|
+
export const generateProjectDocsToolDefinition = {
|
|
106
|
+
name: "memorybank_generate_project_docs",
|
|
107
|
+
description: `Genera documentación estructurada del proyecto usando IA con razonamiento avanzado (gpt-5-mini).
|
|
108
|
+
|
|
109
|
+
Crea 6 documentos markdown que proporcionan una visión global del proyecto:
|
|
110
|
+
- projectBrief.md: Descripción general del proyecto
|
|
111
|
+
- productContext.md: Perspectiva de negocio y usuarios
|
|
112
|
+
- systemPatterns.md: Patrones de arquitectura y diseño
|
|
113
|
+
- techContext.md: Stack tecnológico y dependencias
|
|
114
|
+
- activeContext.md: Estado actual de desarrollo
|
|
115
|
+
- progress.md: Seguimiento de cambios
|
|
116
|
+
|
|
117
|
+
Esta herramienta complementa la búsqueda semántica precisa con conocimiento global del proyecto.
|
|
118
|
+
Útil para que agentes menos avanzados comprendan mejor el contexto completo.`,
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
projectId: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "ID del proyecto (opcional, usa 'default' si no se especifica)",
|
|
125
|
+
},
|
|
126
|
+
force: {
|
|
127
|
+
type: "boolean",
|
|
128
|
+
description: "Forzar regeneración de todos los documentos aunque no hayan cambiado",
|
|
129
|
+
default: false,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tool for reading project documentation
|
|
3
|
+
* Retrieves generated markdown documents for project context
|
|
4
|
+
*/
|
|
5
|
+
const VALID_DOC_TYPES = [
|
|
6
|
+
"projectBrief",
|
|
7
|
+
"productContext",
|
|
8
|
+
"systemPatterns",
|
|
9
|
+
"techContext",
|
|
10
|
+
"activeContext",
|
|
11
|
+
"progress",
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves project documentation
|
|
15
|
+
*/
|
|
16
|
+
export async function getProjectDocs(params, projectKnowledgeService) {
|
|
17
|
+
try {
|
|
18
|
+
const format = params.format || "full";
|
|
19
|
+
const requestedDoc = params.document?.toLowerCase();
|
|
20
|
+
// Check if any documents exist
|
|
21
|
+
if (!projectKnowledgeService.hasDocuments()) {
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
message: "No project documentation has been generated yet. Run memorybank_generate_project_docs first.",
|
|
25
|
+
stats: {
|
|
26
|
+
documentCount: 0,
|
|
27
|
+
totalReasoningTokens: 0,
|
|
28
|
+
totalOutputTokens: 0,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Get stats
|
|
33
|
+
const stats = projectKnowledgeService.getStats();
|
|
34
|
+
const statsResult = {
|
|
35
|
+
documentCount: stats.documentCount,
|
|
36
|
+
totalReasoningTokens: stats.totalReasoningTokens,
|
|
37
|
+
totalOutputTokens: stats.totalOutputTokens,
|
|
38
|
+
lastGenerated: stats.lastGenerated?.toISOString(),
|
|
39
|
+
};
|
|
40
|
+
// Handle summary request
|
|
41
|
+
if (requestedDoc === "summary" || format === "summary") {
|
|
42
|
+
const summary = projectKnowledgeService.getDocumentsSummary();
|
|
43
|
+
return {
|
|
44
|
+
success: true,
|
|
45
|
+
message: `Retrieved summary of ${stats.documentCount} project documents.`,
|
|
46
|
+
summary,
|
|
47
|
+
stats: statsResult,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Handle "all" or no specific document
|
|
51
|
+
if (!requestedDoc || requestedDoc === "all") {
|
|
52
|
+
const documents = projectKnowledgeService.getAllDocuments();
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
message: `Retrieved ${documents.length} project documents.`,
|
|
56
|
+
documents,
|
|
57
|
+
stats: statsResult,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Handle specific document request
|
|
61
|
+
// Normalize document name (allow both "projectBrief" and "projectbrief")
|
|
62
|
+
const normalizedDoc = VALID_DOC_TYPES.find(t => t.toLowerCase() === requestedDoc.replace(".md", "").replace("_", ""));
|
|
63
|
+
if (!normalizedDoc) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
message: `Invalid document type: "${params.document}". Valid types are: ${VALID_DOC_TYPES.join(", ")}`,
|
|
67
|
+
stats: statsResult,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const document = projectKnowledgeService.getDocument(normalizedDoc);
|
|
71
|
+
if (!document) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
message: `Document "${normalizedDoc}" has not been generated yet.`,
|
|
75
|
+
stats: statsResult,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
message: `Retrieved document: ${normalizedDoc}`,
|
|
81
|
+
documents: [document],
|
|
82
|
+
stats: statsResult,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(`Error getting project docs: ${error.message}`);
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: `Failed to retrieve project documentation: ${error.message}`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Tool definition for MCP
|
|
95
|
+
*/
|
|
96
|
+
export const getProjectDocsToolDefinition = {
|
|
97
|
+
name: "memorybank_get_project_docs",
|
|
98
|
+
description: `Lee la documentación del proyecto generada por IA.
|
|
99
|
+
|
|
100
|
+
Recupera documentos markdown estructurados que proporcionan contexto global del proyecto:
|
|
101
|
+
- projectBrief: Descripción general del proyecto
|
|
102
|
+
- productContext: Perspectiva de negocio y usuarios
|
|
103
|
+
- systemPatterns: Patrones de arquitectura y diseño
|
|
104
|
+
- techContext: Stack tecnológico y dependencias
|
|
105
|
+
- activeContext: Estado actual de desarrollo
|
|
106
|
+
- progress: Seguimiento de cambios
|
|
107
|
+
|
|
108
|
+
Usa esta herramienta al inicio de cada sesión para cargar contexto global.
|
|
109
|
+
Complementa la búsqueda semántica precisa (memorybank_search) con visión de alto nivel.`,
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
document: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Documento específico a recuperar. Opciones: projectBrief, productContext, systemPatterns, techContext, activeContext, progress, all, summary",
|
|
116
|
+
default: "summary",
|
|
117
|
+
},
|
|
118
|
+
format: {
|
|
119
|
+
type: "string",
|
|
120
|
+
enum: ["full", "summary"],
|
|
121
|
+
description: "Formato de salida: 'full' devuelve contenido completo, 'summary' devuelve resumen de todos los docs",
|
|
122
|
+
default: "full",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
package/dist/tools/index.js
CHANGED
|
@@ -10,3 +10,6 @@ export * from "./readFile.js";
|
|
|
10
10
|
export * from "./writeFile.js";
|
|
11
11
|
export * from "./getStats.js";
|
|
12
12
|
export * from "./analyzeCoverage.js";
|
|
13
|
+
// Export Project Knowledge Layer tools
|
|
14
|
+
export * from "./generateProjectDocs.js";
|
|
15
|
+
export * from "./getProjectDocs.js";
|
|
@@ -18,7 +18,7 @@ export async function searchMemory(params, indexManager) {
|
|
|
18
18
|
}
|
|
19
19
|
console.error(`\nSearching Memory Bank for: "${params.query}"`);
|
|
20
20
|
console.error(`Top K: ${params.topK || 10}`);
|
|
21
|
-
console.error(`Min score: ${params.minScore || 0.
|
|
21
|
+
console.error(`Min score: ${params.minScore || 0.4}`);
|
|
22
22
|
if (params.filterByFile) {
|
|
23
23
|
console.error(`Filter by file: ${params.filterByFile}`);
|
|
24
24
|
}
|
|
@@ -28,7 +28,7 @@ export async function searchMemory(params, indexManager) {
|
|
|
28
28
|
// Search
|
|
29
29
|
const results = await indexManager.search(params.query, {
|
|
30
30
|
topK: params.topK || 10,
|
|
31
|
-
minScore: params.minScore !== undefined ? params.minScore : 0.
|
|
31
|
+
minScore: params.minScore !== undefined ? params.minScore : 0.4,
|
|
32
32
|
filterByFile: params.filterByFile,
|
|
33
33
|
filterByLanguage: params.filterByLanguage,
|
|
34
34
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grec0/memory-bank-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "@grec0",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@lancedb/lancedb": "^0.9.0",
|
|
41
41
|
"@modelcontextprotocol/sdk": "1.6.1",
|
|
42
42
|
"@types/node": "^22",
|
|
43
|
+
"gpt-tokenizer": "3.4.0",
|
|
43
44
|
"ignore": "^5.3.0",
|
|
44
45
|
"openai": "^4.0.0",
|
|
45
46
|
"zod": "^3.22.4",
|