内容
# UARTBuddy
<p align="center">
<img src="rust_app/assets/uartbuddy-icon.png" width="128" alt="UARTBuddy icon">
</p>
<p align="center">
<strong>让人和 AI Agent 安全地共享同一条真实串口。</strong>
</p>
<p align="center">
<a href="https://github.com/llinzzi/UARTBuddy/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/llinzzi/UARTBuddy?include_prereleases"></a>
<img alt="macOS 12+" src="https://img.shields.io/badge/macOS-12%2B-000000?logo=apple">
<img alt="Apple Silicon" src="https://img.shields.io/badge/Apple%20Silicon-arm64-4385F4">
<img alt="Rust" src="https://img.shields.io/badge/Rust-egui-EA4335?logo=rust">
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-34A853"></a>
</p>
UARTBuddy 是一个用 Rust + egui 编写的 macOS 串口调试器。它独占真实串口,再通过
本机 MCP 服务把串口控制、数据收发、历史记录和烧录能力共享给 Codex、Claude Code
等 AI Agent。
> 当前版本:`v0.5.0` · Apple Silicon · macOS 12+
## 它解决什么问题
嵌入式调试中,真正麻烦的往往不是“打开串口”,而是让多个参与者共享同一份现场。
### 1. 真实串口通常只能被一个进程可靠占用
GUI 串口助手、烧录工具和 AI Agent 如果分别打开 `/dev/cu.*`,容易出现端口占用、
数据被抢读、命令交叉发送,甚至烧录过程被另一个程序打断。
### 2. 人看到的日志,AI 看不到
传统串口助手把日志显示在窗口里,但 Agent 无法增量读取。把串口交给命令行工具后,
Agent 能读了,人又失去了直观界面和手动发送能力。
### 3. 关键故障会随着滚屏消失
设备重启、偶发 I²C 错误、崩溃前最后几行日志,可能在人工来得及检查前就被新输出
覆盖。实时窗口不应该成为唯一记录。
### 4. 烧录工具很多,但不值得重复实现每种协议
ESP32、STM32 以及其他芯片已经有成熟的官方或社区烧录工具。重新实现一遍协议成本高、
风险大;真正需要解决的是串口释放、命令调度、实时输出和任务结束后的连接恢复。
## UARTBuddy 的做法
UARTBuddy 让单一 Serial Broker 成为真实串口的唯一拥有者:
```mermaid
flowchart LR
Device["嵌入式设备"] <--> Port["真实串口"]
Port <--> Broker["UARTBuddy Serial Broker"]
Broker <--> GUI["人工 GUI"]
Broker <--> MCP["本机 MCP :8765"]
MCP <--> Agent["Codex / Claude Code"]
Broker --> History["JSONL 持久历史"]
MCP --> Flash["espflash / esptool / stm32flash"]
Flash -. "烧录前释放,结束后恢复" .-> Port
```
- GUI 和 Agent 读取同一份日志,不争抢串口。
- GUI 发送显示为 `TX`,MCP 发送显示为 `MCP TX`。
- MCP 客户端连接、工具调用、成功和错误会显示为独立事件。
- 烧录工具的 stdout/stderr 会以 `MCP FLASH` 实时进入终端。
- 每次连接建立一个历史会话,完整日志写入磁盘。
## 主要功能
- 原生浅色 GUI,支持串口扫描、连接和参数配置
- UTF-8 与 HEX 显示、发送,支持追加 LF
- 毫秒时间戳以及 RX、TX、MCP、系统事件视觉分组
- “复制全部”、历史会话选择和完整详情查看
- 内置 Streamable HTTP MCP,默认随应用启动
- 13 个 MCP 工具,覆盖串口、烧录与历史管理
- 自动发现 `espflash`、`esptool`、`stm32flash`
- 支持用户自定义烧录适配器,不允许 MCP 任意执行程序
- 烧录前释放串口,结束或取消后恢复原连接
- 实时展示烧录命令、进度、stdout/stderr 和退出状态
- Chrome 风格浅色配色,与应用图标保持一致
- 单一 Rust 进程,不依赖 WebView、Flutter 或后台 Python 服务
## 界面预览
### 串口终端与 MCP 事件

人工收发、设备输出、MCP 调用和烧录过程进入同一条时间线,并通过颜色和事件标签区分。
### MCP 接入与烧录环境

应用内可直接查看 Codex、Claude Code 的配置方式,管理 MCP 服务并扫描本机烧录工具。
## 快速开始
### 下载
从 [Releases](https://github.com/llinzzi/UARTBuddy/releases) 下载:
- `UARTBuddy-0.5.0-arm64.dmg`:macOS 安装镜像
- `UARTBuddy-0.5.0-arm64.zip`:可直接解压的应用包
打开 DMG,把 `UARTBuddy.app` 拖入“应用程序”,然后启动。
> 当前预览版使用 ad-hoc 签名,尚未完成 Apple Developer ID 签名和公证。首次运行时,
> macOS 可能要求在“系统设置 → 隐私与安全性”中确认打开。你也可以选择从源码构建。
### 连接串口
1. 选择 `/dev/cu.*` 设备。
2. 设置波特率、数据位、校验位、停止位和流控制。
3. 点击“连接串口”。
4. 在底部输入文本或 HEX 数据并发送。
5. 点击“历史记录”可查看之前连接会话的完整内容。
## 连接 AI Agent
UARTBuddy 默认只在本机监听:
```text
http://127.0.0.1:8765/mcp
```
它不会监听局域网地址,也不提供公网服务。桌面程序需要保持运行。
### Codex
在 `~/.codex/config.toml` 中添加:
```toml
[mcp_servers.uart_buddy]
url = "http://127.0.0.1:8765/mcp"
required = false
default_tools_approval_mode = "writes"
startup_timeout_sec = 3
tool_timeout_sec = 30
```
保存后重启 Codex。也可以在 Codex 的 MCP 设置页面添加 Streamable HTTP 服务,
名称填写 `uart_buddy`。
### Claude Code
```bash
claude mcp add --transport http --scope user uart_buddy http://127.0.0.1:8765/mcp
```
检查连接:
```bash
claude mcp get uart_buddy
```
### 可以怎样使用
```text
列出当前可用串口。
连接 /dev/cu.usbmodem1301,波特率 115200。
读取最近 200 条设备日志,找出反复出现的 I2C 错误。
向设备发送 help 并追加 LF。
扫描本机烧录环境。
使用 esptool 把 firmware.bin 烧录到当前 ESP32。
```
## MCP 工具
| 工具 | 类型 | 作用 |
| --- | --- | --- |
| `serial_list_ports` | 只读 | 列出本机串口 |
| `serial_status` | 只读 | 查询连接参数和日志游标 |
| `serial_read_logs` | 只读 | 增量读取 RX、TX、MCP TX、系统和烧录日志 |
| `serial_connect` | 写操作 | 连接指定串口 |
| `serial_disconnect` | 写操作 | 断开当前串口 |
| `serial_send` | 写操作 | 发送 UTF-8 或 HEX 数据 |
| `serial_flash_scan` | 只读 | 扫描烧录工具环境 |
| `serial_flash_status` | 只读 | 查询烧录状态和完整工具输出 |
| `serial_flash` | 写操作 | 调度已发现的烧录适配器 |
| `serial_flash_cancel` | 写操作 | 取消烧录并恢复串口 |
| `serial_history_list` | 只读 | 列出持久化会话 |
| `serial_history_read` | 只读 | 分页读取完整历史 |
| `serial_history_delete` | 写操作 | 删除已结束的历史会话 |
`serial_read_logs` 不返回 `MCP CALL` / `MCP OK` 等调用元事件,避免 Agent 轮询日志时
不断读到自己的调用;这些事件仍会完整显示在 GUI 中。
## 烧录环境
UARTBuddy 不重新实现芯片烧录协议,而是调度已安装的工具:
| 适配器 | 默认工具 | 固件格式 |
| --- | --- | --- |
| ESP Rust | `espflash` | `.bin` |
| Espressif | `esptool` / `esptool.py` | `.bin` |
| STM32 UART Bootloader | `stm32flash` | `.bin`, `.hex` |
环境扫描会合并登录 Shell 环境,并检查:
- `PATH`、`~/.cargo/bin`、Homebrew 等常见目录
- `ESPFLASH_PATH`、`ESPTOOL_PATH`、`IDF_PATH`
- `STM32FLASH_PATH`
- `~/.espressif/python_env/*/bin`
- 以 `PATH`、`_HOME`、`_ROOT` 结尾或包含 `TOOLS` 的环境变量
用户配置默认位于:
```text
~/.uartbuddy/flash-providers.json
```
也可以通过 `UARTBUDDY_FLASH_CONFIG` 指定配置文件。适配器只定义可执行文件候选、
参数模板和允许的固件格式;MCP 只能选择已配置并成功扫描到的适配器,不能传入任意
可执行文件。
示例:
```json
{
"providers": [
{
"id": "my-uart-flasher",
"label": "自定义 UART 烧录器",
"executable_candidates": ["my-flasher", "${MY_SDK_HOME}/bin/my-flasher"],
"environment_variables": ["MY_FLASHER_PATH", "MY_SDK_HOME"],
"arguments": ["--port", "{port}", "--baud", "{baud}", "--write", "{firmware}"],
"firmware_extensions": [".bin"],
"default_baud": 115200
}
]
}
```
支持 `{port}`、`{firmware}`、`{address}` 和 `{baud}` 参数占位符。命令通过参数数组
直接启动,不经过 Shell。
## 历史记录
每次连接真实串口都会创建独立会话。日志以 JSONL 追加写入:
```text
~/Library/Application Support/UARTBuddy/history/
```
实时终端只保留最近 5,000 条用于流畅渲染;磁盘历史不受此限制。MCP 单次最多读取
5,000 条,可使用 `next_after` 继续分页。
## 安全边界
- MCP 仅绑定 `127.0.0.1:8765`。
- UARTBuddy 是真实串口的唯一拥有者。
- 烧录只能调用配置白名单中已发现的工具。
- 烧录命令不经过 Shell,参数不会被二次解释。
- 删除历史属于显式 MCP 写操作。
- 不要通过端口转发把 MCP 服务直接暴露到公网。
## 从源码构建
需要 Rust 工具链和 macOS 12 或更高版本。
运行开发版:
```bash
cd rust_app
cargo run --release
```
检查代码:
```bash
cd rust_app
cargo fmt --check
cargo test
cargo clippy -- -D warnings
```
构建独立应用和 DMG:
```bash
./scripts/build_macos.sh
```
输出:
```text
dist/
├── UARTBuddy.app
└── UARTBuddy-0.5.0-arm64.dmg
```
## 项目结构
```text
rust_app/
├── assets/uartbuddy-icon.png
└── src/
├── main.rs # 原生应用入口
├── app.rs # egui 界面
├── broker.rs # 串口唯一所有者与事件流
├── history.rs # JSONL 持久历史
├── flash.rs # 烧录适配器与环境扫描
├── mcp.rs # Streamable HTTP MCP
└── model.rs
packaging/ # macOS App、图标和分发资源
scripts/ # 构建脚本
```
## 当前限制
- 发布包目前只有 Apple Silicon 版本。
- 尚未使用 Apple Developer ID 签名和公证。
- UARTBuddy 运行时,其他串口助手不能再直接打开同一个真实串口。
- 当前没有日志全文搜索和文件导出。
## Roadmap
- 日志搜索、过滤和导出
- 历史会话筛选与批量管理
- 更多经过验证的串口烧录适配器
- Universal 2 / Intel Mac 构建
- Developer ID 签名与 Apple 公证
## 贡献
欢迎提交 Issue 和 Pull Request。提交代码前请运行:
```bash
cd rust_app
cargo fmt --check
cargo test
cargo clippy -- -D warnings
```
涉及真实硬件的改动,请在 PR 中说明设备型号、串口参数、烧录工具版本和验证结果。
## License
[MIT](LICENSE)