@grec0/memory-bank-mcp 0.1.37 → 0.1.39

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.
@@ -37,6 +37,12 @@ export class IndexManager {
37
37
  this.projectKnowledgeService = service;
38
38
  console.error("Project Knowledge Service attached to Index Manager");
39
39
  }
40
+ /**
41
+ * Gets the Embedding Service instance
42
+ */
43
+ getEmbeddingService() {
44
+ return this.embeddingService;
45
+ }
40
46
  /**
41
47
  * Enables or disables auto-update of project docs after indexing
42
48
  */
@@ -0,0 +1,79 @@
1
+ import * as lancedb from "@lancedb/lancedb";
2
+ import * as fs from "fs";
3
+ export class ProjectVectorStore {
4
+ db = null;
5
+ table = null;
6
+ dbPath;
7
+ tableName;
8
+ constructor(dbPath = ".memorybank", tableName = "projects") {
9
+ this.dbPath = dbPath;
10
+ this.tableName = tableName;
11
+ }
12
+ async initialize() {
13
+ try {
14
+ if (!fs.existsSync(this.dbPath)) {
15
+ fs.mkdirSync(this.dbPath, { recursive: true });
16
+ }
17
+ this.db = await lancedb.connect(this.dbPath);
18
+ const tableNames = await this.db.tableNames();
19
+ if (tableNames.includes(this.tableName)) {
20
+ this.table = await this.db.openTable(this.tableName);
21
+ }
22
+ }
23
+ catch (error) {
24
+ console.error(`Error initializing project vector store: ${error}`);
25
+ throw error;
26
+ }
27
+ }
28
+ async ensureInitialized() {
29
+ if (!this.db) {
30
+ await this.initialize();
31
+ }
32
+ }
33
+ async upsertProject(record) {
34
+ await this.ensureInitialized();
35
+ try {
36
+ if (!this.table) {
37
+ this.table = await this.db.createTable(this.tableName, [record]);
38
+ }
39
+ else {
40
+ // Delete existing if any (LanceDB update/merge is tricky, delete+insert is safer for single records)
41
+ try {
42
+ await this.table.delete(`id = '${record.id}'`);
43
+ }
44
+ catch (e) {
45
+ // Ignore if not found or delete fails
46
+ }
47
+ await this.table.add([record]);
48
+ }
49
+ }
50
+ catch (error) {
51
+ console.error(`Error upserting project: ${error}`);
52
+ throw error;
53
+ }
54
+ }
55
+ async search(queryVector, limit = 10) {
56
+ await this.ensureInitialized();
57
+ if (!this.table)
58
+ return [];
59
+ try {
60
+ const results = await this.table.search(queryVector).limit(limit).toArray();
61
+ return results.map((r) => ({
62
+ project: {
63
+ id: r.id,
64
+ vector: r.vector,
65
+ name: r.name,
66
+ description: r.description,
67
+ tags: r.tags,
68
+ path: r.path,
69
+ lastActive: r.lastActive
70
+ },
71
+ score: 1 - (r._distance || 0) // Convert distance to score if needed
72
+ }));
73
+ }
74
+ catch (error) {
75
+ console.error(`Error searching projects: ${error}`);
76
+ return [];
77
+ }
78
+ }
79
+ }
@@ -1,10 +1,13 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import * as os from 'os';
4
+ import { ProjectVectorStore } from './projectVectorStore.js';
4
5
  export class RegistryManager {
5
6
  globalPath;
7
+ projectVectorStore;
6
8
  constructor() {
7
9
  this.globalPath = path.join(os.homedir(), '.memorybank', 'global_registry.json');
10
+ this.projectVectorStore = new ProjectVectorStore();
8
11
  }
9
12
  async ensureRegistry() {
10
13
  try {
@@ -19,7 +22,7 @@ export class RegistryManager {
19
22
  async saveRegistry(registry) {
20
23
  await fs.writeFile(this.globalPath, JSON.stringify(registry, null, 2), 'utf-8');
21
24
  }
22
- async registerProject(projectId, workspacePath, description, keywords = []) {
25
+ async registerProject(projectId, workspacePath, description, keywords = [], embeddingService) {
23
26
  const registry = await this.ensureRegistry();
24
27
  const idx = registry.projects.findIndex(p => p.projectId === projectId);
25
28
  // Preserve existing description/keywords if not provided
@@ -39,11 +42,56 @@ export class RegistryManager {
39
42
  registry.projects.push(card);
40
43
  }
41
44
  await this.saveRegistry(registry);
45
+ // Update vector store if embedding service provides
46
+ if (embeddingService) {
47
+ try {
48
+ await this.updateProjectEmbedding(card, embeddingService);
49
+ }
50
+ catch (error) {
51
+ console.error(`Failed to update project embedding: ${error}`);
52
+ }
53
+ }
54
+ }
55
+ async updateProjectEmbedding(card, embeddingService) {
56
+ const text = `Project: ${card.projectId}\nDescription: ${card.description || ''}\nKeywords: ${card.keywords.join(', ')}`;
57
+ const result = await embeddingService.generateEmbedding(card.projectId, text);
58
+ await this.projectVectorStore.upsertProject({
59
+ id: card.projectId,
60
+ vector: result.vector,
61
+ name: card.projectId, // Using ID as name for now if name not available
62
+ description: card.description || '',
63
+ tags: card.keywords,
64
+ path: card.path,
65
+ lastActive: new Date(card.lastActive).getTime()
66
+ });
42
67
  }
43
- async discoverProjects(query) {
68
+ async discoverProjects(query, embeddingService) {
44
69
  const registry = await this.ensureRegistry();
45
70
  if (!query || query.trim() === '')
46
71
  return registry.projects;
72
+ // Try semantic search if service available
73
+ if (embeddingService) {
74
+ try {
75
+ const queryEmbedding = await embeddingService.generateEmbedding('search-query', query);
76
+ const results = await this.projectVectorStore.search(queryEmbedding.vector);
77
+ // Map back to ProjectCards
78
+ const projectIds = new Set(results.map(r => r.project.id));
79
+ // Return found projects, maintain order from vector search
80
+ const foundProjects = [];
81
+ for (const res of results) {
82
+ const card = registry.projects.find(p => p.projectId === res.project.id);
83
+ if (card)
84
+ foundProjects.push(card);
85
+ }
86
+ // If we found something, return it. If very few, maybe fallback or mix?
87
+ // For now, if we have semantic results, use them.
88
+ if (foundProjects.length > 0)
89
+ return foundProjects;
90
+ }
91
+ catch (error) {
92
+ console.error(`Semantic search failed, falling back to text: ${error}`);
93
+ }
94
+ }
47
95
  const q = query.toLowerCase();
48
96
  return registry.projects.filter(p => p.projectId.toLowerCase().includes(q) ||
49
97
  (p.description && p.description.toLowerCase().includes(q)) ||
@@ -53,4 +101,28 @@ export class RegistryManager {
53
101
  const registry = await this.ensureRegistry();
54
102
  return registry.projects.find(p => p.projectId === projectId);
55
103
  }
104
+ /**
105
+ * Syncs all projects from the JSON registry to the vector store.
106
+ * Useful for migrating existing projects to the new semantic discovery system.
107
+ */
108
+ async syncRegistry(embeddingService) {
109
+ const registry = await this.ensureRegistry();
110
+ let processed = 0;
111
+ let failures = 0;
112
+ console.error(`Syncing ${registry.projects.length} projects to vector store...`);
113
+ for (const project of registry.projects) {
114
+ try {
115
+ await this.updateProjectEmbedding(project, embeddingService);
116
+ processed++;
117
+ if (processed % 10 === 0) {
118
+ console.error(`Synced ${processed}/${registry.projects.length} projects`);
119
+ }
120
+ }
121
+ catch (error) {
122
+ console.error(`Failed to sync project ${project.projectId}: ${error}`);
123
+ failures++;
124
+ }
125
+ }
126
+ return { processed, failures };
127
+ }
56
128
  }
@@ -1,2 +1,2 @@
1
1
  // Version of the MCP Kanban server
2
- export const VERSION = "0.1.33";
2
+ export const VERSION = "0.1.39";
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ import { trackProgress, trackProgressToolDefinition } from "./tools/trackProgres
82
82
  import { manageAgentsTool, manageAgentsToolDefinition } from "./tools/manageAgents.js";
83
83
  import { discoverProjectsTool, discoverProjectsToolDefinition } from "./tools/discoverProjects.js";
84
84
  import { delegateTaskTool, delegateTaskToolDefinition } from "./tools/delegateTask.js";
85
+ import { syncProjectsTool, syncProjectsToolDefinition } from "./tools/syncProjects.js";
85
86
  import { RegistryManager } from "./common/registryManager.js";
86
87
  import { VERSION } from "./common/version.js";
87
88
  // Global services
@@ -837,6 +838,13 @@ server.tool(discoverProjectsToolDefinition.name, discoverProjectsToolDefinition.
837
838
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
838
839
  };
839
840
  });
841
+ // Tool: Sync Projects
842
+ server.tool(syncProjectsToolDefinition.name, syncProjectsToolDefinition.description, {}, async () => {
843
+ const result = await syncProjectsTool();
844
+ return {
845
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
846
+ };
847
+ });
840
848
  // Tool: Delegate Task
841
849
  server.tool(delegateTaskToolDefinition.name, delegateTaskToolDefinition.description, {
842
850
  projectId: z.string().describe("ID del proyecto origen (quien pide)"),
@@ -1,7 +1,17 @@
1
1
  import { RegistryManager } from '../common/registryManager.js';
2
+ import { EmbeddingService } from '../common/embeddingService.js';
2
3
  export async function discoverProjectsTool(params) {
3
4
  const registryManager = new RegistryManager();
4
- const projects = await registryManager.discoverProjects(params.query);
5
+ let embeddingService;
6
+ if (process.env.OPENAI_API_KEY) {
7
+ try {
8
+ embeddingService = new EmbeddingService(process.env.OPENAI_API_KEY);
9
+ }
10
+ catch (e) {
11
+ console.error("Failed to init embedding service for discovery:", e);
12
+ }
13
+ }
14
+ const projects = await registryManager.discoverProjects(params.query, embeddingService);
5
15
  return {
6
16
  success: true,
7
17
  projectCount: projects.length,
@@ -10,6 +10,7 @@ export * from "./readFile.js";
10
10
  export * from "./writeFile.js";
11
11
  export * from "./getStats.js";
12
12
  export * from "./analyzeCoverage.js";
13
+ export * from "./syncProjects.js";
13
14
  // Export Project Knowledge Layer tools
14
15
  export * from "./generateProjectDocs.js";
15
16
  export * from "./getProjectDocs.js";
@@ -6,6 +6,7 @@ import * as path from "path";
6
6
  import { AgentBoard } from "../common/agentBoard.js";
7
7
  import { sessionLogger } from "../common/sessionLogger.js";
8
8
  import { sessionState } from "../common/sessionState.js";
9
+ import { RegistryManager } from "../common/registryManager.js";
9
10
  /**
10
11
  * Indexes code from a directory or file
11
12
  */
@@ -44,6 +45,20 @@ export async function indexCode(params, indexManager, workspaceRoot) {
44
45
  console.error(`Workspace root: ${workspaceRoot}`);
45
46
  console.error(`Recursive: ${params.recursive !== false}`);
46
47
  console.error(`Force reindex: ${params.forceReindex || false}`);
48
+ // Register project and update embedding
49
+ try {
50
+ const registryManager = new RegistryManager();
51
+ const embeddingService = indexManager.getEmbeddingService();
52
+ // Try to get description/keywords from indexManager or project files could be an enhancement
53
+ // For now, we ensure registration with what we have
54
+ await registryManager.registerProject(params.projectId, workspaceRoot, undefined, // Preserve existing description
55
+ [], // Preserve/empty keywords
56
+ embeddingService);
57
+ console.error(`Project ${params.projectId} registered/updated in global registry and vector store`);
58
+ }
59
+ catch (regError) {
60
+ console.error(`Failed to register project: ${regError}`);
61
+ }
47
62
  // Run indexing - pass workspaceRoot for consistent path normalization
48
63
  const result = await indexManager.indexFiles({
49
64
  projectId: params.projectId,
@@ -0,0 +1,34 @@
1
+ import { RegistryManager } from '../common/registryManager.js';
2
+ import { EmbeddingService } from '../common/embeddingService.js';
3
+ export async function syncProjectsTool() {
4
+ if (!process.env.OPENAI_API_KEY) {
5
+ return {
6
+ success: false,
7
+ message: "OPENAI_API_KEY environment variable is required for syncing projects."
8
+ };
9
+ }
10
+ try {
11
+ const registryManager = new RegistryManager();
12
+ const embeddingService = new EmbeddingService(process.env.OPENAI_API_KEY);
13
+ const result = await registryManager.syncRegistry(embeddingService);
14
+ return {
15
+ success: true,
16
+ message: `Synchronization complete. Processed: ${result.processed}, Failures: ${result.failures}`,
17
+ details: result
18
+ };
19
+ }
20
+ catch (error) {
21
+ return {
22
+ success: false,
23
+ message: `Error during synchronization: ${error}`
24
+ };
25
+ }
26
+ }
27
+ export const syncProjectsToolDefinition = {
28
+ name: "memorybank_sync_projects",
29
+ description: "Sincroniza todos los proyectos del registro JSON al store vectorial para habilitar la búsqueda semántica. Útil para migrar datos existentes.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {}
33
+ }
34
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
5
5
  "license": "MIT",
6
6
  "author": "@grec0",