README.md
September 18, 2026 · View on GitHub
platformio.mcp
Give your AI coding agent hands on real hardware.
An MCP server for PlatformIO: build, flash (serial or OTA), watch serial, run tests, decode crashes and core dumps, check partition tables, watch heap and power, debug over GDB, shrink firmware.
Python native · no Node · one line to install · works with Claude Code, Claude Desktop, Cursor, Codex, Windsurf, Cline
⚡ 60-second install
You need uv (curl -LsSf https://astral.sh/uv/install.sh | sh). Then:
uvx platformio.mcp install --claude-code # or --cursor --claude-desktop --codex --windsurf
No PlatformIO on this machine? Add --with-platformio and the server brings PlatformIO Core along. Optional extras: platformio.mcp[coredump] adds the ESP32 core-dump analyzer, platformio.mcp[power] adds the Nordic PPK2 driver.
Any other MCP client (Cline, Zed, Continue, ...)
uvx platformio.mcp install --print
{ "mcpServers": { "platformio": { "command": "uvx", "args": ["platformio.mcp"] } } }
Use "args": ["platformio.mcp[platformio]"] to bundle PlatformIO Core.
As a plugin (Claude Code, Cursor: server + a skill that teaches the loop)
The repo follows the Open Plugins layout: .mcp.json, skills/platformio/SKILL.md, rules/platformio.mdc, plugin.json.
claude plugin marketplace add powerdragonfire/platformio.mcp # Claude Code
claude plugin install platformio@platformio.mcp
Already have PlatformIO?
The server finds platformio / pio on your PATH or in ~/.platformio/penv. Override with PLATFORMIO_MCP_PIO=/path/to/pio. Run uvx platformio.mcp doctor to see what the agent will see.
🎬 What it looks like
You: flash the
viewenv and make sure it boots.Agent →
pio_flash_and_verify(env="view", expect="setup done")FAIL: firmware flashed but the boot log matched the failure pattern: 'Guru Meditation Error: Core 1 panic'ed (LoadProhibited)'. Cause: LoadProhibited. 3/3 addresses resolved: DisplayTask::run() (display_task.cpp:22) <- DisplayTask::run() (display_task.cpp:23) <- vPortTaskWrapper (port.c:143)Agent: null pointer on line 22 of
display_task.cpp,tft_is used beforebegin(). Fixing, rebuilding, flashing again.PASS: flashed env view in 14.2s and saw 'setup done' on /dev/cu.usbserial-0001 after 2.1s of boot output.
No 40 KB build logs in the context window. No human reading the serial monitor. The agent gets a verdict, a file and a line.
🔁 The loop the agent runs
flowchart LR
A[pio_project_envs] --> B[edit code]
B --> C[pio_build]
C -- errors with file:line --> B
C -- ok --> D[pio_flash_and_verify]
D -- PASS --> E([done])
D -- FAIL: decoded backtrace --> B
D -- TIMEOUT --> F[pio_monitor_capture]
F --> B
🧰 The 40 tools
| Group | Tools | What the agent gets back |
|---|---|---|
| 🔍 Discover | pio_system_info · pio_list_boards · pio_board_info · pio_list_devices |
PlatformIO version and policy; ~1,700 boards with MCU, clock, RAM and flash sizes; serial ports with the likely dev boards flagged |
| 📁 Project | pio_project_init · pio_project_envs · pio_project_metadata |
A real pio project init (never a hand-written ini); every env with board, framework, monitor and upload settings; defines and include paths |
| 🔨 Build & flash | pio_build · pio_upload · pio_upload_ota · pio_clean · pio_list_targets · pio_run_target |
Status, parsed errors and warnings (file, line, column), RAM/Flash %, last 40 lines, full log path. Extra targets like buildfs, erase. OTA over Wi-Fi to ArduinoOTA boards. Port failures come back classified (busy, permission, missing, no response) with the fix |
| 📟 Serial | pio_monitor_start / read / write / stop / list · pio_monitor_capture · pio_port_diagnose |
Background sessions with a ring buffer, cursor reads, and wait_for regex; or a one-shot capture with nothing to manage. Port diagnosis: who holds it (our session, another process), permissions, the fix |
| ✅ Verify | pio_test · pio_check |
Unity tests with per-case pass/fail and messages; cppcheck / clang-tidy defects by severity with CWE ids |
| 📦 Packages | pio_pkg_search / install / uninstall / list / outdated / update · pio_deps_check |
Registry search and dependency changes that keep platformio.ini in sync; an audit for name collisions, unpinned specs, leftovers, and circular dependencies |
| 🧠 Analyse | pio_flash_and_verify · pio_decode_backtrace · pio_size_report |
Hardware-in-the-loop pass/fail; crash dumps resolved to file:line; where every byte of flash and RAM goes |
| 💾 Flash layout | pio_partition_table · pio_coredump |
ESP32 partition CSV checks (alignment, overlap, fit, OTA slots) and a diff against the table actually on the chip; core dump pulled from flash and decoded |
| 📈 Runtime | pio_memory_watch · pio_power_profile |
Heap and stack telemetry parsed from serial with a leak verdict and per-task headroom; current draw from a serial meter or a Nordic PPK2 with sleep/active split and battery estimate |
| 🐞 Debug | pio_debug_start / cmd / stop / list |
A live GDB session over pio debug: breakpoints, step, backtrace, variables, with MI records parsed into structured results |
Every tool returns ok, a one-paragraph summary written for the model, structured fields, and a log_path to the full output. Long output stays on disk under ~/.platformio-mcp/logs (newest 200 files kept).
The tools that go beyond the CLI
| What it does | Under the hood | |
|---|---|---|
🚀 pio_flash_and_verify | Flash, open the port, read until expect matches (pass), a crash signature matches (fail, auto-decoded), or the timeout passes (timeout) | pio run -t upload + pyserial; fail_on defaults to Guru Meditation, HardFault, abort(), assert failed, watchdog, brownout, heap corruption |
🩺 pio_decode_backtrace | Turn an ESP32 Backtrace: 0x400d... dump or a Cortex-M pc/lr dump into function, file, line, inlined frames, cause, reset reason | Toolchain located from pio project metadata, then <target>-addr2line -pfiaC on firmware.elf; fixes Xtensa A0 window bits |
📊 pio_size_report | Why is the firmware this big? Flash/RAM %, loaded sections, biggest symbols with file:line, per-file totals, regex filter | pio run -t checkprogsize (partition-aware) + GNU size -A + nm -S -C -l --size-sort |
💾 pio_partition_table | Catch the silent ESP32 corruption where an app-only flash leaves an old partition table on the chip; alignment, overlap, OTA slot, and app-fit checks | Parses the env's partition CSV; read_device=true reads 0x8000 with esptool read_flash and diffs |
🧯 pio_coredump | Pull the core dump from the coredump partition after a crash and decode task, registers, and backtrace | esptool read_flash + optional esp-coredump info_corefile (platformio.mcp[coredump]) |
📈 pio_memory_watch | Leak, fragmentation, and stack-headroom verdicts from what the firmware already prints | Parses Free heap:, heap_caps_print_heap_info, vTaskList, uxTaskGetStackHighWaterMark lines; least-squares slope |
🔋 pio_power_profile | Average/min/max/p95 current, sleep vs active split, energy, battery-life estimate | A serial meter (INA219 sketch, USB meter log) or a Nordic PPK2 (platformio.mcp[power]) |
🐞 pio_debug_* | Breakpoints, step, backtrace, and variable inspection through the debug probe | pio debug --interface=gdb driven over GDB/MI with parsed *stopped events |
🌐 pio_upload_ota | Flash over Wi-Fi with failures mapped to the fix (wrong password, no ArduinoOTA.handle(), firewall, no OTA slot) | pio run -t upload --upload-port <ip> (espota auto-switch) or espota.py directly |
🔌 pio_port_diagnose | Why the upload cannot open the port: our session, another process, permissions, or a board not in bootloader mode | lsof/fuser + pio device list; never kills anything |
📚 pio_deps_check | Library name collisions where lib_deps order silently picks the winner, unpinned specs, leftovers, cycles | Manifests in .pio/libdeps and lib/, plus the LDF dependency graph with build=true |
🔒 Safety policy
Set PLATFORMIO_MCP_POLICY in the server's env, or pass --policy to install:
| Policy | Can build | Can flash / erase / write serial | Use it for |
|---|---|---|---|
full (default) | ✅ | ✅ | Your own bench |
build_only | ✅ | ❌ | Shared labs, CI, "look but don't touch" |
read_only | ❌ | ❌ | Code review, onboarding, untrusted prompts |
MCP clients also prompt before each tool call. Policies are the second layer, not the only one.
⚙️ Settings
| Variable | Purpose | Default |
|---|---|---|
PLATFORMIO_MCP_POLICY | full, build_only, read_only | full |
PLATFORMIO_MCP_PROJECT_DIR | Project used when a tool is called without project_dir | server's cwd |
PLATFORMIO_MCP_PIO | Explicit path to the pio executable | auto-detect |
PLATFORMIO_MCP_LOG_DIR | Where full command logs go | ~/.platformio-mcp/logs |
PLATFORMIO_MCP_MAX_LOGS | How many log files to keep | 200 |
📝 Serial monitor notes
Sessions talk to the port with pyserial directly, because PlatformIO's own monitor needs an interactive terminal. PlatformIO monitor filters such as esp32_exception_decoder therefore do not apply; pio_decode_backtrace does that job. Baud and port default from monitor_speed / monitor_port in platformio.ini when project_dir is passed, otherwise the single detected dev board at 115200. Opening the port resets most dev boards, which is why pio_flash_and_verify sees the boot log from the top.
🛠️ Development
git clone https://github.com/powerdragonfire/platformio.mcp && cd platformio.mcp
uv sync
uv run pytest # unit tests, no hardware or network
uv run pytest -m integration # builds the bundled native fixture with your PlatformIO
uv run platformio-mcp doctor # what the agent's pio_system_info sees
npx @modelcontextprotocol/inspector uv run platformio-mcp # poke tools interactively
To use your checkout in Claude Code instead of the PyPI release:
claude mcp add platformio -- uv run --directory /path/to/platformio.mcp platformio-mcp
Changes are tracked in CHANGELOG.md.
🤝 Contributing
Bug reports from real boards are the most useful thing you can send. Use the issue forms, ask questions in Discussions, and read CONTRIBUTING.md before opening a PR. Issues tagged good first issue are scoped for newcomers.
🔭 Prior art
jl-codes/platformio-mcp is a TypeScript server with the same goal, a web dashboard, and a GPIO pin audit. This project exists for people who want a Python-only install through uvx, one that can bundle PlatformIO itself, and crash decoding, size budgeting, partition checks, core dumps, OTA, live GDB, and memory/power profiling built in.
License
MIT