PlatformIO MCP

Erstelle, flashe und debugge eingebettete Firmware für jedes PlatformIO-Board (ESP32, Arduino, STM32, RP2040): geparste Compiler-Fehler, Flash-dann-verifizieren gegen das serielle Boot-Log, Crash-Backtrace-Dekodierung zu Datei:Zeile, Firmware-Größenberichte, Unit-Tests, statische Analyse. Python, uvx, kein Node.

Dokumentation

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.

CI PyPI Python 3.12+ Tools MIT

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 view env and make sure it boots.

Agentpio_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 before begin(). 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

GroupToolsWhat the agent gets back
🔍 Discoverpio_system_info · pio_list_boards · pio_board_info · pio_list_devicesPlatformIO version and policy; ~1,700 boards with MCU, clock, RAM and flash sizes; serial ports with the likely dev boards flagged
📁 Projectpio_project_init · pio_project_envs · pio_project_metadataA real pio project init (never a hand-written ini); every env with board, framework, monitor and upload settings; defines and include paths
🔨 Build & flashpio_build · pio_upload · pio_clean · pio_list_targets · pio_run_targetStatus, parsed errors and warnings (file, line, column), RAM/Flash %, last 40 lines, full log path. Extra targets like buildfs, erase
📟 Serialpio_monitor_start / read / write / stop / list · pio_monitor_captureBackground sessions with a ring buffer, cursor reads, and wait_for regex; or a one-shot capture with nothing to manage
Verifypio_test · pio_checkUnity tests with per-case pass/fail and messages; cppcheck / clang-tidy defects by severity with CWE ids
📦 Packagespio_pkg_search / install / uninstall / list / outdated / updateRegistry search and dependency changes that keep platformio.ini in sync
🧠 Analysepio_flash_and_verify · pio_decode_backtrace · pio_size_reportHardware-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 doesUnder the hood
🚀 pio_flash_and_verifyFlash, 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_backtraceTurn an ESP32 Backtrace: 0x400d... dump or a Cortex-M pc/lr dump into function, file, line, inlined frames, cause, reset reasonToolchain located from pio project metadata, then <target>-addr2line -pfiaC on firmware.elf; fixes Xtensa A0 window bits
📊 pio_size_reportWhy is the firmware this big? Flash/RAM %, loaded sections, biggest symbols with file:line, per-file totals, regex filterpio 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:

PolicyCan buildCan flash / erase / write serialUse it for
full (default)Your own bench
build_onlyShared labs, CI, "look but don't touch"
read_onlyCode review, onboarding, untrusted prompts

MCP clients also prompt before each tool call. Policies are the second layer, not the only one.

⚙️ Settings

VariablePurposeDefault
PLATFORMIO_MCP_POLICYfull, build_only, read_onlyfull
PLATFORMIO_MCP_PROJECT_DIRProject used when a tool is called without project_dirserver's cwd
PLATFORMIO_MCP_PIOExplicit path to the pio executableauto-detect
PLATFORMIO_MCP_LOG_DIRWhere full command logs go~/.platformio-mcp/logs
PLATFORMIO_MCP_MAX_LOGSHow many log files to keep200

📝 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