Skip to main content
Glama
README.md
# mcp-serial

An [MCP](https://modelcontextprotocol.io) server that lets Claude, Cursor, or any MCP client work with your microcontroller the way you do at a serial console: list ports, open a session, send commands, read and filter the output, and flash firmware to STM32 or ESP32 boards. Flashing is gated behind an explicit `confirm=true` and an allow-listed firmware directory, so the assistant can't overwrite a board by accident.

```
you:    "Flash build/app.bin to the Nucleo, then tell me if the sensor init logs any errors."
claude: flash_stm32(firmware_path="build/app.bin", confirm=true)      -> flashed, reset
        open_port("/dev/cu.usbmodem1103", 115200)
        read_lines(port=..., grep="error|fail", wait_seconds=5)        -> 0 matches
        "Flashed and reset. 1,214 lines in 5 s, no errors; IMU and BMP280 both report OK."
```

## Install

```bash
uv tool install mcp-serial          # or: pipx install mcp-serial
```

Claude Code:

```bash
claude mcp add mcp-serial -e MCP_SERIAL_ALLOWED_DIR=$PWD -- mcp-serial
```

Claude Desktop / other clients (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "mcp-serial": {
      "command": "mcp-serial",
      "env": { "MCP_SERIAL_ALLOWED_DIR": "/path/to/your/firmware/project" }
    }
  }
}
```

Flashing uses whatever you already have installed: `st-flash`/`st-info` (stlink) for STM32, `esptool` for ESP32.

## Tools

| Tool | What it does |
|---|---|
| `list_ports()` | Serial ports with description, hwid, VID/PID, and whether a session is open |
| `open_port(port, baud=115200)` | Open a port (or a pyserial URL like `loop://`) and capture lines in the background |
| `close_port(port)` | Stop capturing and release the port |
| `write_line(port, text)` | Send a command to the firmware's CLI |
| `read_lines(port, max_lines, after_seq, grep, wait_seconds)` | Page through captured output with sequence numbers, a regex filter, and an optional short wait for new data |
| `clear_buffer(port)` | Drop the capture buffer |
| `flash_stm32(firmware_path, address="0x08000000", confirm)` | `st-flash --reset write` |
| `flash_esp32(firmware_path, port, address="0x10000", chip="auto", baud, confirm)` | `esptool write_flash`; closes the port session first |
| `reset_stm32()` | `st-flash reset` |
| `probe_stlink()` | `st-info --probe` |

## Safety model

- Flash tools refuse unless `confirm=true` is passed, so the client has to state the intent explicitly.
- Firmware paths must resolve inside `MCP_SERIAL_ALLOWED_DIR` (default: the server's working directory).
- External tools run with argument lists, never through a shell; addresses and chip names are validated.
- Output is captured in a bounded ring buffer (`MCP_SERIAL_MAX_LINES`, default 10,000) so a chatty board can't exhaust memory.

## Environment

| Variable | Default | Meaning |
|---|---|---|
| `MCP_SERIAL_ALLOWED_DIR` | cwd | Directory firmware files must live under |
| `MCP_SERIAL_BAUD` | `115200` | Default baud for `open_port` |
| `MCP_SERIAL_MAX_LINES` | `10000` | Capture buffer size per port |

## Development

```bash
uv venv && uv pip install -e ".[dev]" && pytest -q
```

The tests use pyserial's `loop://` loopback, so they run without hardware.

## License

MIT © Fady Youssef

TDQS

A3.7/5.0

Scored across 10 tools

Disambiguation4/5

Each tool maps to a distinct resource+action: port discovery (list_ports), ST-Link probing (probe_stlink), session control (open/close_port), I/O (write_line/read_lines/clear_buffer), and flashing/reset. The only minor overlap is reset_stm32 versus flash_stm32, which also resets the target, but descriptions keep them separable.

Naming Consistency4/5

All names are snake_case with a leading verb (list_ports, open_port, read_lines, flash_esp32, reset_stm32), a consistent verb_noun pattern. The nouns shift between resources (port, buffer) and platform targets (stlink, stm32, esp32), which is a slight deviation but still predictable.

Tool Count5/5

Ten tools is well within the ideal 3-15 range and each earns its place: discovery, session lifecycle, textual I/O, buffer management, and per-platform flashing/reset. No redundant or filler tools.

Completeness4/5

The surface covers the full serial workflow (list, open, read, write, clear, close) plus ST-Link/STM32/ESP32 flashing and STM32 probing/reset. Minor asymmetry: there is a flash_esp32 and reset_stm32 but no reset_esp32, and no way to send bytes without a newline.

Maintenance

ActivityMaintained
ResponsivenessNo issues