|
| 1 | +using System.ComponentModel; |
| 2 | +using System.Text.Json; |
| 3 | +using System.Threading.Tasks; |
| 4 | +using ModelContextProtocol.Server; |
| 5 | + |
| 6 | +namespace CodingWithCalvin.MCPServer.Server.Tools; |
| 7 | + |
| 8 | +[McpServerToolType] |
| 9 | +public class DebuggerTools |
| 10 | +{ |
| 11 | + private readonly RpcClient _rpcClient; |
| 12 | + private readonly JsonSerializerOptions _jsonOptions; |
| 13 | + |
| 14 | + public DebuggerTools(RpcClient rpcClient) |
| 15 | + { |
| 16 | + _rpcClient = rpcClient; |
| 17 | + _jsonOptions = new JsonSerializerOptions { WriteIndented = true }; |
| 18 | + } |
| 19 | + |
| 20 | + [McpServerTool(Name = "debugger_status", ReadOnly = true)] |
| 21 | + [Description("Get the current debugger state. Returns the mode (Design = not debugging, Run = executing, Break = paused at breakpoint/step), break reason, current source location (file, line, function), and debugged process name. Always succeeds regardless of debugger state.")] |
| 22 | + public async Task<string> GetDebuggerStatusAsync() |
| 23 | + { |
| 24 | + var status = await _rpcClient.GetDebuggerStatusAsync(); |
| 25 | + return JsonSerializer.Serialize(status, _jsonOptions); |
| 26 | + } |
| 27 | + |
| 28 | + [McpServerTool(Name = "debugger_launch", Destructive = false)] |
| 29 | + [Description("Start debugging the current startup project (equivalent to F5). A solution must be open with a valid startup project configured. Use debugger_status to check the resulting state.")] |
| 30 | + public async Task<string> DebugLaunchAsync() |
| 31 | + { |
| 32 | + var success = await _rpcClient.DebugLaunchAsync(); |
| 33 | + return success ? "Debugging started" : "Failed to start debugging (is a solution open with a startup project configured?)"; |
| 34 | + } |
| 35 | + |
| 36 | + [McpServerTool(Name = "debugger_launch_without_debugging", Destructive = false)] |
| 37 | + [Description("Start the current startup project without the debugger attached (equivalent to Ctrl+F5). The application runs normally without breakpoints or stepping. A solution must be open with a valid startup project configured.")] |
| 38 | + public async Task<string> DebugLaunchWithoutDebuggingAsync() |
| 39 | + { |
| 40 | + var success = await _rpcClient.DebugLaunchWithoutDebuggingAsync(); |
| 41 | + return success ? "Started without debugging" : "Failed to start without debugging (is a solution open with a startup project configured?)"; |
| 42 | + } |
| 43 | + |
| 44 | + [McpServerTool(Name = "debugger_continue", Destructive = false)] |
| 45 | + [Description("Continue execution after a break (equivalent to F5 while paused). Only works when the debugger is in Break mode (paused at a breakpoint or after stepping). Use debugger_status to verify the debugger is in Break mode first.")] |
| 46 | + public async Task<string> DebugContinueAsync() |
| 47 | + { |
| 48 | + var success = await _rpcClient.DebugContinueAsync(); |
| 49 | + return success ? "Execution continued" : "Cannot continue (debugger is not in Break mode)"; |
| 50 | + } |
| 51 | + |
| 52 | + [McpServerTool(Name = "debugger_break", Destructive = false)] |
| 53 | + [Description("Pause execution of the running program (equivalent to Ctrl+Alt+Break). Only works when the debugger is in Run mode. Use debugger_status to verify the debugger is in Run mode first.")] |
| 54 | + public async Task<string> DebugBreakAsync() |
| 55 | + { |
| 56 | + var success = await _rpcClient.DebugBreakAsync(); |
| 57 | + return success ? "Execution paused" : "Cannot break (debugger is not in Run mode)"; |
| 58 | + } |
| 59 | + |
| 60 | + [McpServerTool(Name = "debugger_stop", Destructive = true)] |
| 61 | + [Description("Stop the current debugging session (equivalent to Shift+F5). Terminates the debugged process. Only works when a debugging session is active (Run or Break mode).")] |
| 62 | + public async Task<string> DebugStopAsync() |
| 63 | + { |
| 64 | + var success = await _rpcClient.DebugStopAsync(); |
| 65 | + return success ? "Debugging stopped" : "Cannot stop (no active debugging session)"; |
| 66 | + } |
| 67 | + |
| 68 | + [McpServerTool(Name = "debugger_step_over", Destructive = false)] |
| 69 | + [Description("Step over the current statement (equivalent to F10). Executes the current line and stops at the next line in the same function. Only works when the debugger is in Break mode.")] |
| 70 | + public async Task<string> DebugStepOverAsync() |
| 71 | + { |
| 72 | + var success = await _rpcClient.DebugStepOverAsync(); |
| 73 | + return success ? "Stepped over" : "Cannot step over (debugger is not in Break mode)"; |
| 74 | + } |
| 75 | + |
| 76 | + [McpServerTool(Name = "debugger_step_into", Destructive = false)] |
| 77 | + [Description("Step into the current statement (equivalent to F11). If the current line contains a function call, steps into that function. Only works when the debugger is in Break mode.")] |
| 78 | + public async Task<string> DebugStepIntoAsync() |
| 79 | + { |
| 80 | + var success = await _rpcClient.DebugStepIntoAsync(); |
| 81 | + return success ? "Stepped into" : "Cannot step into (debugger is not in Break mode)"; |
| 82 | + } |
| 83 | + |
| 84 | + [McpServerTool(Name = "debugger_step_out", Destructive = false)] |
| 85 | + [Description("Step out of the current function (equivalent to Shift+F11). Continues execution until the current function returns, then breaks at the caller. Only works when the debugger is in Break mode.")] |
| 86 | + public async Task<string> DebugStepOutAsync() |
| 87 | + { |
| 88 | + var success = await _rpcClient.DebugStepOutAsync(); |
| 89 | + return success ? "Stepped out" : "Cannot step out (debugger is not in Break mode)"; |
| 90 | + } |
| 91 | + |
| 92 | + [McpServerTool(Name = "debugger_add_breakpoint", Destructive = false)] |
| 93 | + [Description("Add a breakpoint at a specific file and line number. Works in any debugger mode (Design, Run, or Break). The file path must be absolute.")] |
| 94 | + public async Task<string> DebugAddBreakpointAsync(string path, int line) |
| 95 | + { |
| 96 | + var success = await _rpcClient.DebugAddBreakpointAsync(path, line); |
| 97 | + return success ? $"Breakpoint added at {path}:{line}" : $"Failed to add breakpoint at {path}:{line}"; |
| 98 | + } |
| 99 | + |
| 100 | + [McpServerTool(Name = "debugger_remove_breakpoint", Destructive = true)] |
| 101 | + [Description("Remove a breakpoint at a specific file and line number. Returns whether a breakpoint was found and removed.")] |
| 102 | + public async Task<string> DebugRemoveBreakpointAsync(string path, int line) |
| 103 | + { |
| 104 | + var success = await _rpcClient.DebugRemoveBreakpointAsync(path, line); |
| 105 | + return success ? $"Breakpoint removed from {path}:{line}" : $"No breakpoint found at {path}:{line}"; |
| 106 | + } |
| 107 | + |
| 108 | + [McpServerTool(Name = "debugger_list_breakpoints", ReadOnly = true)] |
| 109 | + [Description("List all breakpoints in the current solution. Returns file, line, column, function name, condition, enabled state, and hit count for each breakpoint.")] |
| 110 | + public async Task<string> DebugListBreakpointsAsync() |
| 111 | + { |
| 112 | + var breakpoints = await _rpcClient.DebugGetBreakpointsAsync(); |
| 113 | + return JsonSerializer.Serialize(breakpoints, _jsonOptions); |
| 114 | + } |
| 115 | + |
| 116 | + [McpServerTool(Name = "debugger_get_locals", ReadOnly = true)] |
| 117 | + [Description("Get local variables in the current stack frame. Only works when the debugger is in Break mode. Returns name, value, type, and validity for each local variable.")] |
| 118 | + public async Task<string> DebugGetLocalsAsync() |
| 119 | + { |
| 120 | + var locals = await _rpcClient.DebugGetLocalsAsync(); |
| 121 | + return JsonSerializer.Serialize(locals, _jsonOptions); |
| 122 | + } |
| 123 | + |
| 124 | + [McpServerTool(Name = "debugger_get_callstack", ReadOnly = true)] |
| 125 | + [Description("Get the call stack of the current thread. Only works when the debugger is in Break mode. Returns depth, function name, file name, line number, module, language, and return type for each frame.")] |
| 126 | + public async Task<string> DebugGetCallStackAsync() |
| 127 | + { |
| 128 | + var callStack = await _rpcClient.DebugGetCallStackAsync(); |
| 129 | + return JsonSerializer.Serialize(callStack, _jsonOptions); |
| 130 | + } |
| 131 | +} |
0 commit comments