@debugmcp/mcp-debugger
Version:
Run-time step-through debugging for LLM agents.
486 lines (381 loc) • 11.1 kB
Markdown
# 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! 📝