Embedded Debug MCP
by bitshelf
README.md
# Embedded Debug MCP Server
## 架构

```
┌────────────────────────┐
│ Ethernet Switch │
└─┬──────┬──────┬──────┬┘
│ │ │ │
Ethernet │ │ │ │ Ethernet
│ │ │ │
┌────────────────┘ │ │ └──-───────────────────────────┐
│ │ └───-───-───┐ │
▼ ▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ ┌──────────────────┐
│ Windows PC │ │ Build Machine │ │ ARM │──UART───│ Target Board │
│ │ │ Linux x86 │ │ │──USB────│ │
│ SSH / Client │ │ │ │ │.........│ ○ RECOVERY │
│ │ │ Claude Code │ │ ser2net │─────────│ ○ RESET │
│ │ │ MCP Server │ │ USB Host │.........│ ○ Power │
└──────────────────┘ └──────────────────┘ └───────────────┘ └──────▲────▲──────┘
```
### 工作流
```
1. 构建 cargo install --git https://github.com/bitshelf/sermcp --locked --target x86_64-unknown-linux-musl
2. 配置 dutabo init (在项目根目录;或 vi .target.jsonc)
3. 部署 vi .mcp.json (或依赖 hook)
4. 启动 claude (Claude Code 自动 spawn MCP Server)
5. 使用 serial_send_command "uname -a"
6. 查看日志 tail -f .dut-serial/mcp.log
```
## 使用方法
在项目根目录创建 `.target.jsonc`,用 `dutabo init` 交互式创建/更新, 可以使用图形界面进行测试。
`.mcp.json` 缺失或没有`sermcp` 条目时,`dutabo init` 会自动创建/补齐该条目(已有条目则原样保留)。
- `.mcp.json`
```json
{
"mcpServers": {
"sermcp": {
"command": "sermcp",
"args": []
}
}
}
```
```bash
# 确认项目有 .target.jsonc
ls -la .target.jsonc .mcp.json
# 启动 Claude Code 后查看状态栏
# 成功时会显示: ● serial:active 或 ● serial:disconnected
```
### 日志
- 运行时日志写入 `{project}/.dut-serial/mcp.log`。
- 串口数据日志写入 `{project}/.dut-serial/logs/boot-NNN_*.log`。
### 多 Agent 共享与占用状态(busy / dutabo)
多个代码 Agent 在同一项目打开时共享一个 MCP server。第一个 Agent
会话是 Owner,后续会话是 ReadOnly guest。
- `dutabo` — `dutabo serial` 交互会话持有串口;对**所有**会话生效(引擎状态
机真实转移到 `Dutabo`);
- `busy` — 另一个 Agent 会话持有 MCP,本会话只读(控制类工具被
`agent_read_only` 拒绝);
### 故障排除
| 现象 | 原因 | 解决 |
|------|------|------|
| 状态栏不显示 | 无 `.target.jsonc` 或 Server 未启动 | 检查项目根目录; 运行 `--log-to-stderr -v` 调试 |
| `disconnected` | 连不上 ser2net | 检查 IP/端口; `nc -zv host port` |
| `DUT-off` | 目标无输出 | `serial_send_command "echo ping"`; `serial_reset` |
| 二进制不启动 | 依赖库缺失 | 检查动态链接: `ldd target/release/sermcp` |
| 第二个实例被拒 | 同 host:port 互斥 | 退出第一个实例或等它释放锁 |
## 构建
```bash
rustup target add x86_64-unknown-linux-musl
cargo musl
```
### 测试
```bash
# 运行全部单元测试(在仓库根目录执行)
cargo test -p sermcp
# 运行特定模块测试
cargo test -p sermcp boot_detector
cargo test -p sermcp command_queue
cargo test -p sermcp mcp
# 查看测试覆盖率报告
cargo test -p sermcp -- --nocapture
```
### dutabo serial 日志回滚
交互模式连接 MCP 时,会先回放 MCP 内存中最近至多 512 KiB 的串口日志,再接收实时输出。本地终端保留最多 10,000 行滚动历史;MCP 重启前或超出窗口的日志请查看 `full.serial.log`。直接连接 ser2net 时,仅能回滚本次连接收到的输出。
- 鼠标滚轮或 `Alt+PgUp/PgDn`:向上/向下查看日志。
- `Alt+Home/End`:最早的历史/实时输出。
- 主机拦截翻页键时,使用 `Ctrl-T` 后按 `u/d` 翻页,按 `g/G` 跳到最早历史/实时输出。
- `Ctrl-T q`:退出;原有 `Ctrl-T PgUp/PgDn` 和 `Shift+PgUp/PgDn` 仍可用。
向上滚动后,新输出默认保持阅读位置。普通 `PgUp/PgDn` 仍发送给设备;设备上的 vim/less 等全屏程序仍由滚轮控制。非交互管道输出不回放历史。历史回放需要配套更新 MCP 服务端。
### 串口日志分段
`current.serial.log` 保存当前启动周期的原始输出,`full.serial.log` 连续追加。收到下一次启动的 DDR/SPL 前缀时,前一个周期归档为 `boot-NNN_*.log`,前缀归入新的当前日志;文件名时间是归档时刻。连接时设备已运行的首段可能只有部分周期。`dutabo serial` 直接接管 ser2net 的路径也保存到该 DUT 的日志目录。`dutabo serial` 占用期间仍保存和分段,但不会执行 Agent 自动登录等动作。
归档读取完整磁盘文件,不受 2 MiB 内存缓冲限制;MCP 重启继续追加当前文件。重置请求本身不切段,按实际收到的启动前缀划分;没有可识别前缀的启动无法保证自动分段。默认只保留最近 10 个归档,可用 `.target.jsonc` 的 `monitor.max_archived_logs` 调整;`full.serial.log` 不参与归档清理。
### Supply power and the sleep/wake key
Relay `power_ch` controls supply OFF/ON. `serial_power_cycle` cuts supply power,
waits for `power_off_time_ms` (at least 3000 ms), then restores it.
Relay `power_key_ch` (alias `power_key_channel`) controls the separate momentary
power key used for sleep/wake. Configure it on its own relay channel, for example:
```jsonc
"relay": {
"type": "ch340-relay",
"port": 2000,
"power_ch": 3,
"power_key_ch": 4,
"power_off_time_ms": 3000
}
```
Use `dutabo button power_key pulse 500` for a short key press, or call
`serial_button` with `{"button":"power_key","action":"pulse","delay_ms":500}`.
`press` and `release` are also supported. Key pulses use the requested duration
(default 500 ms) without the supply-off minimum. The board's OS determines
whether the press sleeps, wakes, or performs another configured power-key action;
a successful relay operation does not confirm the OS state.
`dutabo init` provides separate `power` and `power key` rows. Channel 0 inherits
the global channel, consistent with the other relay channels. `dev_ctrl=none`
or `disabled` disables both controls. Restart the MCP server after changing its
configuration.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues