/debugging
Use when investigating a bug, failing test, or unexpected runtime behavior and the mcp-debugger MCP server is available — drives real step-through debuggers (breakpoints, stack traces, variable inspection, expression evaluation) for Python, JavaScript/TypeScript, Ruby, Rust, Go,
$ npx -y skills add debugmcp/mcp-debugger --skill debugging --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/debugging
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when investigating a bug, failing test, or unexpected runtime behavior and the mcp-debugger MCP server is available — drives real step-through debuggers (breakpoints, stack traces, variable inspection, expression evaluation) for Python, JavaScript/TypeScript, Ruby, Rust, Go,
SKILL.md
debugging.SKILL.mdname: mcp-debugger
description: Use when investigating a bug, failing test, or unexpected runtime behavior and the mcp-debugger MCP server is available — drives real step-through debuggers (breakpoints, stack traces, variable inspection, expression evaluation) for Python, JavaScript/TypeScript, Ruby, Rust, Go, Java, .NET/C#, C/C++, COBOL, and Dart/Flutter, locally or attached to remote processes.
Debugging with mcp-debugger
mcp-debugger exposes real language debuggers as MCP tools. Prefer it over print-debugging whenever you would otherwise need more than one edit-run cycle to see program state: a breakpoint plus `evaluate_expression` answers in one run what printf answers in three.
When to reach for the debugger
- A test fails and the assertion message doesn't explain *why* the value is wrong.
- Control flow surprises you (a branch that "can't happen", a loop that exits early).
- State mutates somewhere between two known-good points and you need to bisect.
- The bug lives in code you can't easily edit (third-party package, compiled artifact).
- You need ground truth about runtime types/values instead of inferring them from source.
Do NOT reach for it when a single glance at the code or one log line would answer the question — session setup costs a few seconds and the target must be runnable.
The golden path (launch)
1. create_debug_session {language: "python"} -> sessionId
2. set_breakpoint {sessionId, file: "<ABSOLUTE path>", statement: "<line text or distinctive substring>"} (or line: N + expectedContent)
3. start_debugging {sessionId, scriptPath: "<ABSOLUTE path>"} -> paused at the breakpoint, or pending: true -> wait_for_stop {sessionId}
4. get_stack_trace {sessionId} -> frames (use frame.id, never assume 0)
5. get_scopes {sessionId, frameId: <frame.id>} -> scope variablesReference
6. get_variables {sessionId, scope: <variablesReference>}
... or get_local_variables {sessionId} for the common case
7. evaluate_expression {sessionId, expression: "x + y"}
8. step_over / step_into / step_out / continue_execution
wait_for_stop {sessionId} -> blocks until the next stop or the exit (continue_execution does not wait)
9. get_output {sessionId} -> captured debuggee stdout/stderr
10. close_debug_session {sessionId} -> ALWAYS, even on failureRules that prevent 90% of failed sessions:
- **Absolute paths only** for `file` and `scriptPath` (relative paths are rejected in host mode).
- **Use real frame IDs.** Take `id` from `get_stack_trace` frames; it is adapter-assigned and is not 0-indexed.
- **Expand variable containers.** If a variable entry carries a `variablesReference`, call `get_variables` again with that reference to see children (Python's "special variables", object fields, array elements).
- **Respect session state.** Stepping, evaluation, and variable reads require `PAUSED`. After `continue_execution` the session is `RUNNING`; after a step or breakpoint hit it returns to `PAUSED` with a persisted stop reason telling you why it stopped (`breakpoint`, `step`, `entry`, `exception`, ...). `continue_execution` returns at once — call `wait_for_stop {sessionId}` to block until the program stops again or ends; it answers with the stop (`lastStop`, `location`) or the exit code.
- **A `start_debugging` that answers `running` with `pending: true` is not stuck.** The launch holds its answer only briefly — about a second — for the program's first stop, so a breakpoint reached as the program starts is answered in the one call, and anything later is collected with `wait_for_stop {sessionId}`, which blocks until the program stops or ends. Nothing was cancelled: breakpoints stay armed for as long as the program runs, and the message says what is armed. For a server, this is the workflow: set the breakpoint in the handler, `start_debugging` (answers `pending`), send the request, `wait_for_stop`. The same goes for any step, pause or attach that answers `pending: true`. `wait_for_stop` takes a `timeout` in ms (default 30000); when it runs out it answers `pending: true` again — just call it again. Keep each call under your MCP client's own request cap (45 s is safe under the common 60 s cap): a longer wait is several calls, not one long `timeout`, or the client times out first.
- **Breakpoints may verify late.** Some adapters (debugpy, JDI) report breakpoints unverified until the module/class loads; that is normal, not an error.
- **`<redacted:...>` placeholders are masking, not program state.** Credential-shaped values and values of sensitive variable names (`password`, `api_key`, ...) are masked by default in variable/evaluate/output results; a `redaction` field reports what was hidden. The real value is intact in the debuggee — don't "fix" it, and don't retry the read. The user can disable masking by restarting the server with `DEBUG_MCP_NO_REDACT=1`.
- **If `get_variables` demands `names`, the server is in least-privilege mode** (`DEBUG_MCP_VARIABLE_ACCESS=explicit`): pass the exact variable names you need (`names: ["user", "total"]`; case-sensitive, misses reported in `notFound`) instead of dumping the scope. `evaluate_expression` still works for targeted reads.
- **Always `close_debug_session`** when done — it tears down the debuggee process tree.
Root-cause discipline
1. State a hypothesis about where reality diverges from expectation *before* setting breakpoints. 2. Set at most two breakpoints: last-known-good and first-known-bad. Run, inspect, halve the interval. Bisection beats stepping line-by-line from the top. Move the window mid-session with `remove_breakpoint` / `clear_breakpoints`; `list_breakpoints` shows what is currently set (with verified state and adapter ids).
- Prefer `statement: "<line text>"` over line numbers: it matches like an Edit-tool
Read more
name: mcp-debugger description: Use when investigating a bug, failing test, or unexpected runtime behavior and the mcp-debugger MCP server is available — drives real step-through debuggers (breakpoints, stack traces, variable inspection, expression evaluation) for Python, JavaScript/TypeScript, Ruby, Rust, Go, Java, .NET/C#, C/C++, COBOL, and Dart/Flutter, locally or attached to remote processes.
Debugging with mcp-debugger
mcp-debugger exposes real language debuggers as MCP tools. Prefer it over print-debugging whenever you would otherwise need more than one edit-run cycle to see program state: a breakpoint plus `evaluate_expression` answers in one run what printf answers in three.
When to reach for the debugger
- A test fails and the assertion message doesn't explain *why* the value is wrong.
- Control flow surprises you (a branch that "can't happen", a loop that exits early).
- State mutates somewhere between two known-good points and you need to bisect.
- The bug lives in code you can't easily edit (third-party package, compiled artifact).
- You need ground truth about runtime types/values instead of inferring them from source.
Do NOT reach for it when a single glance at the code or one log line would answer the question — session setup costs a few seconds and the target must be runnable.
The golden path (launch)
1. create_debug_session {language: "python"} -> sessionId
2. set_breakpoint {sessionId, file: "<ABSOLUTE path>", statement: "<line text or distinctive substring>"} (or line: N + expectedContent)
3. start_debugging {sessionId, scriptPath: "<ABSOLUTE path>"} -> paused at the breakpoint, or pending: true -> wait_for_stop {sessionId}
4. get_stack_trace {sessionId} -> frames (use frame.id, never assume 0)
5. get_scopes {sessionId, frameId: <frame.id>} -> scope variablesReference
6. get_variables {sessionId, scope: <variablesReference>}
... or get_local_variables {sessionId} for the common case
7. evaluate_expression {sessionId, expression: "x + y"}
8. step_over / step_into / step_out / continue_execution
wait_for_stop {sessionId} -> blocks until the next stop or the exit (continue_execution does not wait)
9. get_output {sessionId} -> captured debuggee stdout/stderr
10. close_debug_session {sessionId} -> ALWAYS, even on failureRules that prevent 90% of failed sessions:
- **Absolute paths only** for `file` and `scriptPath` (relative paths are rejected in host mode).
- **Use real frame IDs.** Take `id` from `get_stack_trace` frames; it is adapter-assigned and is not 0-indexed.
- **Expand variable containers.** If a variable entry carries a `variablesReference`, call `get_variables` again with that reference to see children (Python's "special variables", object fields, array elements).
- **Respect session state.** Stepping, evaluation, and variable reads require `PAUSED`. After `continue_execution` the session is `RUNNING`; after a step or breakpoint hit it returns to `PAUSED` with a persisted stop reason telling you why it stopped (`breakpoint`, `step`, `entry`, `exception`, ...). `continue_execution` returns at once — call `wait_for_stop {sessionId}` to block until the program stops again or ends; it answers with the stop (`lastStop`, `location`) or the exit code.
- **A `start_debugging` that answers `running` with `pending: true` is not stuck.** The launch holds its answer only briefly — about a second — for the program's first stop, so a breakpoint reached as the program starts is answered in the one call, and anything later is collected with `wait_for_stop {sessionId}`, which blocks until the program stops or ends. Nothing was cancelled: breakpoints stay armed for as long as the program runs, and the message says what is armed. For a server, this is the workflow: set the breakpoint in the handler, `start_debugging` (answers `pending`), send the request, `wait_for_stop`. The same goes for any step, pause or attach that answers `pending: true`. `wait_for_stop` takes a `timeout` in ms (default 30000); when it runs out it answers `pending: true` again — just call it again. Keep each call under your MCP client's own request cap (45 s is safe under the common 60 s cap): a longer wait is several calls, not one long `timeout`, or the client times out first.
- **Breakpoints may verify late.** Some adapters (debugpy, JDI) report breakpoints unverified until the module/class loads; that is normal, not an error.
- **`<redacted:...>` placeholders are masking, not program state.** Credential-shaped values and values of sensitive variable names (`password`, `api_key`, ...) are masked by default in variable/evaluate/output results; a `redaction` field reports what was hidden. The real value is intact in the debuggee — don't "fix" it, and don't retry the read. The user can disable masking by restarting the server with `DEBUG_MCP_NO_REDACT=1`.
- **If `get_variables` demands `names`, the server is in least-privilege mode** (`DEBUG_MCP_VARIABLE_ACCESS=explicit`): pass the exact variable names you need (`names: ["user", "total"]`; case-sensitive, misses reported in `notFound`) instead of dumping the scope. `evaluate_expression` still works for targeted reads.
- **Always `close_debug_session`** when done — it tears down the debuggee process tree.
Root-cause discipline
1. State a hypothesis about where reality diverges from expectation *before* setting breakpoints. 2. Set at most two breakpoints: last-known-good and first-known-bad. Run, inspect, halve the interval. Bisection beats stepping line-by-line from the top. Move the window mid-session with `remove_breakpoint` / `clear_breakpoints`; `list_breakpoints` shows what is currently set (with verified state and adapter ids).
- Prefer `statement: "<line text>"` over line numbers: it matches like an Edit-tool
A headless, agentic debugger over MCP — let your AI agents debug running programs in ten languages.

