npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcuport

v0.2.0

Published

MCUPort desktop MCP server: enumerate, flash, build, monitor, and drive GPIO/ADC/PWM/I2C/SPI on microcontrollers over USB serial.

Readme

The MCP for microcontrollers (working name MCUPort), desktop server

An MCP (Model Context Protocol) server that gives Claude Code, Claude Desktop, Cursor, or any other MCP-speaking agent real hands on a real microcontroller over USB serial: find the board, flash it, build for it, watch its serial output, and read/write GPIO, ADC, PWM, I2C and SPI on it. Free tier: the full server, unlimited local boards, every supported family. Pro ($20 one-time) unlocks the phone bridge: the companion app relays a Bluetooth LE board's serial port to this server over your local network (family: "bridge"). The phone app is not in the stores yet, so Pro is not on sale yet; the product's /pricing page says when it is.

What is proven where (read this before trusting anything below)

Honesty first, because this customer reads READMEs. As of 2026-09-19:

| Tool group | Proven on | Never yet run on | |---|---|---| | list_boards, flash_firmware, serial_* | mocked serialport + the phone-bridge fixture; the ESP32 flash command line was handed to a real esptool v4.12.0 and v5.5.0 on 2026-10-13 (ghost port, so the tool answered "could not open port", not "invalid choice"), and the image classifier is calibrated against esptool image_info on the real MicroPython ESP32/S3/C3 v1.29.0 images | real silicon by the studio | | the phone bridge (family: "bridge"), desktop side through the app's own bridge server | test/bridge/relay_through_phone.test.ts: the pin, bus, board_info, mpy_exec and gpio_audit tools through src/boards/bridge.ts (real fetch, real licenseIsValid() against the license fixture) into apps/mcuport/src/bridge/server.ts on a loopback socket, whose BLE side is the real MicroPython interpreter; a wrong pairing token comes back as the app's 401 in the tool result | a real radio, a real phone OS, real silicon; the app has not been paired with a desktop over a real network | | gpio_*, adc_read, pwm_write, i2c_*, spi_transfer, mpy_exec, board_info (MicroPython path) | the real MicroPython interpreter (its WebAssembly build) speaking the real raw-REPL protocol, with only the machine module faked | a physical ESP32 or Pico | | the same tools on the Arduino bridge-sketch path | a TypeScript emulator of the sketch's line protocol | the sketch has not been compiled or run on a board | | gpio_audit and the pin-safety refusals in every pin tool | the tables against the vendors' pinouts (unit) and the refusals end to end on the simulated boards | a real board; no pin has ever been refused on silicon | | install_ble_repl + firmware/mcuport_ble_repl/mcuport_ble_repl.py | the chunked write, boot hook and the firmware's own Python (advertisement bytes, MTU-sized notifications, retry, re-advertise) on the real MicroPython interpreter with a fake bluetooth module | a real radio: no ESP32 or Pico W has advertised it, and no phone has connected to it | | board_reset | sequence data asserted against esptool's documented DTR/RTS order and the 1200-baud touch; applied to simulated ports | a real auto-reset circuit | | compile_sketch, build_project, install_bridge_sketch | fixture output shaped like arduino-cli --format json and pio run logs; the missing-tool path for real | an installed arduino-cli / PlatformIO |

No board was attached to the machine that built this, and neither CLI was installed: nothing in this package has been run on a real board by the studio. Everything above the line is "logic complete"; the first hardware run will find something, and it is owed before any public claim of hardware support. That run is one command: npm run hardware-smoke (scripts/hardware_smoke.ts) drives the built server over real stdio, finds the attached board, checks that pin safety refuses a flash pin on it and reads a real level from an allowed one, optionally installs the BLE REPL (--ble), and writes build/hardware_smoke.json with the board's USB id and every step's real result. With no board attached it exits 3 with verdict UNKNOWN, never a pass. On 2026-10-13 it ran on a Mac with no board: UNKNOWN, 2 ports without a USB id, nothing attached. --simulated runs the same steps against the simulator for calibration and is capped at SIMULATED; the claims registry refuses a "hardware" row that does not name this runner.

Tools

Board discovery and firmware:

  • list_boards: every ESP32 / RP2040 / Arduino board on USB by VID:PID, plus the phone bridge (Pro) and, with MCUPORT_SIMULATOR=1, the simulated boards. Every attached USB serial device whose id is NOT in the tables is listed too (unrecognized_usb_serial, each with the next step: board_info on that path), and the ports ignored for having no USB id are counted, so an empty boards list never hides a plugged-in board behind an unlisted bridge chip.
  • board_info: what a port is running (MicroPython version, or the bridge sketch) and its family; the first call when a tool fails.
  • flash_firmware: esptool / picotool (or UF2 copy) / arduino-cli. For the Pico the argv is picotool load -f -x <file> (the -f resets a board running MicroPython into BOOTSEL; without it picotool v2.3.1 only sees boards already in BOOTSEL); when picotool finds no board the server falls back to a mounted RPI-RP2/RP2350 drive and otherwise names the BOOTSEL step. For ESP32 the offset is read off the image header (src/boards/esp_image.ts: 0x1000 for a bootloader-first image on ESP32/S2, 0x0 on S3/C3/C6/H2, 0x10000 for an app image, 0x0 for a merge-bin output; flashOffset overrides; anything else is refused, not guessed) and the verb follows the installed esptool (v4 write_flash, v5 write-flash).
  • board_reset: run-mode reset or bootloader entry via DTR/RTS (ESP32) or the 1200-baud touch (RP2040, Arduino R4).
  • install_bridge_sketch: put the bridge sketch on an Arduino so the hardware tools work there.
  • install_ble_repl: put a MicroPython board's REPL on Bluetooth LE (Nordic UART Service) so the phone app can reach it and relay it to this server; writes firmware/mcuport_ble_repl/ to the board and hooks boot.py.

Serial monitor (always-on ring buffer, nothing lost between calls):

  • serial_open, serial_read (text + base64, droppedBytes, optional until regex), serial_write, serial_close.

Hardware I/O (MicroPython boards need no custom firmware; Arduino boards need the bridge sketch):

  • gpio_mode, gpio_read, gpio_write
  • adc_read (raw + estimated volts, optional averaging)
  • pwm_write
  • i2c_scan, i2c_read, i2c_write
  • spi_transfer
  • mpy_exec: run any MicroPython on the board and get its output.
  • gpio_audit: the pin-safety map for the board (blocked / needs force / warn / ok per pin, for a stated intent).

Pin safety is code, not advice (src/pin_safety.ts): every tool above that names a pin audits it against the chip's own table BEFORE any generated code reaches the board. SPI flash pins, pins that do not exist and output on an input-only pad are refused outright; the console UART the link runs over, native USB pads and a Pico W's wireless lines are refused unless the call passes force: true after reading the reason; strapping pins go through with the warning in the result. The chip is read from the board itself (MicroPython os.uname().machine, the bridge sketch's hello), falling back to the USB family; a chip with no table is reported as UNCHECKED, never as safe. Tables: ESP32, ESP32-S2/S3/C3/C6, Pico, Pico W, Pico 2, Uno R3, Uno R4.

Build:

  • compile_sketch: arduino-cli compile with structured diagnostics.
  • build_project: PlatformIO pio run (optionally upload) with structured diagnostics. PlatformIO knows STM32 and nRF52 targets, but this server does not enumerate those boards and has never built for one.

Every write tool says in its description that it changes physical state. An LLM driving GPIO can spin a motor or close a relay; wire accordingly.

How the hardware tools work (and why)

ESP32 and RP2040 boards running MicroPython expose a "raw REPL" over the same USB serial port: the server sends Ctrl-C, Ctrl-A, a few lines of Python, Ctrl-D, and reads back stdout / traceback framed by 0x04 bytes, exactly as mpremote does. That gives GPIO/ADC/PWM/I2C/SPI with no firmware of ours on the board; the only side effect is that a running main.py is interrupted. Arduino-class boards have no interpreter, so the server ships a small sketch (firmware/mcuport_bridge/) with a one-line request/reply protocol and a tool to upload it.

The phone bridge needs the board's REPL on the radio. install_ble_repl writes firmware/mcuport_ble_repl/mcuport_ble_repl.py to an ESP32-family board or a Pico W over USB and adds import mcuport_ble_repl to its boot.py; from then on the board advertises as MCUPort-XXXX on the Nordic UART Service and its REPL is on BLE as well as USB (os.dupterm). The phone app connects to it and relays the bytes; the desktop's bridge board module (family: "bridge") speaks the same raw REPL over that link, so the pin and bus tools work on the board through the phone. A plain Pico has no radio and an Arduino has no interpreter; the tool refuses both with that sentence.

Supported boards: ESP32 family, Raspberry Pi RP2040/RP2350 (Pico, Pico 2, Pico W; the MicroPython USB id 2E8A:0005 included), Arduino Uno R4 (Minima, WiFi), Nano R4 and Uno R3. Arduino uploads compile for the board detected on the port (its FQBN comes from the USB VID/PID). STM32 Nucleo and nRF52 are reachable through build_project (PlatformIO) but are not yet enumerated by USB id.

Simulator

Set MCUPORT_SIMULATOR=1 and list_boards adds three boards at sim:// paths: an ESP32 and a Pico running the real MicroPython interpreter (WebAssembly build; only the machine module is an in-memory model), and an Arduino Uno R4 running a protocol emulator of the bridge sketch. Every tool works on them, so an agent can be exercised end to end with no hardware. Their labels say SIMULATED. Any other value than 1/0/true/false for the variable fails at boot, as does any misspelled MCUPORT_* key.

Install

curl -fsSL https://mcuport.com/install.sh | sh        # macOS / Linux
irm https://mcuport.com/install.ps1 | iex             # Windows (PowerShell)

Needs Node.js 20 or newer. The package is not on the npm registry; the script installs a checksum-verified tarball from mcuport.com under ~/.mcuport.

Flashing and building require the relevant family's own CLI on PATH; this package never bundles them:

  • ESP32: pip install esptool
  • RP2040/RP2350: brew install picotool (or just copy the .uf2 to the board's BOOTSEL mass-storage volume; no tool needed for that path)
  • Arduino: brew install arduino-cli
  • PlatformIO: pip install platformio

Configure Claude Code / Claude Desktop

Add this to your MCP client's config (Claude Code: claude mcp add, or edit the config file directly; Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "mcuport": {
      "command": "mcuport",
      "env": {
        "MCUPORT_LICENSE_KEY": "MCUP-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

MCUPORT_LICENSE_KEY is only needed for Pro (the phone bridge); every other tool works with no key set.

If you installed from source instead of npm, point command at node and args at the built entry point:

{
  "mcpServers": {
    "mcuport": {
      "command": "node",
      "args": ["/path/to/mcu-mcp/server/dist/index.js"],
      "env": { "MCUPORT_SIMULATOR": "1" }
    }
  }
}

Development

npm ci
npm test              # vitest: unit + simulator suites
npm run build
npm run conformance   # real MCP client over stdio against dist/, incl. the simulator
npm run typecheck
npm run pack-check
node scripts/embed_sketch.mjs   # after editing firmware/mcuport_bridge/

test/tool_surface.test.ts fails the build if a tool exists that this README does not name, or if the repo's CLAUDE.md promises a capability no tool provides. Keep both honest.