A multi-device embedded debugging workspace shared by humans and AI agents.
English | 简体中文
UARTBuddy owns physical serial ports once, then lets the GUI and local MCP clients share the same connections, event stream, history, flashing jobs, and USB camera. Version 1.0 is designed around multiple embedded devices: every device has an independent actor, connection, configuration, session, counters, and flash reservation.
Current version:
v1.0.0· Apple Silicon · macOS 12+
Serial monitors, flashing tools, and AI agents cannot reliably open the same /dev/cu.* endpoint
at the same time. Competing readers lose bytes and competing writers interleave commands.
UARTBuddy is the single owner and exposes deliberate operations to every participant.
Real systems have controllers, sensors, motor drivers, test fixtures, and multiple prototypes. UARTBuddy keeps them connected concurrently. Selecting All devices produces a merged, color-coded event stream; selecting one device gives focused controls and logs. Normal UI sends always target one device. Multi-device writes require an explicit MCP call.
Dumping a terminal buffer into an agent is slow and can overflow its context. UARTBuddy stores
events in SQLite and exposes a global monotonic event_id. Reads apply device/type/session filters
inside SQLite before a strict limit. events_wait blocks until matching events arrive, so an agent
does not need a polling loop.
UARTBuddy does not reimplement ESP32, STM32, or other vendor protocols. It discovers installed
tools such as espflash, esptool, and stm32flash. A flash job reserves and releases only its
target device, streams progress as events, supports cancellation and completion waiting, then
restores that device's previous connection. Every other serial actor stays online. Each started
flash is also recorded in SQLite with its time, absolute firmware path, byte size, MD5 fingerprint,
version metadata, provider, target device, and final state.
The shared camera workspace gives both the GUI and MCP clients controlled photo and recording operations while serial devices continue running.
flowchart LR
D1["Device A"] <--> A1["Serial actor A"]
D2["Device B"] <--> A2["Serial actor B"]
DN["Device N"] <--> AN["Serial actor N"]
A1 & A2 & AN --> E["Global event stream"]
E --> DB["SQLite WAL store"]
E <--> GUI["egui workspace"]
E <--> MCP["Stateless MCP :8765"]
MCP <--> Agent["Codex / Claude Code"]
MCP --> Jobs["Flash job registry"]
Jobs -. "reserve target only" .-> A1
Camera["USB camera"] <--> GUI
Camera <--> MCP
- One worker actor owns each physical serial handle.
- USB serial numbers provide stable endpoint identities across path changes.
- Auto-reconnect resolves the current path from the stable identity.
- Connection, RX/TX, MCP calls, flashing, errors, and completion are all events.
- SQLite WAL provides durable history while the GUI and agents read concurrently.
- The MCP service is localhost-only and starts with UARTBuddy.
- Concurrent serial connections with native Apple Color Emoji icons, aliases, configuration, and state
- Combined or focused event views with device badges, text/HEX display, and strict send targets
- Persistent SQLite sessions containing RX, TX, system, MCP, and flash events
- In-page history browser instead of a blocking modal
- UTF-8, HEX, and Base64 MCP writes; explicit multi-write and request/response exchange
- Strict cursor pagination and long event waits
- Background flash jobs with progress, completion events, wait, and cancellation
- Durable flash history with firmware path, size, MD5, version source, and final state
- Target-only serial release during flashing
espflash,esptool,stm32flash, and custom provider discovery from the environment- USB camera discovery, live preview, JPEG capture, and MP4 recording
- English and Simplified Chinese UI selected from macOS; English is the fallback
Download the latest .dmg or .zip from
GitHub Releases.
The package is currently built for Apple Silicon and ad-hoc signed. Video features require
ffmpeg; serial and MCP serial features do not:
brew install ffmpegUARTBuddy scans FFMPEG_PATH, the login-shell environment, Homebrew, MacPorts, and common paths.
- Open UARTBuddy and click + in the device rail.
- Choose a scanned endpoint, give it a meaningful alias, and set its serial parameters.
- Add every device used by the project.
- Connect devices independently, or enable auto-reconnect.
- Select All devices for a combined stream or one device for focused sending and history.
The compact device rail is Emoji-only, with Add at the top. Default icons are randomly assigned without repetition while the built-in pool has unused choices. Selection and connection/flashing state remain visible through each icon border.
The device profile stores a stable identity when the USB adapter exposes a serial number. A
renamed /dev/cu.* path can therefore be rediscovered after unplugging and reconnecting it.
UARTBuddy starts a stateless Streamable HTTP endpoint by default:
http://127.0.0.1:8765/mcp
Add this to ~/.codex/config.toml, then restart Codex:
[mcp_servers.uartbuddy]
url = "http://127.0.0.1:8765/mcp"
required = false
default_tools_approval_mode = "writes"
startup_timeout_sec = 3
tool_timeout_sec = 125claude mcp add --transport http --scope user uartbuddy http://127.0.0.1:8765/mcp
claude mcp get uartbuddyUARTBuddy exposes 30 tools. All mutating serial and flashing operations require an explicit
device_id; there is no implicit "current port" in MCP v1.
| Area | Tools |
|---|---|
| Device lifecycle | endpoints_scan, devices_list, device_attach, device_update, device_remove, device_connect, device_disconnect |
| Serial data | serial_write, serial_write_many, serial_exchange, serial_set_signals |
| Events and history | events_read, events_wait, sessions_list, session_read |
| Flash jobs | flashers_list, flash_start, flash_history_list, firmware_library_list, firmware_reflash, jobs_get, jobs_wait, jobs_cancel |
| Video | video_list_devices, video_status, video_connect, video_disconnect, video_capture, video_record_start, video_record_stop |
- Call
devices_listand retainlatest_event_id. - Perform a targeted operation.
- Call
events_waitwithafter_event_id, device/type filters, and a timeout. - Continue with the returned
next_event_id.
limit is enforced after SQL filters and is capped at 2,000 events. The response always includes
next_event_id and latest_event_id.
serial_exchange writes to one device and collects serial.rx events until one of these occurs:
- the optional
untiltext appears; - data has been idle for
idle_ms; or timeout_msexpires.
- Call
flashers_list. - Call
flash_startwithdevice_id,provider_id, and an absolute firmware path. - Call
jobs_waitonce, or watchflash.progressandflash.completed/flash.failedevents withevents_wait.
flash_start accepts an optional authoritative version. Without it, UARTBuddy first reads an
ESP-IDF application descriptor, then checks firmware.bin.json/firmware.json, and finally tries a
version-like filename suffix. Unknown versions remain empty instead of being guessed. Use
flash_history_list to retrieve the durable records, optionally filtered by device_id.
For recognized ESP-IDF application images, UARTBuddy also records the ESP-IDF version, target chip, image-declared Flash capacity, project name, build time, Secure Version, Flash mode/frequency, and ELF SHA-256. The declared capacity comes from the image header and is not presented as a physical probe of the connected chip.
Before a flash starts, UARTBuddy copies the exact image into its content-addressed firmware library.
Images are deduplicated by MD5 and remain available if the original source is moved or deleted. The
Firmware tab can flash the latest device/tool configuration again with one click; MCP clients can
use firmware_library_list and the explicit-target firmware_reflash tool.
~/Library/Application Support/UARTBuddy/
├── uartbuddy-v1.sqlite3
├── uartbuddy-v1.sqlite3-wal
├── uartbuddy-v1.sqlite3-shm
├── uartbuddy.lock
└── video/
├── photos/
└── recordings/
Version 1.0 intentionally does not import the pre-1.0 JSONL broker history. It uses a new device and event model with a separate SQLite database.
UARTBuddy scans the login-shell environment and common SDK locations. Additional providers can be
defined in ~/.uartbuddy/flash-providers.json or paths listed by UARTBUDDY_FLASH_CONFIG.
UARTBuddy invokes executables directly without a shell and substitutes {port}, {baud},
{firmware}, and {address} in the declared argument list.
Requirements: Rust stable, macOS 12+, and optionally ffmpeg.
git clone https://github.com/llinzzi/UARTBuddy.git
cd UARTBuddy
cargo run --manifest-path rust_app/Cargo.tomlBuild the signed local app bundle, DMG, and ZIP:
./scripts/build_macos.shArtifacts are written to dist/ and derive their version from rust_app/Cargo.toml.
cd rust_app
cargo fmt --check
cargo test
cargo clippy -- -D warnings- Release packages target Apple Silicon and are not notarized yet.
- Camera preview is fixed at 640×480; recordings do not include audio.
- Full-text event search and export are not implemented yet.
- UARTBuddy must remain the sole OS-level owner of every attached serial endpoint.
Issues and pull requests are welcome. Hardware reports should include the board or adapter model, stable USB identity if available, serial parameters, relevant event IDs, and reproduction steps.


