Skip to main content

opendeck-mcp

Set up your OpenDeck stream controller by asking an AI agent:

"Put a Multi Action on the top-left key that mutes my mic and opens Discord, make a 'Gaming' profile from my default one, and switch to it whenever Steam is focused."

opendeck-mcp is an MCP server. It lets Claude Code (or any MCP client) read your decks, profiles and plugins, stage changes, show you a diff, and then apply them, with a backup you can undo. It works with any device OpenDeck drives: Elgato Stream Decks and plugin-provided devices such as the Ulanzi D200H/D200X.

Contents

What it can do

  • See everything: devices with their grid size, dials and touchpoints; profiles; every key's action, title and settings; and installed plugins with their actions.
  • Edit keys: place, update (title, font, colour, image, settings), clear, move, swap or copy keys, dials and touchpoints, across profiles and devices.
  • Multi and Toggle Actions: add and remove the actions inside them, and set the delays between them.
  • Profiles: create, duplicate, rename (references to the old name are updated too) and delete profiles, including Folder/Name profiles, and choose which one each device starts on.
  • App switching: switch a device to a profile automatically when an app is focused.
  • Plugins:
    • search the same catalogue as OpenDeck's Plugin Manager, plus the Elgato plugin archive;
    • install, update and remove plugins;
    • read and write plugin settings, with secrets redacted;
    • read plugin logs.
  • Instant edits: title, image and settings tweaks to a key on the profile a device is showing apply at once, with no restart; undo_live takes them back.
  • Icons and folders: make key images from text, emoji, ~3,400 brand logos or ~7,400 symbols, and folder keys that open a sub-profile with a Back key.
  • Pictures: render_profile shows the deck as OpenDeck draws it, with staged changes outlined or before and after side by side, so you can check a layout before applying it.
  • Home Assistant: find entities by name, area or domain, and get ready-made settings for a key that controls one, using the login already saved in the Home Assistant plugin.
  • Undo: every apply takes a backup, and restore_backup puts things back.

Install

You need OpenDeck 2.14, uv and Claude Code (or another MCP client, see below). uv fetches a suitable Python by itself.

claude mcp add opendeck --scope user -- uvx opendeck-mcp@0.3.1

Then restart Claude Code and ask it something like "what's on my OpenDeck?".

It's on PyPI and in the MCP Registry as io.github.kastro5/opendeck-mcp. To run straight from GitHub instead: uvx --from git+https://github.com/kastro5/opendeck-mcp@v0.3.1 opendeck-mcp.

OpenDeck's folders are found automatically. If yours live somewhere unusual, override them with environment variables (claude mcp add … -e NAME=value):

Variable Default (Windows)
OPENDECK_CONFIG_DIR %APPDATA%\opendeck
OPENDECK_EXE %LOCALAPPDATA%\OpenDeck\opendeck.exe
OPENDECK_LOG_DIR %LOCALAPPDATA%\opendeck\logs
OPENDECK_MCP_DATA_DIR %LOCALAPPDATA%\opendeck-mcp (the server's own plan, backups and cache)
OPENDECK_MCP_HA_VERIFY_TLS 1; set 0 for a Home Assistant on https with a self-signed certificate
OPENDECK_MCP_TOKEN the HTTP token (default: generated once, see below)

Upgrading

The version is pinned in the command, so replace the registration with the new tag:

claude mcp remove opendeck --scope user
claude mcp add opendeck --scope user -- uvx opendeck-mcp@0.3.1

The first apply after an upgrade also updates the companion bridge plugin (see How it works), in the same restart.

Other MCP clients

Any client that can start a stdio server works. The usual JSON configuration (Claude Desktop, Cursor, VS Code and others) is:

{
  "mcpServers": {
    "opendeck": {
      "command": "uvx",
      "args": ["opendeck-mcp@0.3.1"]
    }
  }
}

From another computer

The server has to run on the PC that runs OpenDeck, inside your logged-in desktop session: it restarts OpenDeck and talks to it through its command line, which doesn't work from a service or an SSH session. To use it from another machine (a laptop, a dev VM), serve it over HTTP there and connect to it. Tailscale or another private network is the recommended way in; every request also needs a token.

On the OpenDeck PC (Windows):

uvx opendeck-mcp@0.3.1 install-autostart --host <tailscale-ip>
uvx opendeck-mcp@0.3.1 token --host <tailscale-ip>

The first command puts a small script in your Startup folder that starts the server, without a window, at every logon, and starts it now. The second prints the command to run on the other machine:

claude mcp add --transport http opendeck http://<tailscale-ip>:8765/mcp --header "Authorization: Bearer <token>"
  • tailscale ip -4 shows the address. The server refuses 0.0.0.0 (every network) unless you add --allow-all-interfaces.
  • To connect by name (e.g. a MagicDNS name), add --allowed-host <name>; other Host headers are rejected.
  • opendeck-mcp token --reset makes a new token, and opendeck-mcp uninstall-autostart removes the autostart and stops the server.
  • Windows Firewall must allow inbound TCP 8765 on that network; it asks the first time if it isn't already allowed.
  • On Linux or macOS, run opendeck-mcp serve --host <ip> from a systemd user service or LaunchAgent.

Using it

Just describe what you want. Under the hood, the agent:

  1. calls get_overview and show_profile to see your devices and keys;
  2. uses list_actions / describe_action to find the right action and learn its settings;
  3. stages edits with set_key, add_child, manage_profile and so on;
  4. calls preview_changes (and render_profile for a picture), shows you the result, and waits for your OK;
  5. calls apply_changes, which restarts OpenDeck for a few seconds and reports the backup id.

Small tweaks skip all that. Changing an existing key's title, image or settings on the profile a connected device is showing happens instantly, without a restart, and undo_live reverses it. Title style, the shown state, other profiles and every structural change still go through staging.

For example, "make a Fusion 360 profile with keys for my most-used sketch and solid tools, put the rest in a folder, and switch to it whenever Fusion is in front" builds a profile of Simulate Input keys sending Fusion's shortcuts, with icons from make_icon, a "More" folder and the app rule.

Keys are addressed the way show_profile draws them:

     c1              c2              c3              c4              c5
r1    Multi Action    "GitHub"        Toggle Action   ·               ·
r2    ·               Entity (custo…  ·               Switch Profile  ·
r3    Device Bright…  ·               "rew"           ·               (wide →)
Slot Meaning
r1c1 row 1, column 1
key13 raw position 13 (0-based)
dial1 first dial
touch1 first touchpoint
r1c1/child2 second action inside a Multi or Toggle Action

How it works

OpenDeck has no API for changing its layout. It keeps profiles in memory and overwrites any file edited while it runs. So opendeck-mcp works on the files, with OpenDeck stopped:

  1. Staging. Each edit is recorded as an operation in a plan. Nothing is written yet. show_profile shows staged edits marked with *.
  2. Applying.
    • First, OpenDeck is asked to save its in-memory state. This sends a profile switch through its command line, which makes it write the selected profile.
    • Then OpenDeck and its plugin processes are stopped.
    • The plan is replayed onto the freshly read files. If something you staged was changed in OpenDeck's UI in the meantime, apply reports a conflict and doesn't write anything.
    • The files are backed up, written atomically, read back and re-validated. If anything fails, the backup is restored automatically.
    • Finally, OpenDeck is started again, detached and hidden.
  3. Faithful files. Action data is rebuilt from plugin manifests exactly as OpenDeck builds it, and JSON is written byte-for-byte in OpenDeck's format. A profile OpenDeck can't read gets replaced by an empty one, so every write is validated twice.
  4. Device geometry. OpenDeck doesn't store a device's rows × columns anywhere, so the first apply also installs a tiny companion plugin, OpenDeck MCP Bridge (no actions, about 60 lines of JavaScript). It records the geometry OpenDeck reports to plugins into its own settings file, where the server reads it. It's versioned with the server, so a new release updates it on its next apply.
  5. Images. New key images are stored as data: URLs. OpenDeck accepts these and converts them to files itself on its next save.

Instant edits

Titles and images are sent to the running OpenDeck through its command line (--process-message), as setTitle and setImage events. Settings go over OpenDeck's local plugin websocket, posing as the key's settings panel, because that is the only path on which OpenDeck tells the plugin its settings changed. OpenDeck is then asked to save, and the file is checked. Before each edit the old titles, images (as bytes) and settings are written to live_journal.jsonl in the server's data folder, which undo_live replays.

Safety

  • Nothing changes until you confirm. Staging tools never touch OpenDeck. apply_changes, restore_backup and plugin installs all need a second call with confirm=true.
  • Backups. Every apply first copies profiles/, images/, applications.json and anything else it touches into %LOCALAPPDATA%\opendeck-mcp\backups. It keeps the last 30 backups (always at least 5). Restoring also backs up the current state first. Backups contain your plugin settings, including secrets, so they stay on your machine.
  • Secrets are redacted. Anything that looks like a credential (token, password, API key, JWT, Bearer value, …) is shown as «redacted» in settings, diffs, examples and logs. Passing «redacted» back keeps the stored value.
  • Risky keys are flagged. Run Command keys run commands, Simulate Input keys type into the focused window, and Open URL keys with non-web URIs can launch programs. Each is marked ⚠ when staged and in the preview.
  • Plugins are third-party code. stage_plugin first downloads and inspects the plugin: author, version, sha256, how it runs, which platforms it supports, and the catalogue's AI-assisted flag. It only stages the install after you agree. Built-in plugins can't be removed.
  • Home Assistant. The Home Assistant tools reuse the server URL and access token saved by the Home Assistant plugin. The token is only sent to that server, never shown. ha_call_service acts on your real home, so it needs a second call with confirm=true.
  • Instant edits aren't backed up. They don't go through apply, so instead the previous titles, images and settings are kept in a journal (the last 200 edits) that undo_live replays.
  • The HTTP token is a key to your deck. Anyone with it who can reach the port can change keys, install plugins and, through Run Command keys, run programs. Keep the server on a private address, and opendeck-mcp token --reset if the token leaks.
  • Version check. Apply refuses to run on OpenDeck versions other than 2.14.x unless you allow it explicitly.

Network and files

  • Internet: only the plugin catalogues and plugin downloads (find_plugins, stage_plugin), and the title fonts for render_profile and the icon sets for make_icon, fetched once each from jsDelivr and cached. Nothing else, and no telemetry.
  • Local network: your Home Assistant, only when the Home Assistant tools are used.
  • Files in %LOCALAPPDATA%\opendeck-mcp: plan.json (staged changes), backups/, live_journal.jsonl, icons/ (made by make_icon), cache/ (catalogues, fonts, icon sets), device_overrides.json, and for HTTP http_token and http.log. Delete the folder to reset everything.

Tools

Kind Tools
Read get_overview, show_profile, render_profile, list_actions, describe_action, get_plugin_settings, read_logs, get_app_rules, find_plugins, find_icons, preview_changes, list_backups
Stage set_key, update_key, clear_key, move_key, add_child, remove_child, set_multi_delays, make_folder, manage_profile, set_app_rule, set_plugin_settings, stage_plugin, discard_changes, set_device_layout
Icons make_icon (saves a PNG to pass as image)
Apply apply_changes, restore_backup, undo_live
Home Assistant ha_search, ha_describe_entity, ha_call_service

Limitations

  • Apply restarts OpenDeck (about 5 seconds). Unsaved edits in an open settings panel are lost, and plugins restart.
  • Some settings need examples. Settings are known in detail for OpenDeck's own actions and the Starter Pack. For other plugins, the agent works from hints on the plugin's settings page and from your existing keys. If those aren't enough, place the action and finish setting it up in OpenDeck's UI.
  • App rule names. App rules match the name OpenDeck gives the focused app: on Windows the executable's description (e.g. Autodesk Fusion, Visual Studio Code), otherwise its file name. get_app_rules lists the exact names of the apps you have open.
  • Tested platform. Tested on Windows 11 with OpenDeck 2.14.1, a Ulanzi D200H and Home Assistant. The macOS and Linux paths and process handling are written but untested.
  • Limited live control. Title, image and settings edits apply instantly; everything else needs a restart. It doesn't press keys or switch profiles live.
  • Pictures are close, not exact. render_profile can't see images a plugin draws while running (a clock, a live Home Assistant state) unless OpenDeck has saved them, and OpenDeck's own Multi/Toggle Action icons are drawn as look-alikes.
  • An open settings panel goes quiet. After an instant settings edit, a settings panel already open in OpenDeck's window for that key stops updating until it's reopened.

Troubleshooting

  • "OpenDeck's executable wasn't found" / "config folder wasn't found": set OPENDECK_EXE / OPENDECK_CONFIG_DIR (see Install).
  • Apply refuses: untested OpenDeck version. Check the changelog for a release that supports yours; apply_changes(allow_untested_version=true) overrides it at your own risk, with a backup.
  • Apply reports a conflict. A key you staged was changed in OpenDeck meanwhile. Look at preview_changes, then discard and re-stage it, or apply with on_conflict="skip".
  • A device shows "geometry unknown". It's learned by the bridge plugin, installed with the first apply. Until then set_device_layout sets rows and columns by hand.
  • Undo an apply: list_backups, then restore_backup.
  • HTTP client gets 401 / 421. 401: wrong or missing token (opendeck-mcp token). 421: the client used a host name the server doesn't know; add it with --allowed-host. The server logs to http.log in its data folder.

Development

git clone https://github.com/kastro5/opendeck-mcp && cd opendeck-mcp
uv sync
uv run pytest                 # unit tests, against a scrubbed fixture config
uv run pytest -m live         # also check snapshots against your real OpenDeck install (read-only)
claude mcp add opendeck-dev -- uv --directory /path/to/opendeck-mcp run opendeck-mcp

Test fixtures are real OpenDeck profiles with every setting value replaced (scripts/scrub_fixture.py), plus trimmed plugin manifests with empty icon files.

License

MIT. Not affiliated with OpenDeck or Elgato.

Icons and fonts are downloaded at run time, not bundled: Material Design Icons (Apache 2.0), Simple Icons (CC0; brand logos remain their owners' trademarks), Twemoji (graphics CC-BY 4.0, © Twitter/X and contributors), and OpenDeck's title fonts from Google Fonts (OFL).

Metadata

Release files for opendeck-mcp 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opendeck-mcp 0.3.1
File Size Uploaded
opendeck_mcp-0.3.1.tar.gz 187.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for opendeck-mcp 0.3.1
File Interpreter ABI Platform
opendeck_mcp-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 298.3 kB

Release files / opendeck_mcp-0.3.1.tar.gz

Download URL opendeck_mcp-0.3.1.tar.gz
Size 187.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a2cbcb8871e623f0b11e9bae6944faca8a9423ee0d013915ce4d72155e3180e5
BLAKE2b-256 checksum
How to use checksums
ee9e6c66d2fb2ab77527114e07c6d0e4f4fbab7a7689595af9e7f56817952143
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / opendeck_mcp-0.3.1-py3-none-any.whl

Download URL opendeck_mcp-0.3.1-py3-none-any.whl
Size 111.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
317c01d124c86f83e988fbac04d56a221017b1529e871f184261675664bd7304
BLAKE2b-256 checksum
How to use checksums
6a6b18548c0df990e6b41115578acc942d502744bab747248f233368aa8f2f7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

This release

0.3.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page