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/DESIGN-v0.3.md Observability:
#[instrument]tracing spans on all key async functions
Quick Start — New Project
# 1. Build & deploy
cd mcp-rs && cargo build --release && ./deploy.sh
# 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 → SessionStart hook auto-starts MCPRelated MCP server: 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-pdstars"
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-pdstars/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-pdstars"
# ... config ...
[[dut]]
alias = "rk3576-yt9215"
dev_host = "rk-board-pc"
[dut.serial]
port = 2008 # different port!
[dut.target]
login_user = "root"
[dut.monitor]
reference_log = ".dut-serial/rk3576-yt9215/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 |
| 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-pdstars:active ● rk3576-yt9215: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
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 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 stateThis 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.
Latest Blog Posts
- 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