Skip to content

Latest commit

 

History

History
573 lines (458 loc) · 33.6 KB

File metadata and controls

573 lines (458 loc) · 33.6 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

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.

Monorepo Structure

mcp-debugger/
├── packages/
│   ├── shared/             # Shared interfaces, types, and utilities
│   ├── codelldb-common/    # Shared CodeLLDB vendoring/resolution (rust + cpp)
│   ├── adapter-python/     # Python debug adapter using debugpy
│   ├── adapter-ruby/       # Ruby debug adapter using rdbg (debug gem)
│   ├── 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-cpp/        # C/C++ debug adapter using CodeLLDB
│   ├── 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

Package Details

  • @debugmcp/shared: Core interfaces and types used across all packages
  • @debugmcp/codelldb-common: Shared CodeLLDB vendoring, resolver, and spawn glue (used by rust and cpp adapters)
  • @debugmcp/adapter-python: Python debugging support via debugpy
  • @debugmcp/adapter-ruby: Ruby debugging support via rdbg (debug gem)
  • @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-cpp: C/C++ debugging support via CodeLLDB (launch + attach-by-PID)
  • @debugmcp/adapter-mock: Mock adapter for testing and development
  • @debugmcp/mcp-debugger: Self-contained CLI bundle for npm distribution (npx-ready)

Key Commands

Building and Development

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 + ruby 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)

Testing

# 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

Code Quality

# Type-check the shipped sources (src + packages/*/src, no build needed)
npm run typecheck

# Type-check the tests against the per-file ratchet in tests/typecheck-baseline.json
npm run typecheck:tests
npm run typecheck:tests:update  # re-record the baseline (see the note below)
npm run typecheck:tests:raw     # raw tsc output for tsconfig.spec.json
npm run typecheck:tests -- --allow-improvement  # CI/Dependabot only (see the note below)

# Both of the above. This is the exact command CI and pre-push run.
npm run typecheck:all

# 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

Note: the root tsc -p tsconfig.json checks nothing (it is a solution-style config with "files": []), which is why typecheck uses tsconfig.typecheck.json (#562).

The test ratchet fails in both directions:

  • A count went UP — you introduced type errors. Fix them. Re-recording the baseline is the exception, not the remedy: do it only when the new errors are genuinely unavoidable (say, an existing pattern the PR merely extends), and say so in the PR.
  • A count went DOWN, or a test file was removed — that is progress, but the baseline is now stale. Run npm run typecheck:tests:update and commit tests/typecheck-baseline.json in the same PR.

Failing on a decrease is what keeps the recorded numbers honest; the ceiling only ever moves down. The one exception is --allow-improvement, which warns on a decrease instead of failing: CI passes it only for PRs authored by Dependabot, which cannot commit a refreshed baseline. Never use it locally or on a human PR.

That exception has a known cost, accepted deliberately: a Dependabot PR whose bump makes the suite type-cleaner can auto-merge with a stale tests/typecheck-baseline.json, and auto-merge pushes with GITHUB_TOKEN, which suppresses push CI on main — so nothing downstream refreshes it. The next human PR is the documented remedy: it will fail the ratchet on a decrease, and npm run typecheck:tests:update + committing the baseline is the fix. To make that predictable rather than a surprise, the dependabot run prints a ::warning annotation on the baseline file and a job-summary line saying exactly this.

If the script refuses to read tests/typecheck-baseline.json at all (invalid JSON, the wrong shape, a negative or non-integer count, a path outside the test trees), it exits 2 and tells you to delete the file first: --update reads the baseline before writing one, so a corrupt file blocks its own repair.

Docker

# 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

GitHub Actions Testing (Act)

# Test GitHub Actions locally using Act
# NOTE: the act:* scripts invoke scripts\act-runner.cmd and are Windows-only;
# on Linux/macOS run `act` directly with the equivalent flags.
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 workflow

Path Handling Policy 🚨 CRITICAL

The project uses a TRUE HANDS-OFF approach to path handling:

  1. Accept all paths as-is - No interpretation of Windows vs Linux paths
  2. Path resolution and existence checking - For immediate LLM UX feedback (SimpleFileChecker)
  3. Container mode: Simple prefix - Only /workspace/ prepend for existence checks
  4. Pass resolved effective paths - src/server.ts validates paths via SimpleFileChecker and passes the resolved effectivePath to SessionManager
  5. No cross-platform logic - Avoids unsolvable edge cases and complexity

Key Files:

  • src/utils/simple-file-checker.ts - Path resolution (via resolvePathForRuntime), relative-path rejection in host mode, and file existence checking
  • src/server.ts - Uses SimpleFileChecker for validation, passes the resolved effectivePath to 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.

Architecture Overview

The codebase follows a layered architecture with dependency injection and dynamic adapter loading:

Core Components

  1. MCP Server Layer (src/server.ts, src/server/, src/index.ts)

    • Entry point for MCP protocol communication
    • src/server.ts is the composition root (DebugMcpServer: dependencies, lifecycle, session validation, breakpoint gating, the public facade methods); it implements ToolContext and passes itself to the handlers
    • src/server/ holds the tool schemas (tool-schemas.ts), argument coercion (tool-arguments.ts), the tools/call dispatch wrapper (tool-dispatch.ts over handlers/index.ts), argument validation (tool-validation.ts), result envelopes and the session-error catch helpers (tool-result.ts), one handler module per tool family (handlers/*.ts) over their shared helpers (handlers/shared.ts), and the resource (output-resources.ts) and prompt (prompts.ts) handlers
    • Supports STDIO and Streamable HTTP transport modes (legacy SSE deprecated)
    • Dynamically discovers available language adapters
  2. 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
  3. 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 wiring
      • SessionManagerData (session-manager-data.ts): Data retrieval (variables, stack traces, scopes) and adapter policy selection via selectPolicy()
      • SessionManagerOperations (session-manager-operations.ts): Debug operations facade (~350 lines): the tunables, the OperationsContext wiring, the collaborator fields, and one-line delegates — no operation body lives here any more
      • SessionManager (session-manager.ts): Final composition class that extends SessionManagerOperations. Implements handleAutoContinue(sessionId) which auto-continues past entry breakpoints when stopOnEntry=false
    • SessionManagerOperations is itself a facade over per-slice collaborators, wired as protected fields and always reached at call time (this.breakpoints.…):
      • launch/proxy-launcher.ts (ProxyLauncher.start(session, request), the one proxy launch both launch and attach go through): adapter creation under an AdapterLease, the configuration transform, executable resolution, ProxyConfig assembly; launch/proxy-failure-diagnostics.ts (failProxySetup/logProxyFailure): the shared failure teardown and record
      • launch/debug-launcher.ts (DebugLauncher): startDebugging (dry runs included), restartDebugging; launch/launch-readiness.ts (waitForLaunchReadiness): the post-handshake wait for the first stop / configured / termination
      • attach/attach-controller.ts (AttachController): attachToProcess, detachFromProcess; attach/attach-verification.ts (verifyAttachThreads, pauseAfterAttach): the thread poll and post-attach pause that make PAUSED real before it is reported
      • breakpoints/breakpoint-controller.ts (BreakpointController): set/remove/clear plus the replace-all DAP re-sends
      • breakpoints/anchor-resolution.ts (reresolveAnchors) and breakpoints/launch-warnings.ts (pure warning builders)
      • execution/execution-controller.ts (ExecutionController): stepping (table-driven over STEP_KINDS), continue, pause, thread list
      • inspection/expression-evaluator.ts (ExpressionEvaluator), jvm/redefine-classes-controller.ts (RedefineClassesController), mirror/mirror-controller.ts (MirrorController)
    • Collaborators see the facade through OperationsContext (operations-context.ts), whose members are late-bound arrows and getters — never captured references — so reassigning a facade method or writing a tunable on a live instance is visible to them; each takes the narrowest Pick<> slice it uses. Collaborator-to-collaborator dependencies are constructor arguments (DebugLauncher(ctx, proxyLauncher, breakpoints), AttachController(ctx, proxyLauncher, breakpoints)) held as captured instance references: their methods resolve at call time, so vi.spyOn(internals(ops).proxyLauncher, 'start') intercepts, but reassigning a collaborator field after construction is not observed. DebugLauncher owns the restart reentrancy guard, and restartDebugging replays through DebugLauncher.startDebugging itself, not the facade's startDebugging. Shared per-request DAP helpers live in dap-request-helpers.ts
    • Coordinates ProxyManager instances (one per session)
    • Handles breakpoint management and queuing
  4. 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
  5. 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, RubyAdapterPolicy, RustAdapterPolicy, GoAdapterPolicy, JavaAdapterPolicy, DotnetAdapterPolicy, CppAdapterPolicy, MockAdapterPolicy); LLDB-generic pieces shared by rust/cpp live in lldb-policy-shared.ts. Note: Java is fully wired to JavaAdapterPolicy in DapProxyWorker.selectAdapterPolicy() (not falling through to DefaultAdapterPolicy).
    • 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

Key Patterns

  • Dependency Injection: All major components use constructor injection via interfaces
  • Factory Pattern: ProxyManagerFactory, SessionStoreFactory, AdapterFactory for 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

Data Flow

MCP Client → MCP Server → SessionManager → ProxyManager → ProxyWorker → Language Adapter → Debug Runtime
                ↓
         AdapterRegistry → AdapterLoader → Dynamic Import of @debugmcp/adapter-*

Dynamic Adapter Loading

The system supports dynamic adapter loading through:

  1. AdapterLoader: Attempts to load adapters by package name (@debugmcp/adapter-{language})
  2. Fallback Paths: Checks multiple locations (node_modules, packages directory)
  3. Registry Integration: Auto-registers dynamically loaded adapters
  4. Container Mode: Pre-loads known adapters in Docker environments

State Management

Sessions use SessionState as the primary state model, stored directly on each ManagedSession and checked throughout the codebase:

  • SessionState: CREATEDINITIALIZINGREADYRUNNINGPAUSEDSTOPPED | ERROR

A dual-state overlay (SessionLifecycleState + ExecutionState) is derived from SessionState via mapLegacyState() in _updateSessionState():

  • SessionLifecycleState: CREATEDACTIVETERMINATED (coarse lifecycle)
  • ExecutionState: INITIALIZINGRUNNINGPAUSEDTERMINATED | ERROR (fine-grained execution)

SessionState is the actively used model; the dual-state fields are kept in sync as a secondary representation.

Important Files and Directories

Core System

  • src/server.ts - Main MCP server implementation
  • src/session/ - Session management (4-class hierarchy: session-manager-core.tssession-manager-data.tssession-manager-operations.tssession-manager.ts)
  • src/proxy/proxy-manager.ts - Proxy process management
  • src/proxy/dap-proxy-worker.ts - Debug adapter protocol implementation

Adapter System

  • src/adapters/adapter-registry.ts - Adapter lifecycle management
  • src/adapters/adapter-loader.ts - Dynamic adapter loading (9 known adapters)
  • packages/shared/ - Shared interfaces and types
  • packages/adapter-python/ - Python debug adapter (debugpy)
  • packages/adapter-ruby/ - Ruby debug adapter (rdbg)
  • 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-cpp/ - C/C++ debug adapter (CodeLLDB)
  • packages/adapter-mock/ - Mock adapter for testing

Distribution

  • packages/mcp-debugger/ - Self-contained CLI bundle for npm/npx distribution

Supporting Infrastructure

  • src/container/dependencies.ts - Dependency injection container
  • src/utils/error-messages.ts - Centralized error messages
  • tests/ - Comprehensive test suite (unit, integration, e2e)
  • examples/ - Example scripts for debugging
  • docs/architecture/ - Detailed architecture documentation

Development Guidelines

  1. TypeScript Strict Mode: src/ and packages/*/src are strict-clean — npm run typecheck must report zero errors. Tests are strict too but are ratcheted: they carry a recorded per-file backlog in tests/typecheck-baseline.json that may only shrink (see Code Quality above)
  2. Monorepo Management: Use pnpm workspaces for package management (pnpm install, not npm install)
  3. Build Order: Packages must build in order: shared → adapters → main server. This is managed by scripts/build-packages.cjs
  4. Test Coverage: Maintain >90% test coverage
  5. Error Handling: Use centralized error messages from error-messages.ts
  6. Logging: Use Winston logger with appropriate log levels
  7. Async Operations: All DAP operations are async with timeouts
  8. Process Cleanup: Always ensure proper cleanup of spawned processes
  9. Adapter Development: New language adapters should implement IAdapterFactory from @debugmcp/shared

Testing Approach

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

Common Debugging Scenarios

When debugging issues:

  1. Enable debug logging: DEBUG=debug-mcp:* node dist/index.js
  2. Check proxy process output in logs (the per-session proxy-<sessionId>.log follows the server's --log-level/DEBUG_MCP_LOG_LEVEL and carries the DAP routing decisions — MinimalDapClient/ChildSessionManager/CDP-bridge lines land there, not only in the per-pid server log)
  3. Verify language-specific requirements (e.g., python -m debugpy --version)
  4. Use dryRunSpawn: true in start_debugging tool arguments to test configuration without starting a real debug session
  5. Set DAP_TRACE=1 to capture every DAP frame to a per-session dap-trace-<sessionId>.ndjson (off by default; capped at 50 MB; DAP_TRACE_FILE=<path> chooses an explicit file; each record's conn field names the originating connection — parent, child:<id>, or release:<id>)

Adding New Language Adapters

To add support for a new language:

  1. Create Package: Add new package under packages/adapter-{language}/
  2. Implement Interfaces: Implement IAdapterFactory and IDebugAdapter from @debugmcp/shared. Optionally implement describeToolchain() on the factory (using toolchainComponent from @debugmcp/shared) so mcp-debugger doctor renders the adapter's runtime/backend row — without it the doctor table shows empty cells
  3. Export Factory: Export a factory class named {Language}AdapterFactory
  4. Register in root package.json: Add "@debugmcp/adapter-{language}": "workspace:*" to optionalDependencies
  5. Add Vitest alias: Add { find: '@debugmcp/adapter-{language}', replacement: path.resolve(__dirname, './packages/adapter-{language}/src/index.ts') } to resolve.alias in vitest.config.ts
  6. Update adapter count: Update hardcoded adapter counts in tests (adapter-loader.test.ts, models.test.ts, tests/e2e/doctor-smoke.test.ts)
  7. Add Tests: Include unit and integration tests in the package. A new adapter policy is also checked by tests/unit/shared/adapter-policy-contract.test.ts, which asserts the cross-policy invariants (capability pinning, and that extractLocalVariables returns a LocalVariableExtraction { variables, scopeRefs } anchored on stackFrames[0] — use emptyLocalVariableExtraction() / extractionFromScope() from @debugmcp/shared rather than building the object by hand)
  8. 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

Language-Specific Requirements

Python

  • Python 3.7+ must be installed
  • debugpy must be installed: pip install debugpy
  • The system will auto-detect Python path or use PYTHON_PATH env var

Ruby

  • Ruby 2.7+ must be installed (3.1+ recommended — bundles the debug gem)
  • The debug gem (rdbg) must be available: gem install debug
  • Auto-detects ruby/rdbg from PATH and common install locations, or use RUBY_PATH/RDBG_PATH env vars
  • Launch always stops at load (the entry pause is auto-continued for stopOnEntry=false); attach connects directly to a running rdbg --open DAP socket without spawning an adapter process, including remote targets via port mapping/kubectl port-forward (see docs/ruby/README.md)
  • On Windows, rdbg's .bat shim is bypassed by running the sibling rdbg script via the Ruby interpreter (Node cannot spawn .bat without a shell)

JavaScript/Node.js

  • Node.js 22+ must be installed
  • Uses bundled js-debug adapter (VSCode's debugger)
  • Supports JavaScript and TypeScript debugging
  • Auto-detects TypeScript configuration

Rust

  • 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
  • Rust type summaries need the toolchain's LLDB formatter scripts: CodeLLDB locates them via rustc --print sysroot, or — on rustc-less hosts — via CODELLDB_RUST_SYSROOT (a sysroot root whose lib/rustlib/etc holds them; the adapter injects it as _adapterSettings.scriptConfig.lang.rust.sysroot, issue #441). The Docker image vendors them and sets the env var automatically

Java

  • JDK 21+ must be installed (java and javac on PATH, or JAVA_HOME set)
  • Uses JDI bridge (JdiDapServer.java) — a single Java file compiled on first use via javac
  • Must compile target code with javac -g for variable inspection (includes LocalVariableTable)
  • Launch mode: The adapter derives mainClass from the program field in the launch config and transparently forwards classpath, sourcePath, cwd, env, and args — 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 via ClassPrepareRequest
  • Zero external dependencies — JDI (com.sun.jdi.*) ships with every JDK
  • See docs/java/README.md for architecture details

Go

  • 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/C#

  • .NET 6+ SDK must be installed
  • netcoredbg must be installed: download from Samsung releases or build from source
  • Set NETCOREDBG_PATH environment variable to the netcoredbg executable, or add its directory to PATH
  • PDB symbols must be in Portable format (compile with /debug:portable or 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.md for architecture details and debugging guide

C/C++

  • No toolchain needed to debug prebuilt executables — CodeLLDB is vendored (shared with Rust in packages/codelldb-common)
  • A compiler (g++/clang++, or gcc/clang for C) is needed only for source-file launch: a lone .c/.cpp passed as the program is auto-compiled with -gdwarf-4 -O0 into .debug-mcp/ (DWARF-4 explicitly — MinGW's default DWARF-5 line tables are unreadable by LLDB in PE-COFF) next to the source (headers not staleness-tracked; forceRebuild: true to override)
  • Attach by PID supported (attach_to_process with processId); on Linux mind kernel.yama.ptrace_scope
  • On Windows prefer MinGW-w64/MSYS2 g++ (DWARF); MSVC PDB fidelity is partial (CPP_MSVC_BEHAVIOR controls the warning)
  • Advanced CodeLLDB config passes through: initCommands, targetCreateCommands (core dumps), processCreateCommands (gdbserver/rr remote)

Mock (Testing)

  • No external requirements
  • Used for testing the debug infrastructure

MCP Integration with Claude Code CLI

Installation for Claude Code

Choose the installation method that best fits your use case:

Option 1: NPX (No Installation Required)

# 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"]}'

Option 2: Global NPM Install

# 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"]}'

Option 3: Docker

# 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"]}'

Option 4: Build from Source

# 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.

Option 5: Dev Proxy (Current Setup — Recommended for Development)

# 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 by http and sse modes)
  • DEV_PROXY_BUILD_CMD — Build command (default: npm run build)
  • DEV_PROXY_BUILD_TIMEOUT_MS — Build timeout in milliseconds (default: 120000)
  • DEV_PROXY_ROOT — Project root (default: auto-detected)
  • DEV_PROXY_BACKEND_TRANSPORThttp (default), sse (legacy/deprecated), or stdio
  • DEV_PROXY_BACKEND_CMD — Custom backend command override (e.g. docker run ...)

Verify Installation

After adding the MCP server:

  1. Check connection status:

    /home/ubuntu/.claude/local/claude mcp list
    # Should show: mcp-debugger ... - ✓ Connected
  2. Restart Claude Code for the changes to take effect

Configuration Details

  • Location: Configuration saved to /home/ubuntu/.claude.json under the project's mcpServers section
  • Server Type: STDIO (local server)
  • Command: node /home/ubuntu/mcp-debugger/dist/index.js stdio (stdio argument is required!)
  • Status Check: After restart, type /mcp in Claude Code to see connected servers

Available Tools After Integration

Once connected, the following 28 MCP tools become available:

  • create_debug_session - Start a new debug session
  • list_debug_sessions - List active debug sessions
  • list_supported_languages - Show available language adapters
  • set_breakpoint - Set breakpoints in code
  • list_breakpoints - List a session's breakpoints with verified state and adapter ids
  • remove_breakpoint - Remove a breakpoint by id or file+line (works live)
  • clear_breakpoints - Remove all breakpoints, optionally scoped to one file
  • start_debugging - Begin debugging a script
  • restart_debugging - Relaunch with the same config; breakpoints re-applied (launch sessions only)
  • attach_to_process - Attach debugger to a running process
  • detach_from_process - Detach debugger from a process
  • expose_session - Open a read-only DAP mirror endpoint (loopback + token) so an IDE can attach and inspect the live session
  • unexpose_session - Close the mirror endpoint and disconnect IDE clients
  • close_debug_session - Clean up sessions
  • step_over, step_into, step_out - Step through code
  • continue_execution - Continue running
  • pause_execution - Pause a running program
  • list_threads - List all threads in a debug session
  • get_variables - Inspect variables in scope
  • get_local_variables - Get local variables in current frame
  • get_stack_trace - Inspect call stack
  • get_scopes - Get variable scopes for a stack frame
  • evaluate_expression - Evaluate expressions in debug context
  • get_source_context - Get source code around current position
  • get_output - Read captured debuggee stdout/stderr (buffered per launch, cursor-based)
  • redefine_classes - Hot-swap changed Java classes into a running JVM (Java only)

Each session also exposes its captured output as an MCP resource (debug://sessions/{id}/output, plain-text transcript) with resources/subscribe support — subscribed clients receive coalesced resources/updated notifications as the debuggee prints.

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 (pass rebuild: true to build first)
  • dev_rebuild_and_restart - Run npm run build then restart the backend
  • dev_server_status - Check backend state, PID, uptime, transport, project root, port, and display-safe backend environment overrides

Troubleshooting MCP Connection

  • If server shows "Failed to connect":
    • Ensure the stdio argument 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-json command shown above to properly configure with the stdio argument
    • Note: The stdio command is the default subcommand (isDefault: true in CLI setup), so explicitly passing stdio is optional but recommended for clarity
  • 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)