Hi all,
I’ve just released a Claude Code plugin (MCP server + skill + indexed docs) that lets you develop and push custom widgets to an Electra One MK2 / Mini directly from Claude Code, or other Dev LLMs
MIT licensed, source on GitHub:
GitHub - roomi-fields/electra-one-mcp: MCP server + Claude Code skill + indexed docs for developing Electra One MK2/Mini widgets and presets · GitHub
Install: `/plugin marketplace add roomi-fields/claude-plugins` then `/plugin install electra-one@roomi-fields`
What it does
20 MCP tools grouped by need:
- Push — `push_to_device(preset_path, bank, slot)` runs the full pipeline: switch slot → upload preset → upload Lua → slot-flip reload (since `08 08 Reload Preset Slot` NACKs on 4.1.4, see below). Auto-converts the widget repo’s “tiles” schema to the device’s “controls” schema, splits inlined Lua into a separate `01 0C` upload. Verified on hardware with a 25 KB preset + 40 KB Lua bundle.
- Read — `execute_lua(source)` for a live REPL (e.g. `print(parameterMap.get(99))`), captures `print()` output via `7F 00`. `device_state(seconds)` is a passive listener that classifies the `7E XX` event vocabulary (preset switch, page, control set, pot touch, list-change notifications) and tracks client-side what’s on screen. `pull_preset(bank, slot, out_path)` does the reverse direction — preset + lua download, reverse-converts back to repo format.
- Compute — `bundle_widget`, `validate_preset`, `screenshot_widget` (via headless emulator).
- Doc lookup — `get_sysex_command(query)` queries a catalog of all 62 documented SysEx commands extracted from the bundle + official docs. `search_docs`, `get_api`, `list_constants` cover the 79-page mirror of `docs.electra.one`.
Why I built it
Two specific points of friction:
1. The widget JSONs people typically work with use a “tiles” schema (`schemaVersion`, `tiles[]`, `targetDevice`…) that the device firmware doesn’t parse directly via SysEx — `01 01 Upload Preset` ACKs at the transport level but the device’s preset parser silently rejects it. The web editor at `app.electra.one` converts to the documented “controls” schema (`version`, `controls[]`, `groups[]`…) before sending. I ported that converter to Python (byte-identical, verified against a live MIDI sniff), so the same widget JSON can now be pushed straight to hardware.
2. Many of the practical workarounds I found where scattered in staff replies on this forum. I’ve consolidated them with direct quotes in a `docs/FORUM_REFERENCES.md` file shipped with the plugin: threads #592 (file transfer chunking — Martin’s “well under 4 kB”), #410 (Preset Transfer Failed retry), #2370 (parser size limits), #4172 (multi-pot custom tiles), #4116 / #4185 (no `onPotClick`), #807 (Lua-not-running after slot switch), #3590 (external Lua editor pattern), #2022 (default-preset recovery), #4259 (4.1.4 release notes).
Known caveats (firmware 4.1.4 on my test rig)
- `08 08 Reload Preset Slot` NACKs — the plugin uses a slot-flip workaround (`09 08 bank (slot XOR 1)` then back) which forces a clean reinit.
- File Transfer API (`01 2D / 2E / 2F / 04 2D`) commit silently rolls back when uploading `type:“preset”`. I exposed it as `mode=“ft”` for completeness + for Lua module uploads where it’s the only path, but default to the simple `01 01` + `01 0C` route (what the web editor uses per the JS bundle).
- On Windows, big single `midiOutLongMsg` calls fragment unreliably since the late-2025 Windows updates KB5077181/KB5074105. Working around by keeping the preset.json small (< 1 KB without inlined Lua) and uploading Lua separately.
All caveats with their workarounds are in `docs/DEV_GUIDE.md`.
Acknowledgements
- Martin Pavlas and kris — for the publicly documented Lua API + the staff replies on this forum that made the undocumented behaviour debuggable.
- `xot/ElectraOne` (Codeberg), `johnnyclem/electra-one`, `elliotwoods/simularca-electra-one-plugin` — reference open-source consumers of the SysEx protocol I used to cross-check the simple `01 01` + `01 0C` upload path.
Feedback / bug reports / PRs very welcome. I’d especially love to hear from anyone who can confirm or refute the empirical behaviour I observed on firmware 4.1.4 (the `08 08` NACK in particular — is this device-specific, or universal on this firmware?).
Romi