UNPKG

@debugmcp/mcp-debugger

Version:

Run-time step-through debugging for LLM agents.

486 lines (381 loc) 11.1 kB
# MCP Debug Server - Debugging Guide This guide covers how to debug the MCP Debug Server itself during development. Yes, we're debugging the debugger! 🐛 ## Overview Debugging a debug server presents unique challenges: - Multiple processes (server, proxy, debug adapter) - Cross-process communication via IPC - Async operations and event-driven architecture - Protocol-level interactions (MCP, DAP) ## Development Tools ### 1. VS Code Debugger The project includes launch configurations for debugging: ```json // .vscode/launch.json { "type": "node", "request": "launch", "name": "Debug Server (STDIO)", "skipFiles": ["<node_internals>/**"], "program": "${workspaceFolder}/dist/index.js", "outFiles": ["${workspaceFolder}/dist/**/*.js"], "preLaunchTask": "npm: build", "env": { "DEBUG": "*", "LOG_LEVEL": "debug" } } ``` ### 2. Chrome DevTools For advanced debugging: ```bash # Start with inspector node --inspect dist/index.js # Or break on first line node --inspect-brk dist/index.js ``` Then open Chrome and navigate to `chrome://inspect`. ### 3. Debug Logging Enable comprehensive logging: ```bash # All debug output DEBUG=* node dist/index.js # Specific modules DEBUG=mcp:*,proxy:* node dist/index.js # With log file LOG_LEVEL=debug LOG_FILE=debug.log node dist/index.js ``` ## Common Debugging Scenarios ### 1. Server Won't Start **Symptoms**: Server exits immediately or hangs **Debug Steps**: ```typescript // Add to src/index.ts console.log('[DEBUG] Starting server with args:', process.argv); console.log('[DEBUG] Environment:', { NODE_ENV: process.env.NODE_ENV, DEBUG: process.env.DEBUG, PATH: process.env.PATH }); // Check transport initialization try { await server.start(); console.log('[DEBUG] Server started successfully'); } catch (error) { console.error('[DEBUG] Server start failed:', error); console.error('[DEBUG] Stack:', error.stack); } ``` ### 2. Proxy Process Issues **Symptoms**: Proxy doesn't spawn or exits immediately **Debug Steps**: ```typescript // In ProxyManager.start() console.log('[DEBUG] Spawning proxy with:', { script: proxyScriptPath, sessionId: config.sessionId, env: Object.keys(env) }); // Monitor process events this.proxyProcess.on('spawn', () => { console.log('[DEBUG] Proxy spawned, PID:', this.proxyProcess.pid); }); this.proxyProcess.on('error', (err) => { console.error('[DEBUG] Proxy error:', err); }); this.proxyProcess.stderr?.on('data', (data) => { console.error('[DEBUG] Proxy STDERR:', data.toString()); }); ``` ### 3. IPC Communication Problems **Symptoms**: Messages not received, commands timeout **Debug Steps**: ```typescript // In proxy message handler private handleProxyMessage(rawMessage: unknown): void { console.log('[DEBUG] Raw message:', JSON.stringify(rawMessage, null, 2)); if (!isValidProxyMessage(rawMessage)) { console.warn('[DEBUG] Invalid message format:', { type: typeof rawMessage, keys: rawMessage ? Object.keys(rawMessage) : null }); return; } const message = rawMessage as ProxyMessage; console.log('[DEBUG] Parsed message type:', message.type); } // In proxy process process.on('message', (msg) => { console.log('[DEBUG Proxy] Received from parent:', msg); }); if (process.send) { const testMsg = { type: 'test', data: 'hello' }; console.log('[DEBUG Proxy] Sending test message:', testMsg); process.send(testMsg); } ``` ### 4. DAP Protocol Issues **Symptoms**: Debugpy not responding, breakpoints not working **Debug Steps**: ```typescript // Log all DAP traffic this.dapClient.on('send', (message) => { console.log('[DAP ]', JSON.stringify(message, null, 2)); }); this.dapClient.on('receive', (message) => { console.log('[DAP ]', JSON.stringify(message, null, 2)); }); // Track request lifecycle console.log('[DEBUG] Sending DAP request:', { command, requestId, args: JSON.stringify(args) }); // In response handler console.log('[DEBUG] DAP response received:', { requestId: message.requestId, success: message.success, command: pending?.command, elapsed: Date.now() - requestStartTime }); ``` ### 5. State Management Issues **Symptoms**: Incorrect state transitions, stuck states **Debug Steps**: ```typescript // Add state transition logging private _updateSessionState(session: ManagedSession, newState: SessionState): void { const oldState = session.state; console.log('[DEBUG] State transition:', { sessionId: session.id, oldState, newState, stack: new Error().stack?.split('\n')[2] // Caller }); if (!this.isValidTransition(oldState, newState)) { console.error('[DEBUG] Invalid state transition!'); } this.sessionStore.updateState(session.id, newState); } ``` ## Advanced Debugging Techniques ### 1. Process Tree Visualization ```bash # On Unix/macOS ps aux | grep -E "node|python|debugpy" | grep -v grep # With tree view pstree -p $(pgrep -f "mcp-debug-server") # On Windows wmic process where "name like '%node%' or name like '%python%'" get processid,parentprocessid,commandline ``` ### 2. Network Port Monitoring ```bash # Check if debugpy is listening netstat -an | grep 5678 # On macOS lsof -i :5678 # Monitor connection attempts tcpdump -i lo0 port 5678 ``` ### 3. File System Monitoring ```bash # Watch log directory watch -n 1 'ls -la logs/' # Tail all logs tail -f logs/*.log # Monitor file descriptor usage lsof -p $(pgrep -f "proxy-bootstrap") ``` ### 4. Memory Profiling ```typescript // Add heap snapshots import v8 from 'v8'; import fs from 'fs'; function takeHeapSnapshot(label: string) { const fileName = `heap-${label}-${Date.now()}.heapsnapshot`; const stream = fs.createWriteStream(fileName); v8.writeHeapSnapshot(stream); console.log(`[DEBUG] Heap snapshot written to ${fileName}`); } // Usage takeHeapSnapshot('before-session-create'); // ... create sessions takeHeapSnapshot('after-session-create'); ``` ### 5. Event Tracing ```typescript // Trace all events const originalEmit = EventEmitter.prototype.emit; EventEmitter.prototype.emit = function(event: string, ...args: any[]) { console.log('[EVENT]', { emitter: this.constructor.name, event, args: args.length, stack: new Error().stack?.split('\n')[2] }); return originalEmit.apply(this, [event, ...args]); }; ``` ## Debugging Test Failures ### 1. Verbose Test Output ```bash # Run with full output npm test -- --reporter=verbose # Debug specific test DEBUG=* npm test -- tests/unit/proxy/proxy-manager.test.ts # With Node debugging node --inspect-brk node_modules/.bin/vitest run tests/unit/session/session-manager.test.ts ``` ### 2. Test Timeout Debugging ```typescript it('should complete operation', async () => { // Add progress logging console.log('[TEST] Starting operation'); const checkpoints = []; const addCheckpoint = (name: string) => { checkpoints.push({ name, time: Date.now() }); console.log(`[TEST] Checkpoint: ${name}`); }; addCheckpoint('start'); await operation1(); addCheckpoint('after-op1'); await operation2(); addCheckpoint('after-op2'); // If timeout, log checkpoints process.on('uncaughtException', () => { console.log('[TEST] Checkpoints:', checkpoints); }); }); ``` ### 3. Mock Inspection ```typescript // Log all mock calls afterEach(() => { console.log('[TEST] Mock calls:', { logger: mockLogger.info.mock.calls, fileSystem: mockFileSystem.readFile.mock.calls, network: mockNetworkManager.findFreePort.mock.calls }); }); ``` ## Production Debugging ### 1. Enable Debug Mode ```json // In MCP settings { "mcpServers": { "debug-mcp-server": { "command": "node", "args": ["path/to/dist/index.js"], "env": { "DEBUG": "mcp:*,session:*,proxy:*", "LOG_LEVEL": "debug", "LOG_FILE": "/tmp/mcp-debug.log" } } } } ``` ### 2. Diagnostic Commands Add diagnostic tools to the server: ```typescript // Add diagnostic tool server.addTool({ name: 'debug_diagnostics', description: 'Get diagnostic information', inputSchema: { type: 'object', properties: {} }, handler: async () => { return { sessions: sessionManager.getAllSessions(), processInfo: { pid: process.pid, uptime: process.uptime(), memory: process.memoryUsage(), versions: process.versions }, activeProxies: getActiveProxyCount() }; } }); ``` ### 3. Health Checks ```typescript // Add health endpoint for TCP mode if (transport === 'tcp') { const healthServer = http.createServer((req, res) => { if (req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'healthy', sessions: sessionManager.getAllSessions().length, uptime: process.uptime() })); } }); healthServer.listen(port + 1); } ``` ## Debugging Checklists ### Server Startup Issues - [ ] Check Node.js version (`node --version`) - [ ] Verify all dependencies installed (`npm ls`) - [ ] Check for port conflicts - [ ] Verify file permissions on log directory - [ ] Check environment variables - [ ] Look for TypeScript compilation errors ### Proxy Communication Issues - [ ] Verify proxy script exists and is executable - [ ] Check IPC channel is established - [ ] Monitor process spawn events - [ ] Check for stderr output - [ ] Verify message serialization format - [ ] Look for uncaught exceptions in proxy ### Python Debugging Issues - [ ] Verify Python path is correct - [ ] Check debugpy is installed (`pip show debugpy`) - [ ] Verify script path is absolute - [ ] Check for Python syntax errors - [ ] Monitor debugpy adapter output - [ ] Verify DAP message format ### Memory/Performance Issues - [ ] Check for event listener leaks - [ ] Monitor session cleanup - [ ] Verify process termination - [ ] Check for circular references - [ ] Monitor file descriptor usage - [ ] Profile CPU usage during operations ## Tips and Tricks 1. **Use Conditional Breakpoints** ```typescript // Break only for specific session if (sessionId === 'problematic-session-id') { debugger; // VS Code will stop here } ``` 2. **Add Temporary Logging** ```typescript const DEBUG_THIS = true; if (DEBUG_THIS) console.log('[TEMP]', { data }); ``` 3. **Binary Search for Issues** - Comment out half the code - See if issue persists - Narrow down to problematic section 4. **Use Git Bisect** ```bash git bisect start git bisect bad HEAD git bisect good v0.8.0 # Git will help find the breaking commit ``` 5. **Create Minimal Reproduction** - Isolate the problem - Remove unnecessary code - Create standalone test case ## Summary Debugging the MCP Debug Server requires: - Understanding the multi-process architecture - Monitoring IPC communication - Tracking async operations - Using appropriate debugging tools - Following systematic debugging approach Remember: When debugging gets tough, add more logging! 📝