Skip to main content
Glama
gdeber

XR18 MCP Server

by gdeber
README.md
# XR18 MCP Server

An MCP server that controls a Behringer XR18 over its OSC UDP interface. The
client implementation is `xr18`; mixer commands are sent to UDP port `10024`.

The command registry is based on `doc/paramenters.txt`, the supplied X AIR OSC
protocol PDF, and the Behringer World X-Air OSC reference. Generic raw tools
provide complete access to documented paths, including firmware-specific paths
that do not have a dedicated semantic MCP tool.

## Requirements

- Python 3.12 or newer
- [uv](https://docs.astral.sh/uv/)
- An XR18 reachable over the network

## Install and run

```bash
uv sync
XR18_HOST=192.168.1.100 uv run xr18-mcp
```

Configuration environment variables:

- `XR18_HOST` (required): mixer IP address or hostname
- `XR18_PORT` (optional): OSC UDP port; defaults to `10024`
- `XR18_TIMEOUT` (optional): request timeout in seconds; defaults to `2`

## MCP configuration

Use stdio in an MCP client configuration. Replace the directory with this
project's absolute path.

```json
{
  "mcpServers": {
    "xr18": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/xr18", "xr18-mcp"],
      "env": {
        "XR18_HOST": "192.168.1.100"
      }
    }
  }
}
```

## Tools

Mixer-assistant tools, which speak dB rather than raw protocol values:

- `xr18_list_channels`, `xr18_resolve_channel` (regular channels, AUX, FX returns, monitor buses, FX sends, LR, and DCAs)
- `xr18_get_send_db`, `xr18_set_send_db`, `xr18_adjust_send_db`
- `xr18_get_fader_db`, `xr18_set_fader_db`, `xr18_adjust_fader_db`

Send `1`–`6` are the monitor buses and send `7`–`10` are FX sends `1`–`4`. Source names can resolve regular channels, the AUX return, FX returns, monitor buses, FX send masters, LR, and DCAs. Destination arguments accept numeric buses, canonical identifiers such as `bus/5` and `fxsend/2`, or configured destination names. `fxsend/2` maps to channel send slot `08`.

Low-level and administrative tools:

- `xr18_info`, `xr18_status`, `xr18_enable_remote`
- `xr18_get`, `xr18_set`, `xr18_trigger`
- `xr18_list_commands`, `xr18_describe_command`
- `xr18_read_meters` for batches `0` through `9`; returned values are dB
- `xr18_channel_set_fader`, `xr18_channel_set_mute`
- `xr18_headamp_set_phantom`
- `xr18_snapshot_load`, `xr18_snapshot_save`, `xr18_snapshot_delete`, and `xr18_snapshot_set_scope`

Use `xr18_describe_command` before writing an unfamiliar raw OSC path. Native
XR18 float values are normalized between `0.0` and `1.0`; integers and strings
follow the parameter documentation.

Examples of raw paths:

```text
/ch/01/mix/fader
/ch/01/gate/on
/ch/01/eq/1/g
/bus/1/mix/on
/routing/p16/01/src
/fx/1/type
```

## Mixer assistant agent (llama.cpp)

`agents/mixer_assistant` is a Google ADK agent that turns plain-language
requests into mixer changes, running against a local llama.cpp server. It
understands English and Italian.

```text
"raise 1 dB of guitar in monitor 5"
"lower the kick 2 dB in wedge 3"
"set lead vocal fader to -6 dB"
"alza di 1 dB la chitarra nel monitor 5"
"mute channel 8"
```

### 1. Start llama.cpp

Tool calling requires the `--jinja` flag. Without it the model returns prose and
no mixer command is ever issued.

```bash
llama-server --jinja -fa on -hf bartowski/Qwen2.5-7B-Instruct-GGUF:Q4_K_M --port 8080
```

Use a model with a native tool-calling template. Qwen 2.5, Llama 3.1/3.3,
Hermes 2/3, Mistral Nemo, and Functionary are supported natively. Avoid
aggressive KV cache quantization such as `-ctk q4_0`, which measurably degrades
tool-calling accuracy.

### 2. Run the agent

```bash
cp agents/.env.example agents/.env   # then edit XR18_HOST
cd agents
uv run adk run mixer_assistant       # terminal chat
uv run adk web                       # browser chat
```

Configuration:

- `XR18_HOST`, `XR18_PORT`, `XR18_TIMEOUT` for the mixer
- `LLAMA_API_BASE` defaults to `http://localhost:8080/v1`
- `LLAMA_MODEL` defaults to `qwen2.5-7b-instruct`

### Source resolution examples

The assistant distinguishes a source strip from a destination send:

```text
"raise 1 dB of guitar in monitor 5"  -> source guitar, send 5
"raise 1 dB of aux in monitor 2"     -> source AUX return, send 2
"raise reverb in monitor 3"           -> source named FX return, send 3
"raise monitor 5 fader"               -> source monitor bus 5 fader
"lower delay send 2 fader"            -> source FX send master 2 fader
```

Bare `aux`, `aux in`, and `aux input` resolve to `/rtn/aux`. Configured strip
names are read from the mixer and take precedence over generic labels. Ambiguous
names are rejected rather than guessed.

### Design notes

- **All gain mathematics is done in Python, never by the model.** The model may
  only pass a delta such as `1.0` to `xr18_adjust_send_db`. Relative changes are
  a single atomic read-modify-write inside the MCP server.
- **The tool surface is filtered to 9 tools.** Local 7B models select tools far
  less reliably when shown all 24, so raw OSC and console administration are
  hidden from the agent.
- **Instrument names** resolve from the mixer's scribble strips first, then from
  `agents/mixer_assistant/aliases.json`. Ambiguous names raise an error and the
  agent asks which channel is meant rather than guessing.
- The agent cannot enable phantom power, initialize the console, delete
  snapshots, or change network settings.

## OSC write behavior

XR18 parameter writes are fire-and-forget by default because XR18 firmware does
not reliably echo set messages. Query operations still wait for a mixer reply.
The absolute MCP setter tools support `verify=true` to issue a separate readback
query after writing:

```text
xr18_set_send_db(source="ch/03", bus="bus/5", db=-4, verify=true)
xr18_set_fader_db(source="ch/03", db=-6, verify=true)
```

Relative adjustments read the current value, send the new value without waiting,
and return the expected resulting dB value.

## Safety

The server requires `confirm=true` for console initialization, applying network
settings, snapshot deletion, and phantom power changes. Network updates can
disconnect the MCP server from the mixer. The XR18 ignores `/-action/setclock`.

`/xremote` subscriptions expire after ten seconds, so call
`xr18_enable_remote` periodically while listening for parameter changes.

## Test

```bash
uv run pytest
```

Canonical resolver identifiers can be passed directly to later tools: `ch/10`, `rtn/aux`, `rtn/1`, `bus/5`, `fxsend/2`, `lr`, and `dca/1`.

TDQS

B3.1/5.0

Scored across 25 tools

Disambiguation4/5

Most tools are clearly distinct by purpose (e.g., snapshots, meters, sends, faders), but there is overlap in how fader levels can be manipulated (xr18_set, xr18_channel_set_fader, xr18_set_fader_db), which could lead to misselection. Descriptions are detailed enough to reduce ambiguity.

Naming Consistency3/5

Naming is mixed: some tools follow xr18_<action> (get, set, trigger), others xr18_<object>_<action> (snapshot_load, channel_set_fader), and still others xr18_<action>_<object> (get_fader_db, set_send_db). This inconsistency, while readable, could confuse an agent expecting a uniform pattern.

Tool Count4/5

25 tools is on the high end of the acceptable range, but justified for a complex mixer server covering channels, sends, snapshots, meters, and status. The count is not excessive given the domain scope.

Completeness4/5

The tool set covers core mixing operations (faders, mutes, sends), snapshots (load/save/delete/scope/name), channel listing/resolution, meters, and status. Generic get/set allow access to any parameter, filling potential gaps. Missing dedicated EQ or effect tools are mitigated by the generic interface.

Maintenance

ActivityMaintained
ResponsivenessNo issues