# .NET Debug Adapter

The .NET debug adapter (`@debugmcp/adapter-dotnet`) enables AI agents to debug .NET applications through the MCP protocol. It uses [netcoredbg](https://github.com/Samsung/netcoredbg), Samsung's MIT-licensed .NET debugger.

## Architecture

```
MCP Client → MCP Server → SessionManager → ProxyManager → ProxyWorker
                                                              ↓
                                                    DotnetAdapterPolicy
                                                              ↓
                                                    netcoredbg-bridge (TCP↔stdio)
                                                              ↓
                                                         netcoredbg
                                                              ↓
                                                    .NET Runtime (CoreCLR or Desktop CLR)
```

### Key Components

- **DotnetDebugAdapter** (`src/DotnetDebugAdapter.ts`): Implements `IDebugAdapter`. Handles executable discovery, environment validation, launch/attach config transformation, and PDB conversion.

- **DotnetAdapterFactory** (`src/DotnetAdapterFactory.ts`): Implements `IAdapterFactory` for dependency injection. Creates adapter instances and provides metadata.

- **DotnetAdapterPolicy** (`packages/shared/src/interfaces/adapter-policy-dotnet.ts`): DAP proxy policy encoding netcoredbg-specific behaviors — DAP sequence, scope naming, variable filtering, stack frame filtering, and executable resolution. The policy has a built-in fallback in `getAdapterSpawnConfig`: when `payload.adapterCommand` is missing (i.e., the bridge command was not supplied by `DotnetDebugAdapter.buildAdapterCommand`), it launches netcoredbg directly in `--server` mode. Note: this direct mode may not work reliably on all platforms due to a known connection bug.

- **netcoredbg-bridge** (`src/utils/netcoredbg-bridge.ts`): TCP-to-stdio bridge that works around a netcoredbg `--server=PORT` bug on Windows. Listens on a TCP port and spawns netcoredbg lazily on first client connection in stdio mode, forwarding DAP messages bidirectionally. The adapter also depends on the bundled `netcoredbg-bridge.js` script (compiled from this source) which is resolved at runtime from multiple candidate paths.

- **dotnet-utils** (`src/utils/dotnet-utils.ts`): Utility functions for netcoredbg discovery, PDB format detection, and Windows PDB to Portable PDB conversion via Pdb2Pdb.

### Why a TCP-to-stdio Bridge?

netcoredbg supports DAP over TCP via `--server=PORT --interpreter=vscode`, but has a connection bug where the TCP connection drops after the DAP initialize sequence. This bug was originally discovered on Windows but affects all platforms. The bridge spawns netcoredbg in stdio mode (which works reliably on all platforms) and exposes a TCP socket for the proxy to connect to. This is a pure byte-level forwarder with no DAP parsing.

## Supported Runtimes

### .NET Core / .NET 5+ (Stock netcoredbg)

Works out of the box with the standard Samsung netcoredbg release. No special configuration needed.

- Install netcoredbg from [releases](https://github.com/Samsung/netcoredbg/releases) or build from source
- Portable PDB symbols are generated by default

### .NET Framework 4.x (Not officially supported)

Stock netcoredbg only supports CoreCLR (.NET Core / .NET 5+). Community forks exist that add Desktop CLR support, but they are not officially endorsed or bundled. Legacy .NET Framework debugging is left to the community.

## Configuration

### Environment Variables

| Variable | Description |
|----------|-------------|
| `NETCOREDBG_PATH` | Path to netcoredbg executable. Checked first during discovery. |
| `PDB2PDB_PATH` | Path to Pdb2Pdb.exe for Windows PDB conversion. Falls back to bundled version. |

### Executable Discovery Order

The adapter policy (`DotnetAdapterPolicy`) resolves in this order:
1. Explicit provided path (from caller)
2. `NETCOREDBG_PATH` environment variable
3. Default command name `netcoredbg`

The utility function `findNetcoredbgExecutable` in `dotnet-utils.ts` uses a more detailed search:
1. `NETCOREDBG_X86_PATH` environment variable (for x86 attach targets)
2. `NETCOREDBG_PATH` environment variable
3. Caller-provided preferred path
4. `which netcoredbg` (searches PATH) — only when no target architecture is requested
5. Hardcoded platform-specific candidate paths (architecture-aware)
6. Common installation directories (e.g., `/usr/local/netcoredbg`, `C:\netcoredbg`)

## DAP Protocol Details

- **Adapter ID:** `coreclr` (for both .NET Core and .NET Framework)
- **DAP sequence:** netcoredbg requires launch/attach before configurationDone (`sendLaunchBeforeConfig: true`) — `initialize → initialized event → launch/attach → configurationDone`
- **Handshake:** No strict handshake required (`requiresStrictHandshake: false`)
- **Local scope name:** `Locals`
- **Safety:** `terminateDebuggee` is always `false` when detaching from attached processes

### Supported Features

- Conditional breakpoints
- Function breakpoints
- Exception breakpoints (`all`, `user-unhandled`)
- Evaluate for hovers
- Set variable
- Exception info
- Loaded sources
- Module loading
- Terminate request (graceful shutdown of debuggee)
- Exception options configuration (`EXCEPTION_OPTIONS`)

### Filtered Variables

C# compiler-generated variables are filtered out by default:
- `<>c__DisplayClass*` (closure classes)
- `CS$<>*` (compiler temporaries)
- `$VB$*` (VB.NET compiler variables)
- `<>t__*`, `<>s__*` (async state machine fields)

### Filtered Stack Frames

By default, stack frames are filtered to show only user code:
- Frames with no source file are hidden
- `System.*` and `Microsoft.*` runtime frames are hidden
