import { program } from 'commander';
import { setupProject } from './utils/setup';
import { ContextInjectionServer } from './server';
import { createDefaultContextFile } from './templates/defaultContext';
import { ConfigService } from './services/config.service';
import { findProcessUsingPort } from './utils/port';
import { McpAdapterService } from './features/mcp/services/mcp-adapter.service';
import { ensureContextFile } from './features/mcp/services/contextFileUtil';
import path from 'path';
/**
* CLI tool for managing the Context Injection MCP server
*/
program
.name('context-injection-mcp')
.description('MCP server for injecting development context into Claude conversations')
.version('1.0.0');
program
.command('setup')
.description('Set up the project with default configuration and context')
.action(() => {
setupProject();
});
program
.command('start')
.description('Start the MCP server')
.option('-c, --config <path>', 'Path to configuration file')
.option('-f, --force', 'Force kill any process using the configured port', false)
.option('-p, --port <port>', 'Override the port to use')
.action(async (options) => {
console.log('Starting Context Injection MCP server...');
// If a specific port is provided via CLI, update the config
if (options.port) {
const port = parseInt(options.port, 10);
if (!isNaN(port) && port > 0 && port < 65536) {
const configService = new ConfigService(options.config);
configService.updateConfig({ port });
console.log(`Port set to ${port} from command line argument`);
} else {
console.error('Invalid port number provided. Using configuration value instead.');
}
}
const server = new ContextInjectionServer(options.config);
await server.start({ forceKill: options.force });
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\nShutting down server...');
server.stop();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\nShutting down server...');
server.stop();
process.exit(0);
});
});
program
.command('mcp-start')
.description('Start as an MCP adapter for Claude Desktop')
.option('-c, --config <path>', 'Path to configuration file')
.option('-p, --port <port>', 'Override the port to use')
.option('--context-path <path>', 'Override the context file path')
.action((options) => {
console.error('Starting Context Injection MCP adapter for Claude Desktop...');
try {
let configPath = options.config;
// Update config if context path is provided
if (options.contextPath) {
const configService = new ConfigService(configPath);
configService.updateConfig({ contextPath: options.contextPath });
console.error(`Context path set to ${options.contextPath}`);
}
const configService = new ConfigService(configPath);
const config = configService.getConfig();
// Ensure context file exists
if (!ensureContextFile(config.contextPath)) {
console.error('ERROR: Could not ensure context file. MCP adapter will start, but context injection may not work.');
}
const mcpAdapter = new McpAdapterService(configPath);
mcpAdapter.start();
} catch (error) {
console.error('Failed to start MCP adapter:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
program
.command('create-context')
.description('Create a new default context file')
.option('-o, --output <path>', 'Output path for the context file', path.join(process.cwd(), 'context.json'))
.action((options) => {
createDefaultContextFile(options.output);
});
program
.command('config')
.description('Configure the MCP server')
.option('--port <port>', 'Set server port')
.option('--auto-inject', 'Enable automatic context injection')
.option('--manual-inject', 'Disable automatic context injection')
.option('--show', 'Show current configuration')
.action((options) => {
const configService = new ConfigService();
let config = configService.getConfig();
let updated = false;
if (options.show) {
console.log('Current configuration:');
console.log(JSON.stringify(config, null, 2));
console.log('Port:', config.port);
console.log('Auto Inject:', config.autoInject ? 'ENABLED' : 'DISABLED');
console.log('Trigger Keywords:', config.triggerKeywords.join(', '));
return;
}
if (options.port) {
const port = parseInt(options.port, 10);
if (!isNaN(port) && port > 0 && port < 65536) {
config.port = port;
updated = true;
console.log(`Server port set to: ${port}`);
} else {
console.error('Invalid port number. Port must be between 1 and 65535');
}
}
if (options.autoInject) {
config.autoInject = true;
updated = true;
console.log('Automatic context injection ENABLED');
}
if (options.manualInject) {
config.autoInject = false;
updated = true;
console.log('Automatic context injection DISABLED (will use trigger keywords)');
}
if (updated) {
configService.updateConfig(config);
console.log('Configuration updated successfully');
} else {
console.log('No configuration changes made. Use --show to see current config or provide options to change.');
}
});
program
.command('check-port')
.description('Check if a port is in use')
.option('-p, --port <port>', 'Port to check', '3000')
.action(async (options) => {
const port = parseInt(options.port, 10);
if (isNaN(port)) {
console.error('Invalid port number');
return;
}
try {
const pid = await findProcessUsingPort(port);
if (pid) {
console.log(`Port ${port} is in use by process ID: ${pid}`);
console.log('You can start the server with a different port using:');
console.log(` npm run start -- --port <new-port>`);
console.log('Or force kill the process using:');
console.log(` npm run start -- --force`);
} else {
console.log(`Port ${port} is available and ready to use`);
}
} catch (error) {
console.error('Error checking port:', error instanceof Error ? error.message : String(error));
}
});
// Handle unknown commands
program.on('command:*', () => {
console.error(`\nInvalid command: ${program.args.join(' ')}`);
console.error('See --help for a list of available commands.');
process.exit(1);
});
// Parse command-line arguments
program.parse(process.argv);
// Show help if no arguments provided
if (process.argv.length === 2) {
program.outputHelp();
}