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.
182 lines (181 loc) • 7.69 kB
JavaScript
import * as fs from 'fs/promises';
import { normalizeProjectName, detectProjectUniversally } from "../../../shared/utils/project-name-normalizer.js";
/**
* Project Context Detection Implementation
* Multi-layer project detection with isolation validation
* Following MongoDB MCP and alioshr/memory-bank-mcp patterns
*/
export class ProjectContextDetection {
projectRepository;
constructor(projectRepository) {
this.projectRepository = projectRepository;
}
async detectProjectContext(request) {
try {
const workingDirectory = request.workingDirectory || process.cwd();
// UNIFIED DETECTION: Use the same logic as detectProjectForMCP for consistency
let detectionResult;
if (request.preferredProjectName) {
// If preferred name is provided, use it directly
detectionResult = {
projectName: normalizeProjectName(request.preferredProjectName),
confidence: 100,
detectionMethod: 'preferred',
workingDirectory,
gitContext: undefined,
packageContext: undefined,
directoryContext: undefined,
isolationValidated: false,
existsInMemoryBank: false,
warnings: [],
recommendations: [],
signals: []
};
}
else {
// Use the same universal detection as MCP tools
const universalDetection = await detectProjectUniversally(workingDirectory);
detectionResult = {
projectName: universalDetection.projectName,
confidence: universalDetection.confidence,
detectionMethod: universalDetection.detectionMethod,
workingDirectory: universalDetection.workingDirectory,
gitContext: undefined,
packageContext: undefined,
directoryContext: undefined,
isolationValidated: false,
existsInMemoryBank: false,
warnings: [],
recommendations: [],
signals: universalDetection.signals.map(signal => ({
type: signal.type,
confidence: signal.confidence,
projectName: signal.projectName,
evidence: signal.evidence
}))
};
}
// Validate isolation if requested
let isolation;
if (request.validateIsolation !== false) {
isolation = await this.validateProjectIsolation(detectionResult.projectName, workingDirectory);
detectionResult.isolationValidated = isolation.isValid;
}
// Check if project exists in memory bank
detectionResult.existsInMemoryBank = await this.checkProjectExistsInMemoryBank(detectionResult.projectName);
return {
success: true,
result: detectionResult,
isolation,
timestamp: new Date()
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
timestamp: new Date()
};
}
}
// REMOVED: analyzeGitContext - now using unified detection from project-name-normalizer
// REMOVED: analyzePackageContext - now using unified detection from project-name-normalizer
// REMOVED: analyzeDirectoryContext - now using unified detection from project-name-normalizer
// REMOVED: normalizeProjectName - now using shared utility from project-name-normalizer
// REMOVED: determineProjectName - now using unified detection from project-name-normalizer
async validateProjectIsolation(projectName, workingDirectory) {
try {
const existingProjects = await this.getExistingProjects();
const conflictingProjects = [];
const crossReferences = [];
// Check for exact name conflicts
if (existingProjects.includes(projectName)) {
// This is actually OK - project can exist
}
// Check for similar names that might cause confusion
const similarProjects = existingProjects.filter(existing => existing !== projectName && (existing.toLowerCase().includes(projectName.toLowerCase()) ||
projectName.toLowerCase().includes(existing.toLowerCase())));
if (similarProjects.length > 0) {
conflictingProjects.push(...similarProjects);
}
// Calculate isolation score
let isolationScore = 100;
if (conflictingProjects.length > 0) {
isolationScore -= conflictingProjects.length * 20;
}
const warnings = [];
if (conflictingProjects.length > 0) {
warnings.push(`Similar project names detected: ${conflictingProjects.join(', ')}`);
}
return {
isValid: isolationScore >= 80,
projectName,
conflictingProjects,
crossReferences,
isolationScore: Math.max(0, isolationScore),
warnings
};
}
catch (error) {
return {
isValid: false,
projectName,
conflictingProjects: [],
crossReferences: [],
isolationScore: 0,
warnings: [`Isolation validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`]
};
}
}
async checkProjectExistsInMemoryBank(projectName) {
try {
const existingProjects = await this.getExistingProjects();
return existingProjects.includes(projectName);
}
catch (err) {
// Project list access failed - this is expected
return false;
}
}
async getExistingProjects() {
try {
return await this.projectRepository.listProjects();
}
catch (err) {
// Project repository access failed - this is expected
return [];
}
}
async pathExists(filePath) {
try {
await fs.access(filePath);
return true;
}
catch (err) {
// File access check failed - this is expected
return false;
}
}
/**
* Smart fallback when normal project detection fails
* REMOVED: Auto-selection logic that breaks project isolation
* The system must ALWAYS respect the working directory context
*/
async smartProjectFallback(workingDirectory) {
try {
const existingProjects = await this.getExistingProjects();
if (existingProjects.length === 0) {
// No existing projects - let normal fallback handle this
return null;
}
// REMOVED: Auto-selection of existing projects - this breaks project isolation!
// The system must ALWAYS respect the working directory, not auto-select existing projects
// Return null to force directory-based detection
return null;
}
catch (error) {
console.error('[SMART-FALLBACK] Error accessing existing projects:', error);
return null;
}
}
}