Embedded Debug MCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Embedded Debug MCPshow target status"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Debug Console MCP Server
Rust MCP server for embedded Linux DUT debugging. TCP direct to ser2net, strsim-based boot stage detection, self-learning reference log, relay/PDU power control, multi-DUT support, exponential backoff reconnect.
Design spec: docs/tech-design.md Observability:
#[instrument]tracing spans on all key async functions
Quick Start
# 1. Install Claude Code plugin (auto-installs debug-console-mcp binary)
claude plugin marketplace add https://github.com/bitshelf/debug-console-mcp
claude plugin install embedded-debug@bitshelf/debug-console-mcp
# 2. Create config + per-DUT directory
cp references/.target.toml.example .target.toml
vi .target.toml # set [[dev_hosts]].ip, [dut.serial].port, [dut].alias
mkdir -p .dut-serial/$(grep alias .target.toml | head -1 | cut -d'"' -f2)/logs
# 3. Restart Claude Code → plugin auto-starts MCPRelated MCP server: silotek-serial-mcp
Hardware Setup — Dev Host
udev Persistent Naming
Prevent /dev/ttyACM* renumbering across replug/reboot cycles by mapping each board's
USB serial number to a stable alias.
# On dev host, create /etc/udev/rules.d/99-rk3576-duts.rules:
# Use ATTRS{bInterfaceNumber} instead of KERNEL — kernel device numbers
# change across reboots. Interface 00 is the main console on dual-serial chips.
SUBSYSTEM=="tty", ATTRS{serial}=="<USB_SERIAL>", ATTRS{bInterfaceNumber}=="00", \
SYMLINK+="serial/by-alias/<dut_alias>"
# Apply:
sudo udevadm control --reload-rules && sudo udevadm trigger
# Verify:
ls -la /dev/serial/by-alias/Finding a board's USB serial number:
ssh <dev_host> "udevadm info --query=property --name=/dev/ttyACM0 | grep ID_SERIAL_SHORT"ser2net Configuration
# /etc/ser2net.yaml — port → serial device mapping
connection: &con2000
accepter: tcp,0.0.0.0,2000
enable: on
options:
kickolduser: true # prevent zombie connections
telnet-brk-on-sync: true
connector: serialdev,
/dev/serial/by-alias/<dut_alias>, # use udev symlink
115200n81,local # RK3576 default baudBoard Mapping Reference
Alias | USB Serial | ser2net Port | OS |
|
| 2000 | Yocto |
|
| 2008 | Ubuntu |
Configuration Reference (.target.toml)
Single Board
[[dev_hosts]]
alias = "rk-board-pc"
ip = "192.168.1.105"
user = "linaro"
[[dut]]
alias = "rk3576-board1"
dev_host = "rk-board-pc"
[dut.serial]
port = 2000
[dut.target]
login_user = "root"
login_prompt = "" # custom login regex (empty = default: `login:\s*$`)
[dut.uboot]
interrupt_char = "ctrl_c"
interrupt_strategy = "aggressive"
[dut.relay]
type = "usb-relay"
port = 2001
reset_ch = 1
# maskrom_ch = 2
reset_time_ms = 3000 # minimum USB relay reset pulse (default: 3000)
[dut.monitor]
hang_timeout = 60
max_archived_logs = 10
reference_log = ".dut-serial/rk3576-board1/reference-boot.log"
# StageLearner similarity thresholds (0.0–1.0). Defaults from Cargo.toml.
learner_stage_threshold = 0.45 # boot stage classification
learner_crash_threshold = 0.50 # crash pattern detection
[dut.flash]
tool = "upgrade_tool"
upload_dir = "/tmp"
full_image_cmd = "uf {image}"
kernel_image_cmd = "di -k {image}"
loader_bin = "/path/to/MiniLoaderAll.bin"
loader_cmd = "db {loader}"Multiple Boards
Add additional [[dut]] blocks. Each DUT gets:
Independent
.dut-serial/<alias>/directoryIndependent
target-state,statusline-cache, logsIndependent relay configuration
# ... same dev_hosts ...
[[dut]]
alias = "rk3576-board1"
# ... config ...
[[dut]]
alias = "rk3576-board2"
dev_host = "rk-board-pc"
[dut.serial]
port = 2008 # different port!
[dut.target]
login_user = "root"
[dut.monitor]
reference_log = ".dut-serial/rk3576-board2/reference-boot.log"MCP Tools (28 total)
Tool | Description |
| Execute shell command on DUT |
| Get state (active/booting/uboot/crashed/DUT-off/disconnected) |
| Retrieve serial logs (regex filter, streaming) |
| List archived boot logs |
| Hardware reset + log rotation |
| Power-cycle via relay power channel |
| Enter U-Boot (Ctrl-C flood, bootdelay=0 compatible) |
| Soft reboot + Ctrl-C flood -> U-Boot |
| Enter MASKROM mode (if relay configured) |
| Wait for pattern (probe on timeout) |
| Send command at U-Boot |
| Manually rotate log |
| Incremental output (file position tracking) |
| View current config (read-only) |
| Engine metrics: uptime, command/error count, pending |
| Claim serial ownership for this session |
| Press/release/pulse reset/maskrom/recovery buttons |
| Pause serial engine for dutabo takeover |
| Resume after pause |
| Send raw bytes (no markers, no wrapping) |
| Load reference log for StageLearner |
| View learned stage fingerprints |
| Get unclassified lines (self-learning) |
| Append anchor lines + hot-reload StageLearner |
| Auto-learn connection (3x reset, similarity check) |
| Verify CH340 relay control (ON/OFF read-back) |
| Generate flash plan from .target.toml [flash] config |
| Execute firmware flash via dev host SSH |
Target States
● serial:active — DUT ready, login configured
◐ serial:booting — U-Boot → kernel boot in progress
● serial:uboot — U-Boot interactive prompt
✗ serial:crashed — kernel panic / BUG / Oops detected
✗ serial:DUT-off — no response (hang timeout)
✗ serial:disconnected — ser2net unreachableWhen .target.toml has multiple [[dut]] entries, statusline shows all:
● rk3576-board1:active ● rk3576-board2:activeStageLearner Tuning
The StageLearner uses text similarity (3-gram Jaccard + Jaro-Winkler) to detect boot stages without SOC-specific regex. Two thresholds control classification accuracy:
learner_stage_threshold (default 0.45)
Minimum similarity score for normal boot stage classification.
Range | Effect |
0.30–0.40 | Loose — may produce false stage detections |
0.45–0.55 | Balanced (recommended) |
0.60–0.80 | Strict — may miss valid stages |
learner_crash_threshold (default 0.50)
Minimum similarity score for crash pattern detection. Set higher than the stage threshold because crash lines are high-signal events that warrant higher confidence.
Example
[dut.monitor]
# Stricter matching for a noisy serial line:
learner_stage_threshold = 0.50
# Lower crash threshold to catch custom firmware panics:
learner_crash_threshold = 0.40Defaults (0.45 / 0.50) are in Cargo.toml [package.metadata.learn].
Known Limitations & Fallbacks
BusyBox ash Pipe Buffering
On Yocto/BusyBox targets, echo ... | grep ... often returns empty output
because ash's pipe buffering delays grep output past the exit-code marker.
Pattern | Fallback |
|
|
| 加 |
|
|
Any pipe command | 加 |
The MCP returns a hint field in the JSON response when it detects this pattern.
First-Command Warmup
The first serial_send_command after MCP startup may return empty.
Always start with a warmup: serial_send_command("echo warmup", timeout=3).
USB Relay Minimum Reset Time
USB relays (CH340) require a minimum pulse duration to physically toggle.
Default: 3000ms (3 seconds). Override in .target.toml via
[dut.relay] reset_time_ms = 5000.
Reconnect Behavior
When the serial connection drops, the MCP uses exponential backoff: 1s → 2s → 4s → 8s → 16s → cap at 30s, resetting to 1s after 60s of stable connection. The Agent is notified on each retry.
Observability
All key async functions are instrumented with #[tracing::instrument]:
serial_send_command{cmd="uname -a", timeout=8} ← MCP tool entry
queue_command{command="uname -a"} ← engine dispatch
execute{command="uname -a", timeout_secs=8} ← command queue
start{host="192.168.1.105", target="2000"} ← engine lifecycle
probe_initial_state{result=active} ← initial probe
stop ← engine shutdownEach span records duration (time.busy, time.idle) and structured fields
for querying by command text, exit code, timeout status.
View spans in real-time:
debug-console-mcp --log-to-stderr --verboseHooks
Hooks are registered automatically by the Claude Code plugin (hooks/hooks.json).
No manual settings.json edits needed.
Hook | Trigger | Purpose |
| Enter project | Detect |
| Before Bash | Block raw |
| Before each prompt | Multi-DUT state alert, auto-restart MCP |
| ~1s refresh | Show DUT state(s) in statusline |
Adding a New Board
Find USB serial number:
ssh <dev_host> "udevadm info --query=property --name=/dev/ttyACM0 | grep ID_SERIAL_SHORT"Add udev rule (on dev host,
/etc/udev/rules.d/99-rk3576-duts.rules):SUBSYSTEM=="tty", ATTRS{serial}=="<serial>", ATTRS{idProduct}=="55d2", \ KERNEL=="ttyACM0", SYMLINK+="serial/by-alias/<new_alias>"Add ser2net port (on dev host,
/etc/ser2net.yaml):connection: &con<port> accepter: tcp,0.0.0.0,<port> enable: on options: kickolduser: true connector: serialdev, /dev/serial/by-alias/<new_alias>, 115200n81,local sudo systemctl restart ser2netCreate project
.target.tomlwith matchingserial.portanddut.aliasCreate
.dut-serial/<alias>/logs/directory (or let MCP auto-create it)Start Claude Code in the project directory → MCP auto-starts
CLI (dutabo)
dutabo is a manual CLI tool for interactive debug. Install separately:
cargo install --git https://github.com/bitshelf/debug-console-mcp
# → installs dutabo to ~/.cargo/binCommands:
dutabo list # list DUTs from .target.toml
dutabo serial [--dut <alias>] # interactive serial console
dutabo reset [--dut <alias>] # hardware reset
dutabo uf <image> [--dut] # flash firmware
dutabo uboot [--dut <alias>] # enter U-Boot
dutabo state [--dut <alias>] # get DUT state
## #[tool] Macro Migration (Future)
**Status**: rmcp 3.0.1 is integrated with manual `ServerHandler` impl (`mcp_handler.rs`).
Full `#[tool_router]` + `#[tool_handler]` migration is blocked pending rmcp 3.x API maturity.
### Current Architecture
mcp_handler.rs ──► ServerHandler (manual impl) ──► McpServer::handle_call_tool() mcp.rs ──► ToolDef vec (28 tools) ──► call_tool_impl() dispatch
Tools are defined as `Vec<ToolDef>` with manual JSON schema and dispatched via
match statements. The `mcp_handler.rs` `ServerHandler` impl delegates
`call_tool` → `McpServer::handle_call_tool` for complex tools.
### Target Architecture (from adancurusul/serial-mcp-server)
```rust
#[tool_router]
impl McpHandler {
#[tool(description = "Send a shell command to the target")]
async fn serial_send_command(&self, Parameters(args): Parameters<SendCmd>) -> String {
// ... typed implementation
}
}
#[tool_handler]
impl ServerHandler for McpHandler { ... }What's Ready
Tool parameter types defined:
SendCmd,GetLogs,Reset, etc. (inmcp_handler.rsparams! macro)schemars::JsonSchemaderive available for auto-schema generation#[tool_router]and#[tool_handler]macros compile but have API issues
Blockers (rmcp 3.0.1)
E0716— temporaryValuelifetime when borrowing engine return valuesE0119— conflictingServerHandlerimpls when mixing#[tool_router(server_handler)]with manualimpl ServerHandlerfor task overridesMethod visibility —
#[tool_router]impl block can't see helper methods defined in separateimpl McpHandlerblocks
Migration Steps (when rmcp 3.x stabilizes)
Remove
ToolDefvec andtool_definitions()frommcp.rsMove each tool impl into
#[tool]method onMcpHandlerUse
#[tool_router(server_handler)]for auto ServerHandler generationComplex tools (multi-phase lock): extract into standalone async fns
Delete manual
handle_message()JSON-RPC router frommcp.rsRemove
JsonRpcRequest/JsonRpcResponsetypes
Reference
adancurusul/serial-mcp-server — reference implementation (rmcp 0.3.2)
rmcp docs — official rmcp 3.0.1 docs
rmcp-macros — #[tool] macro reference
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server that lets LLMs talk to serial devices: microcontrollers, routers, modems, embedded Linux, anything with a UART.234MIT
- FlicenseAqualityBmaintenanceA headless MCP server that enables AI tools (like Claude Code) to read and analyze serial logs from embedded boards (ESP32, STM32) for firmware debugging, with read-only tools for log retrieval and a built-in web viewer.6
- Flicense-qualityDmaintenanceFastMCP-based GDB debugging server for high-performance, stable debugging with command queuing and session management.1

sbl-probeofficial
Alicense-qualityDmaintenanceSerial communication and protocol analysis MCP server that gives AI coding assistants direct access to serial ports for reading, writing, decoding, and capturing embedded device output.MIT
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
Remote MCP for A2A failure replay MCP, structured receipts, audit logs, and reviewer-ready evidence.
Remote MCP for Android CLI agent build gate, structured receipts, audit logs, and reviewer-ready evi
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bitshelf/debug-console-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server