Skip to main content
Glama

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 build

3. Configure

Environment variables:

Var

Required

Default

Meaning

EOS_HOST

yes

—

IP/hostname of the machine running Eos

EOS_SEND_PORT

no

8000

Eos's OSC RX port

EOS_LISTEN_PORT

no

8001

Local port to receive Eos's OSC TX feedback on

EOS_USER_ID

no

99

OSC user to claim — see below

EOS_VERBOSE

no

off

Set to 1 to log every OSC message to stderr

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_cue and eos_send_raw_command would 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 — playback

  • eos_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 control

  • eos_set_fader, eos_configure_fader_bank — OSC fader banks

  • eos_fire_macro — run a saved macro

  • eos_send_raw_command — escape hatch: send any command-line text

  • eos_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.mjs

What 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 raw Record Cue <list>/<number> command) errors with Cue List Does Not Exist if 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 bare Record Enter command, 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_cue errored partway through (e.g. the cue-list issue above), Eos left it sitting open on the shared command line, and the next /eos/newcmd got appended to that leftover text rather than replacing it — producing garbled commands like LIVE: 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_cmdline before 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 8 left un-submitted on the console keypad, a Chan 2 At 25 Enter sent from this server executed correctly and left channels 5–8 and the operator's command line untouched. Reproduce with node tools/diagnostics/user-isolation.mjs.

Safety notes before this touches a live rig

  • eos_record_cue and eos_send_raw_command are 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: true to 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. See src/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 tools
eos_configure_fader_bankConfigure Eos OSC Fader BankA
Idempotent

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 CueA
Destructive

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 MacroB
Destructive

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 FeedbackA
Read-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 GoA
Destructive

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 CueA
Destructive

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 ChannelA
Read-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 CueA
Read-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 TextA
Destructive

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 IntensityA
Idempotent

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 FaderA
Idempotent

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)A
Idempotent

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

    A4.2/5.0

    Scored across 13 tools

    Disambiguation5/5

    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).

    Naming Consistency5/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      An 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.
      100
      19
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Transforms 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.
      4
      AGPL 3.0
    • A
      license
      A
      quality
      D
      maintenance
      An 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.
      12
      MIT
    • A
      license
      C
      quality
      B
      maintenance
      Enables LLM control of ETC Eos Family lighting consoles via OSC, allowing operation of cues, levels, subs, macros, and the full Eos command line.
      20
      MIT