PlatformIO MCP
Build, flash, and debug embedded firmware for any PlatformIO board (ESP32, Arduino, STM32, RP2040): parsed compiler errors, flash-then-verify against the serial boot log, crash backtrace decoding to file:line, firmware size reports, unit tests, static analysis. Python, uvx, no Node.
Documentation
platformio.mcp
Give your AI coding agent hands on real hardware.
An MCP server for PlatformIO: build, flash, watch serial, run tests, decode crashes, 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.
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 29 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_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 |
| 📟 Serial | pio_monitor_start / read / write / stop / list · pio_monitor_capture | Background sessions with a ring buffer, cursor reads, and wait_for regex; or a one-shot capture with nothing to manage |
| ✅ 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 | Registry search and dependency changes that keep platformio.ini in sync |
| 🧠 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 |
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 three 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 |
🔒 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 and size budgeting built in.
License
MIT