| name | openocd-mcp-debug |
| description | [Mandatory develop-verify loop] Embedded firmware debug, flash, and RTT real-time log verification expert. When asked to change code, add features, fix bugs, or monitor runtime, must follow the "modify code → build → flash → RTT verify → analyze → fix → re-verify" closed loop until the task passes verification. Based on openocd-mcp MCP server, debugs STM32/ARM Cortex-M targets via OpenOCD + GDB. |
| allowed-tools | ["Read","Write","Edit","Bash","Grep","Glob","mcp_openocd-mcp_set_project","mcp_openocd-mcp_refresh_debug_targets","mcp_openocd-mcp_flash_download","mcp_openocd-mcp_debug_start","mcp_openocd-mcp_debug_attach","mcp_openocd-mcp_debug_stop","mcp_openocd-mcp_debug_continue","mcp_openocd-mcp_debug_interrupt","mcp_openocd-mcp_debug_command","mcp_openocd-mcp_debug_state","mcp_openocd-mcp_debug_status","mcp_openocd-mcp_get_runtime_config","mcp_openocd-mcp_read_rtt","mcp_openocd-mcp_shutdown"] |
openocd-mcp Embedded Debug Skill
Project Overview
openocd-mcp is a fastmcp-based MCP server that wraps OpenOCD flashing and GDB debugging workflows into AI-callable tools. It reuses the project's existing .vscode/launch.json as the debug target source, requiring no extra configuration.
| Property | Description |
|---|
| Repository | D:\GitHub_Repository\openocd-mcp |
| Installation | uv tool install . or uv tool install git+https://github.com/luiox/openocd-mcp.git |
| Run mode | stdio (default, VS Code MCP) / SSE (--sse --host 127.0.0.1 --port 9000) |
| Protocol | GDB/MI2 async protocol (--interpreter=mi2), event-driven, no polling |
| Dependencies | OpenOCD + arm-none-eabi-gdb |
| Platforms | Windows / Linux / macOS |
Core Architecture
AI Client → MCP Protocol → openocd-mcp
├── ProjectConfigManager (parses launch.json)
├── OpenOCDController (start/stop OpenOCD process)
├── GDBMISession (MI2 async protocol communication)
├── RTTClient (real-time log reading)
└── DebugSessionManager (coordinates lifecycle)
MCP Tool Complete Reference
All tools return strings by default. On failure, the response starts with "Error: ".
Project & Configuration
| Tool | Description | Typical Usage |
|---|
set_project(project_dir) | Load project .vscode/launch.json | set_project(project_dir="d:/path/to/project") |
refresh_debug_targets() | Reload launch.json configurations | Use after modifying launch.json |
get_runtime_config() | View current OpenOCD/GDB paths and their sources | Use when troubleshooting path issues |
Flash & Debug Lifecycle
| Tool | Description | Typical Usage |
|---|
flash_download(config_name, firmware_path?) | One-time firmware flash (no debug session) | flash_download(config_name="Launch (DAP)") |
debug_start(config_name, firmware_path?) | Start a debug session | debug_start(config_name="Launch (DAP)") |
debug_stop() | Stop the debug session | debug_stop() |
Runtime Control
| Tool | Description | Key Notes |
|---|
debug_continue() | Resume target execution | Async return, non-blocking. GDB remains responsive while target is running |
debug_interrupt() | Pause target | Uses GDB/MI -exec-interrupt. On Windows, auto-fallback to OpenOCD telnet halt |
debug_command(command) | Execute arbitrary GDB command | "continue"/"interrupt" are auto-mapped to MI async commands |
debug_state() | Query target state | Returns `{"state": "running" |
debug_status() | Get full session info | Includes PID, RTT connection status, etc. |
Data Reading
| Tool | Description | Typical Usage |
|---|
read_rtt(max_lines=10) | Read RTT real-time logs | read_rtt(max_lines=20) |
debug_command(command) | Read registers/variables | debug_command("info registers") / debug_command("print x") |
Server Control
| Tool | Description | Typical Usage |
|---|
shutdown() | Gracefully exit the MCP server | Call before reinstalling to release file locks |
⚠️ Mandatory workflow: When asked to add features, fix bugs, or monitor runtime, you must follow the "Develop-Verify Loop" below. Do not skip verification steps and report completion directly.
Mandatory Develop-Verify Loop
This is the core workflow of this skill. Regardless of task type (new feature, bug fix, debug analysis), you must follow this closed loop.
Loop Overview
flowchart TD
A[Understand requirements] --> B[Modify/write code]
B --> C[Build firmware]
C --> D[Flash & start debug]
D --> E[Run program + RTT monitor]
E --> F{Does result match expectations?}
F -->|Yes| G[Stop debug]
F -->|No| H[Analyze RTT / GDB output]
H --> I[Diagnose root cause]
I --> B
G --> J[Report completion]
Step-by-Step Instructions
Step 1: Understand Requirements & Plan
- Identify the code files to modify (driver/HAL/application layer)
- Define verification criteria: what logs should appear in RTT? What behavior changes?
Step 2: Modify Code
- Modify source files according to requirements
- Document changes for later verification reference
Step 3: Build Firmware 🔨
xmake f -p cross -a arm -m debug
xmake build weather_station
- Build must succeed before proceeding to the next step
- Build failure → fix compilation errors → rebuild
Step 4: Flash & Start Debug 🚀
set_project(project_dir="d:/GitHub_Repository/weather-station")
debug_start(config_name="Launch weather_station (DAP)")
- Confirm output includes
RTT connected on port 8888
- Confirm output includes
Stopped at main (breakpoint hit)
Step 5: Run + RTT Verification 📡
debug_continue()
# Wait sufficient time for code logic to execute (varies by scenario: 2s/10s/20s)
debug_interrupt()
read_rtt(max_lines=20)
Step 6: Analyze Verification Results 🔍
- Expected behavior appears in RTT logs → verification passed → stop debug → report completion
- RTT logs abnormal or no output → analyze root cause:
- Code logic error → go back to Step 2
- Build issue → go back to Step 3
- Debug issue → check GDB/RTT status
Step 7: Report Completion
- Summarize: which files were changed, what was verified, key RTT output
- Confirm all task requirements are met
Verification Criteria for Four Common Task Types
| Task Type | Core Verification Method | Expected RTT Output Example |
|---|
| New feature (e.g., adding sensor driver) | Observe if new sensor data logs appear in RTT | [INFO] ACQ: ... NEW_SENSOR=123 ... |
| Bug fix | Confirm the bug-related error disappears, normal logs appear | Original [ERROR] xxx failed disappears, [INFO] xxx OK appears |
| Logic change (e.g., adjusting acquisition period) | Verify RTT log interval/behavior matches new logic | Logs change from every 2s to every 5s, etc. |
| Runtime monitoring (e.g., stability analysis) | Check RTT for anomalies after extended runtime | 30+ consecutive ACQ records with no errors |
Complete Example: Self-Verification After Adding a Feature
User: "Add a wind speed sensor alarm to weather-station"
Agent executes the loop:
① Understand requirements → Need to add threshold alarm in anemometer driver
② Modify code → Edit src/driver/anemometer.c
③ Build → xmake build weather_station (success)
④ Flash & debug → set_project + debug_start
⑤ Run & verify → debug_continue → wait 10s → debug_interrupt → read_rtt
⑥ Analyze → RTT shows "⚠️ Wind speed exceeded: 18.5m/s"
→ Matches expectation ✅
⑦ Report completion → Summarize changes and verification results
If RTT shows no alarm → go back to ② fix logic → ③~⑥ re-verify
Complete Example: Self-Verification After Bug Fix
User: "LoRa sending keeps failing, help me check"
Agent executes the loop:
① Understand requirements → LoRa send returns error, need to troubleshoot
② Locate code → Find timeout handling issue in lora_port.c
③ Modify code → Fix timeout retry logic
④ Build → Success
⑤ Flash & verify → debug_continue → wait 10s → read_rtt
⑥ Analyze → RTT shows "[INFO] LoRa send OK (46B)"
→ Previously was "[ERROR] LoRa send failed"
→ Bug fixed ✅
⑦ Report completion → Explain root cause and fix
If failure still appears → Continue analyzing driver code → Fix → Re-flash & verify
RTT Output Quick Reference
| RTT Log Pattern | Meaning |
|---|
[INFO] ACQ: T=.. H=.. P=.. | Normal data acquisition (every ~2s) |
[ERROR] ... failed | Sensor or communication failure, needs troubleshooting |
[INFO] System ready | System initialization complete |
[INFO] LoRa send OK | LoRa send successful |
| No RTT output | Check RTT connection; or firmware hasn't reached log output point |
rtt: Control block not available | RTT control block not initialized (log_init() not called) |
RTT Log Details
RTT works through an automatic startup process:
debug_start → GDB monitor rtt server start 8888 0
→ GDB print &_SEGGER_RTT (get control block address)
→ GDB monitor rtt setup <address> 1024
→ GDB monitor rtt start
→ TCP connection 127.0.0.1:8888 ← RTTClient
Key points:
- Firmware must include SEGGER RTT implementation (
SEGGER_RTT_Conf.h, SEGGER_RTT.c)
- RTT initialization occurs after
log_init(), rtt setup pre-sets address but doesn't block
- RTT failure does not block the debug session; it degrades silently
- Check
debug_status() rtt_connected field to determine if RTT is working
- Default port 8888, changeable via
--rtt-port / RTT_PORT env var / config.json rtt_port field
Configuration
Parameter priority: CLI args > Environment variables > config.json > Built-in defaults
config.json (workspace root)
{
"openocd_path": "C:/Program Files (x86)/xpack-openocd-.../bin/openocd.exe",
"gdb_path": "C:/Program Files (x86)/Arm GNU Toolchain/.../bin/arm-none-eabi-gdb.exe",
"openocd_scripts": "",
"rtt_port": 8888,
"adapter_speed": 1000
}
Environment Variables
| Variable | Purpose |
|---|
OPENOCD_PATH | OpenOCD executable path |
GDB_PATH | GDB executable path |
OPENOCD_SCRIPTS | OpenOCD scripts directory (-s argument) |
RTT_PORT | RTT server port |
CLI Arguments
openocd-mcp --openocd-path openocd --gdb-path arm-none-eabi-gdb --rtt-port 8888
openocd-mcp --sse --host 127.0.0.1 --port 9000
VS Code MCP Configuration (.vscode/mcp.json)
{
"servers": {
"openocd-mcp": {
"type": "stdio",
"command": "openocd-mcp",
"args": [],
"cwd": "${workspaceFolder}"
}
}
}
Platform Differences
Windows
- Interrupt mechanism: GDB/MI
-exec-interrupt pipe interrupt is unreliable; auto-fallback to OpenOCD telnet halt (connect to 127.0.0.1:4444, send halt\n)
- Paths: OpenOCD requires forward slashes; code handles this automatically
- Process management: GDB launched with
CREATE_NEW_PROCESS_GROUP flag
Unix (Linux/macOS)
- Interrupt mechanism: GDB/MI
-exec-interrupt works reliably
- MI method preferred, telnet fallback as degradation option
Fixing "MCP Tool Disabled by the User" Error
If you see ERROR while calling tool: Tool mcp_openocd-mcp_xxx is currently disabled by the user:
python -c "
import sqlite3, json
ws = sqlite3.connect('C:/Users/xxx/AppData/Roaming/Code/User/workspaceStorage/<workspace_id>/state.vscdb')
for key in ['mcpToolCache', 'mcp.extCachedServers']:
ws.execute('DELETE FROM ItemTable WHERE key = ?', (key,))
tools = ['set_project','refresh_debug_targets','flash_download','debug_start','debug_attach','debug_stop',
'debug_command','debug_continue','debug_interrupt','debug_status','debug_state',
'get_runtime_config','read_rtt','shutdown']
autoconfirm = {f'mcp_openocd-mcp_{t}': True for t in tools}
ws.execute('INSERT OR REPLACE INTO ItemTable (key, value) VALUES (?, ?)',
('chat/autoconfirm', json.dumps(autoconfirm)))
ws.commit(); ws.close()
"
Common Troubleshooting
1. OpenOCD Startup Failure
Error: OpenOCD failed to start: [WinError 2] The system cannot find the specified file.
- Cause:
openocd_path in config.json doesn't exist or wrong config file was read
- Diagnosis: Call
get_runtime_config() to check the actual path and its source
- Fix: Ensure
config.json in workspace root contains the correct path
2. GDB Connection Failure
Error: GDB connection failed: Remote communication error.
- Cause: OpenOCD failed to start GDB server within 15 seconds (port 3333)
- Diagnosis: Check debugger physical connection; check OpenOCD scripts; reduce
adapter_speed
- Fix: Add
"adapter_speed": 500 to config.json
3. Flash Failure
Error erasing flash with vFlashErase packet
- Cause: SWD clock speed too high, unstable communication
- Fix: Reduce
adapter_speed (common stable values: 1000kHz, 500kHz)
4. No RTT Data
RTT: (no new RTT output)
- Cause: Firmware doesn't include SEGGER RTT, or
log_init() not called
- Diagnosis: Check
rtt_connected in debug_status() result
- Fix: Confirm firmware integrates SEGGER RTT library and calls
log_init() at startup
5. debug_interrupt Timeout
Error: GDB command timed out after 30s: -exec-interrupt
6. File Lock Error on Reinstall
error: failed to remove directory ... Scripts: Access denied
- Cause: Old MCP process still running, holding file locks
- Fix: Call
shutdown() to stop the current MCP process first, or manually taskkill //F //IM openocd-mcp.exe
Standard MCP Reinstallation Procedure
taskkill //F //IM openocd-mcp.exe
cd D:/GitHub_Repository/openocd-mcp
git pull
uv tool install --reinstall .
Important Notes
- Single session model: At most one debug session at a time;
debug_start auto-stops previous sessions
- launch.json compatibility: Supports C-style comments and trailing commas (parser auto-cleans)
- Timeout control: Normal GDB commands 30s timeout,
load 120s, flash 180s
- Path resolution:
${workspaceRoot} and ${workspaceFolder} are auto-replaced with project directory
- config.json reading: Based on CWD (current working directory), reads
CWD/config.json
- Async continue:
debug_continue() returns immediately via MI ^running, doesn't block waiting for target to stop
Workflow Enforcement Reminder
⚠️ After every code change, you must go through "build → flash → RTT verification" before reporting completion.
⚠️ When verification fails, you must analyze RTT output, diagnose root cause, fix code, and re-verify to form a closed loop.
⚠️ Do not skip verification steps because it "looks fixed." RTT logs are the sole basis for judging task completion.