@nova-mcp/mcp-nova
Version:
AI Memory System for Claude & Cursor - Stop losing context. Start saving tokens.
240 lines (198 loc) • 7.07 kB
JavaScript
// Set MCP mode to silence all console output
process.env.NOVA_MCP_MODE = 'true';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { createRequire } from 'module';
// Silence ALL console output for MCP compatibility
const originalLog = console.log;
const originalWarn = console.warn;
const originalInfo = console.info;
const originalError = console.error;
// Redirect everything to complete silence
console.log = () => {}; // Complete silence
console.warn = () => {}; // Complete silence
console.info = () => {}; // Complete silence
console.error = () => {}; // Complete silence - even errors should be silent for MCP
const require = createRequire(import.meta.url);
const bundle = require('./nova-memory-bundle.js');
const server = new Server(
{ name: 'nova-memory', version: '0.1.15' },
{ capabilities: { tools: {} } }
);
// Extract all necessary classes from bundle
const {
EnhancedMemorySystem,
EnhancedMCPTools,
ConflictDetectionEngine,
VisualMemoryMapper,
CrossReferenceValidator,
BoardStatusTool,
WorkflowTools,
MetaTools,
ConfigLoader
} = bundle;
// Lazy initialization to prevent startup issues
let memorySystem;
let enhancedTools;
let boardTool;
let workflowTool;
let conflictEngine;
let visualMapper;
let crossValidator;
let metaTools;
// Project switching state
let currentProjectPath = null;
let configLoader = null;
async function reinitializeForProject(newProjectPath) {
if (currentProjectPath === newProjectPath && memorySystem) {
return; // Already initialized for this project
}
// Cleanup existing system
if (memorySystem) {
try {
await memorySystem.cleanup();
} catch (error) {
console.error(`Cleanup error: ${error.message}`);
}
memorySystem = null;
enhancedTools = null;
conflictEngine = null;
visualMapper = null;
crossValidator = null;
metaTools = null;
boardTool = null;
workflowTool = null;
}
// Update project context
currentProjectPath = newProjectPath;
if (!configLoader) {
configLoader = new ConfigLoader(newProjectPath);
} else {
configLoader.switchProject(newProjectPath);
}
// Initialize with new project config
await ensureInitialized();
}
async function ensureInitialized() {
if (!memorySystem) {
// Initialize configLoader if not exists
if (!configLoader) {
// Detect if we're running from a bundled distribution
const path = require('path');
const fs = require('fs');
let scriptPath = process.argv[1];
// Follow symlinks to get the real path (handles npm link)
try {
scriptPath = fs.realpathSync(scriptPath);
} catch (error) {
// If realpath fails, use original path
}
const scriptDir = path.dirname(scriptPath);
const parentDir = path.dirname(scriptDir);
// Check if nova-memory.config.js exists in the parent directory (nova-public)
const distConfigPath = path.join(parentDir, 'nova-memory.config.js');
let projectPath = process.cwd();
if (fs.existsSync(distConfigPath)) {
// We're running from a distribution, use the distribution directory
projectPath = parentDir;
}
configLoader = new ConfigLoader(projectPath);
currentProjectPath = projectPath;
}
const projectConfig = configLoader.getProjectConfig();
const memoryConfig = configLoader.getMemoryConfig();
memorySystem = new EnhancedMemorySystem({
projectPath: projectConfig.projectPath,
databasePath: projectConfig.databasePath,
projectName: projectConfig.projectName,
memoryConfig: memoryConfig
});
// Initialize memory management system
await memorySystem.initializeMemoryManagement();
enhancedTools = new EnhancedMCPTools(memorySystem, {
projectPath: projectConfig.projectPath
});
// Initialize conflict detection engine
conflictEngine = new ConflictDetectionEngine(memorySystem, enhancedTools.relationshipEngine);
// Initialize visual memory mapper
visualMapper = new VisualMemoryMapper(memorySystem, enhancedTools.relationshipEngine);
// Initialize cross-reference validator
crossValidator = new CrossReferenceValidator(memorySystem, enhancedTools.relationshipEngine, conflictEngine);
// Initialize board status tool
boardTool = new BoardStatusTool(memorySystem);
// Initialize workflow tools
workflowTool = new WorkflowTools(memorySystem);
// Initialize meta-tools
metaTools = new MetaTools(memorySystem, enhancedTools, boardTool, workflowTool, conflictEngine, visualMapper, crossValidator);
}
}
// Get tool schemas from MetaTools
const META_TOOLS = MetaTools.getToolSchemas();
server.setRequestHandler(ListToolsRequestSchema, async () => {
await ensureInitialized();
return {
tools: META_TOOLS
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// SMART PROJECT DETECTION: Check for config files to detect project switching
let detectedProjectPath = null;
// 1. Check for explicit project_path in args
if (args?.project_path) {
detectedProjectPath = args.project_path;
}
// 2. Check environment variables that Claude Desktop might set
else if (process.env.CLAUDE_WORKING_DIR) {
detectedProjectPath = process.env.CLAUDE_WORKING_DIR;
}
// 3. Check for config files in possible directories
else {
const fs = require('fs');
const path = require('path');
const possiblePaths = [
process.cwd(),
process.env.PWD || process.cwd()
];
for (const testPath of possiblePaths) {
if (testPath && fs.existsSync(path.join(testPath, 'nova-memory.config.js'))) {
detectedProjectPath = testPath;
break;
}
}
}
// Switch project if detected path differs from current
if (detectedProjectPath && detectedProjectPath !== currentProjectPath) {
await reinitializeForProject(detectedProjectPath);
} else {
await ensureInitialized();
}
try {
// Execute through meta tools methods
const methodName = name; // Tool name maps directly to method name
if (typeof metaTools[methodName] === 'function') {
const result = await metaTools[methodName](args);
return {
content: [{
type: "text",
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
}]
};
}
throw new Error(`Unknown tool: ${name}`);
} catch (error) {
return {
content: [{
type: "text",
text: `❌ Error: ${error.message}`
}]
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);