@grec0/memory-bank-mcp 0.0.3 → 0.0.5
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 +642 -420
- package/dist/common/chunker.js +166 -534
- package/dist/common/embeddingService.js +39 -51
- package/dist/common/fileScanner.js +123 -58
- package/dist/common/indexManager.js +185 -108
- package/dist/common/projectKnowledgeService.js +627 -0
- package/dist/common/setup.js +49 -0
- package/dist/common/utils.js +215 -0
- package/dist/common/vectorStore.js +80 -67
- package/dist/index.js +106 -14
- package/dist/operations/boardMemberships.js +186 -0
- package/dist/operations/boards.js +268 -0
- package/dist/operations/cards.js +426 -0
- package/dist/operations/comments.js +249 -0
- package/dist/operations/labels.js +258 -0
- package/dist/operations/lists.js +157 -0
- package/dist/operations/projects.js +102 -0
- package/dist/operations/tasks.js +238 -0
- package/dist/tools/analyzeCoverage.js +50 -67
- package/dist/tools/board-summary.js +151 -0
- package/dist/tools/card-details.js +106 -0
- package/dist/tools/create-card-with-tasks.js +81 -0
- package/dist/tools/generateProjectDocs.js +133 -0
- package/dist/tools/getProjectDocs.js +126 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/indexCode.js +4 -2
- package/dist/tools/searchMemory.js +4 -2
- package/dist/tools/workflow-actions.js +145 -0
- package/dist/tools/writeFile.js +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getCard } from "../operations/cards.js";
|
|
3
|
+
import { getTasks } from "../operations/tasks.js";
|
|
4
|
+
import { getComments } from "../operations/comments.js";
|
|
5
|
+
import { getLabels } from "../operations/labels.js";
|
|
6
|
+
import { getProjects } from "../operations/projects.js";
|
|
7
|
+
import { getBoards } from "../operations/boards.js";
|
|
8
|
+
import { getLists } from "../operations/lists.js";
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for the getCardDetails function parameters
|
|
11
|
+
* @property {string} cardId - The ID of the card to get details for
|
|
12
|
+
*/
|
|
13
|
+
export const getCardDetailsSchema = z.object({
|
|
14
|
+
cardId: z.string().describe("The ID of the card to get details for"),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves comprehensive details about a card including tasks, comments, labels, and analysis
|
|
18
|
+
*
|
|
19
|
+
* This function aggregates data from multiple sources to provide a complete view of a card,
|
|
20
|
+
* including its tasks, comments, and labels. It also calculates task completion percentage
|
|
21
|
+
* and performs analysis on the card's status.
|
|
22
|
+
*
|
|
23
|
+
* @param {GetCardDetailsParams} params - Parameters for retrieving card details
|
|
24
|
+
* @param {string} params.cardId - The ID of the card to get details for
|
|
25
|
+
* @returns {Promise<object>} Comprehensive card details including tasks, comments, labels, and analysis
|
|
26
|
+
* @throws {Error} If the card is not found or if the board ID cannot be determined
|
|
27
|
+
*/
|
|
28
|
+
export async function getCardDetails(params) {
|
|
29
|
+
const { cardId } = params;
|
|
30
|
+
try {
|
|
31
|
+
// Get the card details
|
|
32
|
+
const card = await getCard(cardId);
|
|
33
|
+
if (!card) {
|
|
34
|
+
throw new Error(`Card with ID ${cardId} not found`);
|
|
35
|
+
}
|
|
36
|
+
// Get tasks for the card
|
|
37
|
+
const tasks = await getTasks(card.id);
|
|
38
|
+
// Get comments for the card
|
|
39
|
+
const comments = await getComments(card.id);
|
|
40
|
+
// Find the board ID by searching through all projects and boards
|
|
41
|
+
let boardId = null;
|
|
42
|
+
// Get all projects
|
|
43
|
+
const projectsResponse = await getProjects(1, 100);
|
|
44
|
+
const projects = projectsResponse.items;
|
|
45
|
+
// For each project, get its boards
|
|
46
|
+
for (const project of projects) {
|
|
47
|
+
if (boardId)
|
|
48
|
+
break; // Stop if we already found the board ID
|
|
49
|
+
const boards = await getBoards(project.id);
|
|
50
|
+
// For each board, get its lists
|
|
51
|
+
for (const board of boards) {
|
|
52
|
+
if (boardId)
|
|
53
|
+
break; // Stop if we already found the board ID
|
|
54
|
+
const lists = await getLists(board.id);
|
|
55
|
+
// Check if the card's list ID is in this board
|
|
56
|
+
const matchingList = lists.find((list) => list.id === card.listId);
|
|
57
|
+
if (matchingList) {
|
|
58
|
+
boardId = board.id;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!boardId) {
|
|
64
|
+
throw new Error(`Could not determine board ID for card ${cardId}`);
|
|
65
|
+
}
|
|
66
|
+
const labels = await getLabels(boardId);
|
|
67
|
+
// Filter to just the labels assigned to this card
|
|
68
|
+
// Note: We need to get the labelIds from the card's data
|
|
69
|
+
// This might require additional API calls or data structure knowledge
|
|
70
|
+
// For now, we'll return all labels for the board
|
|
71
|
+
// Calculate task completion percentage
|
|
72
|
+
const completedTasks = tasks.filter((task) => task.isCompleted).length;
|
|
73
|
+
const totalTasks = tasks.length;
|
|
74
|
+
const completionPercentage = totalTasks > 0
|
|
75
|
+
? Math.round((completedTasks / totalTasks) * 100)
|
|
76
|
+
: 0;
|
|
77
|
+
// Sort comments by date (newest first)
|
|
78
|
+
const sortedComments = comments.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
79
|
+
// Check if the most recent comment is likely from a human (not the LLM)
|
|
80
|
+
// This is a heuristic and might need adjustment
|
|
81
|
+
const hasRecentHumanFeedback = sortedComments.length > 0 &&
|
|
82
|
+
sortedComments[0].data &&
|
|
83
|
+
!sortedComments[0].data.text.includes("Implemented feature") &&
|
|
84
|
+
!sortedComments[0].data.text.includes("Awaiting human review");
|
|
85
|
+
return {
|
|
86
|
+
card,
|
|
87
|
+
taskItems: tasks,
|
|
88
|
+
taskStats: {
|
|
89
|
+
total: totalTasks,
|
|
90
|
+
completed: completedTasks,
|
|
91
|
+
completionPercentage,
|
|
92
|
+
},
|
|
93
|
+
comments: sortedComments,
|
|
94
|
+
labels,
|
|
95
|
+
analysis: {
|
|
96
|
+
hasRecentHumanFeedback,
|
|
97
|
+
isComplete: completionPercentage === 100,
|
|
98
|
+
needsAttention: hasRecentHumanFeedback || completedTasks === 0,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error("Error in getCardDetails:", error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createCard } from "../operations/cards.js";
|
|
3
|
+
import { createTask } from "../operations/tasks.js";
|
|
4
|
+
import { createComment } from "../operations/comments.js";
|
|
5
|
+
/**
|
|
6
|
+
* Zod schema for the createCardWithTasks function parameters
|
|
7
|
+
* @property {string} listId - The ID of the list to create the card in
|
|
8
|
+
* @property {string} name - The name of the card
|
|
9
|
+
* @property {string} [description] - The description of the card
|
|
10
|
+
* @property {string[]} [tasks] - Array of task descriptions to create
|
|
11
|
+
* @property {string} [comment] - Optional comment to add to the card
|
|
12
|
+
* @property {number} [position] - Optional position for the card in the list
|
|
13
|
+
*/
|
|
14
|
+
export const createCardWithTasksSchema = z.object({
|
|
15
|
+
listId: z.string().describe("The ID of the list to create the card in"),
|
|
16
|
+
name: z.string().describe("The name of the card"),
|
|
17
|
+
description: z.string().optional().describe("The description of the card"),
|
|
18
|
+
tasks: z.array(z.string()).optional().describe("Array of task descriptions to create"),
|
|
19
|
+
comment: z.string().optional().describe("Optional comment to add to the card"),
|
|
20
|
+
position: z.number().optional().describe("Optional position for the card in the list"),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new card with tasks, description, and optional comment in a single operation
|
|
24
|
+
*
|
|
25
|
+
* This function streamlines the process of creating a card with associated tasks and comments
|
|
26
|
+
* by handling all the necessary API calls in a single function.
|
|
27
|
+
*
|
|
28
|
+
* @param {CreateCardWithTasksParams} params - Parameters for creating a card with tasks
|
|
29
|
+
* @param {string} params.listId - The ID of the list to create the card in
|
|
30
|
+
* @param {string} params.name - The name of the card
|
|
31
|
+
* @param {string} [params.description] - The description of the card
|
|
32
|
+
* @param {string[]} [params.tasks] - Array of task descriptions to create
|
|
33
|
+
* @param {string} [params.comment] - Optional comment to add to the card
|
|
34
|
+
* @param {number} [params.position] - Optional position for the card in the list
|
|
35
|
+
* @returns {Promise<object>} The created card, tasks, and comment
|
|
36
|
+
* @throws {Error} If there's an error creating the card, tasks, or comment
|
|
37
|
+
*/
|
|
38
|
+
export async function createCardWithTasks(params) {
|
|
39
|
+
const { listId, name, description, tasks, comment, position = 65535 } = params;
|
|
40
|
+
try {
|
|
41
|
+
// Create the card
|
|
42
|
+
const card = await createCard({
|
|
43
|
+
listId,
|
|
44
|
+
name,
|
|
45
|
+
description: description || "",
|
|
46
|
+
position,
|
|
47
|
+
});
|
|
48
|
+
// Create tasks if provided
|
|
49
|
+
const createdTasks = [];
|
|
50
|
+
if (tasks && tasks.length > 0) {
|
|
51
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
52
|
+
const taskName = tasks[i];
|
|
53
|
+
// Calculate position for each task (65535, 131070, 196605, etc.)
|
|
54
|
+
const taskPosition = 65535 * (i + 1);
|
|
55
|
+
const task = await createTask({
|
|
56
|
+
cardId: card.id,
|
|
57
|
+
name: taskName,
|
|
58
|
+
position: taskPosition,
|
|
59
|
+
});
|
|
60
|
+
createdTasks.push(task);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Add a comment if provided
|
|
64
|
+
let createdComment = null;
|
|
65
|
+
if (comment) {
|
|
66
|
+
createdComment = await createComment({
|
|
67
|
+
cardId: card.id,
|
|
68
|
+
text: comment,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
card,
|
|
73
|
+
tasks: createdTasks,
|
|
74
|
+
comment: createdComment,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error("Error in createCardWithTasks:", error);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -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";
|
package/dist/tools/indexCode.js
CHANGED
|
@@ -15,13 +15,15 @@ export async function indexCode(params, indexManager, workspaceRoot) {
|
|
|
15
15
|
: path.join(workspaceRoot, params.path)
|
|
16
16
|
: workspaceRoot;
|
|
17
17
|
console.error(`\nIndexing code at: ${targetPath}`);
|
|
18
|
+
console.error(`Project ID: ${params.projectId}`);
|
|
18
19
|
console.error(`Workspace root: ${workspaceRoot}`);
|
|
19
20
|
console.error(`Recursive: ${params.recursive !== false}`);
|
|
20
21
|
console.error(`Force reindex: ${params.forceReindex || false}`);
|
|
21
|
-
// Run indexing
|
|
22
|
+
// Run indexing - pass workspaceRoot for consistent path normalization
|
|
22
23
|
const result = await indexManager.indexFiles({
|
|
24
|
+
projectId: params.projectId,
|
|
23
25
|
rootPath: targetPath,
|
|
24
|
-
|
|
26
|
+
workspaceRoot: workspaceRoot, // Always normalize paths relative to workspace
|
|
25
27
|
recursive: params.recursive !== false,
|
|
26
28
|
forceReindex: params.forceReindex || false,
|
|
27
29
|
});
|
|
@@ -17,8 +17,9 @@ export async function searchMemory(params, indexManager) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
console.error(`\nSearching Memory Bank for: "${params.query}"`);
|
|
20
|
+
console.error(`Project ID: ${params.projectId}`);
|
|
20
21
|
console.error(`Top K: ${params.topK || 10}`);
|
|
21
|
-
console.error(`Min score: ${params.minScore || 0.
|
|
22
|
+
console.error(`Min score: ${params.minScore || 0.4}`);
|
|
22
23
|
if (params.filterByFile) {
|
|
23
24
|
console.error(`Filter by file: ${params.filterByFile}`);
|
|
24
25
|
}
|
|
@@ -27,8 +28,9 @@ export async function searchMemory(params, indexManager) {
|
|
|
27
28
|
}
|
|
28
29
|
// Search
|
|
29
30
|
const results = await indexManager.search(params.query, {
|
|
31
|
+
projectId: params.projectId,
|
|
30
32
|
topK: params.topK || 10,
|
|
31
|
-
minScore: params.minScore !== undefined ? params.minScore : 0.
|
|
33
|
+
minScore: params.minScore !== undefined ? params.minScore : 0.4,
|
|
32
34
|
filterByFile: params.filterByFile,
|
|
33
35
|
filterByLanguage: params.filterByLanguage,
|
|
34
36
|
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getCard, moveCard } from "../operations/cards.js";
|
|
3
|
+
import { createComment } from "../operations/comments.js";
|
|
4
|
+
import { getLists } from "../operations/lists.js";
|
|
5
|
+
import { getBoard } from "../operations/boards.js";
|
|
6
|
+
import { getTask, updateTask } from "../operations/tasks.js";
|
|
7
|
+
/**
|
|
8
|
+
* Zod schema for the workflow action parameters
|
|
9
|
+
* @property {string} action - The workflow action to perform (start_working, mark_completed, move_to_testing, move_to_done)
|
|
10
|
+
* @property {string} cardId - The ID of the card to perform the action on
|
|
11
|
+
* @property {string} [comment] - Optional comment to add with the action
|
|
12
|
+
* @property {string[]} [taskIds] - Optional task IDs to mark as completed (for mark_completed action)
|
|
13
|
+
* @property {string} [boardId] - Optional board ID (if not provided, will attempt to determine from card)
|
|
14
|
+
*/
|
|
15
|
+
export const workflowActionSchema = z.object({
|
|
16
|
+
action: z.enum([
|
|
17
|
+
"start_working",
|
|
18
|
+
"mark_completed",
|
|
19
|
+
"move_to_testing",
|
|
20
|
+
"move_to_done",
|
|
21
|
+
]).describe("The workflow action to perform"),
|
|
22
|
+
cardId: z.string().describe("The ID of the card to perform the action on"),
|
|
23
|
+
comment: z.string().optional().describe("Optional comment to add with the action"),
|
|
24
|
+
taskIds: z.array(z.string()).optional().describe("Optional task IDs to mark as completed (for mark_completed action)"),
|
|
25
|
+
boardId: z.string().optional().describe("Optional board ID (if not provided, will attempt to determine from card)"),
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Performs a workflow action on a card (start working, mark completed, move to testing, move to done)
|
|
29
|
+
*
|
|
30
|
+
* This function handles common workflow actions for cards in a Kanban board, including
|
|
31
|
+
* moving cards between lists, marking tasks as completed, and adding comments to document progress.
|
|
32
|
+
*
|
|
33
|
+
* @param {WorkflowActionParams} params - Parameters for the workflow action
|
|
34
|
+
* @param {string} params.action - The workflow action to perform (start_working, mark_completed, move_to_testing, move_to_done)
|
|
35
|
+
* @param {string} params.cardId - The ID of the card to perform the action on
|
|
36
|
+
* @param {string} [params.comment] - Optional comment to add with the action
|
|
37
|
+
* @param {string[]} [params.taskIds] - Optional task IDs to mark as completed (for mark_completed action)
|
|
38
|
+
* @param {string} [params.boardId] - Optional board ID (if not provided, will attempt to determine from card)
|
|
39
|
+
* @returns {Promise<object>} The result of the workflow action
|
|
40
|
+
* @throws {Error} If the card or board is not found, or if the action cannot be performed
|
|
41
|
+
*/
|
|
42
|
+
export async function performWorkflowAction(params) {
|
|
43
|
+
const { action, cardId, comment, taskIds, boardId: providedBoardId } = params;
|
|
44
|
+
try {
|
|
45
|
+
// Get the card details
|
|
46
|
+
const card = await getCard(cardId);
|
|
47
|
+
if (!card) {
|
|
48
|
+
throw new Error(`Card with ID ${cardId} not found`);
|
|
49
|
+
}
|
|
50
|
+
// Use the provided boardId or try to determine it
|
|
51
|
+
let boardId = providedBoardId;
|
|
52
|
+
if (!boardId) {
|
|
53
|
+
// Try to get the boardId from the card response
|
|
54
|
+
// @ts-ignore - Some card responses include boardId
|
|
55
|
+
boardId = card.boardId;
|
|
56
|
+
}
|
|
57
|
+
if (!boardId) {
|
|
58
|
+
throw new Error(`Could not determine board ID for card ${cardId}. Please provide a boardId parameter.`);
|
|
59
|
+
}
|
|
60
|
+
// Get the board
|
|
61
|
+
const board = await getBoard(boardId);
|
|
62
|
+
if (!board) {
|
|
63
|
+
throw new Error(`Board with ID ${boardId} not found`);
|
|
64
|
+
}
|
|
65
|
+
// Get all lists on the board
|
|
66
|
+
const boardLists = await getLists(boardId);
|
|
67
|
+
// Find the target list based on the action
|
|
68
|
+
let targetList;
|
|
69
|
+
let actionComment = comment;
|
|
70
|
+
switch (action) {
|
|
71
|
+
case "start_working":
|
|
72
|
+
targetList = boardLists.find((list) => list.name.toLowerCase() === "in progress");
|
|
73
|
+
actionComment = comment || "🚀 Started working on this card.";
|
|
74
|
+
break;
|
|
75
|
+
case "mark_completed":
|
|
76
|
+
// This action doesn't move the card, just completes tasks
|
|
77
|
+
if (taskIds && taskIds.length > 0) {
|
|
78
|
+
// Mark all specified tasks as completed
|
|
79
|
+
const taskUpdates = await Promise.all(taskIds.map(async (taskId) => {
|
|
80
|
+
// First get the task to get its current properties
|
|
81
|
+
const task = await getTask(taskId);
|
|
82
|
+
// Then update it with the same properties plus isCompleted=true
|
|
83
|
+
return updateTask(taskId, {
|
|
84
|
+
name: task.name,
|
|
85
|
+
position: task.position,
|
|
86
|
+
// Use the API's method for marking as completed
|
|
87
|
+
// The updateTask function will handle this correctly
|
|
88
|
+
});
|
|
89
|
+
}));
|
|
90
|
+
// Add a comment if provided
|
|
91
|
+
if (comment) {
|
|
92
|
+
await createComment({
|
|
93
|
+
cardId,
|
|
94
|
+
text: comment,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
action,
|
|
100
|
+
cardId,
|
|
101
|
+
tasksCompleted: taskUpdates.length,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
throw new Error("No task IDs provided for mark_completed action");
|
|
106
|
+
}
|
|
107
|
+
case "move_to_testing":
|
|
108
|
+
targetList = boardLists.find((list) => list.name.toLowerCase() === "testing" ||
|
|
109
|
+
list.name.toLowerCase() === "review");
|
|
110
|
+
actionComment = comment ||
|
|
111
|
+
"✅ Implementation completed and ready for testing.";
|
|
112
|
+
break;
|
|
113
|
+
case "move_to_done":
|
|
114
|
+
targetList = boardLists.find((list) => list.name.toLowerCase() === "done");
|
|
115
|
+
actionComment = comment ||
|
|
116
|
+
"🎉 All work completed and verified.";
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
throw new Error(`Unknown action: ${action}`);
|
|
120
|
+
}
|
|
121
|
+
if (!targetList) {
|
|
122
|
+
throw new Error(`Target list not found for action: ${action}`);
|
|
123
|
+
}
|
|
124
|
+
// Move the card to the target list
|
|
125
|
+
const updatedCard = await moveCard(cardId, targetList.id);
|
|
126
|
+
// Add a comment
|
|
127
|
+
const newComment = await createComment({
|
|
128
|
+
cardId,
|
|
129
|
+
text: actionComment || "",
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
action,
|
|
134
|
+
cardId,
|
|
135
|
+
listId: targetList.id,
|
|
136
|
+
listName: targetList.name,
|
|
137
|
+
card: updatedCard,
|
|
138
|
+
comment: newComment,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error(`Error in performWorkflowAction (${action}):`, error);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
package/dist/tools/writeFile.js
CHANGED
|
@@ -28,8 +28,8 @@ export async function writeFile(params, indexManager, workspaceRoot) {
|
|
|
28
28
|
let chunksCreated = 0;
|
|
29
29
|
if (autoReindex) {
|
|
30
30
|
try {
|
|
31
|
-
console.error(`Auto-reindexing ${params.path}...`);
|
|
32
|
-
const reindexResult = await indexManager.reindexFile(filePath, workspaceRoot);
|
|
31
|
+
console.error(`Auto-reindexing ${params.path} for project ${params.projectId}...`);
|
|
32
|
+
const reindexResult = await indexManager.reindexFile(filePath, workspaceRoot, params.projectId);
|
|
33
33
|
if (reindexResult.success) {
|
|
34
34
|
reindexed = true;
|
|
35
35
|
chunksCreated = reindexResult.chunksCreated;
|
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.5",
|
|
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,8 +40,8 @@
|
|
|
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
|
-
"js-tiktoken": "^1.0.21",
|
|
45
45
|
"openai": "^4.0.0",
|
|
46
46
|
"zod": "^3.22.4",
|
|
47
47
|
"zod-to-json-schema": "^3.23.5"
|