---
slug: "embedded-debugger-mcp"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/adancurusul/embedded-debugger-mcp@main/README.md"
repo: "https://github.com/adancurusul/embedded-debugger-mcp"
source_file: "README.md"
branch: "main"
---
# Embedded Debugger MCP Server

[![Rust](https://img.shields.io/badge/rust-stable-orange.svg)](https://rust-lang.org)
[![RMCP](https://img.shields.io/badge/RMCP-0.3.2-blue.svg)](https://github.com/modelcontextprotocol/rust-sdk)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

Embedded Debugger MCP is a Rust server for embedded debugging through either
probe-rs (native) or OpenOCD (via the GDB Remote Serial Protocol). It exposes a
single MCP tool set for AI assistants — probe discovery, target control, memory,
breakpoints, flash, RTT, and AI-facing crash diagnosis — plus a small CLI and
bundled skill for a command-driven workflow without setting up an MCP client
first.

Language versions: [English](https://github.com/adancurusul/embedded-debugger-mcp/blob/HEAD/README.md) | [中文](https://github.com/adancurusul/embedded-debugger-mcp/blob/HEAD/README_zh.md)

## What It Provides

- One MCP tool set over two interchangeable backends (probe-rs native, or
  OpenOCD via GDB RSP), chosen at connect. Validated on real ESP32-S3.
- MCP tools for probe discovery, target connection, core control, memory access,
  breakpoints, flash programming, RTT communication, and crash diagnosis
  (`diagnose_fault`, `unwind_exception`).
- CLI commands for environment checks, configuration inspection, probe listing,
  MCP serving, and skill prompt handoff.
- A Codex/Claude Code compatible skill at `skills/embedded-debugger`.
- Release checks covering rustfmt, clippy, tests, docs, packaging, and the STM32
  demo build.

## Architecture

```text
MCP client or CLI
        |
        v
embedded-debugger-mcp  (one MCP tool set)
        |
        v
DebugBackend:  probe-rs (native)  |  OpenOCD (GDB RSP)
        |
        v
debug probe / openocd  ->  target MCU
```

## Requirements

- Rust stable toolchain.
- A probe-rs compatible debug probe such as ST-Link, J-Link, DAPLink, Black
  Magic Probe, or a supported FTDI-based probe. Alternatively, a running
  `openocd` exposing its GDB port (e.g. openocd-esp32 for ESP32) to use the
  `openocd` backend.
- A supported target chip and working SWD/JTAG wiring for hardware operations.
- Nightly Rust plus `rust-src` for the bundled STM32 demo firmware check.

## Build

```bash
git clone https://github.com/adancurusul/embedded-debugger-mcp.git
cd embedded-debugger-mcp
cargo build --release
```

The binary is `target/release/embedded-debugger-mcp`.

## MCP Mode

Run the server explicitly:

```bash
embedded-debugger-mcp serve
```

For compatibility, running `embedded-debugger-mcp` without a subcommand also
serves MCP over stdio.

Example MCP client configuration:

```json
{
  "mcpServers": {
    "embedded-debugger": {
      "command": "/path/to/embedded-debugger-mcp/target/release/embedded-debugger-mcp",
      "args": ["serve"],
      "env": {
        "RUST_LOG": "info"
      }
    }
  }
}
```

On Windows, use the `.exe` path and Windows path separators.

## CLI And Skill Mode

CLI mode is useful for setup checks, automation, and agent workflows before an
MCP client is configured.

```bash
embedded-debugger-mcp doctor
embedded-debugger-mcp doctor --json
embedded-debugger-mcp probes list
embedded-debugger-mcp probes list --json
embedded-debugger-mcp config generate
embedded-debugger-mcp config validate
embedded-debugger-mcp config show
embedded-debugger-mcp skill print-prompt
embedded-debugger-mcp skill install --target both
```

The bundled skill lives in:

```text
skills/embedded-debugger/
```

It is written as a plain Codex skill and is also packaged for Claude Code with
`.claude-plugin/plugin.json`. The skill starts with CLI checks and uses MCP
tools only when an MCP client is available.

Install the skill for Codex and Claude Code:

```bash
embedded-debugger-mcp skill install --target both
```

Preview or automate the install with JSON:

```bash
embedded-debugger-mcp skill install --target both --dry-run --json
embedded-debugger-mcp skill install --target both --force --json
```

`--target` accepts `codex`, `claude`, or `both`. The command installs the Codex
skill under `~/.codex/skills/embedded-debugger`, the Claude Code skill under
`~/.claude/skills/embedded-debugger`, and a local Claude plugin-dir package under
`~/.claude/plugins/local/embedded-debugger-mcp`. Existing directories are not
replaced unless `--force` is passed.

Trigger the Codex skill with a prompt such as:

```text
Use $embedded-debugger to inspect my embedded target setup.
```

Load the installed Claude Code plugin package:

```bash
claude --plugin-dir ~/.claude/plugins/local/embedded-debugger-mcp --print '/embedded-debugger inspect my embedded target setup'
```

For skill-only environments, the same `skills/embedded-debugger` directory can
also be copied to a local skills directory.

Validate the skill package:

```bash
python3 .github/scripts/validate_skill.py skills/embedded-debugger
python3 ~/.codex/skills/.system/skill-creator/scripts/quick_validate.py skills/embedded-debugger
claude plugin validate .
embedded-debugger-mcp skill install --target both --home /tmp/embedded-debugger-skill-home --force --json
```

The first command validates the repository skill metadata, the second validates
the standard `SKILL.md` layout when the Codex skill creator validator is
installed, and the third validates the Claude Code plugin manifest.

## Backends

The tools run over one of two interchangeable engines, selected at `connect`:

- `backend: "probe-rs"` (default) — native probe-rs; full flash and RTT support.
- `backend: "openocd"` (experimental) — connects to an already-running `openocd`
  over its GDB port (`openocd_address`, default `127.0.0.1:3333`) using the GDB
  Remote Serial Protocol. Intended for targets probe-rs does not cover (e.g.
  Xtensa ESP32 via openocd-esp32). Memory access and halt/run/step/reset are
  validated on real ESP32-S3; flash and RTT are probe-rs only. Register reads
  currently use ARM gdb register numbers, so PC/SP are wrong on Xtensa (known
  limitation); `diagnose_fault` and `unwind_exception` are Cortex-M specific.
  Start openocd with `gdb_memory_map disable`, otherwise it probes flash on the
  GDB connect, fails, and rejects the connection — e.g.
  `openocd -f board/esp32s3-builtin.cfg -c "gdb_memory_map disable"`.

The AI sees the same tool set for both; only `connect` arguments change.

## MCP Tool Set

Probe management:

| Tool | Purpose |
|------|---------|
| `list_probes` | Discover connected debug probes. |
| `connect` | Open a probe session for a target chip. |
| `probe_info` | Show active session information. |

Target control:

| Tool | Purpose |
|------|---------|
| `halt` | Halt core execution. |
| `run` | Resume execution. |
| `reset` | Reset the core. Only the implemented hardware-style reset path is accepted. |
| `step` | Single-step one instruction. |
| `get_status` | Read core/session status. |
| `disconnect` | Drop the session and clean up resources. |

Memory and breakpoints:

| Tool | Purpose |
|------|---------|
| `read_memory` | Read target memory with configured size/range limits. |
| `write_memory` | Write target memory when enabled by configuration. |
| `set_breakpoint` | Set a hardware breakpoint. |
| `clear_breakpoint` | Clear a hardware breakpoint. |

Diagnostics:

| Tool | Purpose |
|------|---------|
| `diagnose_fault` | Read the Cortex-M SCB fault registers (CFSR/HFSR/MMFAR/BFAR/SHCSR/CPUID) plus PC/SP/LR in one call and return a compact structured evidence bundle with the set fault bits. Reports raw evidence; it does not assert a root cause. Halt the target first. |
| `unwind_exception` | Unwind the stack after a crash and map each frame to a source line. On the probe-rs backend, returns a full DWARF backtrace (function + file:line per frame) via probe-rs's own unwinder; on the OpenOCD backend, reads the Cortex-M exception stack frame and maps the faulting PC / caller LR to source lines. Needs `elf_path` to firmware built with debug info (`.debug_line`). |

Flash:

| Tool | Purpose |
|------|---------|
| `flash_erase` | Erase flash when erase permissions are enabled. |
| `flash_program` | Program ELF, HEX, or BIN files through probe-rs flash algorithms. |
| `flash_verify` | Compare raw expected data with target flash contents. |
| `run_firmware` | Erase, program, reset/run, and optionally attach RTT. |

RTT:

| Tool | Purpose |
|------|---------|
| `rtt_attach` | Attach to a SEGGER RTT control block. |
| `rtt_detach` | Detach RTT. |
| `rtt_channels` | List discovered RTT channels. |
| `rtt_read` | Read from an up channel with max byte and timeout limits. |
| `rtt_write` | Write to a down channel. |

## Crash Diagnosis

After a crash, halt the core and let the model reason over the evidence:

1. `halt`, then `diagnose_fault` — reads the Cortex-M SCB fault registers
   (CFSR/HFSR/MMFAR/BFAR/SHCSR/CPUID) plus PC/SP/LR in one call and returns a
   compact structured bundle with the set fault bits. It reports evidence; it
   does not assert a root cause.
2. `unwind_exception` with `elf_path` — maps the crash to source lines. On the
   probe-rs backend this is a full DWARF backtrace (function + `file:line` per
   frame); on the OpenOCD backend it reads the exception stack frame and maps
   the faulting PC / caller LR.

Both are Cortex-M specific (SCB registers, ARM exception frame) and do not apply
to Xtensa (ESP32) targets. The firmware must be built with debug info
(`.debug_line`) for source mapping.

## Safety Notes

- Flash erase is disabled unless `security.allow_flash_erase` or
  `flash.allow_erase` is enabled.
- Memory writes are controlled by `security.allow_memory_write`.
- Optional memory range restriction uses target memory regions in the
  configuration.
- Firmware file paths are canonicalized, checked against
  `security.allowed_file_paths` when configured, and checked against size
  limits.
- Sector erase by arbitrary address is rejected until target-specific sector
  mapping is implemented.
- `flash_verify` supports raw data comparison. Use raw BIN files or hex data for
  file-based verification.

Generate a starting configuration:

```bash
embedded-debugger-mcp config generate > embedded-debugger.toml
embedded-debugger-mcp --config embedded-debugger.toml config validate
```

## STM32 Demo

The STM32 RTT demo is in `examples/STM32_demo`.

```bash
cd examples/STM32_demo
CARGO_TARGET_DIR=/tmp/embedded-debugger-mcp-stm32-target cargo +nightly check --locked
```

The demo firmware shows multi-channel RTT communication and is intended as a
hardware validation aid. See [examples/STM32_demo/README.md](https://github.com/adancurusul/embedded-debugger-mcp/blob/HEAD/examples/STM32_demo/README.md).

## Release Checks

Run these before cutting a release:

```bash
cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-targets --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --locked --all-features --no-deps
cargo package --locked
python3 .github/scripts/validate_skill.py skills/embedded-debugger
claude plugin validate .
embedded-debugger-mcp skill install --target both --home /tmp/embedded-debugger-skill-home --force --json
(cd examples/STM32_demo && CARGO_TARGET_DIR=/tmp/embedded-debugger-mcp-stm32-target cargo +nightly check --locked)
```

## Acknowledgments

- [probe-rs](https://probe.rs/) for embedded debug probe support.
- [rmcp](https://github.com/modelcontextprotocol/rust-sdk) for the Rust MCP SDK.
- [tokio](https://tokio.rs/) for the async runtime.

## License

This project is licensed under the MIT License. See [LICENSE](https://github.com/adancurusul/embedded-debugger-mcp/tree/HEAD/LICENSE).
