Content
# pyocd-debug-mcp
> **Enabling AI-driven automated debugging for embedded microcontrollers with MCP Server**
By integrating [pyOCD](https://pyocd.io/) and CMSIS-DAP probes, this project provides **58 debugging tools** for AI-driven platforms like Claude and GitHub Copilot, covering a comprehensive debugging workflow from probe connection, firmware flashing, breakpoint setting, register reading, to HardFault analysis.
## ✨ Core Capabilities
| Capability | Description |
|------|------|
| 🔌 Probe Management | Automatic detection and connection/disconnection of CMSIS-DAP probes |
| ⚡ Flashing | Supports flashing `.hex` / `.bin` / `.elf` / `.axf` firmware, with automatic symbol attachment for ELF files |
| ✅ Flash Verification | Post-flash verification with segment-by-segment comparison to detect Flash corruption or abnormal flashing |
| 🎯 Breakpoints & Watchpoints | Hardware breakpoints, software breakpoints, automatic selection (by address or symbol name), and data watchpoints (read/write/read-write) |
| 🔄 Breakpoint Recovery | Automatic restoration of all breakpoints after reset (software breakpoints are lost after reset and re-established) |
| ⏳ Wait Mechanism | `wait_halt` — AI sets breakpoints → runs → waits for hits → analyzes |
| 📊 Variable Sampling | Periodic sampling of global variables, returning statistical data |
| 💥 Fault Analysis | Automatic analysis of HardFault/BusFault/UsageFault |
| 📏 Stack Overflow Detection | Supports RT-Thread/FreeRTOS TCB stack boundary checking |
| 🔍 Stack Backtracing | DWARF CFI/EHABI/heuristics-based three-level fault tolerance, covering AC5/AC6/GCC |
| 🔧 Registers & Memory | Read/write CPU registers, memory, and peripheral registers (SVD), with batch bit-field updates and enumeration values |
| 📋 Symbol Resolution | Mapping addresses to function/variable names through ELF files |
| 📡 RTT Real-time Transmission | SEGGER RTT channel read/write, eliminating UART print debugging |
| 🆕 Address Format | All address parameters accept both integer and hexadecimal string formats (e.g., `"0x1FFE000E"`) |
| 🆕 Combined Tools | `read_symbol` for one-step variable reading, `step_out` for executing to function return |
## 📦 Installation
### Prerequisites
- Python ≥ 3.10
- [uv](https://docs.astral.sh/uv/) package manager (recommended)
- CMSIS-DAP compatible debugging probes (DAPLink/STLINK, etc.)
### Method 1: Install from GitHub (recommended)
```bash
# Using uv
uv pip install git+https://github.com/konbakuyomu/pyocd-debug-mcp.git
# Using pip
pip install git+https://github.com/konbakuyomu/pyocd-debug-mcp.git
# For SVD field-level parsing (optional)
uv pip install "pyocd-debug-mcp[svd] @ git+https://github.com/konbakuyomu/pyocd-debug-mcp.git"
```
### Method 2: Local Development Installation
```bash
git clone https://github.com/konbakuyomu/pyocd-debug-mcp.git
cd pyocd-debug-mcp
uv sync # Create a virtual environment and install dependencies
```
### HC32 Series Chip Support
HC32 chips require CMSIS-Pack installation:
```bash
uv run pyocd pack install hc32f460
uv run pyocd pack install hc32f4a0
```
## 🔧 Register with AI Tools
### Claude Desktop
Add the following to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"pyocd-debug": {
"command": "uv",
"args": [
"--directory", "/path/to/pyocd-debug-mcp",
"run", "pyocd-debug-mcp"
]
}
}
}
```
### Claude Code (CLI)
```bash
claude mcp add pyocd-debug -- uv --directory /path/to/pyocd-debug-mcp run pyocd-debug-mcp
```
### GitHub Copilot (VS Code)
Add the following to `.vscode/mcp.json`:
```json
{
"servers": {
"pyocd-debug": {
"command": "uv",
"args": [
"--directory", "/path/to/pyocd-debug-mcp",
"run", "pyocd-debug-mcp"
]
}
}
}
```
> 💡 Replace `/path/to/pyocd-debug-mcp` with your actual path. Windows users can use `\\` or `/`.
## 🛠️ Tool List (58)
### Project Configuration
| Tool Name | Description |
|--------|------|
| `pyocd_project_load` | Load project configuration (`.pyocd-debug.json`) or automatically discover firmware/ELF/SVD files |
| `pyocd_project_init` | Create `.pyocd-debug.json` configuration file (with automatic relative path conversion) |
| `pyocd_svd_attach_builtin` | ⚠️ Temporarily fall back to pyocd's built-in SVD (strongly recommended to use vendor-provided complete SVD) |
### Probe & Session
| Tool Name | Description |
|--------|------|
| `pyocd_probe_list` | List all connected CMSIS-DAP debugging probes |
| `pyocd_probe_info` | Get detailed information about a specific probe |
| `pyocd_session_connect` | Connect to a probe and open a debugging session |
| `pyocd_session_disconnect` | Disconnect from a debugging session and release the probe |
| `pyocd_session_status` | View the current session status |
### Flashing
| Tool Name | Description |
|--------|------|
| `pyocd_flash_program` | Flash firmware (`.hex` / `.bin` / `.elf` / `.axf`), with automatic symbol attachment for ELF/AXF |
| `pyocd_flash_erase` | Erase Flash |
| `pyocd_flash_verify` | 🆕 Verify Flash content against firmware file (segment-by-segment comparison) |
### Target Control
| Tool Name | Description |
|--------|------|
| `pyocd_target_halt` | Halt the CPU |
| `pyocd_target_resume` | Resume execution |
| `pyocd_target_step` | Single-step execution |
| `pyocd_target_reset` | Reset the MCU (automatically restore all breakpoints) |
| `pyocd_target_status` | Get the target status (running/stopped) and key registers |
| `pyocd_target_wait_halt` | ⏳ **Wait for target halt** (breakpoint hit/watchpoint triggered), supporting `user_hint` |
| `pyocd_target_step_out` | 🆕 Execute to current function return (automatically set temporary breakpoint at LR) |
| `pyocd_target_list_supported` | List all MCU models supported by pyOCD (206+) |
### Registers & Memory
| Tool Name | Description |
|--------|------|
| `pyocd_register_read` | Read CPU registers |
| `pyocd_register_write` | Write CPU registers |
| `pyocd_register_read_all` | Read all registers (including FPU) |
| `pyocd_memory_read` | Read memory |
| `pyocd_memory_write` | Write memory (with readback verification) |
| `pyocd_memory_write_block` | Block write |
| `pyocd_memory_dump` | Hex dump output |
### Breakpoints & Watchpoints
| Tool Name | Description |
|--------|------|
| `pyocd_breakpoint_set` | Set breakpoints (by address or symbol name), supporting hw/sw/auto types |
| `pyocd_breakpoint_clear` | Clear breakpoints |
| `pyocd_breakpoint_clear_all` | Clear all breakpoints |
| `pyocd_breakpoint_list` | List current breakpoints |
| `pyocd_watchpoint_set` | Set data watchpoints (read/write/read-write triggers) |
| `pyocd_watchpoint_clear` | Clear watchpoints |
| `pyocd_watchpoint_clear_all` | Clear all watchpoints |
| `pyocd_watchpoint_list` | List current watchpoints |
### ELF Symbols
| Tool Name | Description |
|--------|------|
| `pyocd_elf_attach` | Load ELF file for symbol resolution |
| `pyocd_elf_lookup` | Find symbol addresses by name |
| `pyocd_read_symbol` | 🆕 **One-step variable reading**: symbol lookup + memory read combination tool |
| `pyocd_elf_symbols` | List symbols (functions/variables) in ELF |
| `pyocd_elf_info` | Get ELF file information |
| `pyocd_elf_address_to_symbol` | Address → symbol reverse lookup (PC/LR → function name + offset) |
### SVD Peripheral Registers
| Tool Name | Description |
|--------|------|
| `pyocd_svd_attach` | Load SVD file |
| `pyocd_svd_list_peripherals` | List all peripherals |
| `pyocd_svd_list_registers` | List peripheral registers |
| `pyocd_svd_read` | Read peripheral registers by name (with bit-field parsing) |
| `pyocd_svd_write` | Write peripheral registers by name |
| `pyocd_svd_list_fields` | List all bit-fields of a register (name, bit range, width) |
| `pyocd_svd_set_field` | Set individual bit-field values (RMW, supporting enumeration names or integers) |
| `pyocd_svd_update_fields` | 🆕 Batch set multiple bit-fields (single RMW, atomic operation) |
| `pyocd_svd_describe` | 🆕 View peripheral/register detailed descriptions (including enumeration value definitions) |
### Advanced Debugging
| Tool Name | Description |
|--------|------|
| `pyocd_debug_fault_analyze` | 💥 Analyze HardFault crashes (SCB registers + exception stack frame + EXC_RETURN + backtrace) |
| `pyocd_debug_stack_overflow_check` | 📏 Check thread stack overflows (RT-Thread/FreeRTOS) |
| `pyocd_debug_sample_variable` | 📊 Periodically sample variables (e.g., every 0.5s for 200 times) |
| `pyocd_debug_backtrace` | 🔍 **Accurate stack backtracing** — DWARF CFI/EHABI/heuristics-based three-level fault tolerance, covering AC5/AC6/GCC |
### RTT Real-time Transmission
| Tool Name | Description |
|--------|------|
| `pyocd_rtt_start` | 🆕 Start RTT and discover channels (automatically search for SEGGER RTT control block) |
| `pyocd_rtt_stop` | 🆕 Stop RTT and release resources |
| `pyocd_rtt_read` | 🆕 Read data from RTT uplink channel (non-blocking) |
| `pyocd_rtt_write` | 🆕 Write data to RTT downlink channel |
| `pyocd_rtt_status` | 🆕 View RTT status and channel information |
## 📋 Project Configuration (Recommended)
Create `.pyocd-debug.json` in the project root directory, allowing AI to automatically obtain all debugging file paths:
```json
{
"target": "hc32f4a0xi",
"firmware": "build/usart_uart_dma_Debug/usart_uart_dma.hex",
"elf": "build/usart_uart_dma_Debug/usart_uart_dma.axf",
"svd": "HC32F4A0PIHB.svd"
}
```
Alternatively, have AI create it:
```
AI: pyocd_project_load("/path/to/project") → Automatically scan and discover files
AI: pyocd_project_init("/path/to/project", → Create configuration
target="hc32f4a0xi",
firmware="build/xxx.hex",
elf="build/xxx.axf",
svd="HC32F4A0PIHB.svd")
```
> 💡 Paths in the configuration are automatically converted to relative paths from the project root directory, facilitating version control.
### SVD File Search Strategy
The search for SVD files (peripheral register descriptions) follows a three-level priority:
1. **Configuration file specification** (best): `svd` field in `.pyocd-debug.json` — vendor-provided complete SVD
2. **Project directory scan**: automatically search for `.svd` / `.xml` files in the project directory
3. **pyocd built-in fallback** (last resort): `pyocd_svd_attach_builtin` — match from pyocd's built-in 105 generic SVDs
> ⚠️ **Strongly recommended** to obtain SVD files matching specific chip models from the chip vendor's SDK. Built-in SVDs are generic and simplified, potentially missing peripherals or pin definitions.
## 🚀 Typical Debugging Workflow
Here's a typical conversation flow for AI-driven debugging using this MCP Server:
### 1. Connect → Flash → Run to main
```
AI: pyocd_probe_list → Discover probes
AI: pyocd_session_connect("hc32f460xe") → Connect
AI: pyocd_flash_program("firmware.hex") → Flash
AI: pyocd_elf_attach("firmware.elf") → Load symbols
AI: pyocd_breakpoint_set(symbol="main") → Set breakpoint at main
AI: pyocd_target_reset(halt=true) → Reset and halt
AI: pyocd_target_wait_halt(timeout=10) → Wait for main hit
```
### 2. Serial Data Debugging (Waiting for Breakpoints)
```
AI: pyocd_breakpoint_set(symbol="UART_RxCallback")
AI: pyocd_target_wait_halt(timeout=60) ← Wait for data arrival
... Data arrives within 60s, breakpoint hit ...
AI: pyocd_memory_dump(rx_buffer_addr, 64) → View receive buffer
AI: pyocd_register_read_all() → View register context
```
### 3. Periodic Variable Monitoring
```
AI: pyocd_debug_sample_variable(
address=0x20001000, # Global variable address
interval=0.5, # Every 0.5s
count=200 # 200 samples = 100s
)
→ Return sampled values + min/max/change statistics
```
### 4. HardFault Crash Analysis
```
AI: pyocd_debug_fault_analyze()
→ Return:
- fault_type: "BusFault"
- fault_address: "0x40012400"
- active_faults: [PRECISERR, BFARVALID]
- exception_frame: {PC_fault, LR_saved, R0-R3, ...}
- exc_return: {stack_used: "PSP", fpu_context: ...}
```
### 5. Wild Pointer/Array Out-of-Bounds Tracking
```
AI: pyocd_watchpoint_set(
address=0x20002000, # Array end address
size=4,
access_type="write" # Monitor write access
)
AI: pyocd_target_wait_halt(timeout=30)
... Out-of-bounds write occurs, DWT triggers halt ...
AI: pyocd_target_status() → View PC to locate write instruction
AI: pyocd_debug_backtrace() → View complete call chain
```
### 6. Accurate Stack Backtracing (Three-Level Fault Tolerance, Full Toolchain Coverage)
```
AI: pyocd_debug_backtrace(max_frames=16)
→ Return:
frames:
[0] USART_RxCallback+0x1A (pc=0x0800135C)
[1] DMA_IrqHandler+0x18 (unwound via DWARF CFI)
[2] main+0x42 (unwound via DWARF CFI)
method: "dwarf_cfi" ← AC6/GCC: .debug_frame accurate unwinding
method: "ehabi" ← AC5: .ARM.exidx accurate unwinding
method: "heuristic" ← Without debug info: stack scan + BL verification
# wait_halt breakpoint hit with brief backtrace:
AI: pyocd_target_wait_halt(timeout=30)
→ { ..., "backtrace": ["UART_RxCallback", "DMA_IrqHandler+0x18", "main+0x1A"] }
# fault_analyze crash with backtrace:
AI: pyocd_debug_fault_analyze()
→ { ..., "backtrace": ["HardFault_Handler", "memcpy+0x12", "cJSON_Parse+0x8A", ...] }
```
### 🆕 Address Format Flexibility
All tools accepting address parameters (11 in total) support both integer and hexadecimal string formats:
```python
```
# Two Formats are Equivalent
AI: pyocd_memory_read(address=0x20000000, size=16)
AI: pyocd_memory_read(address="0x20000000", size=16)
# Breakpoints are Also Supported
AI: pyocd_breakpoint_set(address="0x0800135C")
# Similarly for Watchpoints
AI: pyocd_watchpoint_set(address="0x20001000", access_type="write")
### 🆕 Combined Tools
```python
# read_symbol: Read a variable in one step (automatically find address + read memory + type inference)
AI: pyocd_read_symbol(name="g_rxBuffer")
→ { "name": "g_rxBuffer", "address": "0x20000100", "size": 64,
"hex_dump": "48 65 6C 6C 6F ...", "as_string": "Hello..." }
# step_out: Execute to the return of the current function
AI: pyocd_target_step_out(timeout=5.0)
→ { "status": "returned", "from_function": "UART_RxCallback",
"returned_to": "DMA_IrqHandler+0x18" }
```
## 🏗️ Project Structure
```
pyocd-debug-mcp/
├── pyproject.toml # Project configuration and dependencies
├── src/pyocd_debug_mcp/
│ ├── server.py # MCP Server entry, 58 tools registered
│ ├── session_manager.py # Session lifecycle management (singleton)
│ └── tools/
│ ├── probe.py # Probe discovery
│ ├── target.py # Target control
│ ├── flash.py # Flash programming + verification
│ ├── register.py # Register read/write
│ ├── memory.py # Memory read/write
│ ├── breakpoint.py # Breakpoint management (HW/SW/AUTO + Reset recovery)
│ ├── watchpoint.py # Watchpoint management
│ ├── elf.py # ELF symbol parsing
│ ├── svd.py # SVD peripheral registers (including batch update, enumeration, description)
│ ├── rtt.py # RTT real-time transmission (SEGGER RTT channel read/write)
│ ├── debug.py # Advanced debugging (fault analysis, stack backtracking, stack checking, sampling)
│ └── project.py # Project configuration (.pyocd-debug.json management)
└── tests/
```
## ⚙️ Supported Hardware
- **Debugging Probes**: All CMSIS-DAP compatible probes (DAPLink, STLINK via pyOCD, J-Link via pyOCD)
- **MCU**: 206+ chips supported by pyOCD, including:
- **HDSC/XHSC**: HC32F460, HC32F4A0, etc.
- **STMicroelectronics**: STM32F0/F1/F2/F3/F4/F7/H7/L0/L4/G0/G4, etc.
- **Nordic**: nRF52832, nRF52840, nRF9160, etc.
- **NXP**: LPC, Kinetis, i.MX RT, etc.
- More: `pyocd_target_list_supported` to view the complete list
## 📄 License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available