eos-mcp-server
The eos-mcp-server enables AI agents to control an ETC Eos lighting console via OSC, offering automated show control, channel manipulation, and diagnostics primarily for test systems. Key capabilities:
Playback control: Fire specific cues, advance cue lists (Go), and select cues without firing.
Recording: Record live state into cues with explicit confirmation and rate-limiting for safety.
Channel and parameter control: Set channel intensities, select channels, set non-intensity parameters (pan, tilt, color, etc.) to absolute values, and nudge encoder wheels for fine adjustments.
Fader control: Configure OSC fader banks and set fader levels.
Macros: Execute saved macros by number.
Raw command access: Send arbitrary command-line text to Eos, requiring confirmation for destructive operations, with rate-limiting.
Status monitoring: Retrieve recent OSC feedback, such as active cue and command-line echoes, with optional filtering.
OSC user isolation: Uses a dedicated virtual user (default 99) to avoid command line collisions.
Safety features: Destructive commands (record, raw) require explicit confirmation and are rate-limited.
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., "@eos-mcp-server fire cue 5"
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.
eos-mcp-server
MCP server that lets Claude control an ETC Eos lighting console (Nomad Puck or any Eos-family desk) over OSC — fire and record cues, set channel levels, drive faders, nudge encoders. Built for a test system, not a live rig — see Safety notes below before pointing it at anything with an audience.
How it talks to your rig
Claude → this MCP server → OSC over UDP → Eos (running on your Puck) → sACN → Luminode gateways → DMX → fixtures.
This server never touches DMX/sACN directly — it drives Eos the same way a TouchOSC panel or QLab would, via Eos's OSC command set. Eos remains the single source of truth for the show.
Related MCP server: Eos MCP
1. Enable OSC on Eos
Setup > System > Show Control > OSC:
OSC RX: enabled, port 8000 (ETC's recommended default)
OSC TX: enabled, port 8001, TX IP address = the machine running this server
Also confirm UDP Strings & OSC is enabled for your network interface under Setup > System > Network > Interface Protocols
The TX IP address is the one people get wrong: it must point at the machine running this server, not at the console. If it doesn't, commands will still work but no feedback or status will ever arrive.
2. Install and build
npm install
npm run build3. Configure
Environment variables:
Var | Required | Default | Meaning |
| yes | — | IP/hostname of the machine running Eos |
| no | 8000 | Eos's OSC RX port |
| no | 8001 | Local port to receive Eos's OSC TX feedback on |
| no | 99 | OSC user to claim — see below |
| no | off | Set to |
Both port variables must be whole numbers in 1–65535. Anything else — a typo, or a variable that is set but empty — is rejected at startup rather than defaulted, because a wrong-but-plausible port produces a server that looks healthy and silently never talks to the console.
EOS_USER_ID
Eos gives each OSC user its own command line. This server claims a dedicated virtual user (default 99) so its commands can never merge into a half-typed command on the console operator's line.
1–99 — a virtual user with its own command line. Recommended.
-1 — whoever is currently at the desk. Allowed, but warns at startup: a command sent while the operator is mid-entry can garble theirs.
0 — the Eos background user, which has no command line at all. Rejected, because
eos_record_cueandeos_send_raw_commandwould silently do nothing.
4. Run the tests
npm test # unit + integration tests
npm run test:coverage # with coverage report (80% threshold enforced)5. Point Claude at it
Add to your MCP client config (e.g. Claude Desktop's claude_desktop_config.json):
{
"mcpServers": {
"eos": {
"command": "node",
"args": ["/absolute/path/to/eos-mcp-server/dist/index.js"],
"env": {
"EOS_HOST": "192.168.1.50"
}
}
}
}Tools
eos_fire_cue,eos_go,eos_select_cue— playbackeos_record_cue— record live state into a cue (overwrites existing cues)eos_set_channel_level,eos_select_channel,eos_set_parameter,eos_nudge_wheel— channel/parameter controleos_set_fader,eos_configure_fader_bank— OSC fader bankseos_fire_macro— run a saved macroeos_send_raw_command— escape hatch: send any command-line texteos_get_status— read back recent OSC feedback from Eos (active cue, command line echo, etc.)
Diagnostics
Read-only probes for a live console live in
tools/diagnostics/ — connectivity, show inventory, and
per-channel parameter ranges:
npm run build
EOS_HOST=10.0.0.5 node tools/diagnostics/probe.mjsWhat we learned from real hardware
docs/eos-osc-findings.md records behaviour verified against
an actual console, including several points where ETC's published OSC documentation is
wrong — most importantly that colour arguments are 0–100, not the documented 0–1. Read it
before building anything on top of this.
Known issues from live testing
Found while running this against a real Nomad console with a blank/no-cue show file:
Recording into a cue list that doesn't exist fails.
eos_record_cue(and the rawRecord Cue <list>/<number>command) errors withCue List Does Not Existif the target cue list hasn't been created yet — this includes cue list 1 on a genuinely blank show. Eos does not auto-create the list for you; create it on the console first (or record the very first cue with the bareRecord Entercommand, which targets cue list 1/cue 1 by default), then subsequent numbered records into that list work normally.A failed/unsubmitted command could leak into the next one. (Fixed.) If a command sent via
eos_send_raw_command/eos_record_cueerrored partway through (e.g. the cue-list issue above), Eos left it sitting open on the shared command line, and the next/eos/newcmdgot appended to that leftover text rather than replacing it — producing garbled commands likeLIVE: Record Cue 99 /.The fix is to stop sharing the command line at all: the server now claims its own OSC user on connect (see
EOS_USER_ID), so there is no operator text to collide with. An earlier attempt sent an unverified/eos/key/clear_cmdlinebefore each command; that has been removed in favour of the user-ID approach, which is the documented mechanism.Confirmed on hardware: with
Chan 5 Thru 8left un-submitted on the console keypad, aChan 2 At 25 Entersent from this server executed correctly and left channels 5–8 and the operator's command line untouched. Reproduce withnode tools/diagnostics/user-isolation.mjs.
Safety notes before this touches a live rig
eos_record_cueandeos_send_raw_commandare destructive — they can overwrite show data with no undo prompt over OSC. Fine for a test show file; be deliberate before pointing this at production.Both tools require
confirm: trueto actually execute — without it, they return a preview of what would happen and touch nothing. Calls are also rate-limited (one per few seconds per action) to guard against a runaway loop hammering Record or the command line. Seesrc/services/destructive-guard.ts.OSC over UDP has no auth — anyone on the same network segment can send Eos commands. Keep the console's network isolated the way you already do for DMX/sACN.
Available Tools
13 toolseos_configure_fader_bankConfigure Eos OSC Fader BankAIdempotent
Create (or re-page) an OSC fader bank so eos_set_fader can address it. Must be called once before using a given bank_index. Safe to call again to jump pages.
Args:
bank_index (number): 1-based index for this OSC fader bank (your own numbering, independent of console fader numbers).
fader_count (number): How many faders in this bank.
page (number, optional): Which page to start on. Defaults to page 1.
Inputs: bank_index (required), fader_count (required), page.
eos_fire_cueFire Eos CueADestructive
Fire a specific cue immediately, regardless of playback sequence (equivalent to selecting it and pressing Go, but works even if it's out of order).
Args:
cue_list (number): Cue list number the cue lives in.
cue_number (string): Cue number, e.g. "5" or "12.5" for a numbered part.
Returns confirmation text once the OSC message is sent. Eos does not send a synchronous acknowledgement, so success here means "the command was sent," not "the cue finished." Use eos_get_status to check playback position afterward.
Example: cue_list=1, cue_number="5" fires cue 5 in cue list 1.
Inputs: cue_list (required), cue_number (required).
eos_fire_macroFire Eos MacroBDestructive
Run a saved macro by number (1-127).
Args:
macro_number (number): Macro to fire.
Inputs: macro_number (required).
eos_get_statusGet Recent Eos Status FeedbackARead-onlyIdempotent
Read back recent OSC feedback Eos has sent (active cue, command line text, live/blind state, etc). Eos pushes this asynchronously — this tool returns whatever has arrived so far in this session, not a live query, so call it shortly after an action if you want to see its effect.
Args:
filter (string, optional): Only return feedback whose address contains this substring, e.g. "active/cue" or "out/cmd". Omit for everything.
limit (number, optional): Max entries to return, most recent last. Default 20.
Returns: JSON array of {address, args, receivedAt} entries. Empty array means either nothing has happened yet or Eos's OSC TX isn't reaching this server (check host/ports and that {OSC TX} is enabled on Eos).
Inputs: limit, filter.
eos_goPress Eos GoADestructive
Press the [Go] button on a cue list, advancing to the next cue in sequence (the normal way a show runs, as opposed to eos_fire_cue which jumps to an arbitrary cue out of order).
Args:
cue_list (number, optional): Cue list to advance. Omit to press Go on the default/active cue list.
Inputs: cue_list.
eos_nudge_wheelNudge Eos Encoder WheelA
Simulate turning an encoder wheel by a relative number of ticks, for whatever channel/parameter is currently selected (use eos_select_channel first). This is the closest OSC equivalent to physically twisting a console knob, useful for live "nudge it a bit" adjustments rather than jumping to an absolute value.
Args:
parameter (string, optional): Parameter to nudge, e.g. "pan". Omit to nudge the intensity/level wheel.
ticks (number): Positive nudges up, negative nudges down. Magnitude controls how far — small values (~1) for fine nudges, larger (~5-10) for bigger moves.
fine (boolean, optional): Use fine mode instead of coarse. Default false.
Inputs: ticks (required), fine, parameter.
eos_record_cueRecord Eos CueADestructive
Record the current live state into a cue. This OVERWRITES existing cue data if the cue number already exists — Eos has no separate "confirm" step over OSC, so double-check the cue number before calling this on a real rig. Safe to experiment with on a test system.
Uses the Eos command line under the hood (there's no dedicated OSC verb for recording), equivalent to typing "Record Cue / Enter" on the console.
The target cue_list must already exist — Eos does not auto-create cue lists and will error with "Cue List Does Not Exist" otherwise (this includes cue list 1 on a genuinely blank show). If recording fails, create the list on the console first, or record the very first cue with a bare "Record Enter" via eos_send_raw_command (targets cue list 1/cue 1 by default), then retry numbered records into that list. Use eos_get_status afterward to check for an error in the command-line echo, since success here isn't guaranteed just because the OSC message was sent.
Args:
cue_list (number): Cue list to record into.
cue_number (string): Cue number to record, e.g. "5" or "12.5".
label (string, optional): Text label to apply to the cue.
confirm (boolean): Must be explicitly set to true to actually execute. Omit or set false to get a preview of what would be recorded without touching the show.
Calls are also rate-limited to one per few seconds to guard against a runaway loop hammering Record.
Inputs: cue_list (required), cue_number (required), label, confirm.
eos_select_channelSelect Eos ChannelARead-onlyIdempotent
Select a channel on the command line without changing its level. Use this before eos_adjust_parameter or eos_nudge_wheel, which act on whatever is currently selected.
Args:
channel (number): Channel number to select.
Inputs: channel (required).
eos_select_cueSelect Eos CueARead-onlyIdempotent
Select a cue on the command line without firing it — useful before eos_record_cue-style raw edits, or to inspect a cue before running it.
Args:
cue_list (number): Cue list number.
cue_number (string): Cue number to select.
Inputs: cue_list (required), cue_number (required).
eos_send_raw_commandSend Raw Eos Command Line TextADestructive
Escape hatch: send arbitrary text to the Eos command line, exactly as if it were typed on the keypad. Use this for anything the other tools don't cover (patch changes, group/preset creation, palette recording, "Sneak", etc.).
Args:
command (string): Command line text, e.g. "Chan 1 Thru 10 At 50 Enter" or "Group 1 Record Enter".
confirm (boolean): Must be explicitly set to true to actually send. Omit or set false to get a preview of the command without sending anything.
The command is auto-terminated with Enter if you don't already end it with "#" or "Enter". Because this can do literally anything the console can do, treat it like you would typing directly on the desk — double-check destructive commands (Record, Delete, Update) before sending.
Calls are also rate-limited to one per few seconds to guard against a runaway loop hammering the command line.
Inputs: command (required), confirm.
eos_set_channel_levelSet Eos Channel IntensityAIdempotent
Set a channel's intensity level directly (0-100). This both selects the channel and sets its level in one call, equivalent to "Chan At Enter" on the console.
Args:
channel (number): Channel number.
level (number): Intensity 0-100.
Note: this affects Live output immediately. It does not get recorded into a cue unless you separately call eos_record_cue.
Inputs: level (required), channel (required).
eos_set_faderSet Eos OSC FaderAIdempotent
Set a fader in an OSC fader bank to a level. Requires the bank to already exist on the console (call eos_configure_fader_bank first if you haven't created bank_index yet — it only needs to be done once per session).
Args:
bank_index (number): 1-based OSC fader bank index (created via eos_configure_fader_bank). Use 0 for the master fader.
fader_index (number): Fader position within the bank.
level (number): 0.0-1.0 (0% to 100%).
Inputs: level (required), bank_index (required), fader_index (required).
eos_set_parameterSet Eos Parameter (encoder-style absolute value)AIdempotent
Set a non-intensity parameter (pan, tilt, a color channel, zoom, etc.) to an absolute value for a specific channel — the OSC equivalent of dialing an encoder to a target value.
Args:
channel (number): Channel number.
parameter (string): Parameter name as Eos knows it, e.g. "pan", "tilt", "zoom", "red", "green", "blue".
value (number): Target value. Range depends on the parameter (e.g. pan/tilt are typically in degrees, color channels 0-100).
Example: channel=12, parameter="pan", value=45 sets channel 12's pan to 45.
Inputs: value (required), channel (required), parameter (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool has a distinct purpose: firing cues vs macros vs advancing playback, recording vs selecting cues, setting levels vs parameters vs faders vs nudging, and a raw command escape hatch. The descriptions explicitly clarify potential overlaps (e.g., eos_fire_cue vs eos_go).
All tools follow a consistent eos_verb_noun pattern in snake_case, e.g., eos_fire_cue, eos_record_cue, eos_set_channel_level, eos_get_status. No mixed conventions or vague verbs.
13 tools is well-scoped for a lighting console control server, covering the main workflows (playback, recording, channel/parameter control, faders, status) without excessive granularity or unnecessary omissions.
Core lifecycle is covered: cue playback (fire, go), recording (record, select), channel/parameter control (set, nudge), fader management, and a raw command escape hatch for anything else. Minor gaps like no explicit delete cue or show save/load exist, but they are workaroundable via raw commands.
Maintenance
Related MCP Connectors
The official Planning Center MCP server for interacting with your ministry's data.
The official MCP Server for the Mux API
Related MCP Servers
- AlicenseBqualityDmaintenanceAn MCP server that lets AI assistants control grandMA2 lighting consoles via Telnet, exposing 41 high-level tools for cue management, fixture control, preset management, executor control, macro editing, appearance assignment, bulk operations, console state queries, show file management, read-back verification, and music show workflows.10019Apache 2.0
- AlicenseNot gradedqualityBmaintenanceTransforms an ETC Eos lighting console into a service controllable by AI assistants and automation tools via MCP and OSC, enabling cue management, preset recall, and channel level control.4AGPL 3.0
- AlicenseAqualityDmaintenanceAn MCP server for controlling Zero 88 lighting consoles running ZerOS over OSC, exposing playbacks, cues, the grand master, blackout, and user macros as MCP tools.12MIT
- AlicenseCqualityBmaintenanceEnables LLM control of ETC Eos Family lighting consoles via OSC, allowing operation of cues, levels, subs, macros, and the full Eos command line.20MIT