@grec0/memory-bank-mcp 0.0.3 → 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.
@@ -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
+ };
@@ -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";
@@ -21,7 +21,6 @@ export async function indexCode(params, indexManager, workspaceRoot) {
21
21
  // Run indexing
22
22
  const result = await indexManager.indexFiles({
23
23
  rootPath: targetPath,
24
- projectRoot: workspaceRoot,
25
24
  recursive: params.recursive !== false,
26
25
  forceReindex: params.forceReindex || false,
27
26
  });
@@ -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.7}`);
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.7,
31
+ minScore: params.minScore !== undefined ? params.minScore : 0.4,
32
32
  filterByFile: params.filterByFile,
33
33
  filterByLanguage: params.filterByLanguage,
34
34
  });
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.0.3",
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,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"