Imported from todevelopers/todevelopers-marketplace (
plugins/vs-mcp-client/skills/vs-mcp-debug/SKILL.md). Install upstream withnpx skills add todevelopers/todevelopers-marketplace --skill vs-mcp-debug. Copyright stays with the author.
AI Debugging Guide for MCP AI Server (vs-mcp)
Overview
MCP AI Server exposes 19 debugging tools that let AI assistants interact with the Visual Studio debugger. The debugger is state-based — you must check the current mode before calling tools, and use polling to detect state changes.
There are two fundamental ways to start a debug session:
debug_start— Press F5, launches the startup project with debugger attacheddebug_attach— Attach to an already-running process (local, Docker, or WSL)
In both cases, once in Break mode, all inspection tools work identically.
Debugger Modes
| Mode | Meaning | How you get here |
|---|---|---|
| Design | No debug session active | Default state, or after debug_stop / program exits |
| Running | App is executing | After debug_start, debug_attach, or debug_continue |
| Break | Paused at breakpoint/exception | Breakpoint hit, debug_break, or debug_step completed |
Available Tools (19)
Debug Control (10 tools)
| Tool | Required Mode | Description |
|---|---|---|
debug_get_mode |
Any | Returns current mode: Design, Running, or Break |
debug_start |
Design | Start debugging (F5). Fire-and-forget — returns immediately |
debug_stop |
Running/Break | Stop debugging session (detach + terminate) |
debug_break |
Running | Pause the running application |
debug_continue |
Break | Resume execution |
debug_step |
Break | Step over/into/out. Direction: "over", "into", "out" |
immediate_execute |
Break | Execute expression with side effects (e.g. myVar = 42) |
debug_list_transports |
Any | List available transports (Default, Docker, WSL, etc.) |
debug_list_processes |
Any | List processes on a transport for attachment |
debug_attach |
Design | Attach debugger to a running process by name or PID |
Debug Inspection (5 tools)
| Tool | Required Mode | Description |
|---|---|---|
debug_get_callstack |
Break | Get call stack of current thread |
debug_get_locals |
Break | Get local variables in current frame |
debug_evaluate |
Break | Evaluate expression read-only (e.g. myList.Count) |
output_read |
Any | Read VS Output window pane (Build, Debug, etc.) |
error_list_get |
Any | Get errors/warnings from VS Error List |
Breakpoint Management (4 tools)
| Tool | Required Mode | Description |
|---|---|---|
breakpoint_set |
Any | Set breakpoint by file+line or function name |
breakpoint_remove |
Any | Remove breakpoint at file+line |
breakpoint_list |
Any | List all breakpoints in solution |
exception_settings_set |
Any | Configure break-on-exception |
Core Concept: Polling
MCP is request-response — there are no push notifications. You must poll debug_get_mode to detect state changes.
debug_start → returns immediately
(VS is building + launching in background)
poll debug_get_mode → "Design" (still building)
wait 3-5 seconds
poll debug_get_mode → "Running" (app launched)
wait 2 seconds
poll debug_get_mode → "Break" (breakpoint hit!)
→ now inspect: callstack, locals, evaluate
The same polling applies after debug_attach + debug_break, or after debug_continue.
Workflows
1. Attach to a Running Process — THE PRIMARY AI WORKFLOW
This is the most natural and powerful debugging workflow for AI:
- The application is already running (launched by user, by a script, or as a service)
- VS has the source code open in a solution
- The AI attaches, pauses, investigates, and detaches
1. debug_list_processes () → see what's running locally
2. debug_attach (processName: "MyApp") → attach debugger
3. debug_get_mode → confirm "Running"
4. breakpoint_set (functionName: "HandleRequest") → set breakpoint on hot path
5. poll debug_get_mode → wait for "Break"
... or use debug_break to pause immediately:
5. debug_break → pause the app NOW
6. debug_get_callstack → see where we are
7. debug_get_locals → see all variable values
8. debug_evaluate ("request.Url") → evaluate specific expression
9. debug_step (direction: "over") → step forward
10. debug_get_locals → see how values changed
11. debug_continue → let it run
12. debug_stop → detach when done
When to use attach vs start:
debug_attach: Process already running. Web servers, services, Docker containers, user-launched apps. This is 90% of real debugging.debug_start: Need VS to build and launch the startup project (F5). Good for console apps during development.
2. Attach to Docker Container
For debugging .NET apps running inside Docker containers on the local machine.
1. debug_list_transports → verify "Docker (Linux Container)" available
2. debug_list_processes (transport: "Docker (Linux Container)",
qualifier: "my-container-name") → find the dotnet process
3. breakpoint_set (functionName: "MyController.Get") → set breakpoint first
4. debug_attach (processName: "dotnet",
transport: "Docker (Linux Container)",
qualifier: "my-container-name") → attach
5. poll debug_get_mode → wait for breakpoint or use debug_break
6. debug_get_callstack → debug_get_locals → investigate
Note: The process name inside a container is usually dotnet (for dotnet run) or the executable name (e.g. MyApp). Use debug_list_processes to discover it.
3. Attach to WSL Process
For debugging .NET apps running in Windows Subsystem for Linux.
1. debug_list_processes (transport: "WSL") → find WSL processes
2. debug_attach (processName: "MyApp",
transport: "WSL") → attach
3. debug_break → pause
4. debug_get_callstack → debug_get_locals → investigate
4. User-Initiated Debug (TDD Workflow)
The user launches debug manually in VS (F5, or right-click → Debug Test). The AI investigates the state. This is the most common workflow for TDD.
User: "I started debugging, it hit a breakpoint, help me investigate"
1. debug_get_mode → confirm "Break"
2. debug_get_callstack → see the full call chain
3. debug_get_locals → see all local variables
4. debug_evaluate ("this.Name") → drill into specific values
5. debug_step (direction: "over")→ step to next line
6. debug_get_locals → see how values changed
7. debug_evaluate ("result") → check return value
The AI doesn't need to launch the debug session — it just needs debug_get_mode to return "Break", and all inspection tools work regardless of who started the session.
5. TDD: Debug a Failing Test
Unit tests cannot be launched in debug mode via MCP. The AI sets breakpoints, the user launches "Debug Test":
AI: breakpoint_set (functionName: "MyService.ProcessOrder")
AI: "Please right-click the failing test → Debug Test in VS"
User: runs Debug Test in VS
AI: poll debug_get_mode → "Break" ← breakpoint hit!
AI: debug_get_callstack → see the exact code path
AI: debug_get_locals → see variable values
AI: debug_evaluate ("order.Items.Count") → check specifics
AI: debug_step (direction: "over") → step to see what happens next
AI: debug_get_locals → values after the step
AI: "Found the bug: order.Items is empty because X calls Y before Z"
6. AI-Initiated Debug Session (F5)
When the AI wants to build, launch, and debug the startup project:
1. breakpoint_set (functionName: "MyClass.MyMethod") ← set breakpoint
2. debug_start ← launch (F5)
3. poll debug_get_mode until "Break" ← wait for breakpoint hit
4. debug_get_callstack ← see where we are
5. debug_get_locals ← see variable values
6. debug_evaluate ("myObject.Property") ← evaluate specific expressions
7. debug_step (direction: "over") ← step through code
8. debug_get_locals ← see updated values
9. debug_continue ← resume execution
10. debug_stop ← end session
7. Set Breakpoints Before User Debugs
The AI can prepare breakpoints, then the user launches debug:
AI: breakpoint_set (functionName: "OrderService.ProcessOrder")
AI: breakpoint_set (file: "D:\\Path\\Validator.cs", line: 42)
AI: "Breakpoints set. Please run your test in debug mode (right-click → Debug Test)"
User: runs debug test in VS
AI: poll debug_get_mode → "Break"
AI: debug_get_callstack → investigate
8. Investigate a Crash/Exception
1. exception_settings_set ("System.NullReferenceException", breakWhenThrown: true)
2. "Please run the failing scenario — I configured VS to break on NullReferenceException"
3. User runs the app or test
4. debug_get_mode → "Break" (exception hit!)
5. debug_get_callstack → see exactly where it crashed
6. debug_get_locals → see null values
7. debug_evaluate ("myObj") → confirm the null reference
8. output_read (pane: "Debug") → check debug output for clues
9. Read Build/Test Output
No debug session needed:
output_read () → list available panes
output_read (pane: "Build") → see build results
output_read (pane: "Debug") → see debug trace output
output_read (pane: "Tests") → see test results
error_list_get (severity: "error") → see only errors
error_list_get (severity: "all") → see everything
10. Modify Values at Runtime
When paused at a breakpoint, you can change variable values to test hypotheses:
debug_get_locals → see current values
immediate_execute ("retryCount = 0") → reset a counter
immediate_execute ("isDebug = true") → flip a flag
immediate_execute ("name = \"test\"") → set a string
debug_continue → continue with modified values
Breakpoint Types
File + Line Breakpoint
breakpoint_set (file: "D:\\Path\\MyClass.cs", line: 42)
- Use full Windows path for reliability
- The file must exist and be part of the solution
Function Breakpoint
breakpoint_set (functionName: "MyClass.MyMethod")
- More reliable than file+line (no path issues)
- Binds when the assembly loads
- Use
ClassName.MethodNameformat
Conditional Breakpoint
breakpoint_set (functionName: "ProcessItem", condition: "item.Id > 100")
Transport System
Visual Studio supports debugging across different environments through transports. Use debug_list_transports to see what's available on the current machine.
Common Transports
| Transport Name | Qualifier | Use Case |
|---|---|---|
Default |
"" (empty) or machine name |
Local processes, or remote with Remote Debugger |
Docker (Linux Container) |
Container name or ID | Docker containers on local machine |
WSL |
"" (empty) |
Processes in Windows Subsystem for Linux |
Discovery Flow
1. debug_list_transports → see what's installed
2. debug_list_processes → for local (default)
debug_list_processes (transport: "Docker (Linux Container)",
qualifier: "container-name") → for Docker
debug_list_processes (transport: "WSL") → for WSL
3. debug_attach (processName: ..., transport: ..., qualifier: ...)
Docker Debugging Prerequisites
For debug_attach to work with Docker containers, the container needs vsdbg (Visual Studio Debugger for .NET Core) installed inside it, plus a Debug build with PDB files.
Add to your Dockerfile:
# Install vsdbg for remote debugging
RUN apt-get update && apt-get install -y curl unzip procps \
&& curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg
The app must be built in Debug configuration — Release builds strip debug symbols. dotnet publish -c Debug includes PDB files automatically. Source code in VS must match the container build or breakpoints won't bind.
WSL Debugging Prerequisites
- Same source code — The VS solution must contain the same source as what's running in WSL
- Debug build — The app must be built with
dotnet build -c Debuginside WSL - No extra setup needed — VS 2022 has built-in WSL transport support
# Inside WSL:
cd /mnt/c/MyProject
dotnet run --configuration Debug
# Then from MCP:
# debug_list_processes(transport: "WSL")
# debug_attach(processName: "MyApp", transport: "WSL")
Mode Validation
All inspection tools validate the debugger mode and return helpful errors:
{
"error": "Debugger must be in Break mode to get locals. Current mode: Running. Poll debug_get_mode to wait for a breakpoint hit."
}
Always check mode before calling inspection tools, or be prepared to handle these errors gracefully.
Important Behaviors
Fire-and-Forget Start
debug_start returns immediately. VS builds the project and launches it in the background. This can take seconds to minutes for large solutions. Always poll debug_get_mode after starting.
No Internal Timeouts
DTE operations have no internal timeout — they await until VS completes the operation. The MCP client controls timeouts externally. DTE COM calls are not cancellable once started on the UI thread, so an internal timeout would only return null while leaving VS blocked.
Function Breakpoint Validation
Function breakpoints (breakpoint_set with functionName) are pre-validated via Roslyn before calling DTE. If the symbol doesn't exist in the solution, the tool returns an error immediately instead of risking a VS hang. Use ClassName.MethodName format.
Managed Frame Navigation
When stopped in native code (e.g., CLR internals), debug_get_locals automatically navigates to the first managed (.NET) frame so you see relevant C#/VB/F# variables instead of native pointers.
Cross-Frame Expression Evaluation
debug_evaluate searches across all stack frames if the expression can't be evaluated in the current frame. This helps when stopped in framework code but you need to see your variables.
Variable Tree Navigation
Local variables are trees. debug_get_locals returns root-level variables with hasMembers indicating expandable nodes. Use debug_evaluate to drill into children by expression path.
1. debug_get_locals → root variables
args: string[] (hasMembers: false)
testCases: List<T> (hasMembers: true, memberCount: 7)
httpClient: HttpClient (hasMembers: true, memberCount: 1)
2. debug_evaluate ("testCases") → expand testCases
→ members: { "[0]": "TestCase", "[1]": "TestCase", ... "Count": "7" }
3. debug_evaluate ("testCases[0]") → drill into first item
→ members: { "Data": "Dictionary<string,string>", "Description": "...", "Name": "..." }
4. debug_evaluate ("testCases[0].Data") → drill into dictionary
→ members: { "[0]": "KeyValuePair<string,string>", "Count": "4" }
5. debug_evaluate ("testCases[0].Data.ElementAt(0)") → get first entry
→ members: { "Key": "username", "Value": "alice_doe" }
Pattern: Each debug_evaluate call goes one level deeper. The expression path is standard C# — array indexing, property access, LINQ methods all work.
Compact Output
All tools default to compact output format, optimized for token efficiency:
- Callstack:
"0": "Main|MyApp|Program.cs|14"(function|module|file|line) - Processes:
"svchost": "2360,2544,2588"(name: PIDs) — paths stripped to just the name - Locals:
"testCases": "List<TestCase>[7]"(expandable with memberCount) - Breakpoints:
"Program.cs:10": "bound|enabled"(file:line: type|state) - Errors:
"Validator.cs:42": "CS1002: ; expected"(file:line: description)
Use outputFormat: "verbose" for full details when needed.
Real-World Workflow Examples
Escaping an Infinite Loop
The program has a while(!done) loop waiting for input. The AI breaks in, modifies the flag, and continues:
1. debug_get_mode → "Running" (stuck in while loop)
2. debug_break → pause the app
3. debug_get_locals → see done=false
4. immediate_execute ("done = true") → flip the flag
5. debug_continue → loop exits, execution continues to next code
Bypassing Environment Checks
The app exits if ANTHROPIC_API_KEY is not set. The AI sets a breakpoint before the check, injects a fake value, and continues past the exit:
1. breakpoint_set (file: "Program.cs", line: 20) → before the if(IsNullOrEmpty) check
2. debug_start → launch the app
3. debug_break + immediate_execute ("done = true") → escape the while loop first
4. debug_continue → hits breakpoint at line 20
5. immediate_execute ("apiKey = \"fake-key\"") → inject value BEFORE the check
6. debug_continue → IsNullOrEmpty returns false, app continues!
Key insight: Set the variable BEFORE stepping over the if check. If you step first, you're already inside the if block heading toward Environment.Exit.
Investigating Selected Code
The user selects code they want to investigate. The AI reads the selection, sets breakpoints, and inspects at runtime:
1. get_selection → read what the user selected
2. breakpoint_set (file: "...", line: startLine) → breakpoint on the selected code
3. [navigate to the breakpoint — escape loops, bypass checks as needed]
4. debug_get_locals → see variable state at that point
5. debug_step (direction: "over") → execute the selected line
6. debug_get_locals → see result (e.g. testCases.Count = 7)
7. debug_step (direction: "over") → continue through the selection
Tips for AI Agents
-
Prefer
debug_attachoverdebug_startwhen the process is already running. This is the most natural AI debugging workflow. -
Poll, don't assume. After
debug_start,debug_attach,debug_continue, ordebug_step, always checkdebug_get_modebefore inspecting state. -
Prefer function breakpoints over file+line when you know the method name. They're more reliable across code changes. Symbols are pre-validated via Roslyn — you get an immediate error if the function doesn't exist.
-
The user can start debug themselves. You don't always need
debug_startordebug_attach. The most common TDD workflow is: user runs "Debug Test" in VS, AI investigates when it hits Break. -
Use
output_readanderror_list_getfreely — they work in any mode, no debug session needed. -
Don't call inspection tools in Design or Running mode — they'll return errors. Always check mode first or handle the error gracefully.
-
Use
immediate_executecarefully — it can modify program state.debug_evaluateis read-only and safer for just looking at values. -
When debugging tests: You cannot launch a test in debug mode via MCP. Ask the user to right-click → Debug Test. Once they hit a breakpoint, you have full access to inspect and step.
-
After
debug_step, the mode is still Break — you can immediately call inspection tools without polling. -
After
debug_continue, poll for the next Break or Design (program exited). -
Use
breakpoint_listto see what breakpoints exist before adding duplicates. -
Use
debug_list_transportsto discover Docker/WSL support before trying to attach. Not every VS installation has the same transports. -
For Docker/WSL, the process name is usually
dotnetfor .NET apps. Usedebug_list_processesto discover the exact process name and PID. -
After attaching to Docker/WSL, all tools work identically to local debugging — callstack, locals, evaluate, step, breakpoints all work the same way.