Skip to main content

Workflow: Debug an Exception

This guide walks through using debug-mcp to catch, analyze, and diagnose exceptions in a .NET application — from simple crashes to complex exception chains.

Scenario

Your .NET application throws an exception at runtime. You want an AI agent to catch it at the throw site, inspect the state, and identify the root cause.

Steps

1. Launch the application

"Launch /app/MyService.dll and stop at the entry point"

The process starts and pauses at the entry point.

Tool call details

Request (debug_launch):

{
"program": "/app/MyService.dll",
"stop_at_entry": true
}

2. Set an exception breakpoint

"Break on any NullReferenceException"

The debugger will pause the instant a matching exception is thrown — before any catch block runs.

Tool call details

Request (breakpoint_set_exception):

{
"exception_type": "System.NullReferenceException",
"break_on_first_chance": true,
"include_subtypes": true
}

3. Continue and wait for the exception

"Continue and wait for the exception to fire"

The application runs normally until the exception is thrown.

Tool call details

Request (debug_continue):

{}

Then:

Request (breakpoint_wait):

{
"timeout_ms": 60000
}

When the exception fires, you get the exact location:

{
"hit": true,
"location": {
"file": "/app/Services/UserService.cs",
"line": 42,
"function": "GetUser"
}
}

4. Quick diagnosis with autopsy

For a fast overview, use include_autopsy: true on breakpoint_wait (step 3). This returns the exception, stack frames, and local variables in a single response.

Tool call details

Request (breakpoint_wait):

{
"timeout_ms": 60000,
"include_autopsy": true
}

The response includes the full autopsy:

{
"hit": true,
"autopsy": {
"exception": {
"type": "System.NullReferenceException",
"message": "Object reference not set to an instance of an object.",
"isFirstChance": true
},
"frames": [
{
"index": 0,
"function": "MyApp.Services.UserService.GetUser",
"location": { "file": "/app/Services/UserService.cs", "line": 42 },
"variables": {
"locals": [
{ "name": "user", "type": "User", "value": "null" },
{ "name": "userId", "type": "string", "value": "\"abc123\"" }
]
}
}
]
}
}

5. Deep dive with exception_get_context

If you need more detail — more frames, more variables, inner exception chains — call exception_get_context:

"Get the full exception context with 20 frames and inner exceptions"

Tool call details

Request (exception_get_context):

{
"max_frames": 20,
"include_variables_for_frames": 3,
"max_inner_exceptions": 5
}

This returns up to 20 stack frames, local variables for the top 3 frames, and up to 5 levels of inner exceptions.

6. Manual exploration

For fine-grained investigation, use individual tools:

"Show me the stack trace"

Tool call details

Request (stacktrace_get):

{}

"Show me the variables in the top frame"

Tool call details

Request (variables_get):

{
"frame_index": 0,
"scope": "all"
}

"Evaluate this._repository.FindById(userId)"

Tool call details

Request (evaluate):

{
"expression": "this._repository.FindById(userId)"
}

7. Disconnect

"Stop debugging"

Tool call details

Request (debug_disconnect):

{
"terminate": true
}

Choosing Your Approach

ApproachTool callsBest for
Autopsy (breakpoint_wait + include_autopsy)1 callQuick diagnosis — get everything at once
Deep dive (exception_get_context)1 callMore detail — control frame count, variable depth, inner chain
Manual (stacktrace_get + variables_get + evaluate)3+ callsFine-grained exploration after initial autopsy

Summary

StepToolPurpose
1debug_launchStart the app under debugger
2breakpoint_set_exceptionCatch exceptions at the throw site
3debug_continue + breakpoint_waitRun and wait for the exception
4breakpoint_wait (with autopsy)Quick one-call diagnosis
5exception_get_contextDeep dive with full context
6stacktrace_get / variables_get / evaluateManual exploration
7debug_disconnectClean up

Tips

  • Use break_on_first_chance: true to catch exceptions before any catch block handles them. If you wait for unhandled, the original call stack may be unwound.
  • Use include_subtypes: true to catch derived exception types. Setting a breakpoint on System.Exception catches all exceptions.
  • Inner exceptions reveal root causes. An InvalidOperationException might wrap an IOException which wraps a SocketException. Set max_inner_exceptions high enough to see the full chain.
  • Variables are collected per-frame. Set include_variables_for_frames: 3 to see locals in the throwing frame plus its two callers.
  • Combine with tracepoints. Set tracepoints on key methods with log messages, then set an exception breakpoint. When the exception fires, the tracepoint log shows what happened leading up to it.
  • If the crash only happens with specific input, use debug_launch with args to pass that input.