# .NET Debugger for MCP (nuget · DotnetDebugger.Mcp)

Give an AI agent a real .NET debugger: breakpoints, stepping, locals, expression evaluation.

- Trust score: 66/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-20

## Components

- nuget · `DotnetDebugger.Mcp`: 66/100 (this document), [markdown](https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp.md), [page](https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp)

## Channel facts

- Registry: `nuget`
- Package: `DotnetDebugger.Mcp`
- Version: `0.2.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-20.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - No production dependencies, so there is no dependency health to assess.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 75/100
  - AI-judged instruction clarity (good).
  - Tool/resource definitions use about 2720 tokens (~90/item across 30 items; 30 tools + 0 resources), lean.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 17/100
  - Stability observed for 5 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 64/100
  - 97% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
- **Capabilities**: 60/100
  - Spec-recency check failed: implements MCP spec 2025-06-18; the latest is 2026-07-28.

## Install

### Claude

```bash
claude mcp add nevse-dotnet-debugger-mcp -- dnx DotnetDebugger.Mcp@0.2.0 --yes
```

### Codex

```bash
codex mcp add nevse-dotnet-debugger-mcp -- dnx DotnetDebugger.Mcp@0.2.0 --yes
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "nevse-dotnet-debugger-mcp": {
      "type": "local",
      "command": [
        "dnx",
        "DotnetDebugger.Mcp@0.2.0",
        "--yes"
      ],
      "enabled": true
    }
  }
}
```

### Hermes

```yaml
mcp_servers:
  nevse-dotnet-debugger-mcp:
    command: "dnx"
    args: ["DotnetDebugger.Mcp@0.2.0", "--yes"]
```

### Other

```json
{
  "mcpServers": {
    "nevse-dotnet-debugger-mcp": {
      "command": "dnx",
      "args": [
        "DotnetDebugger.Mcp@0.2.0",
        "--yes"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-20 (score 66, +16)

- [security improvement] Malware scan: unverified → pass

### 2026-08-19 (score 50, −15)

- [security regression] Malware scan: pass → unverified
- [functional] Package version: 0.1.1 → 0.2.0

### 2026-08-18 (score 65, +1)

No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-08-16 (score 64, +1)

- [security] Stability: Stability not yet verified: we do not have a sandbox capture of the MCP schema this version of the package serves yet.
- [functional regression] Schema quality: 2263 → 2531
- [functional regression] Capabilities: fail → unverified
- [functional regression] Tool coverage: 97 → unverified
- [functional improvement] Stability: unverified → 0.03
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.1.0 → 0.1.1

### 2026-08-15 (score 63)

First indexed and scored.

## MCP tools (30)

### `expand_variable` (~73 tokens)

Expand a variables_reference into its members, which may themselves carry references to expand further. References come from get_variables, from evaluate_expression and from this tool. Expand while still stopped - a reference only applies to the stop it was taken in.

Input parameters:

- `session_id` (integer|null)
- `variables_reference` (integer, required)

### `set_function_breakpoint` (~272 tokens)

Set a breakpoint on a method by name, for when the method is known but the file and line are not. function_name accepts 'Method', 'Type.Method' or 'Namespace.Type.Method'; the type part matches by suffix, so 'Program.Work' matches 'MyApp.Program.Work'. Every method matching the name binds, which includes overloads and same-named methods in several assemblies - bound_locations reports each place it bound, so check it to see what was actually caught. Narrow it with a parameter list, 'Work(int, string)', where C# keywords, nullables and generics are understood ('int', 'string?', 'List<int>'), or by generic arity, 'Work<T>'. condition and hit_condition work as they do for set_breakpoint, and re-sending resets the hit counts of all function breakpoints. Like line breakpoints this needs portable PDBs for the target; verified=false with 'No functions matching' means the name matched nothing in the modules loaded so far, and it will bind by itself if a later assembly contains it. Remove it with remove_breakpoint, the same as a line breakpoint.

Input parameters:

- `condition` (string)
- `function_name` (string, required)
- `hit_condition` (string)
- `session_id` (integer|null)

### `launch_program` (~170 tokens)

Launch a .NET program under the debugger without running it yet. This is the only way to debug what happens at startup: breakpoints set now are in place before the first line executes. Pass the built program - the .dll or the executable next to it - not a project file. Then set breakpoints and call start_program to run it. The program's output is captured rather than printed, and read with get_program_output. A program launched this way is killed when the session is detached or closed, though the debugger does not always confirm it - both report program_may_be_running when it did not.

Input parameters:

- `args` (array)
- `environment` (object)
- `program_path` (string, required)
- `session_id` (integer|null)
- `working_directory` (string)

### `attach_to_process` (~100 tokens)

Attach the debugger to a .NET process by process ID. Attaching while another process is already being debugged opens a second session instead of failing, up to SHARPDBG_MAX_SESSIONS (one by default). The session_id in the response is what the other tools take to say which process they mean; it can be omitted while only one session is open.

Input parameters:

- `process_id` (integer, required)
- `session_id` (integer|null)

### `start_program` (~90 tokens)

Run the program prepared by launch_program. Returns as soon as it is running, which can be after it has already hit a breakpoint; use wait_for_stop to catch that. process_id is the program the debugger started, reported from the moment it exists; it is null if the debugger has not said so yet, and get_process_status has it once it does.

Input parameters:

- `session_id` (integer|null)

### `get_debugging_flow` (~41 tokens)

Get the step-by-step flow for a debugging operation such as 'setting a breakpoint' or 'evaluating an expression'

Input parameters:

- `operation` (string, required)

### `get_program_output` (~94 tokens)

Read what the debuggee has written to stdout and stderr, oldest line first. Only a launched program's output is captured - a process attached to with attach_to_process keeps its own console, and nothing appears here. Returns at most max_lines (default 100) of the most recent output; the last 1000 lines are kept.

Input parameters:

- `max_lines` (integer)
- `session_id` (integer|null)

### `continue_execution` (~27 tokens)

Continue execution until the next breakpoint or process exit

Input parameters:

- `session_id` (integer|null)

### `step_over` (~41 tokens)

Step over the current line (execute and stop at next line in same method)

Input parameters:

- `session_id` (integer|null)
- `thread_id` (integer, required)

### `set_breakpoint` (~220 tokens)

Set a breakpoint at a specific file and line number. Optionally pass condition, a C# expression evaluated in the frame that must be true to stop, and/or hit_condition, a hit count in the form '5', '==5', '>5', '>=5', '<5', '<=5' or '%5' (every 5th hit). Hit counts include hits where condition was false, and are reset whenever any breakpoint in the same file is added, changed or removed. Calling this again for the same file and line replaces both conditions, so omitting one clears it. verified=false means the breakpoint is not bound: usually the path or line does not exist in the target, but a breakpoint in an assembly that has not been loaded yet binds by itself once it loads, so check list_breakpoints again rather than setting it repeatedly.

Input parameters:

- `condition` (string)
- `file_path` (string, required)
- `hit_condition` (string)
- `line` (integer, required)
- `session_id` (integer|null)

### `pause_execution` (~27 tokens)

Pause execution (break into debugger at current location)

Input parameters:

- `session_id` (integer|null)

### `get_variables` (~35 tokens)

Get local variables for a specific stack frame ID

Input parameters:

- `frame_id` (integer, required)
- `session_id` (integer|null)

### `evaluate_expression` (~94 tokens)

Evaluate a C# expression in the context of a stack frame. An expression that runs code in the target - a property getter, ToString(), any method call - is allowed and does not cost the session. When the result is an object, variables_reference is non-zero and can be walked with expand_variable.

Input parameters:

- `expression` (string, required)
- `frame_id` (integer, required)
- `session_id` (integer|null)

### `get_exception_info` (~268 tokens)

Read the exception the debuggee is stopped on: its type, message, HResult, the assembly it came from and the stack trace it carries. Nothing else reports these - a stop with stop_reason 'exception' names neither the type nor the message - so this is what turns one into something you can act on. Only works while stopped on an exception; omit thread_id to read the thread the stop was reported on. Note that set_exception_break_mode 'never' resumes exception stops before a caller can see them, so there is nothing to read in that mode. This is the one read that runs code inside the target - four property getters - so it is much slower than a stack trace or a variable read, and it gives up after SHARPDBG_EVAL_TIMEOUT_MS. Inner exceptions are not reported, and neither is whether the program is going to handle this one: the debugger does not pass that on. For anything beyond these fields - an inner exception, Data, a property of your own exception type - the exception object itself is in scope as $exception, so evaluate_expression with '$exception.InnerException' reads it, and get_variables on the frame lists it among the locals to expand.

Input parameters:

- `session_id` (integer|null)
- `thread_id` (integer|null)

### `set_exception_break_mode` (~308 tokens)

Control which exceptions suspend the debuggee. mode 'always' (the default) stops on every throw, including ones the program catches itself, so a program that uses exceptions routinely suspends constantly. mode 'user_unhandled' stops only on exceptions that escape your own code, which is what a debugger normally defaults to and usually what you want. mode 'unhandled' stops only on exceptions that go on to kill the program - the cheapest quiet mode, since a crash cannot be hidden and nothing else is reported or resumed. mode 'never' reports nothing at all and resumes every stop, which costs a round trip per exception and is worth it only for exceptions_seen and exceptions_ignored: they are the sole sign that a program is throwing steadily behind a mode that shows nothing. Narrow 'always' or 'user_unhandled' to particular exception types with exception_types, one fully-qualified name per entry - 'System.IO.IOException', matched exactly, not by suffix. Set types_are_excluded to stop on everything except those instead. The list is one way or the other, never both at once, which is the debugger's rule. The two quiet modes take no types, because they report nothing to filter. Changing mode takes effect at once, and releases an exception stop already in progress if the new mode hides it.

Input parameters:

- `exception_types` (array)
- `mode` (string, required)
- `session_id` (integer|null)
- `types_are_excluded` (boolean)

### `get_threads` (~25 tokens)

Get all threads in the attached process

Input parameters:

- `session_id` (integer|null)

### `detach_from_process` (~136 tokens)

Detach the debugger from a session's process, leaving that process running. A program the session launched is killed instead: it exists only for this session. program_may_be_running is true when the debugger never confirmed the terminate, meaning the program may have survived - check it rather than assuming. The flag covers launched programs only: an attached process the debugger never confirmed releasing is left suspended rather than running, and only the message says so. The session stays open and free, so the next attach_to_process or launch_program reuses it rather than taking another slot; close_session removes it.

Input parameters:

- `session_id` (integer|null)

### `step_into` (~35 tokens)

Step into the current line (enter called methods)

Input parameters:

- `session_id` (integer|null)
- `thread_id` (integer, required)

### `remove_breakpoint` (~49 tokens)

Remove a previously set breakpoint by its ID, whether it was set with set_breakpoint or set_function_breakpoint

Input parameters:

- `breakpoint_id` (integer, required)
- `session_id` (integer|null)

### `step_out` (~36 tokens)

Step out of the current method (execute until return)

Input parameters:

- `session_id` (integer|null)
- `thread_id` (integer, required)

### `wait_for_stop` (~170 tokens)

Wait for the debuggee to stop and return the same fields as get_process_status. Blocks until a breakpoint is hit, a step completes, the process is paused or throws, or the process exits, and gives up after timeout_ms (default 10000, maximum 300000). Use this after continue_execution or a step instead of polling get_process_status in a loop. stopped=false means the process was still running when the wait expired, which is not an error - call again to keep waiting. stop_reason 'exited' means the process is gone and cannot be stepped or continued; exit_code is what it returned, and is null for a process attached to rather than launched, whose code the debugger cannot read.

Input parameters:

- `session_id` (integer|null)
- `timeout_ms` (integer)

### `list_sessions` (~60 tokens)

List the open debug sessions, which is how you find the session_id to pass to the other tools. A session is created by attach_to_process or launch_program and lives until close_session or detach. started=false means the program is launched and waiting to run.

### `get_stack_trace` (~36 tokens)

Get the call stack for a specific thread ID

Input parameters:

- `session_id` (integer|null)
- `thread_id` (integer, required)

### `list_breakpoints` (~29 tokens)

List all breakpoints currently set in the debug session

Input parameters:

- `session_id` (integer|null)

### `close_session` (~108 tokens)

Close a debug session, detaching from its process if it is still attached. A program this session launched is killed, but the debugger does not always confirm it: program_may_be_running is true when it did not, and the program may have survived, so check it. An attached process the debugger never confirmed releasing stays suspended, and only the message says so. Use this to free a slot when SHARPDBG_MAX_SESSIONS has been reached.

Input parameters:

- `session_id` (integer, required)

### `list_debugging_concepts` (~20 tokens)

List all available debugging concepts organized by category

### `search_debugging_concepts` (~31 tokens)

Search the .NET debugger documentation for concepts related to the query

Input parameters:

- `query` (string, required)

### `get_process_status` (~48 tokens)

Get the status of a debug session. Omit session_id unless more than one session is open, in which case list_sessions shows which is which.

Input parameters:

- `session_id` (integer|null)

### `explain_icordebug_interface` (~16 tokens)

Input parameters:

- `interface_name` (string, required)

### `list_dotnet_processes` (~61 tokens)

List all .NET processes currently running on the system. owner tells you whether a process belongs to the user running this server: attaching is refused for 'other_user' and 'unknown' unless SHARPDBG_ALLOW_OTHER_USER_PROCESSES is set.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp#diagnostics

## Score history

- 2026-08-20: 66
- 2026-08-19: 50
- 2026-08-18: 65
- 2026-08-17: 64
- 2026-08-16: 64
- 2026-08-15: 63

## Links

- NuGet package: https://www.nuget.org/packages/DotnetDebugger.Mcp
- Socket report: https://socket.dev/nuget/package/DotnetDebugger.Mcp
- Repository: https://github.com/nevse/dotnet-debugger-mcp
- Changelog RSS feed: https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp.json
- HTML version of this page: https://verifymcp.io/servers/nevse-dotnet-debugger-mcp/dotnetdebugger-mcp
