debugmcp/mcp-debugger/debugging

Claude Code and agent skill by debugmcp/mcp-debugger

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

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.

Install

npx vskill@latest install debugmcp/mcp-debugger/debugging

SKILL.md

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)

text
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}
Failing test (Python)? Skip step 2: start_debugging {sessionId, scriptPath: "<ABSOLUTE test file>", testRunner: "pytest", testTarget: "test_name"}
-> paused at the failing assert (or the escaping exception's raise site) with the autopsy below; a passing test runs to exit code 0.
4. READ THE PAUSED ANSWER: it already carries location, the source line (context), and snapshot
{exception?, frames, locals} — the exception message, top frames, and the top frame's locals
(redacted, capped; snapshot.truncation says when there is more). Often that is the whole inspection.
5. To go deeper: get_stack_trace {sessionId} for the full stack (use frame.id, never assume 0),
get_scopes {sessionId, frameId: <frame.id>} -> scope variablesReference,
get_variables {sessionId, scope: <variablesReference>} ... or get_local_variables {sessionId}
6. evaluate_expression {sessionId, expression: "x + y"}
7. 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)
Each landed step's snapshot carries changedVariables {name: {before, after}} — what the executed
line changed — instead of re-sending full locals; an empty {} means nothing visible changed.
8. get_output {sessionId} -> captured debuggee stdout/stderr
9. close_debug_session {sessionId} -> ALWAYS, even on failure

Rules 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 session whose program spawned child sessions (Python, on by default; dapLaunchArgs: { subProcess: false } opts a launch out) is waited on as a tree: the answer's sessionId names the session that stopped — use it to inspect — and children lists the rest with their states.
  • 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 old_string (whole line or a distinctive substring — whitespace-trimmed, trailing comments ignored, exact matches win), only lands on a line containing your text (inexact or multi-candidate matches are flagged in the response warning), lists every occurrence on ambiguity (add nearLine to pick one), and re-resolves across restart_debugging after you edit the file. When you do address by line, pass expectedContent: "<line text or distinctive substring>" (trailing comments ignored) so a stale or off-by-one line number fails immediately with the actual nearby lines. A response saying requested line N, bound to line M means the adapter moved the breakpoint — trust the bound line.
  • function: "name" breaks on entry to a symbol with no file or line at all — names survive edits best. Supported by Python/Go/Rust/.NET/Java/JavaScript, C/C++ and COBOL (paragraph, section or PROGRAM-ID names) — not Ruby or Dart/Flutter, which reject it up front (Java accepts bare method, Class.method, or fully-qualified names and binds every concrete overload; JavaScript names are dotted runtime paths like obj.method bound to the current function value — main-module function declarations bind at launch, functions in lazily-loaded modules bind at the next pause).
  1. When pausing is too disruptive (hot loops, live or attached processes), use a logpoint: set_breakpoint with logMessage: "x={x}" streams interpolated values into get_output without stopping the program (Python/JS/Go/Rust, C/C++, COBOL — where {WS-NAME} is a COBOL data reference — and Dart/Flutter, where a condition on the logpoint is ignored and every hit logs; Java, .NET and Ruby reject it with a clear error).
  2. At each pause, record what you learned (variable values, actual control flow), not just where you are.
  3. When the diverging line is found, inspect every input to that line before concluding — the bug is usually an operand, not the operator.
  4. Fix, then restart_debugging {sessionId} — one call relaunches with the same configuration and re-applies every breakpoint (the output buffer resets; read get_output from since: 0). Confirm the observed state changed as predicted. Works even after the program exited; attach sessions are rejected (detach and re-attach instead).

Program output

get_output {sessionId} returns buffered debuggee stdout/stderr with a cursor: pass the returned nextSince back as since to read only new output. Each session also exposes the transcript as MCP resource debug://sessions/{id}/output with subscription support. Caveat: Ruby attach sessions capture no stdout (launch sessions stream it) — when attached to Ruby, verify behavior via evaluate_expression/breakpoints or have the program write a file.

Attach instead of launch

For an already-running process (including remote machines, containers, and Kubernetes pods via port-forward):

text
attach_to_process {sessionId, host: "localhost", port: 5678, sourcePaths: ["<local src>"], adapterConfig: {...}}

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
144
Repository forks
20
7-day trend score
25

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/debugmcp/mcp-debugger/debugging/badge)](https://verified-skill.com/skills/debugmcp/mcp-debugger/debugging)

Something wrong with this skill or its page? Tell us.