This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
mcp-debugger is a Model Context Protocol (MCP) server that provides step-through debugging capabilities for AI agents. It acts as a bridge between MCP clients (like Claude) and debug adapters, enabling structured debugging operations through JSON-based tool calls.
The project uses a monorepo architecture with dynamic adapter loading, allowing language-specific debug adapters to be developed and deployed independently.
mcp-debugger/
├── packages/
│ ├── shared/ # Shared interfaces, types, and utilities
│ ├── adapter-python/ # Python debug adapter using debugpy
│ ├── adapter-javascript/ # JavaScript/Node.js adapter using js-debug
│ ├── adapter-rust/ # Rust debug adapter using CodeLLDB
│ ├── adapter-go/ # Go debug adapter using Delve
│ ├── adapter-java/ # Java debug adapter using JDI bridge
│ ├── adapter-dotnet/ # .NET/C# debug adapter using netcoredbg
│ ├── adapter-mock/ # Mock adapter for testing
│ └── mcp-debugger/ # Self-contained CLI bundle (npx distribution)
├── src/
│ ├── adapters/ # Adapter loading and registry system
│ ├── container/ # Dependency injection container
│ ├── proxy/ # DAP proxy system
│ └── session/ # Session management
└── tests/ # Comprehensive test suite
- @debugmcp/shared: Core interfaces and types used across all packages
- @debugmcp/adapter-python: Python debugging support via debugpy
- @debugmcp/adapter-javascript: JavaScript/Node.js debugging support via js-debug
- @debugmcp/adapter-rust: Rust debugging support via CodeLLDB
- @debugmcp/adapter-go: Go debugging support via Delve
- @debugmcp/adapter-java: Java debugging support via JDI bridge
- @debugmcp/adapter-dotnet: .NET/C# debugging support via netcoredbg
- @debugmcp/adapter-mock: Mock adapter for testing and development
- @debugmcp/mcp-debugger: Self-contained CLI bundle for npm distribution (npx-ready)
IMPORTANT: This project uses pnpm, not npm. The workspace:* protocol in dependencies requires pnpm. Run scripts via npm run <script> (which delegates to pnpm) or use pnpm directly.
# Install dependencies (including workspace packages)
pnpm install
# Build all packages and main project
npm run build
# Build specific packages
npm run build:shared
npm run build:adapters # Build mock + python adapters
npm run build:adapters:all # Build all adapters including JavaScript
npm run build:packages # Build all packages in correct order via build-packages.cjs
# Clean build
npm run build:clean
# Development mode with watch
npm run dev
# Start the server (after building)
npm start
# or
node dist/index.js
# Run with specific transport modes
node dist/index.js # STDIO mode (default)
node dist/index.js http -p 3001 # Streamable HTTP mode (recommended for remote)
node dist/index.js sse -p 3001 # SSE mode (DEPRECATED — use http instead)# Run all tests
npm test
# Run specific test suites
npm run test:unit # Unit tests only
npm run test:integration # Integration tests only
npm run test:e2e # End-to-end tests only
# Run tests with coverage
npm run test:coverage
npm run test:coverage:summary # Show coverage summary
# Run tests in watch mode
npm run test:watch
# Run a specific test file
npx vitest run tests/unit/session/session-manager.test.ts
# Run smoke tests for quick validation
npm run test:e2e:smoke# Lint code
npm run lint
# Fix linting issues automatically
npm run lint:fix
# Check for personal paths (pre-commit hook)
npm run check:personal-paths
npm run check:all-personal-paths # Check all files# Build Docker image
npm run docker-build
# or
docker build -t mcp-debugger:local .
# Test container locally
npm run test:e2e:container
# Run container
docker run -v $(pwd):/workspace mcp-debugger:local# Test GitHub Actions locally using Act
npm run act:check # Verify Act is installed
npm run act:lint # Run lint job
npm run act:test # Run test job (Ubuntu)
npm run act:test:all # Run all test jobs
npm run act:full # Run full CI workflowThe project uses a TRUE HANDS-OFF approach to path handling:
- Accept all paths as-is - No interpretation of Windows vs Linux paths
- Path resolution and existence checking - For immediate LLM UX feedback (
SimpleFileChecker) - Container mode: Simple prefix - Only
/workspace/prepend for existence checks - Pass resolved effective paths -
src/server.tsvalidates paths viaSimpleFileCheckerand passes the resolvedeffectivePathto SessionManager - No cross-platform logic - Avoids unsolvable edge cases and complexity
Key Files:
src/utils/simple-file-checker.ts- Path resolution (viaresolvePathForRuntime), relative-path rejection in host mode, and file existence checkingsrc/server.ts- Uses SimpleFileChecker for validation, passes the resolvedeffectivePathto SessionManager
Rationale: Cross-platform path handling is theoretically impossible due to ambiguous edge cases. The debug adapter and OS know best how to handle paths for their environment.
The codebase follows a layered architecture with dependency injection and dynamic adapter loading:
-
MCP Server Layer (
src/server.ts,src/index.ts)- Entry point for MCP protocol communication
- Handles tool registration and routing
- Supports STDIO and Streamable HTTP transport modes (legacy SSE deprecated)
- Dynamically discovers available language adapters
-
Adapter System (NEW)
- AdapterRegistry (
src/adapters/adapter-registry.ts): Manages adapter lifecycle - AdapterLoader (
src/adapters/adapter-loader.ts): Dynamically loads adapters on-demand - Language Adapters (
packages/adapter-*): Language-specific implementations - Supports both pre-registered and dynamically loaded adapters
- AdapterRegistry (
-
SessionManager (
src/session/)- Central orchestrator for debug sessions, implemented as a 4-class inheritance hierarchy:
SessionManagerCore(session-manager-core.ts): Lifecycle, state management, event handling, dependency wiringSessionManagerData(session-manager-data.ts): Data retrieval (variables, stack traces, scopes) and adapter policy selection viaselectPolicy()SessionManagerOperations(session-manager-operations.ts): Debug operations (start, step, continue, breakpoints, attach/detach)SessionManager(session-manager.ts): Final composition class that extends SessionManagerOperations. ImplementshandleAutoContinue(sessionId)which auto-continues past entry breakpoints whenstopOnEntry=false
- Coordinates ProxyManager instances (one per session)
- Handles breakpoint management and queuing
- Central orchestrator for debug sessions, implemented as a 4-class inheritance hierarchy:
-
ProxyManager (
src/proxy/proxy-manager.ts)- Manages communication with debug proxy process
- Spawns proxy worker in separate process
- Implements typed event system for DAP events
- Handles request/response correlation with timeouts
-
DAP Proxy System (
src/proxy/dap-proxy-*.ts,src/proxy/minimal-dap.ts)- ProxyRunner (
dap-proxy-core.ts): Pure business logic, message processing - DapProxyWorker (
dap-proxy-worker.ts): Core worker handling debugging operations - Adapter Policies: Language-specific behavior via policy pattern (
DefaultAdapterPolicy,PythonAdapterPolicy,JsDebugAdapterPolicy,RustAdapterPolicy,GoAdapterPolicy,JavaAdapterPolicy,DotnetAdapterPolicy,MockAdapterPolicy). Note: Java is fully wired toJavaAdapterPolicyinDapProxyWorker.selectAdapterPolicy()(not falling through toDefaultAdapterPolicy). - ChildSessionManager (
src/proxy/child-session-manager.ts): Manages DAP child sessions within a single proxy process. Currently used by the js-debug adapter (childSessionStrategy: 'launchWithPendingTarget'), which spawns a child debug session for the actual debuggee while the parent session manages the launch orchestration. - Implements full Debug Adapter Protocol (DAP) communication
- ProxyRunner (
- Dependency Injection: All major components use constructor injection via interfaces
- Factory Pattern:
ProxyManagerFactory,SessionStoreFactory,AdapterFactoryfor testability - Dynamic Loading: Language adapters loaded on-demand via ES modules
- Event-Driven: Extensive EventEmitter usage for async communication
- Process Isolation: Each debug session runs in separate process for stability
- Error Boundaries: Centralized error handling with user-friendly messages
MCP Client → MCP Server → SessionManager → ProxyManager → ProxyWorker → Language Adapter → Debug Runtime
↓
AdapterRegistry → AdapterLoader → Dynamic Import of @debugmcp/adapter-*
The system supports dynamic adapter loading through:
- AdapterLoader: Attempts to load adapters by package name (
@debugmcp/adapter-{language}) - Fallback Paths: Checks multiple locations (node_modules, packages directory)
- Registry Integration: Auto-registers dynamically loaded adapters
- Container Mode: Pre-loads known adapters in Docker environments
Sessions use SessionState as the primary state model, stored directly on each ManagedSession and checked throughout the codebase:
- SessionState:
CREATED→INITIALIZING→READY→RUNNING⇄PAUSED→STOPPED|ERROR
A dual-state overlay (SessionLifecycleState + ExecutionState) is derived from SessionState via mapLegacyState() in _updateSessionState():
- SessionLifecycleState:
CREATED→ACTIVE→TERMINATED(coarse lifecycle) - ExecutionState:
INITIALIZING→RUNNING⇄PAUSED→TERMINATED|ERROR(fine-grained execution)
SessionState is the actively used model; the dual-state fields are kept in sync as a secondary representation.
src/server.ts- Main MCP server implementationsrc/session/- Session management (4-class hierarchy:session-manager-core.ts→session-manager-data.ts→session-manager-operations.ts→session-manager.ts)src/proxy/proxy-manager.ts- Proxy process managementsrc/proxy/dap-proxy-worker.ts- Debug adapter protocol implementation
src/adapters/adapter-registry.ts- Adapter lifecycle managementsrc/adapters/adapter-loader.ts- Dynamic adapter loading (7 known adapters)packages/shared/- Shared interfaces and typespackages/adapter-python/- Python debug adapter (debugpy)packages/adapter-javascript/- JavaScript/Node.js debug adapter (js-debug)packages/adapter-rust/- Rust debug adapter (CodeLLDB)packages/adapter-go/- Go debug adapter (Delve)packages/adapter-java/- Java debug adapter (JDI bridge)packages/adapter-dotnet/- .NET/C# debug adapter (netcoredbg)packages/adapter-mock/- Mock adapter for testing
packages/mcp-debugger/- Self-contained CLI bundle for npm/npx distribution
src/container/dependencies.ts- Dependency injection containersrc/utils/error-messages.ts- Centralized error messagestests/- Comprehensive test suite (unit, integration, e2e)examples/- Example scripts for debuggingdocs/architecture/- Detailed architecture documentation
- TypeScript Strict Mode: All code must pass TypeScript strict mode checks
- Monorepo Management: Use pnpm workspaces for package management (
pnpm install, notnpm install) - Build Order: Packages must build in order: shared → adapters → main server. This is managed by
scripts/build-packages.cjs - Test Coverage: Maintain >90% test coverage
- Error Handling: Use centralized error messages from
error-messages.ts - Logging: Use Winston logger with appropriate log levels
- Async Operations: All DAP operations are async with timeouts
- Process Cleanup: Always ensure proper cleanup of spawned processes
- Adapter Development: New language adapters should implement
IAdapterFactoryfrom@debugmcp/shared
The project uses Vitest with three test levels:
- Unit Tests: Test components in isolation with mocks
- Integration Tests: Test component interactions
- E2E Tests: Test full debugging workflows with real debug adapters
When debugging issues:
- Enable debug logging:
DEBUG=debug-mcp:* node dist/index.js - Check proxy process output in logs
- Verify language-specific requirements (e.g.,
python -m debugpy --version) - Use
dryRunSpawn: trueinstart_debuggingtool arguments to test configuration without starting a real debug session
To add support for a new language:
- Create Package: Add new package under
packages/adapter-{language}/ - Implement Interfaces: Implement
IAdapterFactoryandIDebugAdapterfrom@debugmcp/shared - Export Factory: Export a factory class named
{Language}AdapterFactory - Register in root
package.json: Add"@debugmcp/adapter-{language}": "workspace:*"tooptionalDependencies - Add Vitest alias: Add
{ find: '@debugmcp/adapter-{language}', replacement: path.resolve(__dirname, './packages/adapter-{language}/src/index.ts') }toresolve.aliasinvitest.config.ts - Update adapter count: Update hardcoded adapter counts in tests (
adapter-loader.test.ts,models.test.ts) - Add Tests: Include unit and integration tests in the package
- Run
pnpm install: To link the new workspace package
Example structure:
packages/adapter-{language}/
├── src/
│ ├── index.ts # Export {Language}AdapterFactory
│ ├── {language}-debug-adapter.ts # Implement IDebugAdapter
│ └── {language}-adapter-factory.ts # Implement IAdapterFactory
├── tests/
├── package.json # Must include "type": "module" and workspace:* dep on @debugmcp/shared
├── tsconfig.json
└── vitest.config.ts # Optional if tests run from root
- Python 3.7+ must be installed
- debugpy must be installed:
pip install debugpy - The system will auto-detect Python path or use
PYTHON_PATHenv var
- Node.js 22+ must be installed
- Uses bundled js-debug adapter (VSCode's debugger)
- Supports JavaScript and TypeScript debugging
- Auto-detects TypeScript configuration
- Rust toolchain must be installed (rustc, cargo)
- Uses vendored CodeLLDB debug adapter (auto-downloaded during
pnpm install) - Supports both MSVC and GNU toolchains on Windows
- JDK 21+ must be installed (
javaandjavacon PATH, orJAVA_HOMEset) - Uses JDI bridge (
JdiDapServer.java) — a single Java file compiled on first use viajavac - Must compile target code with
javac -gfor variable inspection (includesLocalVariableTable) - Launch mode: The adapter derives
mainClassfrom theprogramfield in the launch config and transparently forwardsclasspath,sourcePath,cwd,env, andargs— JDI bridge spawns the JVM and connects via JDI - Attach mode: Connect to JVM with JDWP agent (
-agentlib:jdwp=...). JDI bridge handles deferred breakpoints natively viaClassPrepareRequest - Zero external dependencies — JDI (
com.sun.jdi.*) ships with every JDK - See
docs/java/README.mdfor architecture details
- Go 1.18+ must be installed
- Delve debugger must be installed:
go install github.com/go-delve/delve/cmd/dlv@latest - Uses Delve's native DAP protocol support
- .NET 6+ SDK must be installed
- netcoredbg must be installed: download from Samsung releases or build from source
- Set
NETCOREDBG_PATHenvironment variable to the netcoredbg executable, or add its directory to PATH - PDB symbols must be in Portable format (compile with
/debug:portableor the adapter's auto Pdb2Pdb conversion) - Uses TCP-to-stdio bridge on all platforms (works around a netcoredbg --server mode bug originally discovered on Windows)
- See
docs/dotnet/README.mdfor architecture details and debugging guide
- No external requirements
- Used for testing the debug infrastructure
Choose the installation method that best fits your use case:
# Best for: Trying out mcp-debugger
/home/ubuntu/.claude/local/claude mcp add-json mcp-debugger \
'{"type":"stdio","command":"npx","args":["@debugmcp/mcp-debugger","stdio"]}'# Best for: Regular use across projects
npm install -g @debugmcp/mcp-debugger
/home/ubuntu/.claude/local/claude mcp add-json mcp-debugger \
'{"type":"stdio","command":"mcp-debugger","args":["stdio"]}'# Best for: Isolation and consistency
/home/ubuntu/.claude/local/claude mcp add-json mcp-debugger \
'{"type":"stdio","command":"docker","args":["run","-i","--rm","-v","${PWD}:/workspace","debugmcp/mcp-debugger:latest","stdio"]}'# Best for: One-off use from a local clone
pnpm install && npm run build
/home/ubuntu/.claude/local/claude mcp add-json mcp-debugger \
'{"type":"stdio","command":"node","args":["/home/ubuntu/mcp-debugger/dist/index.js","stdio"]}'Note: The stdio argument is critical - it tells the server to suppress all console output which would otherwise corrupt the JSON-RPC protocol communication.
# Best for: Active development of mcp-debugger itself
pnpm install && npm run build
claude mcp add-json mcp-debugger \
'{"type":"stdio","command":"node","args":["tools/dev-proxy/dev-proxy.mjs"]}'The dev proxy (tools/dev-proxy/dev-proxy.mjs) is a lightweight MCP proxy that sits between Claude Code and mcp-debugger. It maintains a stable stdio connection to Claude Code while managing the backend as a restartable Streamable HTTP child process (default; legacy SSE and stdio modes are also supported via DEV_PROXY_BACKEND_TRANSPORT). This means you can rebuild and restart mcp-debugger without restarting Claude Code (which would lose conversation context).
When to use: Whenever you are actively developing mcp-debugger — making code changes, adding adapters, or installing new toolchains (Go, etc.) that need to be picked up by the running server.
How it works: After code changes, call the dev_rebuild_and_restart tool. The proxy kills the backend, runs npm run build, spawns a fresh process, and reconnects — all transparently. If the backend crashes, dev tools remain available to bring it back.
Configuration (env vars, all optional):
DEV_PROXY_PORT— Backend HTTP port (default: 3001; used byhttpandssemodes)DEV_PROXY_BUILD_CMD— Build command (default:npm run build)DEV_PROXY_ROOT— Project root (default: auto-detected)DEV_PROXY_BACKEND_TRANSPORT—http(default),sse(legacy/deprecated), orstdio
After adding the MCP server:
-
Check connection status:
/home/ubuntu/.claude/local/claude mcp list # Should show: mcp-debugger ... - ✓ Connected -
Restart Claude Code for the changes to take effect
- Location: Configuration saved to
/home/ubuntu/.claude.jsonunder the project'smcpServerssection - Server Type: STDIO (local server)
- Command:
node /home/ubuntu/mcp-debugger/dist/index.js stdio(stdio argument is required!) - Status Check: After restart, type
/mcpin Claude Code to see connected servers
Once connected, the following 21 MCP tools become available:
create_debug_session- Start a new debug sessionlist_debug_sessions- List active debug sessionslist_supported_languages- Show available language adaptersset_breakpoint- Set breakpoints in codestart_debugging- Begin debugging a scriptattach_to_process- Attach debugger to a running processdetach_from_process- Detach debugger from a processclose_debug_session- Clean up sessionsstep_over,step_into,step_out- Step through codecontinue_execution- Continue runningpause_execution- Pause a running programlist_threads- List all threads in a debug sessionget_variables- Inspect variables in scopeget_local_variables- Get local variables in current frameget_stack_trace- Inspect call stackget_scopes- Get variable scopes for a stack frameevaluate_expression- Evaluate expressions in debug contextget_source_context- Get source code around current positionredefine_classes- Hot-swap changed Java classes into a running JVM (Java only)
Dev proxy only (these 3 tools are injected by the dev proxy process itself, not by the main mcp-debugger server):
dev_restart_debugger- Restart the backend (passrebuild: trueto build first)dev_rebuild_and_restart- Runnpm run buildthen restart the backenddev_server_status- Check backend state, PID, uptime, tool count
- If server shows "Failed to connect":
- Ensure the
stdioargument is included in the configuration - The server silences console output at startup to protect stdio/IPC transports, but misconfigured builds or third-party code may still produce output
- Use the
add-jsoncommand shown above to properly configure with the stdio argument - Note: The
stdiocommand is the default subcommand (isDefault: truein CLI setup), so explicitly passingstdiois optional but recommended for clarity
- Ensure the
- Test the server manually:
echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"roots":{},"sampling":{}},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}' | node dist/index.js stdio # Should return clean JSON without any log messages
- Verify Python and debugpy are installed:
python3 -m debugpy --version - Check logs if needed: Set
DEBUG=debug-mcp:*environment variable (only for troubleshooting, not for normal operation)