MCP server that gives AI coding assistants a closed edit-build-flash-observe loop on a real ESP32 device.
Strong tool naming (verb-first, clear intent) and comprehensive parameter schemas with types and descriptions. All 11 tools have detailed docstrings explaining purpose, behavior, and prerequisites. Output structures are well-documented (e.g., list_ports returns ports array + autodetect field). However, error handling lacks recovery guidance, most tools return {ok: False, error: string} without suggesting next steps. No input validation constraints (enums, ranges) visible in schemas. Parameter descriptions are good but could be more prescriptive about constraints (e.g., baud rate limits, regex pattern rules for grep_log). Missing tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite clear risk levels assigned in metadata.
Run `idf.py build` in the project directory. Returns command, returncode, stdout, stderr. Non-zero returncode means the build failed — the error is in stderr. Requires ESP-IDF activated in the shell that launched this MCP server (export.sh / export.ps1 / EIM).
Run `idf.py clean` (removes build artefacts; keeps sdkconfig).
Stop the running serial monitor and release the port. Safe to call when no monitor is running — returns ok=True either way. The log file is left in place; subsequent start_monitor calls append unless you pass truncate=True.
Flash the firmware to the device. If a serial monitor is running, it's automatically asked to release the port before esptool takes it, then resumed afterward — even if the flash fails. If `port` is omitted, idf.py autodetects (works when exactly one ESP-class board is connected). `target` is rarely needed; `idf.py flash` picks the right partition table from your build output.
Search the serial log for `pattern` (Python regex), return matches. `lines` caps the returned match count. `last_n_only` (if given) restricts the search to the last N lines of the file — useful for "did the heartbeat print in the last 30 seconds" style queries.
Error responses lack recovery guidance. Tools return {ok: False, error: string} without suggesting next steps (e.g., 'no port specified and autodetect didn't find a single ESP32. Call list_ports to inspect' is good, but most errors are bare). LLMs cannot self-correct without actionable guidance.
No input validation constraints visible in schemas. Parameters like 'baud' (integer), 'lines' (integer), 'pattern' (regex) lack min/max bounds or format rules. Schemas should declare baud ∈ [300, 921600], lines ∈ [1, 5000], pattern as regex pattern.
Tool annotations missing. Despite clear risk levels (READ_ONLY, WRITE, IRREVERSIBLE, REVERSIBLE) in metadata, tools lack MCP readOnlyHint, destructiveHint, and idempotentHint annotations. These guide agent planning and safety.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
List USB serial ports the OS can see, with ESP32 likelihood marked. Returns a dict with `ports` (list) and `autodetect` (the port that would be used if a flash/monitor tool were called without an explicit port). Each port entry includes device, description, vid, pid, serial_number, manufacturer, and is_known_esp.
Inspect the running monitor: port, baud, log path, uptime, last error.
Reboot the chip via an RTS pulse, without reflashing. Coordinates with the running monitor (if any) so the port is released for the duration of the pulse and reacquired automatically afterward. Use this when the firmware has wedged and you want a clean boot banner.
Set the chip target for this project (e.g. esp32, esp32s3, esp32c6). Equivalent to `idf.py set-target <target>`. Resets the build directory.
Start the background serial monitor. Reads from the given USB-CDC port at `baud`, appends each line to a log file (default: <project>/logs/serial.log), and auto-reconnects when the port goes away (e.g. while flashing). The monitor pulses RTS on every connect to reset the chip and capture the boot banner; pass reset_on_connect=False to attach without disturbing a running device. `truncate=True` clears the log file before starting. If `port` is omitted, the server tries to autodetect a single ESP32-class device. Idempotent-ish: if a monitor is already running, this returns an error. Call close_monitor first if you want to switch ports or settings.
Return the last `lines` of the serial log as a single string. Replaces shell `tail -n N`. Works whether or not the monitor is currently running — the log file persists. `log_path` defaults to the active monitor's log file, or <project>/logs/serial.log if no monitor is running.
Irreversible operations (flash, set_target) lack dry-run or confirmation step. Agents can accidentally flash wrong firmware or set wrong target without a safety gate.
Parameter descriptions could be more prescriptive. E.g., 'port' parameter says 'USB-CDC port (optional; autodetected if omitted)' but doesn't explain format (/dev/ttyUSB0, COM3, etc.) or how autodetection works. 'pattern' in grep_log says 'Python regex pattern' but doesn't warn about performance on large files.