@grec0/memory-bank-mcp 0.1.21 → 0.1.22

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.
@@ -152,14 +152,18 @@ Generate a markdown document useful for setting up the development environment.`
152
152
  filename: "activeContext.md",
153
153
  title: "Active Context",
154
154
  description: "Current development state, recent changes, and work in progress",
155
- promptTemplate: `Analyze the following recently modified code chunks and generate an Active Context document.
155
+ promptTemplate: `Analyze the following recently modified code chunks and current session history to generate an Active Context document.
156
156
 
157
157
  Document:
158
- 1. **Recent Changes**: What parts of the code were recently modified?
159
- 2. **Work in Progress**: Features or fixes that appear incomplete
160
- 3. **Hot Areas**: Parts of the code with high activity
161
- 4. **Potential Issues**: Code that might need attention (TODOs, FIXMEs)
162
- 5. **Current Focus**: What seems to be the current development focus?
158
+ 1. **Current Session Status**: Summary of actions performed in the current session (from history).
159
+ 2. **Recent Changes**: What parts of the code were recently modified?
160
+ 3. **Work in Progress**: Features or fixes that appear incomplete
161
+ 4. **Hot Areas**: Parts of the code with high activity
162
+ 5. **Potential Issues**: Code that might need attention (TODOs, FIXMEs)
163
+ 6. **Current Focus**: What seems to be the current development focus?
164
+
165
+ Recent session history:
166
+ {sessionHistory}
163
167
 
164
168
  Recent code chunks:
165
169
  {chunks}
@@ -320,6 +324,9 @@ export class ProjectKnowledgeService {
320
324
  .join("|");
321
325
  return crypto.createHash("md5").update(content).digest("hex");
322
326
  }
327
+ hashString(str) {
328
+ return crypto.createHash("md5").update(str).digest("hex");
329
+ }
323
330
  /**
324
331
  * Prepares chunks for inclusion in a prompt
325
332
  * Uses Map-Reduce summarization if content exceeds context window threshold
@@ -603,9 +610,9 @@ ${chunk.content}
603
610
  /**
604
611
  * Generates a single document for a specific project
605
612
  */
606
- async generateDocument(projectId, type, chunks, force = false, previousProgress) {
613
+ async generateDocument(projectId, type, chunks, force = false, previousProgress, sessionHistory) {
607
614
  const definition = DOC_DEFINITIONS[type];
608
- const inputHash = this.hashChunks(chunks);
615
+ const inputHash = this.hashChunks(chunks) + (sessionHistory ? this.hashString(sessionHistory) : 0);
609
616
  const docsPath = this.ensureProjectDocsDirectory(projectId);
610
617
  const metadataCache = this.loadProjectMetadata(projectId);
611
618
  // Check if regeneration is needed
@@ -620,6 +627,12 @@ ${chunk.content}
620
627
  const preparedChunks = await this.prepareChunksForPrompt(chunks, this.options.maxChunksPerDoc);
621
628
  console.error(` Chunks text length: ${preparedChunks.text.length} chars${preparedChunks.usedMapReduce ? ' (after Map-Reduce)' : ''}`);
622
629
  let prompt = definition.promptTemplate.replace("{chunks}", preparedChunks.text);
630
+ if (sessionHistory) {
631
+ prompt = prompt.replace("{sessionHistory}", sessionHistory);
632
+ }
633
+ else {
634
+ prompt = prompt.replace("{sessionHistory}", "No recent session history available.");
635
+ }
623
636
  if (type === "progress" && previousProgress) {
624
637
  prompt = prompt.replace("{previousProgress}", previousProgress);
625
638
  }
@@ -655,7 +668,7 @@ ${chunk.content}
655
668
  /**
656
669
  * Generates all project documents (in parallel for speed)
657
670
  */
658
- async generateAllDocuments(projectId, chunks, force = false) {
671
+ async generateAllDocuments(projectId, chunks, force = false, sessionHistory) {
659
672
  const result = {
660
673
  success: true,
661
674
  documentsGenerated: [],
@@ -694,7 +707,7 @@ ${chunk.content}
694
707
  const docChunks = docType === "activeContext" ? recentChunks : chunks;
695
708
  const existingMetadata = metadataCache.get(docType);
696
709
  const isNew = !existingMetadata;
697
- const doc = await this.generateDocument(projectId, docType, docChunks, force, docType === "progress" ? previousProgress : undefined);
710
+ const doc = await this.generateDocument(projectId, docType, docChunks, force, docType === "progress" ? previousProgress : undefined, sessionHistory);
698
711
  return { docType, doc, isNew, error: null };
699
712
  }
700
713
  catch (error) {
@@ -0,0 +1,24 @@
1
+ export class SessionState {
2
+ static instance;
3
+ currentAgentId = null;
4
+ currentProjectId = null;
5
+ constructor() { }
6
+ static getInstance() {
7
+ if (!SessionState.instance) {
8
+ SessionState.instance = new SessionState();
9
+ }
10
+ return SessionState.instance;
11
+ }
12
+ setCurrentAgent(agentId, projectId) {
13
+ this.currentAgentId = agentId;
14
+ this.currentProjectId = projectId;
15
+ console.error(`[SessionState] Set Active Agent: ${agentId} for Project: ${projectId}`);
16
+ }
17
+ getCurrentAgentId() {
18
+ return this.currentAgentId;
19
+ }
20
+ getCurrentProjectId() {
21
+ return this.currentProjectId;
22
+ }
23
+ }
24
+ export const sessionState = SessionState.getInstance();
@@ -1,2 +1,2 @@
1
1
  // Version of the MCP Kanban server
2
- export const VERSION = "0.1.21";
2
+ export const VERSION = "0.1.22";
package/dist/index.js CHANGED
@@ -159,10 +159,6 @@ server.tool("memorybank_search", "Busca código relevante mediante búsqueda sem
159
159
  .string()
160
160
  .optional()
161
161
  .describe("Filtrar resultados por lenguaje de programación (ej: 'typescript', 'python')"),
162
- agentId: z
163
- .string()
164
- .optional()
165
- .describe("Identificador del agente (para logging de sesión)"),
166
162
  }, async (args) => {
167
163
  const result = await searchMemory({
168
164
  projectId: args.projectId,
@@ -171,7 +167,6 @@ server.tool("memorybank_search", "Busca código relevante mediante búsqueda sem
171
167
  minScore: args.minScore,
172
168
  filterByFile: args.filterByFile,
173
169
  filterByLanguage: args.filterByLanguage,
174
- agentId: args.agentId,
175
170
  }, indexManager, workspaceRoot);
176
171
  return {
177
172
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -193,11 +188,16 @@ Ejemplo: "C:/workspaces/proyecto/src/index.ts"`, {
193
188
  .number()
194
189
  .optional()
195
190
  .describe("Línea final (opcional)"),
191
+ projectId: z
192
+ .string()
193
+ .optional()
194
+ .describe("Identificador del proyecto (Opcional, pero necesario para logging de sesión)"),
196
195
  }, async (args) => {
197
196
  const result = await readFile({
198
197
  path: args.path,
199
198
  startLine: args.startLine,
200
199
  endLine: args.endLine,
200
+ projectId: args.projectId,
201
201
  }, workspaceRoot);
202
202
  return {
203
203
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
@@ -314,7 +314,7 @@ server.tool(generateProjectDocsToolDefinition.name, generateProjectDocsToolDefin
314
314
  const result = await generateProjectDocs({
315
315
  projectId: args.projectId,
316
316
  force: args.force,
317
- }, projectKnowledgeService, vectorStore);
317
+ }, projectKnowledgeService, vectorStore, workspaceRoot);
318
318
  return {
319
319
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
320
320
  };
@@ -2,15 +2,43 @@
2
2
  * @fileoverview Tool for generating project documentation
3
3
  * Uses the Project Knowledge Service to create structured markdown docs
4
4
  */
5
+ import { AgentBoard } from "../common/agentBoard.js";
6
+ import { sessionLogger } from "../common/sessionLogger.js";
7
+ import { sessionState } from "../common/sessionState.js";
5
8
  /**
6
9
  * Generates project documentation using AI reasoning
7
10
  */
8
- export async function generateProjectDocs(params, projectKnowledgeService, vectorStore) {
11
+ export async function generateProjectDocs(params, projectKnowledgeService, vectorStore, workspaceRoot = process.cwd() // Add workspaceRoot
12
+ ) {
9
13
  try {
10
14
  console.error("\n=== Generating Project Documentation ===");
11
- console.error(`Project ID: ${params.projectId || "default"}`);
15
+ const projectId = params.projectId || "default";
16
+ console.error(`Project ID: ${projectId}`);
17
+ // Fetch Session History via Session State
18
+ let sessionHistory;
19
+ const activeAgentId = sessionState.getCurrentAgentId();
20
+ if (activeAgentId) {
21
+ try {
22
+ const board = new AgentBoard(workspaceRoot, projectId);
23
+ const sessionId = await board.getSessionId(activeAgentId);
24
+ if (sessionId) {
25
+ console.error(`Fetching session history for session: ${sessionId}`);
26
+ const history = await sessionLogger.getSessionHistory(projectId, sessionId);
27
+ if (history && history.length > 0) {
28
+ // Summarize last 20 events
29
+ const recentEvents = history.slice(-20);
30
+ sessionHistory = recentEvents.map(e => {
31
+ const dataStr = JSON.stringify(e.data).slice(0, 200); // Truncate data
32
+ return `- [${e.timestamp.split('T')[1].split('.')[0]}] ${e.type}: ${dataStr}`;
33
+ }).join('\n');
34
+ }
35
+ }
36
+ }
37
+ catch (e) {
38
+ console.error(`Warning: Failed to fetch session history: ${e}`);
39
+ }
40
+ }
12
41
  console.error(`Force regeneration: ${params.force || false}`);
13
- // Get all chunks from the vector store
14
42
  const chunks = await vectorStore.getAllChunks(params.projectId);
15
43
  if (chunks.length === 0) {
16
44
  return {
@@ -34,8 +62,7 @@ export async function generateProjectDocs(params, projectKnowledgeService, vecto
34
62
  }
35
63
  console.error(`Found ${chunks.length} code chunks to analyze`);
36
64
  // Generate documents - projectId is required
37
- const projectId = params.projectId || "default";
38
- const result = await projectKnowledgeService.generateAllDocuments(projectId, chunks, params.force || false);
65
+ const result = await projectKnowledgeService.generateAllDocuments(projectId, chunks, params.force || false, sessionHistory);
39
66
  // Calculate estimated cost (approximate rates for gpt-5-mini)
40
67
  // Reasoning tokens are typically more expensive
41
68
  const reasoningCostPer1K = 0.003; // $0.003 per 1K reasoning tokens
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { AgentBoard } from "../common/agentBoard.js";
6
6
  import { sessionLogger } from "../common/sessionLogger.js";
7
+ import { sessionState } from "../common/sessionState.js";
7
8
  const VALID_DOC_TYPES = [
8
9
  "projectBrief",
9
10
  "productContext",
@@ -17,25 +18,25 @@ const VALID_DOC_TYPES = [
17
18
  */
18
19
  export async function getProjectDocs(params, projectKnowledgeService, workspaceRoot) {
19
20
  const logSession = async (outputSummary) => {
20
- if (params.agentId && workspaceRoot) {
21
+ const activeAgentId = sessionState.getCurrentAgentId();
22
+ if (activeAgentId && workspaceRoot) {
21
23
  try {
22
24
  const board = new AgentBoard(workspaceRoot, params.projectId);
23
- const sessionId = await board.getSessionId(params.agentId);
25
+ const sessionId = await board.getSessionId(activeAgentId);
24
26
  if (sessionId) {
25
- // Use singleton sessionLogger
26
27
  await sessionLogger.logSessionEvent(params.projectId, sessionId, {
27
28
  timestamp: new Date().toISOString(),
28
29
  type: 'read_doc',
29
30
  data: {
30
- document: params.document || 'all',
31
- format: params.format || 'full',
31
+ document: params.document,
32
+ format: params.format,
32
33
  summary: outputSummary
33
34
  }
34
35
  });
35
36
  }
36
37
  }
37
- catch (error) {
38
- console.error(`Failed to log doc session: ${error}`);
38
+ catch (logError) {
39
+ console.error(`Failed to log read_doc session: ${logError}`);
39
40
  }
40
41
  }
41
42
  };
@@ -3,6 +3,9 @@
3
3
  * Indexes code files semantically
4
4
  */
5
5
  import * as path from "path";
6
+ import { AgentBoard } from "../common/agentBoard.js";
7
+ import { sessionLogger } from "../common/sessionLogger.js";
8
+ import { sessionState } from "../common/sessionState.js";
6
9
  /**
7
10
  * Indexes code from a directory or file
8
11
  */
@@ -16,6 +19,28 @@ export async function indexCode(params, indexManager, workspaceRoot) {
16
19
  : workspaceRoot;
17
20
  console.error(`\nIndexing code at: ${targetPath}`);
18
21
  console.error(`Project ID: ${params.projectId}`);
22
+ // Session Logging via Session State
23
+ const activeAgentId = sessionState.getCurrentAgentId();
24
+ if (activeAgentId) {
25
+ try {
26
+ const board = new AgentBoard(workspaceRoot, params.projectId);
27
+ const sessionId = await board.getSessionId(activeAgentId);
28
+ if (sessionId) {
29
+ await sessionLogger.logSessionEvent(params.projectId, sessionId, {
30
+ timestamp: new Date().toISOString(),
31
+ type: 'index',
32
+ data: {
33
+ path: targetPath,
34
+ recursive: params.recursive !== false,
35
+ force: params.forceReindex || false
36
+ }
37
+ });
38
+ }
39
+ }
40
+ catch (logError) {
41
+ console.error(`Failed to log session event: ${logError}`);
42
+ }
43
+ }
19
44
  console.error(`Workspace root: ${workspaceRoot}`);
20
45
  console.error(`Recursive: ${params.recursive !== false}`);
21
46
  console.error(`Force reindex: ${params.forceReindex || false}`);
@@ -1,5 +1,6 @@
1
1
  import { AgentBoard } from '../common/agentBoard.js';
2
2
  import { RegistryManager } from '../common/registryManager.js';
3
+ import { sessionState } from '../common/sessionState.js';
3
4
  import * as crypto from 'crypto';
4
5
  const WORKSPACE_ROOT = process.cwd(); // Will be overridden by actual workspace logic if passed
5
6
  export async function manageAgentsTool(params, workspaceRoot = WORKSPACE_ROOT) {
@@ -10,6 +11,8 @@ export async function manageAgentsTool(params, workspaceRoot = WORKSPACE_ROOT) {
10
11
  case 'register':
11
12
  if (!agentId)
12
13
  throw new Error('agentId is required for register');
14
+ // Set Global Session State
15
+ sessionState.setCurrentAgent(agentId, projectId);
13
16
  // Auto-generate session ID if not provided.
14
17
  const effectiveSessionId = sessionId || crypto.randomUUID();
15
18
  // 1. Register agent on local board
@@ -4,6 +4,9 @@
4
4
  */
5
5
  import * as fs from "fs";
6
6
  import * as path from "path";
7
+ import { AgentBoard } from "../common/agentBoard.js";
8
+ import { sessionLogger } from "../common/sessionLogger.js";
9
+ import { sessionState } from "../common/sessionState.js";
7
10
  /**
8
11
  * Reads a file from the workspace
9
12
  */
@@ -13,6 +16,27 @@ export async function readFile(params, workspaceRoot) {
13
16
  const filePath = path.isAbsolute(params.path)
14
17
  ? params.path
15
18
  : path.join(workspaceRoot, params.path);
19
+ // Session Logging via Session State
20
+ const activeAgentId = sessionState.getCurrentAgentId();
21
+ if (activeAgentId && params.projectId) {
22
+ try {
23
+ const board = new AgentBoard(workspaceRoot, params.projectId);
24
+ const sessionId = await board.getSessionId(activeAgentId);
25
+ if (sessionId) {
26
+ await sessionLogger.logSessionEvent(params.projectId, sessionId, {
27
+ timestamp: new Date().toISOString(),
28
+ type: 'read_file',
29
+ data: {
30
+ path: params.path,
31
+ lines: params.startLine && params.endLine ? `${params.startLine}-${params.endLine}` : 'all'
32
+ }
33
+ });
34
+ }
35
+ }
36
+ catch (logError) {
37
+ console.error(`Failed to log session event: ${logError}`);
38
+ }
39
+ }
16
40
  // Check if file exists
17
41
  if (!fs.existsSync(filePath)) {
18
42
  return {
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { AgentBoard } from "../common/agentBoard.js";
6
6
  import { sessionLogger } from "../common/sessionLogger.js";
7
+ import { sessionState } from "../common/sessionState.js";
7
8
  /**
8
9
  * Searches the Memory Bank for relevant code
9
10
  */
@@ -36,11 +37,12 @@ export async function searchMemory(params, indexManager, workspaceRoot) {
36
37
  filterByFile: params.filterByFile,
37
38
  filterByLanguage: params.filterByLanguage,
38
39
  });
39
- // Session Logging
40
- if (params.agentId && workspaceRoot) {
40
+ // Session Logging via Session State
41
+ const activeAgentId = sessionState.getCurrentAgentId();
42
+ if (activeAgentId && workspaceRoot) {
41
43
  try {
42
44
  const board = new AgentBoard(workspaceRoot, params.projectId);
43
- const sessionId = await board.getSessionId(params.agentId);
45
+ const sessionId = await board.getSessionId(activeAgentId);
44
46
  if (sessionId) {
45
47
  await sessionLogger.logSessionEvent(params.projectId, sessionId, {
46
48
  timestamp: new Date().toISOString(),
@@ -4,6 +4,9 @@
4
4
  */
5
5
  import * as fs from "fs";
6
6
  import * as path from "path";
7
+ import { AgentBoard } from "../common/agentBoard.js";
8
+ import { sessionLogger } from "../common/sessionLogger.js";
9
+ import { sessionState } from "../common/sessionState.js";
7
10
  /**
8
11
  * Writes a file and optionally reindexes it
9
12
  */
@@ -13,6 +16,40 @@ export async function writeFile(params, indexManager, workspaceRoot) {
13
16
  const filePath = path.isAbsolute(params.path)
14
17
  ? params.path
15
18
  : path.join(workspaceRoot, params.path);
19
+ // Auto-Locking & Logging via Session State
20
+ const activeAgentId = sessionState.getCurrentAgentId();
21
+ if (activeAgentId) {
22
+ const board = new AgentBoard(workspaceRoot, params.projectId);
23
+ // 1. Check/Claim Lock
24
+ // Normalize path for locking (relative to workspace)
25
+ const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
26
+ const canWrite = await board.claimResource(activeAgentId, relativePath);
27
+ if (!canWrite) {
28
+ return {
29
+ success: false,
30
+ filePath: params.path,
31
+ message: `File is locked by another agent. Write rejected.`
32
+ };
33
+ }
34
+ // 2. Log Session Event
35
+ try {
36
+ const sessionId = await board.getSessionId(activeAgentId);
37
+ if (sessionId) {
38
+ await sessionLogger.logSessionEvent(params.projectId, sessionId, {
39
+ timestamp: new Date().toISOString(),
40
+ type: 'decision',
41
+ data: {
42
+ action: 'write_file',
43
+ path: params.path,
44
+ byteSize: Buffer.byteLength(params.content, "utf-8")
45
+ }
46
+ });
47
+ }
48
+ }
49
+ catch (e) {
50
+ console.error("Failed to log session event:", e);
51
+ }
52
+ }
16
53
  // Ensure directory exists
17
54
  const dir = path.dirname(filePath);
18
55
  if (!fs.existsSync(dir)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
5
5
  "license": "MIT",
6
6
  "author": "@grec0",