Skip to main content
Glama
MaybeItsAdam

zeros-mcp

by MaybeItsAdam

zeros-mcp

An MCP server for controlling Zero 88 lighting consoles running ZerOS over OSC. It exposes playbacks, cues, the grand master, blackout and user macros as MCP tools.

Implements the OSC surface documented in ZerOS & OSC ("Open Sound Control"), which requires ZerOS 7.14 or later — OSC did not exist before that release.

Zero 88 document OSC for FLX, FLX S24/S48 and ZerOS Server. The 7.14 release notes also list ORB, Solution, SCD and Leap Frog under products affected, and the OSC section carries no console qualifier — but the manual's variant filter does not include them, so treat OSC on those desks as unconfirmed and try it rather than assuming either way.

This drives real lights. Commands take effect immediately on whatever the console is connected to. Point it at a desk with nothing patched, or one that isn't in front of an audience, until you trust it.

The thing to understand first

ZerOS receives OSC. It never sends any. The console has a Receive Port Number and no transmit port; there is no feedback, no acknowledgement, and no way to read the desk's state from here. This is not a gap in this server — it is the shape of the protocol.

Two consequences run through everything below:

  • No queries about the console exist, or can. There is no get_active_cue. The only reads are get_command_journal (what this server sent) and get_connection_health (whether the link looks alive). Both say so in their own output, so a model reading them cannot quietly promote "I sent 50%" into "the desk is at 50%".

  • ok: true means the packet left this machine, nothing more. Over UDP that is all it can ever mean, because UDP happily accepts writes addressed to a switched-off console. Every success result carries confirmed_by_console: false permanently.

If you want the console to talk back, it can't. If you want to know whether it's there, set ZEROS_TRANSPORT=tcp — see Transports.

Related MCP server: Eos MCP

How it works

One outbound path:

                 commands  ->  ZEROS_PORT (console's "Receive Port Number")
  zeros-mcp  ─────────────────────────────────────────────────────►  ZerOS console

                 (nothing comes back, ever)

ZerOS takes its arguments in the address path/zeros/cue/go/1/5 fires cue 5 on playback 1 — rather than as OSC arguments. Every message this server sends therefore has an empty argument list, which also sidesteps the fact that Zero 88 never document which OSC type tag the console expects. It does mean every tool argument is interpolated into an address, so validating them is load-bearing: a / slipping through wouldn't error, it would fire a different command.

zeros_mcp/
├── app.py            FastMCP instance
├── server.py         entry point: logging, mcp.run()
├── config.py         ZerosConfig, validated from the environment
├── journal.py        what this server sent - not console state
├── errors.py         ZerosConfigError / ZerosValidationError / ZerosSendError
├── logging_setup.py  stderr logging (stdout carries the MCP protocol)
├── osc/
│   ├── address.py    validates anything interpolated into an OSC address
│   ├── slip.py       RFC 1055 framing for the TCP transport
│   └── client.py     ZerosClient - lazy socket, UDP or TCP, structured errors
└── tools/            one module per area of the console

Prerequisites

  • A ZerOS console running 7.14 or later

  • uv — no separate Python install or clone needed; uv fetches Python and the server itself.

Setup

1. Enable OSC on the console. Setup → Triggers → OSC:

Setting

Value

OSC

Enabled

Receive Port Number

8830

Protocol

UDP (or TCP (SLIP) — see below)

Receive Port Number must match this server's ZEROS_PORT, and the protocol must match ZEROS_TRANSPORT. There is no transmit side to configure, which removes the most common setup failure on other consoles — and replaces it with a quieter one: because nothing is ever returned, a wrong port over UDP looks exactly like a working setup from this end. Commands report ok: true and the rig does nothing.

A console that is not set to DHCP ships on a static 192.168.1.10 / 255.255.255.0.

2. Add it to your MCP client.

For Claude Desktop, add this to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "zeros": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/MaybeItsAdam/zeros-mcp", "zeros-mcp"],
      "env": {
        "ZEROS_IP": "192.168.1.10",
        "ZEROS_PORT": "8830"
      }
    }
  }
}

uvx fetches and caches the server straight from GitHub on first launch — nothing to clone or install by hand. Restart Claude Desktop after editing the config. Any MCP client that launches a command works the same way; drop the command/args/env above into its config format.

To pin to a specific commit instead of tracking main, append @<commit-sha> to the repo URL.

The server also publishes a system_instructions prompt covering the no-feedback rule, the macro escape hatch, and when to confirm before acting.

Configuration

All optional. Defaults suit a console on the same machine.

Variable

Default

Description

ZEROS_IP

127.0.0.1

Console address. A desk on factory settings is 192.168.1.10.

ZEROS_PORT

8830

Match the console's Receive Port Number.

ZEROS_TRANSPORT

udp

udp or tcp. Must match the console's protocol setting.

ZEROS_LOG_LEVEL

INFO

DEBUG logs every OSC message sent.

Invalid values are rejected at startup with a message naming the variable. Note there is one port here, not the TX/RX pair an Eos-style server needs — inventing a second would misdescribe the protocol.

Transports

UDP is the default and matches ZerOS's own default. It is fire-and-forget: nothing can be learned about the console from it at all.

TCP (SLIP) carries the same messages under RFC 1055 framing. It is worth choosing for one reason: a TCP connection either establishes or it does not, which is the only evidence of the console's existence available anywhere in this protocol. With it, get_connection_health can distinguish "something is listening on that address" from "the packet left this machine". Without it, that question is unanswerable.

The TCP path is written from Zero 88's documented "TCP (SLIP)" option and is covered by tests against a local listener, but it has not been verified against a real console. UDP is the tested-in-anger default. If TCP misbehaves on your desk, please open an issue.

Tools

Every tool returns a structured result with an ok field. ok: false means the command never left this machine, and error says why. ok: true means it did — and, as above, never more than that.

Cuesfire_cue, go_cue, set_pending_cue

fire_cue takes a cue and an optional playback, and supports point cues such as 1.5. set_pending_cue is ZerOS's "Next": it moves the standby cue without firing it.

The Master Playback is playback 0. Playback numbers start at 0, not 1. If a cue will not fire, try omitting the playback — an FLX S48 user reported /zeros/cue/go/0/4 being ignored where /zeros/cue/go/4 worked, so the cue-only form is worth trying before concluding the console isn't listening.

Playbackspause_playback, release_playback, view_playback, set_playback_level

go_cue, pause_playback and release_playback all take an optional playback number. Omitting it targets whichever playback the console currently considers active — which this server has no way to know, so naming the playback is the safer call.

Omitting fade_seconds on a level is not the same as passing 0: ZerOS then uses the playback's own raise and lower times, moving rather than snapping.

Whole rigset_grand_master, set_blackout

Both affect everything at once, and because nothing is reported back you will not be able to tell that you have blacked out a show.

Macrosfire_macro

This is the escape hatch, and it is the answer to most "can it…?" questions. The documented OSC surface has no command line, no channel or colour control, and no way to record anything. Anything outside the list above is done by recording a user macro at the desk once, and firing it by number from here. So "set channel 1 to 50%" is not impossible — it is a macro.

This serverget_connection_health, get_command_journal

Neither reports console state, because none is observable. get_command_journal returns this server's own history of sent messages, bounded to the most recent 50; an operator moving a fader by hand a second after you sent a command leaves no trace in it.

Argument ranges are declared in each tool's schema, so out-of-range input is rejected before it reaches the console.

Troubleshooting

Start with get_connection_health. Over TCP it can tell you whether anything is listening, which narrows the list below considerably. Over UDP it cannot, and it says so rather than guess.

Commands report ok: true but nothing happens. The likeliest failure, and the one with no signal to go on. Check, in this order:

  1. ZerOS version is 7.14 or later. Older software ignores OSC entirely while looking perfectly healthy — nothing in the UI indicates the feature is absent.

  2. OSC is enabled under Setup → Triggers → OSC.

  3. Receive Port Number equals ZEROS_PORT.

  4. The console's protocol setting (UDP / TCP (SLIP)) matches ZEROS_TRANSPORT. A UDP sender talking to a console expecting TCP produces exactly this symptom.

To confirm the packets are at least leaving this machine, listen for them yourself:

python3 -c "import socket;s=socket.socket(2,2);s.bind(('',8830));print(s.recvfrom(9999))"

Point ZEROS_IP/ZEROS_PORT at that listener and fire a tool. If the datagram arrives, the send path is fine and the problem is at the console.

Commands return ok: false with a send error. The console is unreachable at ZEROS_IP. Check the address and that both machines are on the same network. Over TCP this is also what a refused connection looks like, which is more informative than UDP's silence.

Firewall. On Windows, allow the Python interpreter through for the port you configured.

Nothing works and there is no log output. Set ZEROS_LOG_LEVEL=DEBUG. Logs go to stderr; your MCP client may hide them.

Development

git clone https://github.com/MaybeItsAdam/zeros-mcp
cd zeros-mcp
uv sync --all-groups
uv run pytest          # test suite; no console required
uv run ruff check .
uv run ruff format .
uv run mypy

To point a client at your local checkout instead of GitHub, use "command": "uv", "args": ["--directory", "/absolute/path/to/zeros-mcp", "run", "zeros_server.py"] in place of the uvx block above.

The integration tests bind real sockets on ephemeral ports and assert on the bytes that arrive, including the SLIP frame over TCP, so message construction and framing are covered without hardware. There is no receive path to test.

Two rules the test suite enforces mechanically:

  • Never write to stdout. Under the stdio transport stdout carries JSON-RPC frames; a stray print() corrupts the session. Use zeros_mcp.logging_setup.get_logger. (tests/test_no_stdout.py)

  • Validate anything interpolated into an OSC address. On ZerOS the address carries the arguments, so a / in a tool argument silently becomes a different command. Use the helpers in zeros_mcp/osc/address.py.

A third is enforced by convention and worth stating: no tool may imply the console confirmed anything. tests/test_schemas.py pins the set of get_* tools for that reason — a new one would mean inventing a feedback path that does not exist.

eos-mcp — the same idea for ETC Eos family consoles. Eos publishes its state over OSC, so that server mirrors the console live and has a far larger tool surface. The difference is the protocol, not the effort.

Licence

MIT — see LICENSE.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    B
    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
    13
    Apache 2.0
  • A
    license
    -
    quality
    A
    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.
    AGPL 3.0
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for controlling Behringer X-Air digital mixers via OSC, with symbolic naming and dB-to-float conversion.
    19
    1
    Apache 2.0
  • A
    license
    B
    quality
    C
    maintenance
    An MCP server for controlling Voicemeeter (Basic, Banana, or Potato) on Windows: strip and bus gains, mutes, output routing, live levels, macro buttons, presets, and audio-engine restarts.
    17
    MIT

View all related MCP servers

Related MCP Connectors

  • MCP server for Zooza — class scheduling, attendance, and booking for activity businesses.

  • The MCP server for Azure DevOps, bringing the power of Azure DevOps directly to your agents.

  • Remote MCP server for full read/write access to a Zotero library

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MaybeItsAdam/zeros-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server