mongodb-memory-bank-mcp
Version:
FIXED: MongoDB Memory Bank MCP with bulletproof error handling, smart operations, and session state management. Eliminates [object Object] errors and user confusion.
252 lines (251 loc) • 9.89 kB
JavaScript
/**
* Smart Operation Router
* Fixes the project setup confusion and operation inconsistencies
*/
import { MemoryBankSession } from '../state/session-state.js';
import { MCPErrorHandler } from '../errors/mcp-error.js';
export class SmartOperationRouter {
session;
constructor() {
this.session = MemoryBankSession.getInstance();
}
/**
* Main entry point for smart operations
*/
async executeSmartOperation(context) {
const startTime = new Date();
try {
let result;
switch (context.intent) {
case 'setup_project':
result = await this.smartProjectSetup(context);
break;
case 'save_content':
result = await this.smartContentSave(context);
break;
case 'explore_project':
result = await this.smartProjectExplore(context);
break;
case 'connect_project':
result = await this.smartProjectConnect(context);
break;
default:
throw new Error(`Unknown intent: ${context.intent}`);
}
// Record successful operation
this.session.updateAfterOperation({
name: context.intent,
parameters: context.parameters,
timestamp: startTime,
success: true,
result
});
return {
success: true,
result,
sessionState: this.session.getState(),
nextActions: this.session.getSuggestedNextActions(),
statusMessage: this.session.getStatusMessage()
};
}
catch (error) {
const mcpError = MCPErrorHandler.formatError(error, context);
// Record failed operation
this.session.updateAfterOperation({
name: context.intent,
parameters: context.parameters,
timestamp: startTime,
success: false,
error: mcpError
});
return {
success: false,
error: mcpError,
sessionState: this.session.getState(),
nextActions: this.session.getSuggestedNextActions(),
statusMessage: this.session.getStatusMessage(),
recoveryOptions: mcpError.recoveryOptions
};
}
}
/**
* Smart project setup - handles all project creation/connection scenarios
*/
async smartProjectSetup(context) {
const { parameters } = context;
// Step 1: Check if project already exists
const existingProjects = await this.findSimilarProjects(parameters.projectName);
if (existingProjects.length > 0) {
// Project exists - offer options
return {
projectExists: true,
existingProjects,
options: [
{
action: 'connect',
description: `Connect to existing project: ${existingProjects[0]}`,
parameters: { projectName: existingProjects[0] }
},
{
action: 'create_new',
description: 'Create a new project with different name',
parameters: { projectName: `${parameters.projectName}-new` }
},
{
action: 'merge',
description: 'Merge with existing project',
parameters: { projectName: existingProjects[0], merge: true }
}
],
message: `Found existing project(s): ${existingProjects.join(', ')}. What would you like to do?`
};
}
// Step 2: Auto-detect project context
const detection = await this.detectProjectContext(context.workingDirectory);
// Step 3: Create new project with smart naming
const finalProjectName = parameters.projectName || detection.suggestedName || this.generateProjectName();
const result = await this.createNewProject(finalProjectName, {
description: parameters.description || detection.description,
workingDirectory: context.workingDirectory || detection.workingDirectory
});
this.session.setActiveProject(finalProjectName);
return {
projectCreated: true,
projectName: finalProjectName,
coreFiles: result.coreFiles,
message: `Successfully created project: ${finalProjectName}`,
autoDetected: detection
};
}
/**
* Smart content save - handles template vs content logic
*/
async smartContentSave(context) {
const { parameters } = context;
const { fileName, content } = parameters;
// Ensure we have an active project
const currentState = this.session.getState();
if (!currentState.activeProject) {
throw new Error('No active project. Please set up a project first.');
}
// Check if file exists
const existingFile = await this.getFile(fileName);
if (!existingFile) {
// Simple create
return await this.createFile(fileName, content);
}
// Smart handling of existing files
if (this.isTemplateFile(existingFile)) {
// Replace template with real content
return await this.updateFile(fileName, content, { replaceTemplate: true });
}
// Intelligent merge for user content
const mergeResult = await this.intelligentMerge(existingFile.content, content);
return await this.updateFile(fileName, mergeResult.content, { merged: true });
}
/**
* Smart project exploration
*/
async smartProjectExplore(context) {
const currentState = this.session.getState();
if (!currentState.activeProject) {
// No active project - show available projects
const projects = await this.listAllProjects();
return {
availableProjects: projects,
message: 'No active project. Here are available projects:',
suggestedAction: 'connect_to_project'
};
}
// Active project - show project details
const files = await this.listProjectFiles(currentState.activeProject);
const recentActivity = await this.getRecentActivity(currentState.activeProject);
return {
activeProject: currentState.activeProject,
files,
recentActivity,
message: `Exploring project: ${currentState.activeProject}`,
fileCount: files.length
};
}
/**
* Smart project connection
*/
async smartProjectConnect(context) {
const { parameters } = context;
const { projectName } = parameters;
// Validate project exists
const projects = await this.listAllProjects();
if (!projects.includes(projectName)) {
throw new Error(`Project '${projectName}' not found. Available projects: ${projects.join(', ')}`);
}
// Connect to project
this.session.setActiveProject(projectName);
// Get project overview
const files = await this.listProjectFiles(projectName);
return {
connected: true,
projectName,
files,
message: `Successfully connected to project: ${projectName}`,
fileCount: files.length
};
}
// Helper methods (these would integrate with existing repository methods)
async findSimilarProjects(projectName) {
// Implementation would search for similar project names
return [];
}
async detectProjectContext(workingDirectory) {
// Implementation would detect project context
return {
suggestedName: this.generateProjectName(),
description: 'Auto-detected project',
workingDirectory: workingDirectory || process.cwd()
};
}
generateProjectName() {
const date = new Date().toISOString().split('T')[0];
const random = Math.random().toString(36).substr(2, 5);
return `project-${date}-${random}`;
}
async createNewProject(name, options) {
// Implementation would create new project
return {
projectName: name,
coreFiles: ['projectbrief.md', 'productContext.md', 'systemPatterns.md', 'techContext.md', 'activeContext.md', 'progress.md']
};
}
async getFile(fileName) {
// Implementation would get file
return null;
}
isTemplateFile(file) {
// Implementation would check if file is template
return file.content.includes('[Brief description of the project]');
}
async createFile(fileName, content) {
// Implementation would create file
return { fileName, created: true };
}
async updateFile(fileName, content, options) {
// Implementation would update file
return { fileName, updated: true, options };
}
async intelligentMerge(existingContent, newContent) {
// Implementation would merge content intelligently
return { content: existingContent + '\n\n' + newContent };
}
async listAllProjects() {
// Implementation would list all projects
return [];
}
async listProjectFiles(projectName) {
// Implementation would list project files
return [];
}
async getRecentActivity(projectName) {
// Implementation would get recent activity
return [];
}
}