/**
* Debug MCP Server - Entry Point
*
* This is the main entry point for the Debug MCP Server.
*/
// CRITICAL: Console silencing MUST be first - before ANY imports
// This prevents stdout pollution which can:
// 1. Break MCP protocol in stdio mode
// 2. Corrupt IPC channels when spawning proxy processes in SSE mode
// Console output MUST always be silenced to ensure reliable operation
(() => {
// Always silence console output to prevent protocol corruption
process.env.CONSOLE_OUTPUT_SILENCED = '1';
const noop = () => {};
console.log = noop;
console.error = noop;
console.warn = noop;
console.info = noop;
console.debug = noop;
console.trace = noop;
console.dir = noop;
console.table = noop;
console.group = noop;
console.groupEnd = noop;
console.time = noop;
console.timeEnd = noop;
console.assert = noop;
// Suppress process warnings
process.removeAllListeners('warning');
process.on('warning', noop);
})();
// Clean argv before any code processes it - strip quotes from all arguments
process.argv = process.argv.map(arg =>
typeof arg === 'string' ? arg.replace(/^["'](.*)["']$/, '$1') : arg
);
import { createLogger } from './utils/logger.js';
import { DebugMcpServer } from './server.js';
import { setupErrorHandlers } from './cli/error-handlers.js';
import {
createCLI,
setupStdioCommand,
setupSSECommand,
setupCheckRustBinaryCommand,
} from './cli/setup.js';
import { handleStdioCommand } from './cli/stdio-command.js';
import { handleSSECommand } from './cli/sse-command.js';
import { handleCheckRustBinaryCommand } from './cli/commands/check-rust-binary.js';
import { getVersion } from './cli/version.js';
import fs from 'fs';
import path from 'path';
export interface ServerOptions {
logLevel?: string;
logFile?: string;
}
// Factory function for creating server instances
export function createDebugMcpServer(options: ServerOptions): DebugMcpServer {
return new DebugMcpServer(options);
}
/**
* Emit an early, file-only breadcrumb when running in stdio mode (useful under Docker/bundling).
* This avoids console output but gives us deterministic startup traces inside the container.
*/
try {
// In containers, always emit an early breadcrumb so we can diagnose startup before logger/CLI
if (process.env.MCP_CONTAINER === 'true') {
const logDir = '/app/logs';
try {
fs.mkdirSync(logDir, { recursive: true });
} catch {}
try {
const msg = `Bundle entry loaded @ ${new Date().toISOString()} | argv=${JSON.stringify(process.argv)}\n`;
fs.appendFileSync(path.join(logDir, 'bundle-start.log'), msg);
} catch {}
}
// Environment flag is already set unconditionally above
} catch {
// ignore diagnostics write failures
}
// Main execution function
export async function main(): Promise<void> {
const logger = createLogger('debug-mcp:cli');
// Setup error handlers
setupErrorHandlers({ logger });
// Create CLI
const program = createCLI('debug-mcp-server', 'Step-through debugging MCP server for LLMs', getVersion());
// Setup commands
setupStdioCommand(program, (options) =>
handleStdioCommand(options, { logger, serverFactory: createDebugMcpServer })
);
setupSSECommand(program, (options) =>
handleSSECommand(options, { logger, serverFactory: createDebugMcpServer })
);
setupCheckRustBinaryCommand(program, (binaryPath, options) =>
handleCheckRustBinaryCommand(binaryPath, options)
);
// Parse command line arguments
program.parse();
}
// Only execute if this is the main module
// Check if the script is being run directly (not imported)
// Handle both ESM (import.meta.url) and CJS (__filename) contexts
const isMainModule = (() => {
// In CJS context (bundled), use require.main check
if (typeof require !== 'undefined' && typeof module !== 'undefined' && require.main === module) {
return true;
}
// In ESM context, check import.meta.url
if (typeof import.meta !== 'undefined' && import.meta.url && process.argv[1]) {
const scriptPath = process.argv[1].replace(/\\/g, '/');
const moduleUrl = import.meta.url.replace(/\\/g, '/');
return moduleUrl === `file://${scriptPath}` ||
moduleUrl.endsWith(scriptPath) ||
scriptPath.endsWith('dist/index.js');
}
// Fallback: assume it's main if we can't determine otherwise
return true;
})();
if (isMainModule) {
const skipAutoStart = process.env.DEBUG_MCP_SKIP_AUTO_START === '1';
if (!skipAutoStart) {
main().catch(() => {
// Console output is always silenced - errors go to the logger
// Never write to console as it can corrupt protocols
process.exit(1);
});
}
}
// Export for testing
export {
setupErrorHandlers,
createCLI,
setupStdioCommand,
setupSSECommand,
setupCheckRustBinaryCommand,
handleStdioCommand,
handleSSECommand,
handleCheckRustBinaryCommand
};