Skip to content

Latest commit

 

History

History
390 lines (330 loc) · 9.52 KB

File metadata and controls

390 lines (330 loc) · 9.52 KB

Using the mcp-debugger

This document describes how to use the mcp-debugger with Large Language Models (LLMs) for step-through debugging, based on real testing conducted on 2025-06-11.

Installation

Prerequisites

  • Node.js 18+
  • Python 3.7+ with debugpy (for Python debugging)

Installing from NPM

npm install -g @debugmcp/mcp-debugger

Building from Source

git clone https://github.com/debugmcp/mcp-debugger.git
cd mcp-debugger
pnpm install
npm run build

Configuration

MCP Client Configuration

Add the server to your MCP settings:

{
  "mcpServers": {
    "mcp-debugger": {
      "command": "mcp-debugger",
      "args": ["stdio"],
      "disabled": false,
      "autoApprove": ["create_debug_session", "set_breakpoint", "get_variables"]
    }
  }
}

If running from a source checkout instead of a global install, use the CLI entrypoint:

{
  "mcpServers": {
    "mcp-debugger": {
      "command": "node",
      "args": ["C:/path/to/mcp-debugger/packages/mcp-debugger/dist/cli", "stdio"],
      "disabled": false,
      "autoApprove": ["create_debug_session", "set_breakpoint", "get_variables"]
    }
  }
}

Complete Debugging Workflow Example

Here's a real example of debugging a Python script with a bug:

The Buggy Script

# swap_vars.py
def swap_variables(a, b):
    print(f"Initial values: a = {a}, b = {b}")
    a = b  # Bug: 'a' loses its original value here
    b = a  # Bug: 'b' gets the new value of 'a' (which is original 'b')
    print(f"Swapped values: a = {a}, b = {b}")
    return a, b

def main():
    x = 10
    y = 20
    print("Starting variable swap demo...")
    swapped_x, swapped_y = swap_variables(x, y)
    
    if swapped_x == 20 and swapped_y == 10:
        print("Swap successful!")
    else:
        print(f"Swap NOT successful. Expected x=20, y=10 but got x={swapped_x}, y={swapped_y}")

if __name__ == "__main__":
    main()

Step 1: Create a Debug Session

// Tool: create_debug_session
// Request:
{
  "language": "python",
  "name": "Investigate Swap Bug"
}
// Response:
{
  "success": true,
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "message": "Created python debug session: Investigate Swap Bug"
}

Step 2: Set Breakpoints

Set a breakpoint where the bug occurs:

// Tool: set_breakpoint
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "file": "swap_vars.py",
  "line": 4
}
// Response:
{
  "success": true,
  "breakpointId": "28e06119-619e-43c0-b029-339cec2615df",
  "file": "C:\\path\\to\\swap_vars.py",
  "line": 4,
  "verified": false,
  "message": "Breakpoint set at C:\\path\\to\\swap_vars.py:4"
}

Step 3: Start Debugging

// Tool: start_debugging
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "scriptPath": "swap_vars.py"
}
// Response:
{
  "success": true,
  "state": "paused",
  "message": "Debugging started for swap_vars.py. Current state: paused",
  "data": {
    "message": "Debugging started for swap_vars.py. Current state: paused",
    "reason": "breakpoint"
  }
}

Step 4: Inspect the Stack

// Tool: get_stack_trace
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7"
}
// Response:
{
  "success": true,
  "stackFrames": [
    {
      "id": 3,
      "name": "swap_variables",
      "file": "C:\\path\\to\\swap_vars.py",
      "line": 4,
      "column": 1
    },
    {
      "id": 4,
      "name": "main",
      "file": "C:\\path\\to\\swap_vars.py",
      "line": 13,
      "column": 1
    },
    {
      "id": 2,
      "name": "<module>",
      "file": "C:\\path\\to\\swap_vars.py",
      "line": 21,
      "column": 1
    }
  ],
  "count": 3
}

Step 5: Get Variable Scopes

// Tool: get_scopes
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "frameId": 3
}
// Response:
{
  "success": true,
  "scopes": [
    {
      "name": "Locals",
      "variablesReference": 5,
      "expensive": false,
      "presentationHint": "locals",
      "source": {}
    },
    {
      "name": "Globals",
      "variablesReference": 6,
      "expensive": false,
      "source": {}
    }
  ]
}

Step 6: Inspect Variables Before the Bug

// Tool: get_variables
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "scope": 5
}
// Response:
{
  "success": true,
  "variables": [
    {"name": "a", "value": "10", "type": "int", "variablesReference": 0, "expandable": false},
    {"name": "b", "value": "20", "type": "int", "variablesReference": 0, "expandable": false}
  ],
  "count": 2,
  "variablesReference": 5
}

Step 7: Step Through the Bug

// Tool: step_over
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7"
}
// Response:
{
  "success": true,
  "state": "paused",
  "message": "Stepped over"
}

Step 8: Check Variables After First Assignment

// Tool: get_variables
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "scope": 5
}
// Response:
{
  "success": true,
  "variables": [
    {"name": "a", "value": "20", "type": "int", "variablesReference": 0, "expandable": false},
    {"name": "b", "value": "20", "type": "int", "variablesReference": 0, "expandable": false}
  ],
  "count": 2,
  "variablesReference": 5
}

Now we can see the bug! After a = b, both variables have the value 20.

Step 8b: Evaluate Expressions (Optional)

You can also evaluate arbitrary expressions in the current debug context:

// Tool: evaluate_expression
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7",
  "expression": "a == b"
}
// Response:
{
  "success": true,
  "result": "True",
  "type": "bool",
  "variablesReference": 0,
  "message": "Evaluated expression: a == b"
}

Step 9: Continue Execution

// Tool: continue_execution
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7"
}
// Response:
{
  "success": true,
  "state": "running",
  "message": "Continued execution"
}

Step 10: Close the Session

// Tool: close_debug_session
// Request:
{
  "sessionId": "a4d1acc8-84a8-44fe-a13e-28628c5b33c7"
}
// Response:
{
  "success": true,
  "message": "Closed debug session: a4d1acc8-84a8-44fe-a13e-28628c5b33c7"
}

Important Implementation Details

Session IDs

  • All session IDs are UUIDs in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • Sessions can terminate unexpectedly, always check if a session exists before operations

Variable Scope References

  • The variablesReference from get_scopes is what you pass to get_variables
  • This is NOT the same as the frame ID from get_stack_trace
  • Common mistake: Using frame ID instead of variablesReference

Breakpoint Behavior

  • Breakpoints initially show "verified": false because verification happens asynchronously by the debug adapter once the module is loaded (e.g., debugpy verifies after the script starts)
  • Avoid setting breakpoints on non-executable lines (comments, blank lines)
  • Best lines for breakpoints: assignments, function calls, conditionals

File Paths

  • The server uses SimpleFileChecker for both path validation and resolution. It returns a FileExistenceResult containing the effectivePath (the resolved path actually used downstream). The server passes this effectivePath to SessionManager for all subsequent operations (breakpoints, launch, source context)
  • In container mode, resolvePathForRuntime() rewrites paths to be under the workspace root (default /workspace/), then SimpleFileChecker validates existence at that resolved location
  • In host mode, SimpleFileChecker rejects non-absolute resolved paths during preflight existence checks (relative paths may still pass through other code paths)
  • Use forward slashes (/) or escaped backslashes (\\) in JSON

Common Errors and Solutions

"Managed session not found"

{
  "code": -32603,
  "message": "MCP error -32603: Failed to continue execution: Managed session not found: {sessionId}"
}

Solution: The session has terminated. Create a new session.

Invalid Scope Reference

{
  "code": -32602,
  "message": "scope (variablesReference) parameter is required and must be a number"
}

Solution: Use the variablesReference from get_scopes, not the frame ID.

Fully Implemented Features

All 20 tools are fully implemented, including:

  • pause_execution: Sends a DAP pause request and returns immediately; paused state is updated asynchronously. The session normally must be in the running state, but calling pause on an already paused session succeeds as a no-op.
  • evaluate_expression: Evaluates arbitrary expressions in the current debug context. When frameId is not specified, the server infers it by fetching the stack trace and using the topmost frame -- this works reliably only when a single frame exists or the top frame is the desired context. Callers should provide frameId explicitly when debugging code with multiple stack frames. Expressions with side effects are allowed (can modify program state).

Best Practices

  1. Always create a session first - No debugging operations work without an active session
  2. Check the stack trace - Understand where you are in the code before inspecting variables
  3. Get scopes before variables - You need the variablesReference to inspect variables
  4. Handle errors gracefully - Sessions can terminate, files might not exist
  5. Use meaningful session names - Helps when debugging multiple scripts

Last updated: 2026-03-21 - All 20 tools including list_threads, pause_execution, and evaluate_expression are fully implemented (v0.19.0)