Skip to main content

Architecture

info

This page explains how debug-mcp works internally. You don't need this to use the tool — start with Getting Started.

Overview

debug-mcp is structured as a bridge between the MCP protocol (JSON-RPC over stdio) and the .NET debugging infrastructure (ICorDebug COM APIs).

Components

MCP Layer

The MCP layer handles protocol communication and exposes debugging capabilities as MCP tools.

Tool Classes

ClassResponsibility
SessionToolsProcess lifecycle: launch, attach, disconnect, state query
BreakpointToolsBreakpoint CRUD and waiting for hits
ExecutionToolsProgram flow: continue, pause, stepping
InspectionToolsData access: threads, stack, variables, evaluation

Each tool class:

  1. Receives MCP tool calls with JSON parameters
  2. Validates input and translates to debugger operations
  3. Calls the appropriate Debugger Core methods
  4. Formats results as JSON responses

Debugger Core

The core debugging logic that wraps ICorDebug functionality.

DebugSession

Central manager for the debugging session:

  • Holds the ICorDebugProcess instance
  • Tracks session state (running, stopped, at breakpoint)
  • Coordinates between components
  • Thread-safe state management
public class DebugSession
{
public DebugState State { get; }
public ICorDebugProcess? Process { get; }

public Task<int> LaunchAsync(LaunchOptions options);
public Task AttachAsync(int pid);
public Task DisconnectAsync();
public Task ContinueAsync();
public Task PauseAsync();
// ...
}

DebugEventHandler

Implements ICorDebugManagedCallback to receive debugging events:

  • Breakpoint hits
  • Step completions
  • Exception throws
  • Process exit
  • Module loads

Events are converted to async signals that breakpoint_wait can await.

BreakpointManager

Manages breakpoint lifecycle:

  • Creates breakpoints from file:line or method references
  • Tracks pending breakpoints (before module loads)
  • Maps source locations to IL offsets
  • Handles conditional breakpoints

ExpressionEvaluator

Evaluates expressions in the context of a stopped thread:

  • Uses ICorDebugEval to execute code in the debuggee
  • Handles complex expressions with method calls
  • Returns typed values with proper formatting

SourceMapper

Maps between source code and IL:

  • Reads PDB/portable PDB symbol files
  • Converts source lines to IL offsets (for breakpoints)
  • Converts IL offsets to source lines (for stack traces)
  • Uses System.Reflection.Metadata for portable PDBs

Infrastructure

DbgShimLoader

Handles loading the native dbgshim library:

  • Locates dbgshim for the target runtime
  • Platform-specific loading (Windows/Linux/macOS)
  • Exports CreateDebuggingInterfaceFromVersion

CorDebugFactory

Creates and initializes ICorDebug instances:

  • Calls dbgshim to get ICorDebug
  • Sets up managed callback handler
  • Attaches to or creates processes

Data Flow Examples

Setting a Breakpoint

Observing a Breakpoint Hit

There is no breakpoint_wait tool — polling was removed in favor of a push notification (feature 030). The client subscribes once and is told the instant a breakpoint fires, instead of blocking a request on it:

Inspecting Variables

Threading Model

DebugMcp uses a single-threaded apartment (STA) for COM interop with ICorDebug:

Cross-Cutting Concerns (MCP Surface Modernization)

Four concerns cut across every tool, layered onto the request/response path described above rather than living in any single component. See the specs/069-mcp-surface-modernization/ design docs in the repository for the full design.

Progress reporting

Five tools whose work can genuinely take a while — resharper_inspect_solution, resharper_inspect_project, batch_evaluate, debug_launch, code_load — report named stages through the MCP SDK's IProgress<ProgressNotificationValue> as they run (e.g. acquiring enginerunning inspectionparsing report for a ReSharper call). A client that never asked for progress sees nothing extra — the SDK silently discards reports when no progress token was supplied, so this degrades structurally rather than conditionally. Every other tool's work completes fast enough that a stage sequence wouldn't mean anything.

Deferred results (MCP Tasks)

The same five tools additionally support the MCP Tasks extension: a client that declares the tasks capability gets back a pollable, cancellable handle instead of blocking the request on the full operation — useful for resharper_inspect_solution's first-run ~180 MB engine download, or a batch_evaluate run against many experiments. A client that doesn't opt in gets the exact byte-for-byte result it always did; task deferral is negotiated per-request, not a server-wide mode switch.

Typed structured outputs

Every tool method returns a typed C# record ([McpServerTool(UseStructuredContent = true)]) instead of a hand-built JSON string. The MCP SDK derives both the tool's published outputSchema and its structuredContent from that return type by reflection — schema and payload can no longer drift apart, because there is only one source of truth. All 39 tools share one envelope shape ({success, ...fields, error?}), one error shape ({code, message, details?}), and where a result can be arbitrarily large, one truncation shape ({returned, available, reason}) — see Tools Overview for the wire contract.

Per-call timeouts

Every tool whose work waits on something outside the server's own memory — the debuggee, a build, a symbol server, the ReSharper engine — accepts an optional timeout parameter with a documented default (30 seconds, except a handful of tools that already had their own longer or shorter documented default before this concern existed, which they kept). Exhausting the budget returns a distinct, documented error naming the elapsed time and leaves the session usable for the next call — a timeout bounds waiting, it never forcibly aborts a step already in flight, so it can never leave DebugSession/ICorDebug state inconsistent. Tools that only read already-captured, in-memory server state (e.g. diffing two existing snapshots) do not accept a timeout at all — there is nothing external for one to bound.

Dependencies

PackagePurpose
ModelContextProtocolMCP server implementation
ClrDebugManaged ICorDebug wrappers
System.Reflection.MetadataPDB reading for source mapping

Platform Support

Platformdbgshim Source
Windows x64Microsoft.Diagnostics.DbgShim.win-x64 NuGet
Windows x86Microsoft.Diagnostics.DbgShim.win-x86 NuGet
Linux x64Microsoft.Diagnostics.DbgShim.linux-x64 NuGet
Linux ARM64Microsoft.Diagnostics.DbgShim.linux-arm64 NuGet
macOS x64Microsoft.Diagnostics.DbgShim.osx-x64 NuGet
macOS ARM64Microsoft.Diagnostics.DbgShim.osx-arm64 NuGet