Skip to main content
Glama

gdbstub-mcp

Let your AI agent debug a kernel, and tell you why it triple-faulted.

gdbstub-mcp is an MCP server that gives AI agents (Claude Code, or any MCP client) a debugger for bare-metal code running in QEMU. It speaks the GDB Remote Serial Protocol directly to QEMU's built-in gdbstub, so you don't need a gdb binary. That matters most on Windows and macOS, where a cross-architecture gdb is a pain to get.

It's the companion to qemu-mcp. qemu-mcp lets an agent boot a kernel and see the crash. gdbstub-mcp lets it find out why.

What it looks like

An agent debugging a hobby kernel. This is real output, captured from the test kernel in tests/fixtures/kernel:

> debug_break kernel.c:10
#1 sw breakpoint at 0x100029 <add+0x9> at kernel.c:10

> debug_continue
Stopped: SIGTRAP (breakpoint/step) - breakpoint #1 (kernel.c:10)
pc = 0x100029 <add+0x9> at kernel.c:10
next: mov eax, dword ptr [ebp + 8]

> debug_backtrace
#0  0x100029 <add+0x9> at kernel.c:10
#1  0x100071 <compute+0x31> at kernel.c:16
#2  0x1000b2 <kmain+0x12> at kernel.c:28
#3  0x100016 <_start+0xa> at boot.s:21

> debug_registers eip,eflags,cr0
     eip = 0x00100029  <add+0x9> at kernel.c:10
  eflags = 0x00000093  [SF AF CF]
     cr0 = 0x00000011  [ET PE]

And when the machine silently reboots, debug_explain_fault reads QEMU's interrupt log and explains the crash:

> debug_explain_fault
A TRIPLE FAULT occurred: the CPU reset itself.

[0] #UD invalid opcode at triple_fault+0xa (kernel.c:23)
[1] #GP general protection fault at triple_fault+0xa (kernel.c:23)
     selector 0x32: IDT entry 6 = #UD
     -> the IDT entry for vector 0x6 is missing or invalid - the CPU couldn't
        even start your handler. ...
[2] #DF double fault at triple_fault+0xa (kernel.c:23)

Root cause: the chain started with #UD at triple_fault+0xa.
It escalated to a triple fault because the handler for #UD (IDT entry 0x6)
could not be invoked. Fix the IDT entry so the original exception reaches
your handler, then fix the #UD itself.

A real QEMU log of a crash is thousands of lines of timer interrupts and register dumps. This tool picks out the exception chain, decodes the error codes, and points at the cause.

Related MCP server: GDB MCP Server

Why another debugger MCP?

Existing GDB MCP servers wrap a gdb binary and target userland programs or core dumps. The embedded ones target microcontrollers through probe-rs or OpenOCD. None of them is built for the osdev loop: a kernel you wrote yourself, running in QEMU, crashing before it can print anything.

  • No gdb required. It's pure Python (pyelftools + capstone). Run pip install gdbstub-mcp and you're set.

  • Any architecture QEMU supports. The register layout comes from the stub's own target.xml. Control registers (CR0/CR2/CR3/CR4, EFER) are there on x86, and flag registers are decoded.

  • Symbols and source lines. Break on kmain, kmain+0x10 or kernel.c:42, and every stop shows function+offset at file:line. Prefixed ABIs work too: kmain resolves to mort_kmain when that name is unique.

  • Backtraces that work on real kernels. It walks the frame-pointer chain, and handles x86 prologues and epilogues correctly. For -O2 kernels without frame pointers it can scan the stack for return addresses instead.

  • Triple-fault diagnosis. debug_explain_fault, described above. I haven't found another tool that does this.

Install

pip install gdbstub-mcp

Claude Code:

claude mcp add gdbstub -- gdbstub-mcp

Other MCP clients use the same idea: run gdbstub-mcp as a stdio server. Python 3.10+.

Usage

Start QEMU with its gdbstub on, with the CPU halted:

qemu-system-i386 -kernel kernel.elf -s -S -d int,cpu_reset -D int.log -no-reboot
  • -s opens the stub on tcp::1234.

  • -S halts the CPU before the first instruction.

  • -d int,cpu_reset -D int.log -no-reboot is only needed for debug_explain_fault.

With qemu-mcp, pass the same flags through qemu_boot's extra_args: extra_args="-s -S -d int,cpu_reset -D int.log -no-reboot".

Then ask your agent something like "connect to the kernel with kernel.elf, break in kmain, and step until the page fault".

Addresses can be written as 0x1234, symbol, symbol+0x10, file.c:42 or $esp+8.

Tools

Tool

What it does

debug_connect

Connect to a gdbstub (port, default 1234) and optionally load an elf with symbols. Several named sessions can be open at once.

debug_disconnect

Remove breakpoints, detach (the guest keeps running) and close the session.

debug_sessions

List sessions: endpoint, running or halted, ELF, breakpoint count.

debug_registers

Read registers: a chosen list, the general set, or all=True for control, segment and FPU registers. Flags are decoded and pointers symbolized.

debug_set_register

Write a register.

debug_memory

Read memory as a hexdump, as symbolized pointer-sized words, or as a string.

debug_write_memory

Write bytes.

debug_break

Set a breakpoint (sw, or hw for code before paging or not yet loaded) or a watchpoint (write, read, access).

debug_delete / debug_breakpoints

Remove a breakpoint, or list them all.

debug_continue

Resume and wait up to timeout_s for a stop. If no stop comes, the target keeps running.

debug_wait / debug_interrupt

Keep waiting for a stop, or halt the target now (like Ctrl-C).

debug_step

Single-step N instructions.

debug_backtrace

Call stack by frame-pointer chain (fp), stack scan (scan), or auto.

debug_disassemble

Disassemble at an address or the pc. bits=16 decodes real-mode code.

debug_symbol

Look up an address, symbol or line offline, with no target needed.

debug_explain_fault

Explain a crash or triple fault from a QEMU -d int,cpu_reset log (x86).

Limits (honest ones)

  • Machine-level, not source-level. There's no next/finish, and no local variables or C expressions yet (see BACKLOG.md). You get symbols, lines, registers, memory and disassembly, which covers most kernel debugging.

  • Stack scanning is a heuristic. It reports any stack word that points just after a call inside a known function, so stale frames can appear. Build with -fno-omit-frame-pointer for exact backtraces.

  • debug_explain_fault understands x86 logs only.

  • Tested against QEMU's gdbstub. Other RSP stubs (OpenOCD, Bochs, real-hardware probes) speak the same protocol and will likely work, but they're untested.

Tests

pip install ".[test]" anyio "ruff==0.16.0" "mypy==2.3.0"
ruff check src tests && mypy --strict src
pytest tests

Unit tests use a fake gdbstub on a real TCP socket, plus a real QEMU log of a triple fault, so they need no QEMU. tests/test_qemu_integration.py boots the fixture kernel in real QEMU (CI installs qemu-system-x86). It checks breakpoints, watchpoints, line numbers, a 4-deep backtrace and an end-to-end triple fault, and skips itself if QEMU isn't installed. To rebuild the fixture kernel, run python tests/fixtures/kernel/build.py, which needs pip install ziglang.

License

MIT

Available Tools

18 tools
debug_backtraceB

Show the call stack.

mode "fp" walks the frame-pointer chain (exact, but only if the kernel was built with frame pointers, e.g. -fno-omit-frame-pointer); "scan" scans the stack for return addresses (works on -O2 kernels without frame pointers, x86 only, needs elf; may include stale frames); "auto" tries fp and falls back to scan if the chain is empty.

Inputs: name (required), mode, max_frames. Returns: result

debug_breakA

Set a breakpoint or watchpoint.

location: 0x1234, symbol, symbol+0x10, or file.c:42. kind: "sw" (software breakpoint), "hw" (hardware breakpoint - use this before paging is enabled or for code that isn't loaded yet), or a watchpoint: "write", "read", "access" (length bytes, default one word).

Inputs: name (required), location (required), kind, length. Returns: result

debug_breakpointsC

List breakpoints and watchpoints.

Inputs: name (required). Returns: result

debug_connectA

Connect to a gdbstub and register the session under name.

For QEMU, start it with -s (stub on tcp::1234) or -gdb tcp::PORT, and add -S to halt the CPU before the first instruction so you can set breakpoints before anything runs. With qemu-mcp, pass these via qemu_boot's extra_args, e.g. extra_args="-s -S". elf is the kernel image with symbols (and ideally DWARF debug info); it enables symbol names, file:line breakpoints and source locations. load_bias is added to every ELF address, for images loaded somewhere other than their link address (0 for a normally linked kernel). The target stays halted after connecting.

Inputs: name (required), elf, host, port, +2 more. Returns: result

debug_continueA

Resume the target and wait up to timeout_s for it to stop.

If it hasn't stopped by then it keeps running: call debug_wait to keep waiting, or debug_interrupt to halt it where it is.

Inputs: name (required), timeout_s. Returns: result

debug_deleteC

Remove a breakpoint/watchpoint by its # id.

Inputs: id (required), name (required). Returns: result

debug_disassembleB

Disassemble count instructions from address (default: current pc).

bits overrides the decode mode on x86 (16 for real mode, 32, 64) - useful in bootloader code that runs before the CPU mode matches the ELF.

Inputs: name (required), bits, count, address. Returns: result

debug_disconnectA

Remove breakpoints, detach from the stub so the guest keeps running, and forget the session. A running target is halted first.

Inputs: name (required). Returns: result

debug_explain_faultA

Explain a crash / triple fault from a QEMU interrupt log.

Produce the log by running QEMU with -d int,cpu_reset -D <log_path> (add -no-reboot so a triple fault stops the VM instead of looping). Decodes the exception chain (vector names, page-fault and selector error codes, CR2), filters out timer IRQs and software interrupts, points at the root cause, and symbolizes addresses when elf is given. x86 only.

Inputs: log_path (required), elf, max_events. Returns: result

debug_interruptB

Halt a running target immediately (like Ctrl-C in gdb).

Inputs: name (required). Returns: result

debug_memoryA

Read guest memory (virtual addresses, as the CPU currently sees them).

address accepts 0x1234, symbol, symbol+0x10, file.c:42 or $esp+8. format: "hex" (hexdump with ASCII), "words" (pointer-sized words, symbolized), or "string" (NUL-terminated string at address). length is capped at 4096 bytes.

Inputs: name (required), address (required), format, length. Returns: result

debug_registersA

Read registers. Target must be halted.

registers is a comma-separated list (e.g. "eip,esp,cr2,cr3"); default is the general set from the stub's g packet. all=True also reads every extra register the target describes (control registers, segment bases, FPU/SSE...). Flag registers (eflags, cr0, cr4...) are decoded.

Inputs: name (required), all, registers. Returns: result

debug_sessionsB

List active debug sessions.

Inputs: no named parameters. Returns: result

debug_set_registerC

Write a register. value is an address expression (0x10, symbol, ...).

Inputs: name (required), value (required), register (required). Returns: result

debug_stepB

Single-step count machine instructions (1..1000) and show where it stopped.

Inputs: name (required), count. Returns: result

debug_symbolA

Look up symbols without touching the target.

query: an address (0x100abc) -> symbol+offset and source line; a symbol or symbol+off -> its address, size and source line; or file.c:42 -> address. Uses the session's ELF (name=) or a standalone ELF path (elf=).

Inputs: query (required), elf, name. Returns: result

debug_waitB

Wait for a running target to stop (after debug_continue timed out).

Inputs: name (required), timeout_s. Returns: result

debug_write_memoryC

Write raw bytes (hex string, e.g. "90 90 cc") to guest memory.

Inputs: name (required), address (required), hex_bytes (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 18 tools

    Disambiguation4/5

    Most tools target clearly distinct operations: break vs delete vs breakpoints, continue vs wait vs interrupt, and registers vs memory are all well separated by their descriptions. Minor confusion risks remain: debug_delete's name doesn't indicate it only removes breakpoints (as opposed to disconnecting a session), and the read/write asymmetry of debug_registers vs debug_set_register could momentarily puzzle an agent.

    Naming Consistency3/5

    All tools share the debug_ prefix, but the suffixes mix conventions: verb_noun tools (debug_connect, debug_set_register, debug_write_memory) coexist with bare-noun read/list tools (debug_registers, debug_memory, debug_sessions, debug_breakpoints). The set is readable and predictable enough to navigate, but not a uniform pattern.

    Tool Count4/5

    18 tools is on the high side but justified for a debugger surface spanning connection, registers, memory, breakpoints, execution control, stack/symbol inspection, and fault analysis. Each tool maps to a real gdb operation; none appear obviously redundant.

    Completeness5/5

    The surface covers the full debugger lifecycle: connect/disconnect, session listing, register and memory read/write, breakpoint/watchpoint create/list/delete, continue/wait/interrupt/step execution control, disassembly, backtrace, symbol lookup, and fault explanation. No obvious dead ends for the stated purpose.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      A
      quality
      D
      maintenance
      Enables AI agents to debug embedded systems by providing a comprehensive interface for GDB operations across multiple architectures like ARM and x86. It supports remote debugging via gdbserver or QEMU, allowing for detailed inspection of memory, registers, stack frames, and variables.
      31
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI agents to interact with GDB for debugging via the MCP protocol. Supports setting breakpoints, stepping through code, inspecting memory and registers, and more.
      85
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to debug ARM Cortex-M targets via GDB and OpenOCD, supporting attach, breakpoints, stepping, register/memory inspection, and SVD peripheral decoding.
      MIT
    • A
      license
      D
      quality
      B
      maintenance
      Enables AI agents to debug and repair historical Solaris/SPARC systems by providing an out-of-band control plane with guest DTrace, QEMU monitor access, SPARC-aware GDB, host eBPF/perf tracing, and an immutable evidence ledger for cross-layer hypothesis testing.
      16
      MIT