index.ts•7.42 kB
/**
* MCP Prompt Enhancer Server
*
* Main entry point for the Model Context Protocol server that provides
* intelligent prompt preprocessing with adaptive context management.
*/
import path from 'path';
import fs from 'fs-extra';
// Use STDIO for communication instead of remote MCP
// This simplifies the implementation and eliminates network dependencies
import readline from 'readline';
import ProjectContextManager from './context/ProjectContextManager';
import TaskTracker, { TaskType } from './tracker/TaskTracker';
import PromptEnhancer from './enhancer/PromptEnhancer';
import logger, { LogLevel } from './utils/Logger';
// Default configuration
const DEFAULT_CONFIG = {
port: 3000,
contextCacheTTL: 900, // 15 minutes in seconds
maxContextSize: 10000,
logLevel: 'info',
ignorePatterns: ['node_modules', 'dist', '.git', 'build', 'coverage', '*.log'],
};
// Main logger instance
const log = logger.createChildLogger('Server');
/**
* Load configuration from file or environment
*/
function loadConfig() {
const configPath = path.join(process.cwd(), 'mcp-prompt-enhancer.config.js');
let config = { ...DEFAULT_CONFIG };
try {
// Try to load config file if it exists
if (fs.existsSync(configPath)) {
try {
const userConfig = require(configPath);
config = { ...config, ...userConfig };
log.info('Loaded configuration from mcp-prompt-enhancer.config.js');
} catch (error) {
log.error('Error loading configuration file', error as Error);
}
} else {
log.info('No configuration file found, using defaults');
}
// Override with environment variables if present
if (process.env.MCP_PORT) {
config.port = parseInt(process.env.MCP_PORT, 10);
}
if (process.env.MCP_LOG_LEVEL) {
config.logLevel = process.env.MCP_LOG_LEVEL;
}
// Set logger level
const logLevelMap: Record<string, LogLevel> = {
'debug': LogLevel.DEBUG,
'info': LogLevel.INFO,
'warn': LogLevel.WARN,
'error': LogLevel.ERROR,
};
logger.configure({
level: logLevelMap[config.logLevel.toLowerCase()] ?? LogLevel.INFO,
isDevelopment: process.env.NODE_ENV !== 'production',
});
return config;
} catch (error) {
log.error('Error in configuration setup', error as Error);
return config;
}
}
/**
* Initialize core components
*/
function initializeComponents(config: any) {
// Create project context manager
const projectContextManager = new ProjectContextManager({
projectPath: process.cwd(),
ignorePatterns: config.ignorePatterns,
cacheTTL: config.contextCacheTTL,
maxContextSize: config.maxContextSize,
});
// Create task tracker
const taskTracker = new TaskTracker();
// Create prompt enhancer
const promptEnhancer = new PromptEnhancer(
projectContextManager,
taskTracker,
{
maxContextSize: config.maxContextSize,
contextFormat: 'markdown',
autoEvaluateNeed: true,
}
);
return {
projectContextManager,
taskTracker,
promptEnhancer,
};
}
/**
* Start the STDIO-based MCP server
*/
async function startServer() {
try {
// Load configuration
const config = loadConfig();
log.info('Starting MCP Prompt Enhancer STDIO Server');
// Initialize components
const { projectContextManager, taskTracker, promptEnhancer } = initializeComponents(config);
// Create readline interface for STDIO communication
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
log.info('STDIO server ready. Waiting for input...');
// Process incoming messages
rl.on('line', async (line) => {
try {
const request = JSON.parse(line);
const { action, params } = request;
let response;
switch (action) {
case 'enhance_prompt':
try {
const { prompt, taskType, focusFiles, includeFullContext } = params;
log.info(`Enhancing prompt: "${prompt.substring(0, 50)}${prompt.length > 50 ? '...' : ''}"`);
const result = await promptEnhancer.enhancePrompt({
prompt,
taskType: taskType as TaskType,
focusFiles: focusFiles as string[],
includeFullContext: !!includeFullContext,
});
log.info(`Enhanced prompt with ${result.contextSize} tokens of context for ${result.taskType} task`);
response = {
status: 'success',
data: {
enhancedPrompt: result.enhancedPrompt,
taskType: result.taskType,
focusArea: result.focusArea,
contextAdded: result.contextAdded,
contextSize: result.contextSize,
}
};
} catch (error) {
log.error('Error enhancing prompt', error as Error);
response = {
status: 'error',
error: (error as Error).message
};
}
break;
case 'set_project_context':
try {
const { projectPath, forceRefresh } = params;
if (projectPath) {
log.info(`Project path specified: ${projectPath}, but changing project path is not supported yet`);
}
log.info(`Refreshing project context${forceRefresh ? ' (forced)' : ''}`);
const context = await projectContextManager.getContext(!!forceRefresh);
response = {
status: 'success',
data: {
message: `Project context updated for ${context.projectName}`,
projectName: context.projectName,
frameworks: context.frameworks,
timestamp: new Date(context.timestamp).toISOString(),
}
};
} catch (error) {
log.error('Error setting project context', error as Error);
response = {
status: 'error',
error: (error as Error).message
};
}
break;
default:
response = {
status: 'error',
error: `Unknown action: ${action}`
};
}
// Send response
process.stdout.write(JSON.stringify(response) + '\n');
} catch (error) {
log.error('Error processing request', error as Error);
process.stdout.write(JSON.stringify({
status: 'error',
error: 'Invalid JSON input'
}) + '\n');
}
});
// Handle shutdown
const shutdown = () => {
log.info('Shutting down server...');
rl.close();
projectContextManager.dispose();
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
} catch (error) {
log.error('Failed to start server', error as Error);
process.exit(1);
}
}
// Auto-start when executed directly
if (require.main === module) {
startServer();
}
// Export for programmatic usage
export { startServer };