Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 107 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,160 +2,178 @@

<!-- mcp-name: io.github.verygoodplugins/streamdeck-mcp -->

AI-first MCP server for Elgato Stream Deck profile management. The default server writes directly to the Stream Deck desktop app's native profile files, and the original USB-direct server is still available as a legacy fallback.
Tell your AI what kind of Stream Deck you want. Get back a fully authored profile — buttons, icons, colors, dials, and the shell scripts behind them.

## Installation
streamdeck-mcp is the bridge: an MCP server that reads and writes Elgato Stream Deck profiles directly, in the format the desktop app already uses. Themed decks, per-project layouts, app-specific control boards — built in a single prompt instead of an hour in the GUI. Works with Claude Desktop, Claude Code, Cursor, Codex, and any MCP-compatible client.

### Default: Desktop Profile Writer
![A Stream Deck + XL profile showing a Slack control board with around thirty themed buttons, color-coded icons, and six configured dials in the touch strip — all auto-generated by Claude Desktop from a single prompt.](docs/screenshots/slack-control-board.jpg)

The default packaged entrypoint is the profile writer. It edits `ProfilesV3` when present, then falls back to `ProfilesV2`.
> Asked Claude Desktop for *"a Slack control board"* — got back this profile. Buttons, icons, colors, and dials all authored in one shot via the MCP server.

```bash
uvx streamdeck-mcp
```
## What you can build

The decks aren't generic. When other MCP servers are loaded — Slack, Home Assistant, OBS, GitHub, Hue, the ones you already use — your AI queries them first to discover *your* channels, *your* devices, *your* scenes, then authors a deck around what's actually there. Icons render to match (~7,400 Material Design Icons bundled offline, or freeform text), so every button looks like it belongs. No Stream Deck SDK, no plugin authoring — just shell scripts and a prompt.

A few worth trying:

- ***"Make me a control board for Slack."***
→ Queries your Slack MCP for channels, status, and unread state. Generates one channel-jump button per channel you actually use, status toggles (Active / Away / DND), a Read-All, and dials for unread counts. The screenshot above is one such result.

- ***"A hello-kitty-themed Home Assistant dashboard for the living room."***
→ Pulls Home Assistant entities scoped to the living room area, then lays them out in pastel kawaii — scenes on row one, lights on row two, media on row three. Palette and icon style follow the theme.

- ***"OBS control panel based on my actual scenes and audio inputs."***
→ Queries OBS for scenes, sources, and audio devices. Generates scene-switch buttons with the right transitions, source toggles, and dials for per-input gain on the touch strip.

- ***"A dev deck for this repo in Nordic colors."***
→ Reads your project's scripts (npm, Make, just — whatever's there), recent PRs via the GitHub MCP, and the local docs structure. Drops shell-script buttons for the most-used commands, PR/CI jumps, and a Nordic-palette icon set.

- ***"A 'Friday demo' deck: open Zoom, mute Slack, set Hue to 'focus', start a screen recording."***
→ Composes across whatever MCPs you have loaded. Writes one shell script per action to `~/StreamDeckScripts/` and wires them to a single page with custom icons.

Same pattern every time: ask, your AI inventories your hardware and your other MCPs, plans the layout, generates icons, writes the profile files, restarts the Elgato app. Iteration is free — tweak the prompt, get a different deck.

### Local Repo Configuration
## How it works

1. **Install the MCP server** in your AI client (snippets below).
2. **Install the designer skill** in Claude Code, or invoke the `design_streamdeck_deck` MCP prompt in any other client.
3. **Ask for a deck.** Your AI calls `streamdeck_read_profiles` to inventory the hardware, plans the layout, generates icons (~7,400 Material Design Icons bundled offline, or freeform text), writes the profile files, and restarts the Elgato app so the device picks up the changes.

## Install

The packaged entrypoint is `streamdeck-mcp`, run via [`uvx`](https://docs.astral.sh/uv/). It edits the desktop app's `ProfilesV3` files when present and falls back to `ProfilesV2`.

### Cursor

[![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=streamdeck&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJzdHJlYW1kZWNrLW1jcCJdfQ==)

Or paste into `~/.cursor/mcp.json`:

```json
{
"mcpServers": {
"streamdeck": {
"command": "uv",
"args": [
"--directory",
"/path/to/streamdeck-mcp",
"run",
"profile_server.py"
]
"command": "uvx",
"args": ["streamdeck-mcp"]
}
}
}
```

### Legacy USB Server
### Claude Desktop

If you still want direct hardware control that bypasses the Elgato app entirely, keep using the legacy server:
Paste into `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows), then restart Claude Desktop:

```json
{
"mcpServers": {
"streamdeck-usb": {
"command": "uv",
"args": [
"--directory",
"/path/to/streamdeck-mcp",
"run",
"server.py"
]
"streamdeck": {
"command": "uvx",
"args": ["streamdeck-mcp"]
}
}
}
```

Or use the packaged legacy entrypoint:
### Claude Code

```bash
uvx --from streamdeck-mcp streamdeck-mcp-usb
claude mcp add streamdeck -- uvx streamdeck-mcp
```

## Designing Decks with the `streamdeck-designer` Skill

streamdeck-mcp ships with an **Agent Skill** that teaches Claude how to design, theme, and author complete Stream Deck layouts using the MCP tools below. With the skill loaded, Claude can one-shot an authored deck from a high-level prompt — "give me a hello-kitty-themed Twitch deck with Hue light controls", "build a dev deck for this repo in Nordic colors" — including palette planning, integration discovery, consistent icon generation, and shell-script wiring.
### OpenAI Codex

### Install the skill (Claude Code / Claude.ai)
Add to `~/.codex/config.toml`:

```bash
# After installing streamdeck-mcp (e.g. via uvx or pip install -e .)
streamdeck-mcp-install-skill
# or, without the console script:
uv run python -m install_skill
```toml
[mcp_servers.streamdeck]
command = "uvx"
args = ["streamdeck-mcp"]
```

The skill is copied to `~/.claude/skills/streamdeck-designer/`. Restart Claude Code (or start a new session) and it auto-loads when your request matches a themed/custom deck design intent.
### Other MCP clients

To upgrade after a `streamdeck-mcp` version bump, rerun with `--force`:
Anything that speaks MCP over stdio works the same way — point it at `uvx streamdeck-mcp`. The JSON snippet above is the canonical shape.

```bash
streamdeck-mcp-install-skill --force
```
### Note for Linux users

### Other MCP clients
The default profile writer needs the Elgato Stream Deck desktop app, which is macOS- and Windows-only. On Linux, use the legacy USB-direct server instead — see [Legacy USB mode](#legacy-usb-mode) below.

## The `streamdeck-designer` skill

streamdeck-mcp ships with an **Agent Skill** that teaches Claude (in Claude Code) how to plan, theme, and author full decks end-to-end. The skill covers:

- **Hardware inventory** — always calls `streamdeck_read_profiles` first, then matches authoring style to your model.
- **Palette + typography planning** — 8 theme archetypes (kawaii, retrowave, brutalist, nordic, terminal, nature, minimal, corporate) with ready palettes and per-strategy icon-color guidance.
- **Dials + touchstrip** — decision tree for + / + XL encoder layouts (`$X1` / `$A0` / …).
- **Integration recipes** — per-service patterns for Hue, OBS, Spotify, Home Assistant, Twitch, shell, browser. Credentials live in `~/StreamDeckScripts/.env`, never baked into scripts.
- **Starter recipes** — streamer/hello-kitty (+ XL), dev/Nordic (XL), music/retrowave (Original) as adaptation shapes.

### Install the skill (Claude Code)

Clients that don't load Claude Code skills (Claude Desktop, Cursor, ChatGPT-with-MCP, …) get a condensed mirror via the MCP prompt **`design_streamdeck_deck`**. Invoke it before asking for a deck — most clients expose it as a slash command or prompt picker. Pass the user's intent via the `intent` argument if your client supports it.
```bash
uvx --from streamdeck-mcp streamdeck-mcp-install-skill
```

### What the skill covers
The skill is copied to `~/.claude/skills/streamdeck-designer/`. Restart Claude Code (or start a new session) and it auto-loads when your request matches a deck-design intent. Re-run with `--force` to upgrade after a version bump.

- Hardware inventory — the skill always calls `streamdeck_read_profiles` first, then consults bundled references to match authoring style to the user's model (Original, MK.2, XL, Plus XL, Neo, Mini).
- Palette + typography planning — 8 theme archetypes (kawaii, retrowave, brutalist, nordic, terminal, nature, minimal, corporate) with ready palettes + per-strategy icon-color guidance.
- Dials + touchstrip — decision tree for Plus XL encoder layouts (`$X1` / `$A0` / …) with Phase 1 constraints called out (value/indicator slots render empty until the live channel lands).
- Integration recipes — per-service patterns for Hue, OBS, Spotify, Home Assistant, Twitch, shell, browser. Authoring-time discovery via companion MCPs; credentials stored in `~/StreamDeckScripts/.env`, never baked into scripts.
- Starter recipes — streamer/hello-kitty (Plus XL), dev/Nordic (XL), music/retrowave (Original) as adaptation shapes.
### Other MCP clients

The skill lives at `streamdeck_assets/skill/streamdeck-designer/` in the repo; the `SKILL.md` body is ≤500 lines and `references/` are loaded on demand.
Clients that don't load Claude Code skills (Claude Desktop, Cursor, Codex, …) get a condensed mirror via the **`design_streamdeck_deck`** MCP prompt. Most clients expose it as a slash command or prompt picker — invoke it before describing the deck you want, and pass the user's intent via the `intent` argument if your client supports it.

## Default Tools
## Tools

| Tool | What it does |
|------|---------------|
| `streamdeck_read_profiles` | Lists desktop profiles and page directories from the active ProfilesV3 or ProfilesV2 store |
| `streamdeck_read_page` | Reads a page manifest and returns simplified button details plus the raw manifest |
| `streamdeck_write_page` | Creates a new page or rewrites an existing page manifest |
| `streamdeck_create_icon` | Generates a PNG icon from a Material Design Icons name (e.g. `mdi:cpu-64-bit`) or from text (but not both). `shape="button"` (72x72, default) for keypad keys and encoder dial faces; `shape="touchstrip"` (200x100) for Stream Deck + / + XL dial segment backgrounds. ~7400 MDI icons are bundled offline; unknown names return close-match suggestions |
| `streamdeck_create_icon` | Generates a PNG icon from a Material Design Icons name (e.g. `mdi:cpu-64-bit`) or from text — provide one or the other, not both. `shape="button"` (72×72, default) for keypad keys and dial faces; `shape="touchstrip"` (200×100) for + / + XL touch strip backgrounds. ~7,400 MDI icons are bundled offline; unknown names return close-match suggestions |
| `streamdeck_create_action` | Creates an executable shell script in `~/StreamDeckScripts/` and returns an Open action block |
| `streamdeck_restart_app` | Restarts the macOS Stream Deck desktop app after profile changes |
| `streamdeck_install_mcp_plugin` | Installs the bundled streamdeck-mcp Stream Deck plugin into the user's Elgato Plugins directory. `streamdeck_write_page` auto-installs it when an encoder button needs it, so direct use is rarely necessary |
| `streamdeck_restart_app` | Restarts the Stream Deck desktop app after profile changes (macOS only — raises on other platforms) |
| `streamdeck_install_mcp_plugin` | Installs the bundled streamdeck-mcp Stream Deck plugin into the Elgato Plugins directory. `streamdeck_write_page` auto-installs it the first time an encoder needs it, so direct use is rarely required |

## How the Profile Writer Works
## Editing workflow

- `ProfilesV3` is preferred when it exists because page UUIDs map cleanly to directories.
- `ProfilesV2` is still supported, but existing pages should be targeted by `directory_id` or `page_index` because Elgato stores opaque page directory names there.
- `streamdeck_write_page` can accept raw native action objects, or use convenience fields like `path`, `action_type`, `plugin_uuid`, and `action_uuid`.
- Generated icons are stored in `~/.streamdeck-mcp/generated-icons/`.
- Generated shell scripts are stored in `~/StreamDeckScripts/`.
- The bundled streamdeck-mcp Stream Deck plugin is installed into the Stream Deck Plugins directory (e.g., `~/Library/Application Support/com.elgato.StreamDeck/Plugins/` on macOS, `%APPDATA%\Elgato\StreamDeck\Plugins\` on Windows) once installed. It's a minimal shell whose only job is to declare encoder support so per-instance `Encoder.Icon` / `Encoder.background` writes survive an Elgato app restart. `streamdeck_write_page` installs it automatically the first time an encoder button needs it.
The Elgato desktop app keeps every profile in memory and rewrites the on-disk manifests from that snapshot when it quits — so any edit made while the app is running is wiped the next time it closes. The profile writer enforces a quit → write → relaunch cycle:

## Editing Workflow (Important)
1. Ensure the Elgato app is not running, or pass `auto_quit_app: true` to `streamdeck_write_page` to have it quit the app for you (AppleScript first, `killall` fallback).
2. Make as many `streamdeck_write_page` calls as you need — the app stays quit between them.
3. Call `streamdeck_restart_app` when you're done. The device re-reads the manifests on launch and your changes appear.

The Elgato desktop app keeps every profile in memory and rewrites the on-disk manifests from that snapshot when it quits, so any edit made while the app is running is wiped the next time it closes. The profile writer enforces a quit → write → relaunch cycle:
`streamdeck_write_page` raises `StreamDeckAppRunningError` when the app is running and `auto_quit_app` isn't set, so you can't accidentally write changes that get silently discarded.

1. Ensure the Elgato app is not running, or pass `auto_quit_app: true` to `streamdeck_write_page` to have it quit the app for you (AppleScript first, `killall` fallback).
2. Make as many `streamdeck_write_page` calls as you need — the app stays quit across them.
3. Call `streamdeck_restart_app` when you are done. The device re-reads the manifests on launch and your changes appear.
**Platform support.** The running-app guard, `auto_quit_app`, and `streamdeck_restart_app` are macOS-only. On Windows the Elgato app still clobbers manifests on close, so you'll need to quit it manually before authoring and relaunch it after — `auto_quit_app: true` is silently a no-op on non-macOS, and `streamdeck_restart_app` errors.

`streamdeck_write_page` raises a `StreamDeckAppRunningError` when the app is running and `auto_quit_app` is not set, so you cannot accidentally write changes that will be silently discarded.
If your Elgato app lives somewhere other than `/Applications/Elgato Stream Deck.app`, set `STREAMDECK_APP_PATH` to the bundle path.
Comment thread
jack-arturo marked this conversation as resolved.

If your Elgato app is installed somewhere other than `/Applications/Elgato Stream Deck.app`, set `STREAMDECK_APP_PATH` to the bundle path.
## Under the hood

## Usage Notes
- **`ProfilesV3` is preferred** when present (page UUIDs map cleanly to directories). `ProfilesV2` is still supported, but existing pages should be targeted by `directory_id` or `page_index` because Elgato uses opaque directory names there.
- **Native action objects** are accepted directly by `streamdeck_write_page`, alongside convenience fields like `path`, `action_type`, `plugin_uuid`, and `action_uuid`.
- **Generated icons** live in `~/.streamdeck-mcp/generated-icons/`. **Generated shell scripts** live in `~/StreamDeckScripts/`.
- **The bundled streamdeck-mcp plugin** is installed into the Stream Deck Plugins directory (e.g., `~/Library/Application Support/com.elgato.StreamDeck/Plugins/` on macOS, `%APPDATA%\Elgato\StreamDeck\Plugins\` on Windows). It's a minimal shell whose only job is to declare encoder support so per-instance `Encoder.Icon` / `Encoder.background` writes survive an Elgato app restart.

- `streamdeck_create_action` is the safest way to build shell-command buttons because it writes a standalone script and returns the native Open action block for it.
- The profile writer does not require exclusive USB access.
## Legacy USB mode

## Legacy USB Tools
The original USB-direct server is preserved for backwards compatibility — useful when you'd rather have the MCP server own the hardware directly (Linux, headless setups, or environments where the Elgato app isn't running). It exposes a different tool surface focused on direct hardware control:

The original USB-direct server is preserved for backwards compatibility. It still provides:
`streamdeck_connect`, `streamdeck_info`, `streamdeck_set_button`, `streamdeck_set_buttons`, `streamdeck_clear_button`, `streamdeck_get_button`, `streamdeck_clear_all`, `streamdeck_set_brightness`, `streamdeck_create_page`, `streamdeck_switch_page`, `streamdeck_list_pages`, `streamdeck_delete_page`, `streamdeck_disconnect`.

- `streamdeck_connect`
- `streamdeck_info`
- `streamdeck_set_button`
- `streamdeck_set_buttons`
- `streamdeck_clear_button`
- `streamdeck_get_button`
- `streamdeck_clear_all`
- `streamdeck_set_brightness`
- `streamdeck_create_page`
- `streamdeck_switch_page`
- `streamdeck_list_pages`
- `streamdeck_delete_page`
- `streamdeck_disconnect`
Run via:

```bash
uvx --from streamdeck-mcp streamdeck-mcp-usb
```

Use that mode only when you want the MCP server to own the hardware directly and the Elgato desktop app is not running.
In a client config, keep `"command": "uvx"` and use `"args": ["--from", "streamdeck-mcp", "streamdeck-mcp-usb"]`.

## Development

```bash
uv venv
uv pip install -e ".[dev]"
git clone https://github.com/verygoodplugins/streamdeck-mcp.git
cd streamdeck-mcp
uv venv && uv pip install -e ".[dev]"
uv run pytest tests/ -v
uv run ruff check .
```
Expand Down
Binary file added docs/screenshots/slack-control-board.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading