opendeck-mcp 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opendeck_mcp-0.3.1/.gitattributes +2 -0
- opendeck_mcp-0.3.1/.github/workflows/publish.yml +57 -0
- opendeck_mcp-0.3.1/.github/workflows/tests.yml +34 -0
- opendeck_mcp-0.3.1/.gitignore +8 -0
- opendeck_mcp-0.3.1/CHANGELOG.md +66 -0
- opendeck_mcp-0.3.1/LICENSE +21 -0
- opendeck_mcp-0.3.1/PKG-INFO +347 -0
- opendeck_mcp-0.3.1/README.md +319 -0
- opendeck_mcp-0.3.1/ROADMAP.md +243 -0
- opendeck_mcp-0.3.1/pyproject.toml +57 -0
- opendeck_mcp-0.3.1/scripts/scrub_fixture.py +41 -0
- opendeck_mcp-0.3.1/server.json +22 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/__init__.py +3 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/__main__.py +74 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/bridge_plugin/dev.kastro5.opendeck-mcp-bridge.sdPlugin/bridge.js +78 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/bridge_plugin/dev.kastro5.opendeck-mcp-bridge.sdPlugin/icon.png +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/bridge_plugin/dev.kastro5.opendeck-mcp-bridge.sdPlugin/icon@2x.png +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/bridge_plugin/dev.kastro5.opendeck-mcp-bridge.sdPlugin/index.html +9 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/bridge_plugin/dev.kastro5.opendeck-mcp-bridge.sdPlugin/manifest.json +14 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/__init__.py +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/apps.py +99 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/catalog.py +103 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/devices.py +183 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/fonts.py +104 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/homeassistant.py +224 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/icons.py +202 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/images.py +95 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/jsonio.py +52 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/knowledge.py +135 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/manifest.py +285 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/paths.py +119 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/pi_scrape.py +104 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/preview.py +267 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/profile.py +147 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/render.py +113 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/secrets.py +77 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/core/validate.py +125 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/http.py +186 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/plugins/__init__.py +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/plugins/catalogue.py +127 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/plugins/installer.py +124 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/runtime/__init__.py +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/runtime/applier.py +267 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/runtime/backups.py +120 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/runtime/live.py +171 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/runtime/process.py +144 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/server.py +437 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/staging/__init__.py +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/staging/diff.py +113 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/staging/ops.py +528 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/staging/plan.py +74 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/staging/workspace.py +167 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/__init__.py +0 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/bridge_hook.py +48 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/edit_tools.py +508 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/ha_tools.py +95 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/icon_tools.py +30 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/plugin_tools.py +108 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/preview_tools.py +40 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/read_tools.py +211 -0
- opendeck_mcp-0.3.1/src/opendeck_mcp/tools/view.py +77 -0
- opendeck_mcp-0.3.1/tests/conftest.py +31 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/applications.json +5 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/images/ulanzi-d200h/Default/Keypad.12.0/0.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/deviceBrightness.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/inputSimulation.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/openUrl.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/plugin.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/runCommand.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/icons/switchProfile.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/manifest.json +108 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.amansprojects.starterpack.sdPlugin/propertyInspector/check.svg +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.ulanzi.d200x.sdPlugin/assets/icon.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.ulanzi.d200x.sdPlugin/assets/widescreen.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/com.ulanzi.d200x.sdPlugin/manifest.json +37 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/actionImage.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/actionImage@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/category.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/category@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button-off.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button-off@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button-on.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button-on@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/ha-button@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/plugin/plugin.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/images/plugin/plugin@2x.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/manifest.json +62 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/plugins/de.perdoctus.streamdeck.homeassistant.sdPlugin/previews/1-preview.png +0 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/profiles/ulanzi-d200h/Default.json +688 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/profiles/ulanzi-d200h.json +3 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/profiles/ulanzi-d200x/Default.json +145 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/profiles/ulanzi-d200x.json +3 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/settings/de.perdoctus.streamdeck.homeassistant.sdPlugin.json +1 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/settings/dev.kastro5.opendeck-mcp-bridge.sdPlugin.json +1 -0
- opendeck_mcp-0.3.1/tests/fixtures/config/settings.json +4 -0
- opendeck_mcp-0.3.1/tests/test_apps.py +20 -0
- opendeck_mcp-0.3.1/tests/test_core.py +139 -0
- opendeck_mcp-0.3.1/tests/test_homeassistant.py +95 -0
- opendeck_mcp-0.3.1/tests/test_http.py +72 -0
- opendeck_mcp-0.3.1/tests/test_icons.py +103 -0
- opendeck_mcp-0.3.1/tests/test_live.py +111 -0
- opendeck_mcp-0.3.1/tests/test_preview.py +84 -0
- opendeck_mcp-0.3.1/tests/test_server_and_plugins.py +53 -0
- opendeck_mcp-0.3.1/tests/test_staging.py +129 -0
- opendeck_mcp-0.3.1/uv.lock +1158 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# On a version tag: test, publish the package to PyPI, then list the release in the MCP Registry.
|
|
2
|
+
#
|
|
3
|
+
# git tag -a v1.2.3 -m "opendeck-mcp 1.2.3" && git push origin v1.2.3
|
|
4
|
+
#
|
|
5
|
+
# The tag must match pyproject.toml's version. Both publishes use the workflow's OIDC identity, so
|
|
6
|
+
# no tokens are stored: PyPI needs this repo + workflow + the "pypi" environment registered once as a
|
|
7
|
+
# trusted publisher for the opendeck-mcp project, and the registry accepts io.github.kastro5/* from it.
|
|
8
|
+
name: Publish
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags: ["v*"]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
environment: pypi
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
id-token: write # OIDC for PyPI trusted publishing and the MCP Registry
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
|
|
27
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
28
|
+
|
|
29
|
+
- name: Check the tag matches the version
|
|
30
|
+
run: |
|
|
31
|
+
version=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
32
|
+
test "v$version" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != version $version"; exit 1; }
|
|
33
|
+
test "$(jq -r .version server.json)" = "$version" || { echo "server.json version != $version"; exit 1; }
|
|
34
|
+
|
|
35
|
+
- name: Test
|
|
36
|
+
run: |
|
|
37
|
+
uv sync --locked
|
|
38
|
+
uv run pytest -q -m "not live"
|
|
39
|
+
|
|
40
|
+
- name: Build
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
45
|
+
|
|
46
|
+
- name: Publish to the MCP Registry
|
|
47
|
+
run: |
|
|
48
|
+
curl -fsSL -o mcp-publisher.tar.gz \
|
|
49
|
+
https://github.com/modelcontextprotocol/registry/releases/download/v1.8.1/mcp-publisher_linux_amd64.tar.gz
|
|
50
|
+
echo "a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc mcp-publisher.tar.gz" | sha256sum -c -
|
|
51
|
+
tar xzf mcp-publisher.tar.gz mcp-publisher
|
|
52
|
+
# PyPI can take a moment to serve a new release, and the registry checks it exists.
|
|
53
|
+
for i in 1 2 3 4 5 6; do
|
|
54
|
+
./mcp-publisher login github-oidc && ./mcp-publisher publish && exit 0
|
|
55
|
+
sleep 20
|
|
56
|
+
done
|
|
57
|
+
exit 1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Lint and unit tests on every push and pull request, on Windows (where it is used) and Linux
|
|
2
|
+
# (where the platform code is otherwise untested). The live tests need a real OpenDeck and are
|
|
3
|
+
# skipped here.
|
|
4
|
+
name: Tests
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
pull_request:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
os: [windows-latest, ubuntu-latest]
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
|
|
25
|
+
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
26
|
+
|
|
27
|
+
- name: Install
|
|
28
|
+
run: uv sync --locked
|
|
29
|
+
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: uv run ruff check src tests
|
|
32
|
+
|
|
33
|
+
- name: Test
|
|
34
|
+
run: uv run pytest -q -m "not live"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 — 2026-10-15
|
|
4
|
+
|
|
5
|
+
- Published on PyPI (`uvx opendeck-mcp`) and listed in the MCP Registry as
|
|
6
|
+
`io.github.kastro5/opendeck-mcp`; releases are published by GitHub Actions on version tags.
|
|
7
|
+
|
|
8
|
+
Upgrading: re-register with `uvx opendeck-mcp@0.3.1` (see the README).
|
|
9
|
+
|
|
10
|
+
## 0.3.0 — 2026-10-15
|
|
11
|
+
|
|
12
|
+
- **Icons.** `make_icon` makes a key image from text, an emoji (Twemoji), a brand logo (Simple
|
|
13
|
+
Icons, in the brand's colour) or a symbol (Material Design Icons), and saves it as a PNG to pass
|
|
14
|
+
as `image`. `find_icons` searches the icon sets by name, alias and tag.
|
|
15
|
+
- **Folders.** `make_folder` creates a sub-profile, a key that opens it and a Back key inside, with
|
|
16
|
+
titles placed under the icons.
|
|
17
|
+
- `get_app_rules` lists the exact names of the apps you have open, as OpenDeck matches them (the
|
|
18
|
+
executable's description on Windows), so app rules no longer need a trip through OpenDeck's UI.
|
|
19
|
+
- Staged moves to another profile no longer show a stray space in their description.
|
|
20
|
+
|
|
21
|
+
Upgrading: re-register with `@v0.3.0` (see the README).
|
|
22
|
+
|
|
23
|
+
## 0.2.0 — 2026-10-15
|
|
24
|
+
|
|
25
|
+
- **Instant edits.** `update_key` now changes a key's title, image and settings in the running
|
|
26
|
+
OpenDeck without restarting it, when the key is on the profile a connected device is showing.
|
|
27
|
+
Settings reach the plugin the same way OpenDeck's own settings panel sends them. Everything else
|
|
28
|
+
is still staged, and the result says which happened. A new `mode` parameter (`auto`, `live`,
|
|
29
|
+
`staged`) overrides the choice.
|
|
30
|
+
- New `undo_live` tool: puts back the titles, images and settings changed by instant edits. Images
|
|
31
|
+
are saved as bytes before each edit, so a custom key image can always be restored.
|
|
32
|
+
- **Home Assistant awareness.** `ha_search` finds entities by name, domain or area,
|
|
33
|
+
`ha_describe_entity` shows an entity's state and services and prints ready-made settings for a
|
|
34
|
+
Home Assistant key, and `ha_call_service` calls a service on request (after a confirmation step).
|
|
35
|
+
The server uses the login already saved in the Home Assistant plugin, and the token never appears
|
|
36
|
+
in any output.
|
|
37
|
+
- `describe_action` now explains the Home Assistant plugin's key settings.
|
|
38
|
+
- **Pictures of the deck.** `render_profile` draws a profile the way OpenDeck draws it (icons,
|
|
39
|
+
titles with their font, colour, stroke and alignment, background colours, the wide key, dials and
|
|
40
|
+
touchpoints), with staged keys outlined, or before and after side by side. OpenDeck's title fonts
|
|
41
|
+
are downloaded once from Google Fonts; offline, a system font with the same metrics is used.
|
|
42
|
+
- **HTTP transport.** `opendeck-mcp serve --host <ip>` serves over streamable HTTP for clients on
|
|
43
|
+
other machines, with a bearer token on every request and Host-header checks, and it refuses to
|
|
44
|
+
listen on every interface unless told to. `install-autostart` starts it hidden at each Windows
|
|
45
|
+
logon (from the Startup folder; no admin rights), `token` prints the client's `claude mcp add`
|
|
46
|
+
command, and `uninstall-autostart` removes it. Running `opendeck-mcp` with no arguments still
|
|
47
|
+
serves stdio.
|
|
48
|
+
- README: upgrading, other MCP clients, network use and stored files, troubleshooting.
|
|
49
|
+
- New dependencies: `websockets`, `resvg-py` (SVG icons in previews).
|
|
50
|
+
|
|
51
|
+
Upgrading: re-register with `@v0.2.0` (see the README). The bridge plugin updates itself on the next
|
|
52
|
+
apply.
|
|
53
|
+
|
|
54
|
+
## 0.1.0 — 2026-10-14
|
|
55
|
+
|
|
56
|
+
First release.
|
|
57
|
+
|
|
58
|
+
- Read devices, profiles, keys, plugins, plugin settings (redacted), logs and app rules.
|
|
59
|
+
- Stage key edits (set, update, clear, move/swap/copy), Multi/Toggle Action children and delays,
|
|
60
|
+
profile create/duplicate/rename/delete/select, app switching rules and plugin settings.
|
|
61
|
+
- Preview staged changes as a per-key diff with warnings; apply them with OpenDeck stopped, a backup,
|
|
62
|
+
verification and an automatic rollback; undo with `restore_backup`.
|
|
63
|
+
- Search the OpenAction catalogue and the Elgato archive; install, update and remove plugins after
|
|
64
|
+
inspecting them.
|
|
65
|
+
- Companion *OpenDeck MCP Bridge* plugin that records device geometry.
|
|
66
|
+
- Tested on Windows 11 with OpenDeck 2.14.1.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kastro5
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: opendeck-mcp
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: MCP server for configuring OpenDeck (open-source Stream Deck software) in natural language
|
|
5
|
+
Project-URL: Homepage, https://github.com/kastro5/opendeck-mcp
|
|
6
|
+
Project-URL: Changelog, https://github.com/kastro5/opendeck-mcp/blob/main/CHANGELOG.md
|
|
7
|
+
Project-URL: Issues, https://github.com/kastro5/opendeck-mcp/issues
|
|
8
|
+
Author: kastro5
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: claude,mcp,model-context-protocol,opendeck,stream-deck,streamdeck
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Desktop Environment
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: httpx==0.28.1
|
|
21
|
+
Requires-Dist: mcp==2.2.0
|
|
22
|
+
Requires-Dist: pillow==12.3.0
|
|
23
|
+
Requires-Dist: platformdirs==4.12.2
|
|
24
|
+
Requires-Dist: psutil==7.2.2
|
|
25
|
+
Requires-Dist: resvg-py==0.5.0
|
|
26
|
+
Requires-Dist: websockets==15.0.1
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# opendeck-mcp
|
|
30
|
+
|
|
31
|
+
<!-- mcp-name: io.github.kastro5/opendeck-mcp -->
|
|
32
|
+
|
|
33
|
+
Set up your [OpenDeck](https://github.com/nekename/OpenDeck) stream controller by asking an AI agent:
|
|
34
|
+
|
|
35
|
+
> "Put a Multi Action on the top-left key that mutes my mic and opens Discord, make a 'Gaming'
|
|
36
|
+
> profile from my default one, and switch to it whenever Steam is focused."
|
|
37
|
+
|
|
38
|
+
`opendeck-mcp` is an [MCP](https://modelcontextprotocol.io) server. It lets Claude Code (or any MCP client)
|
|
39
|
+
read your decks, profiles and plugins, **stage** changes, show you a diff, and then apply them, with a
|
|
40
|
+
backup you can undo. It works with any device OpenDeck drives: Elgato Stream Decks and plugin-provided
|
|
41
|
+
devices such as the [Ulanzi D200H/D200X](https://github.com/kastro5/opendeck-ulanzi-d200).
|
|
42
|
+
|
|
43
|
+
## Contents
|
|
44
|
+
|
|
45
|
+
- [What it can do](#what-it-can-do)
|
|
46
|
+
- [Install](#install)
|
|
47
|
+
- [Upgrading](#upgrading)
|
|
48
|
+
- [Other MCP clients](#other-mcp-clients)
|
|
49
|
+
- [From another computer](#from-another-computer)
|
|
50
|
+
- [Using it](#using-it)
|
|
51
|
+
- [How it works](#how-it-works)
|
|
52
|
+
- [Safety](#safety)
|
|
53
|
+
- [Tools](#tools)
|
|
54
|
+
- [Limitations](#limitations)
|
|
55
|
+
- [Troubleshooting](#troubleshooting)
|
|
56
|
+
- [Development](#development)
|
|
57
|
+
|
|
58
|
+
## What it can do
|
|
59
|
+
|
|
60
|
+
- **See everything:** devices with their grid size, dials and touchpoints; profiles; every key's action,
|
|
61
|
+
title and settings; and installed plugins with their actions.
|
|
62
|
+
- **Edit keys:** place, update (title, font, colour, image, settings), clear, move, swap or copy keys, dials
|
|
63
|
+
and touchpoints, across profiles and devices.
|
|
64
|
+
- **Multi and Toggle Actions:** add and remove the actions inside them, and set the delays between them.
|
|
65
|
+
- **Profiles:** create, duplicate, rename (references to the old name are updated too) and delete
|
|
66
|
+
profiles, including `Folder/Name` profiles, and choose which one each device starts on.
|
|
67
|
+
- **App switching:** switch a device to a profile automatically when an app is focused.
|
|
68
|
+
- **Plugins:**
|
|
69
|
+
- search the same catalogue as OpenDeck's Plugin Manager, plus the Elgato plugin archive;
|
|
70
|
+
- install, update and remove plugins;
|
|
71
|
+
- read and write plugin settings, with secrets redacted;
|
|
72
|
+
- read plugin logs.
|
|
73
|
+
- **Instant edits:** title, image and settings tweaks to a key on the profile a device is showing
|
|
74
|
+
apply at once, with no restart; `undo_live` takes them back.
|
|
75
|
+
- **Icons and folders:** make key images from text, emoji, ~3,400 brand logos or ~7,400 symbols, and
|
|
76
|
+
folder keys that open a sub-profile with a Back key.
|
|
77
|
+
- **Pictures:** `render_profile` shows the deck as OpenDeck draws it, with staged changes outlined or
|
|
78
|
+
before and after side by side, so you can check a layout before applying it.
|
|
79
|
+
- **Home Assistant:** find entities by name, area or domain, and get ready-made settings for a key
|
|
80
|
+
that controls one, using the login already saved in the Home Assistant plugin.
|
|
81
|
+
- **Undo:** every apply takes a backup, and `restore_backup` puts things back.
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
You need OpenDeck 2.14, [uv](https://docs.astral.sh/uv/) and Claude Code (or another MCP client, see
|
|
86
|
+
below). uv fetches a suitable Python by itself.
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
claude mcp add opendeck --scope user -- uvx opendeck-mcp@0.3.1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then restart Claude Code and ask it something like *"what's on my OpenDeck?"*.
|
|
93
|
+
|
|
94
|
+
It's on [PyPI](https://pypi.org/project/opendeck-mcp/) and in the
|
|
95
|
+
[MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.kastro5/opendeck-mcp`. To run
|
|
96
|
+
straight from GitHub instead: `uvx --from git+https://github.com/kastro5/opendeck-mcp@v0.3.1 opendeck-mcp`.
|
|
97
|
+
|
|
98
|
+
OpenDeck's folders are found automatically. If yours live somewhere unusual, override them with
|
|
99
|
+
environment variables (`claude mcp add … -e NAME=value`):
|
|
100
|
+
|
|
101
|
+
| Variable | Default (Windows) |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `OPENDECK_CONFIG_DIR` | `%APPDATA%\opendeck` |
|
|
104
|
+
| `OPENDECK_EXE` | `%LOCALAPPDATA%\OpenDeck\opendeck.exe` |
|
|
105
|
+
| `OPENDECK_LOG_DIR` | `%LOCALAPPDATA%\opendeck\logs` |
|
|
106
|
+
| `OPENDECK_MCP_DATA_DIR` | `%LOCALAPPDATA%\opendeck-mcp` (the server's own plan, backups and cache) |
|
|
107
|
+
| `OPENDECK_MCP_HA_VERIFY_TLS` | `1`; set `0` for a Home Assistant on https with a self-signed certificate |
|
|
108
|
+
| `OPENDECK_MCP_TOKEN` | the HTTP token (default: generated once, see below) |
|
|
109
|
+
|
|
110
|
+
### Upgrading
|
|
111
|
+
|
|
112
|
+
The version is pinned in the command, so replace the registration with the new tag:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
claude mcp remove opendeck --scope user
|
|
116
|
+
claude mcp add opendeck --scope user -- uvx opendeck-mcp@0.3.1
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The first apply after an upgrade also updates the companion bridge plugin (see
|
|
120
|
+
[How it works](#how-it-works)), in the same restart.
|
|
121
|
+
|
|
122
|
+
### Other MCP clients
|
|
123
|
+
|
|
124
|
+
Any client that can start a stdio server works. The usual JSON configuration (Claude Desktop,
|
|
125
|
+
Cursor, VS Code and others) is:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"opendeck": {
|
|
131
|
+
"command": "uvx",
|
|
132
|
+
"args": ["opendeck-mcp@0.3.1"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### From another computer
|
|
139
|
+
|
|
140
|
+
The server has to run on the PC that runs OpenDeck, inside your logged-in desktop session: it
|
|
141
|
+
restarts OpenDeck and talks to it through its command line, which doesn't work from a service or an
|
|
142
|
+
SSH session. To use it from another machine (a laptop, a dev VM), serve it over HTTP there and
|
|
143
|
+
connect to it. [Tailscale](https://tailscale.com) or another private network is the recommended way
|
|
144
|
+
in; every request also needs a token.
|
|
145
|
+
|
|
146
|
+
On the OpenDeck PC (Windows):
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
uvx opendeck-mcp@0.3.1 install-autostart --host <tailscale-ip>
|
|
150
|
+
uvx opendeck-mcp@0.3.1 token --host <tailscale-ip>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The first command puts a small script in your Startup folder that starts the server, without a
|
|
154
|
+
window, at every logon, and starts it now. The second prints the command to run on the other
|
|
155
|
+
machine:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
claude mcp add --transport http opendeck http://<tailscale-ip>:8765/mcp --header "Authorization: Bearer <token>"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- `tailscale ip -4` shows the address. The server refuses `0.0.0.0` (every network) unless you add
|
|
162
|
+
`--allow-all-interfaces`.
|
|
163
|
+
- To connect by name (e.g. a MagicDNS name), add `--allowed-host <name>`; other `Host` headers are
|
|
164
|
+
rejected.
|
|
165
|
+
- `opendeck-mcp token --reset` makes a new token, and `opendeck-mcp uninstall-autostart` removes
|
|
166
|
+
the autostart and stops the server.
|
|
167
|
+
- Windows Firewall must allow inbound TCP 8765 on that network; it asks the first time if it isn't
|
|
168
|
+
already allowed.
|
|
169
|
+
- On Linux or macOS, run `opendeck-mcp serve --host <ip>` from a systemd user service or LaunchAgent.
|
|
170
|
+
|
|
171
|
+
## Using it
|
|
172
|
+
|
|
173
|
+
Just describe what you want. Under the hood, the agent:
|
|
174
|
+
|
|
175
|
+
1. calls `get_overview` and `show_profile` to see your devices and keys;
|
|
176
|
+
2. uses `list_actions` / `describe_action` to find the right action and learn its settings;
|
|
177
|
+
3. stages edits with `set_key`, `add_child`, `manage_profile` and so on;
|
|
178
|
+
4. calls `preview_changes` (and `render_profile` for a picture), shows you the result, and waits for your
|
|
179
|
+
OK;
|
|
180
|
+
5. calls `apply_changes`, which restarts OpenDeck for a few seconds and reports the backup id.
|
|
181
|
+
|
|
182
|
+
Small tweaks skip all that. Changing an existing key's title, image or settings on the profile a
|
|
183
|
+
connected device is showing happens instantly, without a restart, and `undo_live` reverses it. Title
|
|
184
|
+
style, the shown state, other profiles and every structural change still go through staging.
|
|
185
|
+
|
|
186
|
+
For example, *"make a Fusion 360 profile with keys for my most-used sketch and solid tools, put the
|
|
187
|
+
rest in a folder, and switch to it whenever Fusion is in front"* builds a profile of Simulate Input
|
|
188
|
+
keys sending Fusion's shortcuts, with icons from `make_icon`, a "More" folder and the app rule.
|
|
189
|
+
|
|
190
|
+
Keys are addressed the way `show_profile` draws them:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
c1 c2 c3 c4 c5
|
|
194
|
+
r1 Multi Action "GitHub" Toggle Action · ·
|
|
195
|
+
r2 · Entity (custo… · Switch Profile ·
|
|
196
|
+
r3 Device Bright… · "rew" · (wide →)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
| Slot | Meaning |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `r1c1` | row 1, column 1 |
|
|
202
|
+
| `key13` | raw position 13 (0-based) |
|
|
203
|
+
| `dial1` | first dial |
|
|
204
|
+
| `touch1` | first touchpoint |
|
|
205
|
+
| `r1c1/child2` | second action inside a Multi or Toggle Action |
|
|
206
|
+
|
|
207
|
+
## How it works
|
|
208
|
+
|
|
209
|
+
OpenDeck has no API for changing its layout. It keeps profiles in memory and overwrites any file edited
|
|
210
|
+
while it runs. So `opendeck-mcp` works on the files, with OpenDeck stopped:
|
|
211
|
+
|
|
212
|
+
1. **Staging.** Each edit is recorded as an operation in a plan. Nothing is written yet.
|
|
213
|
+
`show_profile` shows staged edits marked with `*`.
|
|
214
|
+
2. **Applying.**
|
|
215
|
+
- First, OpenDeck is asked to save its in-memory state. This sends a profile switch through its command
|
|
216
|
+
line, which makes it write the selected profile.
|
|
217
|
+
- Then OpenDeck and its plugin processes are stopped.
|
|
218
|
+
- The plan is replayed onto the freshly read files. If something you staged was changed in OpenDeck's
|
|
219
|
+
UI in the meantime, apply reports a conflict and doesn't write anything.
|
|
220
|
+
- The files are backed up, written atomically, read back and re-validated. If anything fails, the
|
|
221
|
+
backup is restored automatically.
|
|
222
|
+
- Finally, OpenDeck is started again, detached and hidden.
|
|
223
|
+
3. **Faithful files.** Action data is rebuilt from plugin manifests exactly as OpenDeck builds it, and
|
|
224
|
+
JSON is written byte-for-byte in OpenDeck's format. A profile OpenDeck can't read gets replaced by an
|
|
225
|
+
empty one, so every write is validated twice.
|
|
226
|
+
4. **Device geometry.** OpenDeck doesn't store a device's rows × columns anywhere, so the first apply also
|
|
227
|
+
installs a tiny companion plugin, *OpenDeck MCP Bridge* (no actions, about 60 lines of JavaScript). It
|
|
228
|
+
records the geometry OpenDeck reports to plugins into its own settings file, where the server reads it.
|
|
229
|
+
It's versioned with the server, so a new release updates it on its next apply.
|
|
230
|
+
5. **Images.** New key images are stored as `data:` URLs. OpenDeck accepts these and converts them to
|
|
231
|
+
files itself on its next save.
|
|
232
|
+
|
|
233
|
+
### Instant edits
|
|
234
|
+
|
|
235
|
+
Titles and images are sent to the running OpenDeck through its command line (`--process-message`),
|
|
236
|
+
as `setTitle` and `setImage` events. Settings go over OpenDeck's local plugin websocket, posing as
|
|
237
|
+
the key's settings panel, because that is the only path on which OpenDeck tells the plugin its
|
|
238
|
+
settings changed. OpenDeck is then asked to save, and the file is checked. Before each edit the old
|
|
239
|
+
titles, images (as bytes) and settings are written to `live_journal.jsonl` in the server's data
|
|
240
|
+
folder, which `undo_live` replays.
|
|
241
|
+
|
|
242
|
+
## Safety
|
|
243
|
+
|
|
244
|
+
- **Nothing changes until you confirm.** Staging tools never touch OpenDeck. `apply_changes`,
|
|
245
|
+
`restore_backup` and plugin installs all need a second call with `confirm=true`.
|
|
246
|
+
- **Backups.** Every apply first copies `profiles/`, `images/`, `applications.json` and anything else it
|
|
247
|
+
touches into `%LOCALAPPDATA%\opendeck-mcp\backups`. It keeps the last 30 backups (always at least 5).
|
|
248
|
+
Restoring also backs up the current state first. Backups contain your plugin settings, including
|
|
249
|
+
secrets, so they stay on your machine.
|
|
250
|
+
- **Secrets are redacted.** Anything that looks like a credential (token, password, API key, JWT, Bearer
|
|
251
|
+
value, …) is shown as `«redacted»` in settings, diffs, examples and logs. Passing `«redacted»` back keeps
|
|
252
|
+
the stored value.
|
|
253
|
+
- **Risky keys are flagged.** Run Command keys run commands, Simulate Input keys type into the focused
|
|
254
|
+
window, and Open URL keys with non-web URIs can launch programs. Each is marked ⚠ when staged and in the
|
|
255
|
+
preview.
|
|
256
|
+
- **Plugins are third-party code.** `stage_plugin` first downloads and inspects the plugin: author, version,
|
|
257
|
+
sha256, how it runs, which platforms it supports, and the catalogue's *AI-assisted* flag. It only stages
|
|
258
|
+
the install after you agree. Built-in plugins can't be removed.
|
|
259
|
+
- **Home Assistant.** The Home Assistant tools reuse the server URL and access token saved by the
|
|
260
|
+
Home Assistant plugin. The token is only sent to that server, never shown. `ha_call_service` acts
|
|
261
|
+
on your real home, so it needs a second call with `confirm=true`.
|
|
262
|
+
- **Instant edits aren't backed up.** They don't go through apply, so instead the previous titles,
|
|
263
|
+
images and settings are kept in a journal (the last 200 edits) that `undo_live` replays.
|
|
264
|
+
- **The HTTP token is a key to your deck.** Anyone with it who can reach the port can change keys,
|
|
265
|
+
install plugins and, through Run Command keys, run programs. Keep the server on a private address,
|
|
266
|
+
and `opendeck-mcp token --reset` if the token leaks.
|
|
267
|
+
- **Version check.** Apply refuses to run on OpenDeck versions other than 2.14.x unless you allow it
|
|
268
|
+
explicitly.
|
|
269
|
+
|
|
270
|
+
### Network and files
|
|
271
|
+
|
|
272
|
+
- **Internet:** only the plugin catalogues and plugin downloads (`find_plugins`, `stage_plugin`), and
|
|
273
|
+
the title fonts for `render_profile` and the icon sets for `make_icon`, fetched once each from
|
|
274
|
+
jsDelivr and cached. Nothing else, and no telemetry.
|
|
275
|
+
- **Local network:** your Home Assistant, only when the Home Assistant tools are used.
|
|
276
|
+
- **Files** in `%LOCALAPPDATA%\opendeck-mcp`: `plan.json` (staged changes), `backups/`,
|
|
277
|
+
`live_journal.jsonl`, `icons/` (made by `make_icon`), `cache/` (catalogues, fonts, icon sets),
|
|
278
|
+
`device_overrides.json`, and for HTTP
|
|
279
|
+
`http_token` and `http.log`. Delete the folder to reset everything.
|
|
280
|
+
|
|
281
|
+
## Tools
|
|
282
|
+
|
|
283
|
+
| Kind | Tools |
|
|
284
|
+
|---|---|
|
|
285
|
+
| 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` |
|
|
286
|
+
| 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` |
|
|
287
|
+
| Icons | `make_icon` (saves a PNG to pass as `image`) |
|
|
288
|
+
| Apply | `apply_changes`, `restore_backup`, `undo_live` |
|
|
289
|
+
| Home Assistant | `ha_search`, `ha_describe_entity`, `ha_call_service` |
|
|
290
|
+
|
|
291
|
+
## Limitations
|
|
292
|
+
|
|
293
|
+
- **Apply restarts OpenDeck** (about 5 seconds). Unsaved edits in an open settings panel are lost, and
|
|
294
|
+
plugins restart.
|
|
295
|
+
- **Some settings need examples.** Settings are known in detail for OpenDeck's own actions and the Starter
|
|
296
|
+
Pack. For other plugins, the agent works from hints on the plugin's settings page and from your existing
|
|
297
|
+
keys. If those aren't enough, place the action and finish setting it up in OpenDeck's UI.
|
|
298
|
+
- **App rule names.** App rules match the name OpenDeck gives the focused app: on Windows the
|
|
299
|
+
executable's description (e.g. `Autodesk Fusion`, `Visual Studio Code`), otherwise its file name.
|
|
300
|
+
`get_app_rules` lists the exact names of the apps you have open.
|
|
301
|
+
- **Tested platform.** Tested on Windows 11 with OpenDeck 2.14.1, a Ulanzi D200H and Home Assistant.
|
|
302
|
+
The macOS and Linux paths and process handling are written but untested.
|
|
303
|
+
- **Limited live control.** Title, image and settings edits apply instantly; everything else needs
|
|
304
|
+
a restart. It doesn't press keys or switch profiles live.
|
|
305
|
+
- **Pictures are close, not exact.** `render_profile` can't see images a plugin draws while running (a
|
|
306
|
+
clock, a live Home Assistant state) unless OpenDeck has saved them, and OpenDeck's own Multi/Toggle
|
|
307
|
+
Action icons are drawn as look-alikes.
|
|
308
|
+
- **An open settings panel goes quiet.** After an instant settings edit, a settings panel already open
|
|
309
|
+
in OpenDeck's window for that key stops updating until it's reopened.
|
|
310
|
+
|
|
311
|
+
## Troubleshooting
|
|
312
|
+
|
|
313
|
+
- **"OpenDeck's executable wasn't found" / "config folder wasn't found"**: set `OPENDECK_EXE` /
|
|
314
|
+
`OPENDECK_CONFIG_DIR` (see [Install](#install)).
|
|
315
|
+
- **Apply refuses: untested OpenDeck version.** Check the changelog for a release that supports
|
|
316
|
+
yours; `apply_changes(allow_untested_version=true)` overrides it at your own risk, with a backup.
|
|
317
|
+
- **Apply reports a conflict.** A key you staged was changed in OpenDeck meanwhile. Look at
|
|
318
|
+
`preview_changes`, then discard and re-stage it, or apply with `on_conflict="skip"`.
|
|
319
|
+
- **A device shows "geometry unknown".** It's learned by the bridge plugin, installed with the first
|
|
320
|
+
apply. Until then `set_device_layout` sets rows and columns by hand.
|
|
321
|
+
- **Undo an apply:** `list_backups`, then `restore_backup`.
|
|
322
|
+
- **HTTP client gets 401 / 421.** 401: wrong or missing token (`opendeck-mcp token`). 421: the client
|
|
323
|
+
used a host name the server doesn't know; add it with `--allowed-host`. The server logs to
|
|
324
|
+
`http.log` in its data folder.
|
|
325
|
+
|
|
326
|
+
## Development
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
git clone https://github.com/kastro5/opendeck-mcp && cd opendeck-mcp
|
|
330
|
+
uv sync
|
|
331
|
+
uv run pytest # unit tests, against a scrubbed fixture config
|
|
332
|
+
uv run pytest -m live # also check snapshots against your real OpenDeck install (read-only)
|
|
333
|
+
claude mcp add opendeck-dev -- uv --directory /path/to/opendeck-mcp run opendeck-mcp
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Test fixtures are real OpenDeck profiles with every setting value replaced
|
|
337
|
+
(`scripts/scrub_fixture.py`), plus trimmed plugin manifests with empty icon files.
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT. Not affiliated with OpenDeck or Elgato.
|
|
342
|
+
|
|
343
|
+
Icons and fonts are downloaded at run time, not bundled:
|
|
344
|
+
[Material Design Icons](https://pictogrammers.com/library/mdi/) (Apache 2.0),
|
|
345
|
+
[Simple Icons](https://simpleicons.org) (CC0; brand logos remain their owners' trademarks),
|
|
346
|
+
[Twemoji](https://github.com/jdecked/twemoji) (graphics CC-BY 4.0, © Twitter/X and contributors), and
|
|
347
|
+
OpenDeck's title fonts from [Google Fonts](https://github.com/google/fonts) (OFL).
|