companion-editor-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@companion-editor-mcpDry run pressing the STREAM STOP button, then show me the audit log"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
companion-editor-mcp
Let an AI assistant read and, when you allow it, control Bitfocus Companion 5.1 through a small, security-gated MCP server.
Built for live-event operators. The AI can only touch buttons and variables you have put on an allowlist, every write defaults to a dry run, and nothing is written until you turn writes on.
Based on companion-mcp by MyEvent Labs, extended with trigger editing, feedback styling and Companion 5.1 support. MIT licensed.
Quick start
You need Node.js 22 or later, git, and Companion 5.1 running on this machine or your private network.
1. Enable Companion's HTTP API. In the Companion web UI: Settings, HTTP, turn on HTTP API. Note the port (default 8000).
2. Install. One line, macOS or Linux:
curl -fsSL https://raw.githubusercontent.com/davidadevore/companion-editor-mcp/main/scripts/install.sh | bashWindows PowerShell:
irm https://raw.githubusercontent.com/davidadevore/companion-editor-mcp/main/scripts/install.ps1 | iexThe installer clones to ~/companion-editor-mcp, builds, creates an empty allowlist, checks the server starts, and prints a ready-made client config with your paths filled in. Set COMPANION_EDITOR_MCP_DIR first to install elsewhere. Prefer to do it by hand? git clone, npm ci, npm run build.
3. Add buttons to the allowlist. Edit ~/companion-editor-mcp/config/allowlist.json (see Allowlist). Until you do, nothing can be pressed.
4. Connect your AI client. Paste the config the installer printed into your client (see Connect your AI client), restart the client, and ask it to run ping.
5. Turn writes on when you want them. Change COMPANION_ALLOW_WRITES from "false" to "true" in the client config and restart the client. Turn it back off after the show.
If you use an AI assistant with a terminal, you can paste this instead of steps 2 and 4:
Install companion-editor-mcp from https://github.com/davidadevore/companion-editor-mcp using
scripts/install.sh (macOS/Linux) or scripts/install.ps1 (Windows), then add the JSON it
prints to my MCP client config and tell me where you put it. Leave writes disabled.Related MCP server: OBS MCP
Safety model
Read-only by default. Nothing is written unless
COMPANION_ALLOW_WRITES=true.Allowlist. Only buttons, variables and connections you list can be written. Anything else is refused, whatever the AI asks.
Dry run by default. Every write tool reports what it would send unless the AI passes
dry_run: false.Confirm for high risk. Buttons marked
highand all connection or config edits also needconfirm: true.One attempt, no retries. On timeout, reset or server error it reports "outcome unknown" instead of guessing or retrying.
Cooldown. The same target cannot be written twice within 2 seconds, or while a write is in flight.
Audit log first. An "attempt" line is written before every real write. If the log cannot be written, writes are refused.
Private network only. Refuses to talk to a public address unless
COMPANION_ALLOW_REMOTE=true.No open ports. It is a local process your AI client launches over stdio.
Documented API only for live control. The optional config-edit tools use Companion's internal API and sit behind a second flag.
Allowlist
config/allowlist.json is the list of things the AI may write. It is git-ignored, so your rig layout is never committed. The installer creates it empty:
{ "buttons": [], "variables": [] }Add only buttons that really exist. Open each one in the Companion button editor and copy the page, row and column from there. Full example (config/allowlist.example.json):
{
"buttons": [
{ "page": 1, "row": 0, "column": 0, "label": "Program: Camera 1", "risk": "low" },
{ "page": 1, "row": 3, "column": 7, "label": "STREAM STOP", "risk": "high" }
],
"variables": ["cue", "speaker_name"],
"connections": ["replace-with-id-from-list_connections"],
"surfaces_rescan": false,
"pages_create": false
}Field | Meaning |
| Page number, 1 to 99 |
| 0-based grid position as shown in the button editor, 0 to 31 |
| Your description. Shown to the AI and written to the log |
|
|
| Custom variable names the AI may set. Empty or missing forbids all |
| Connection ids (from |
|
|
|
|
|
|
Allow everything. Instead of the object, the whole file can be the string "all". Every button (as low risk), variable and connection is then permitted, and surfaces_rescan, pages_create and triggers_edit are on. The write flags and confirm rules still apply.
Rules: no duplicates, no unknown keys, names use letters, digits, _ and -. A missing or invalid file stops the server with a clear message.
If you move or delete a button in Companion, update the allowlist before the next show. A press on an empty slot does nothing; a press on a slot you later reused fires whatever is there now.
Connect your AI client
All clients need the same three things: node, the path to dist/index.js, and the environment variables. Replace /abs/path/companion-editor-mcp with your install path (the installer prints this already filled in). On Windows use forward slashes in JSON, or double every backslash.
Claude Desktop, Cursor, Windsurf, Cline and most others
Add this inside the mcpServers object of the client's config file (Claude Desktop: Settings, Developer, Edit Config. Cursor: ~/.cursor/mcp.json. Windsurf: ~/.codeium/windsurf/mcp_config.json).
"companion": {
"command": "node",
"args": ["/abs/path/companion-editor-mcp/dist/index.js"],
"env": {
"COMPANION_URL": "http://127.0.0.1:8000",
"COMPANION_ALLOW_WRITES": "false",
"COMPANION_ALLOWLIST_PATH": "/abs/path/companion-editor-mcp/config/allowlist.json",
"COMPANION_LOG_DIR": "/abs/path/companion-editor-mcp/logs"
}
}Windows example of the path form: "C:\\Users\\you\\companion-editor-mcp\\dist\\index.js".
Fully quit and reopen the client afterwards; servers are only launched at startup. Closing the window is not enough. Claude Desktop keeps running in the background, so force it from a terminal:
Windows:
taskkill /IM Claude.exe /FmacOS:
pkill -x ClaudeThen open the client again.
Claude Code (CLI)
claude mcp add companion --scope user \
-e COMPANION_URL=http://127.0.0.1:8000 \
-e COMPANION_ALLOW_WRITES=false \
-e COMPANION_ALLOWLIST_PATH=$HOME/companion-editor-mcp/config/allowlist.json \
-e COMPANION_LOG_DIR=$HOME/companion-editor-mcp/logs \
-- node $HOME/companion-editor-mcp/dist/index.jsWindows PowerShell: same command with ` instead of \ at line ends and $env:USERPROFILE\companion-editor-mcp\... instead of $HOME/companion-editor-mcp/.... Check with claude mcp list, remove with claude mcp remove companion.
VS Code (Copilot agent mode)
.vscode/mcp.json in a workspace, same fields under "servers" with "type": "stdio" added.
Check it works
Ask the client to run ping. Expected:
companion-editor-mcp is running. Writes disabled. Config edits disabled. Companion at 127.0.0.1:8000.Then list_allowlist should show your buttons, and get_custom_variable with a variable you have should return its value.
Turning writes on and off
Change "COMPANION_ALLOW_WRITES": "false" to "true" (or -e COMPANION_ALLOW_WRITES=true for Claude Code, after claude mcp remove companion) and restart the client. ping then says Writes ENABLED. Set it back to "false" after the show.
Settings
All are environment variables. All are optional.
Variable | Default | Meaning |
|
| Companion base URL. Scheme and host only |
|
|
|
|
|
|
|
|
|
|
| Path to the allowlist. Must end in |
|
| Per request timeout, 100 to 30000 |
|
| Where the audit log goes |
Any invalid value, including an empty string, stops the server at startup with a message on stderr.
Tools
Read tools work whenever the server runs. Write tools need COMPANION_ALLOW_WRITES=true, an allowlist entry, and dry_run: false.
Tool | Writes | What it does |
| no | Liveness. Reports write and config-edit state. No Companion request |
| no | Shows everything the write tools may touch |
| no | Read |
| no | Read |
| no | All connections with id, module and status |
| no | One connection by id |
| yes | Press and release an allowlisted button |
| yes |
|
| yes | Set the current step (1-based) of a multi-step button |
| yes | Change text, text colour, background colour and/or size |
| yes | Set an allowlisted custom variable (text, 1 to 1000 characters) |
| yes |
|
| yes | Rescan for USB surfaces. Needs |
Every tool returns a one-line summary plus structured data. Refusals and errors come back as errors with a plain reason, never a stack trace. A press result of ok means Companion accepted the request, not that the device acted; confirm by reading a variable back.
This covers every command in Companion's documented HTTP API. Commands that exist only on other transports (surface page changes on TCP/UDP) are not available.
Config-edit tools (experimental)
Five more tools edit Companion's configuration instead of pressing things. They need both COMPANION_ALLOW_WRITES=true and COMPANION_ALLOW_CONFIG_EDITS=true.
Tool | What it does |
| Read a button's control id, type, feedbacks and actions with entity ids and options |
| Create an empty button at an allowlisted slot. Replaces anything there, so needs |
| Delete the button at an allowlisted slot. Needs |
| Apply operations in order: |
| Insert empty pages at a position. Needs |
Feedback colours. set_feedback_style sets what a feedback changes on the button while it is active: bgcolor and color as #rrggbb, and text. They are stored as style overrides on the button's default box0 (background) and text0 (text) layers. Setting a property again replaces its override in place. remove_feedback_style takes properties: ["bgcolor", "color", "text"]. get_button shows each feedback's styleOverrides and the button's layers.
Trigger tools
Reads need COMPANION_ALLOW_CONFIG_EDITS=true. Writes also need COMPANION_ALLOW_WRITES=true and "triggers_edit": true in the allowlist. All writes default to dry_run: true.
Tool | What it does |
| Every trigger: control id, name, enabled, event description, collection, last run |
| One trigger's events, conditions and actions with their ids and options |
| New empty trigger with a name, always created disabled |
| Companion's own clone of a trigger |
| Delete a trigger. Needs |
| Operations in order: |
Removing an event or entity, and set_enabled: true (the trigger can then fire on its own), need confirm: true. Event options are plain values (for example { "seconds": 60 } for interval, { "variableId": "custom:cue" } for variable_changed). Actions and conditions inside logic groups are not supported yet.
Why experimental. Companion's HTTP API cannot do any of this, so these use the internal admin API that Companion's own web UI uses. It is undocumented and unauthenticated. The calls here were first taken from the Companion 5.0.7 source, then updated for 5.1 from the 5.1.0 web UI's own calls and read-only snapshots of a live 5.1.0 instance. Every config-edit, feedback-style and trigger write was then run end to end against a Companion 5.1.1 test instance. Every response is validated, so if a Companion update changes the internal API the tools fail with "unexpected shape" rather than doing something odd. Verify them once on a test Companion (script below) before trusting them, and keep config edits off on show days.
Tips:
Call
get_buttonfirst so you know existing entity ids.Action and feedback ids (for example
program,tally) come from the connection module.list_connectionsgives the connection id.New actions go to step 1, action set
down, unless you passset: "up" | "rotate_left" | "rotate_right".update_buttondoes not change the button's own text or colours. Useset_button_style. It does set the colours a feedback applies (set_feedback_style).Inserting a page renumbers the pages after it. Review the allowlist afterwards.
Verify once on a test instance. Put a low entry at a slot you can sacrifice (say page 1, row 3, column 7) and "pages_create": true in the allowlist, start with both flags true, then:
Step | Do | Expect |
1 |
|
|
2 |
|
|
3 |
| New empty button appears in the editor |
4 |
| Action appears on the button's Down step. |
5 |
| Option changes, then the action disappears |
6 |
| Text changes |
7 |
| Slot is empty |
8 |
| New page appears. Delete it in the Companion UI afterwards |
If any step reports "unexpected shape" or "Companion rejected", stop and open an issue with your Companion version.
Audit log
<COMPANION_LOG_DIR>/companion-editor-mcp.jsonl, one JSON object per line:
{
"ts": "2026-01-01T10:00:00.000Z",
"tool": "press_button",
"args": { "page": 1, "row": 0, "column": 0, "dry_run": false, "confirm": false },
"outcome": "ok",
"allowed": true,
"dryRun": false,
"detail": "pressed \"Program: Camera 1\" at 1/0/0. Companion accepted the press.",
"durationMs": 12
}outcome is attempt (written before a real write), ok, refused or error. Variable values are never logged. Secrets-looking keys are redacted. The file is created mode 0600, rotates at 5 MB and keeps 5 backups.
Before a show
Companion is 5.1 with the HTTP API enabled.
Allowlist matches the current page layout.
COMPANION_URLpoints at the show instance, not a test machine.Writes on only for the event, off afterwards. Config edits off.
Dry run every button you plan to use (
press_buttonwith the defaultdry_run: true) and check the labels.Log directory is writable with free space.
pingreports the expected write state and host.
First-time check on a test instance (never the show network): with writes off, ping, get_custom_variable, and a press_button with dry_run: false (expect "writes are disabled"). With writes on: a dry-run press (nothing fires), a real press (fires), a high-risk press without then with confirm (refused, then fires), a press off the allowlist (refused), set_custom_variable then read it back.
Troubleshooting
Message | Fix |
| Create |
| Use Companion's private LAN address, or set |
| Settings, HTTP, enable HTTP API |
| Wrong host or port, Companion not running, or a firewall. Test: |
| Something other than Companion is on that port, or a different Companion version |
| The allowlist entry points at an empty slot |
| Set |
| Set both |
|
|
Tools do not appear in the client | Paths not absolute, or the client was not fully restarted. Run |
Development
npm test # vitest, 170 tests incl. end to end over stdio against mocks
npm run check # everything CI runs: typecheck, lint, format, build, coverage, audit, secret scan
npx husky # optional pre-commit and pre-push hooks (needs gitleaks on PATH)docs/companion-api.md is the ground truth for the HTTP API; src/companion-config.ts documents the internal procedures used. AGENTS.md holds the rules for humans and AI agents working on the code. This is a stdio process launched by an MCP client; it has no web output and is not meant to be hosted.
Credits
Based on companion-mcp by MyEvent Labs (MIT). Maintained by davidadevore. Companion is a product of Bitfocus AS; this project is not affiliated with or endorsed by Bitfocus.
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect AI assistants to your approved computer for files, commands, browser and desktop tasks.
Operate GitHub from your AI assistant: repos, PRs, issues, Actions. Previews and read-only mode.
Connect AI assistants to explicitly paired computers, servers, and VMs for permitted remote actions.
Connect any AI to your Foundry VTT world: actors, combat, dice, journals, tokens, compendiums.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables AI assistants to control DualStream streaming studio: switch scenes, compose layouts, manage widgets, and react to stream events.30Apache 2.0
- AlicenseBqualityBmaintenanceEnables AI assistants to control and automate OBS Studio via natural language, covering scenes, sources, audio, recording, streaming, transitions, filters, media playback, diagnostics, and multi-step workflows over the OBS WebSocket protocol.89MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to inspect and safely control OBS Studio over obs-websocket v5, exposing scenes, inputs, filters, outputs and video settings as read and write tools that default to dry runs and refuse changes while live unless explicitly confirmed. It also provides Source Record and IRL readiness checks, a BRB auto-switcher, and a scene timeline log for cutting clips from VODs.17MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP-capable AI agents such as Claude, Codex, Gemini CLI, Cursor and Zed to design and manage a Stream Deck by reading layouts, building pages and keys, setting icons and wallpapers, managing profiles and taking screenshots to verify their own work. Access is governed by read-only, edit-layout and full-control levels, with approval prompts before any tool runs something on the user's computer.MIT