presonus-studiolive-mcp
An MCP (Model Context Protocol) server that connects AI coding agents and assistants to PreSonus StudioLive III series mixers over the local network.
Exposes live mixer context — channel names, mute/solo/fader state, Fat Channel compressor/EQ models, meter activity, and scene information — as MCP resources and tools so that AI agents can read, reason about, and assist with live sound engineering without touching hardware autonomously.
Current status: Experimental read-mostly backend (
v0.1.0). Primary empirical inspection on StudioLive 32SC firmware 3.3.0.109659. Other StudioLive III models are expected-compatible but require additional HIL testing. Some routing and Fat Channel parameter values are confidence-tagged asinferredorprobe_required. Write tools are gated and experimental. See docs/capability-matrix.generated.md for the current generated MCP tool/resource inventory.
---
Contents
- What this does
- Hardware requirements
- Quick start
- Packages
- MCP server — tools and resources
- Probe CLI
- Fat Channel skill
- Development
- Architecture
---
What this does
The MCP server gives an AI agent these capabilities:
| Capability | How | |---|---| | Discover mixers on the network | discover_mixers tool | | Verify FOH vs. stagebox identity | validate_mixer_identity tool | | Refresh state cache on demand | refresh_mixer_state tool | | Read channel names, mute, solo, fader, pan, color, Fat Channel models | presonus://mixer/{id}/channels resource | | Read per-channel Fat Channel DSP state (EQ, comp, gate, limiter) | get_fat_channel tool / presonus://mixer/{id}/fat-channel/{id} resource | | Validate Fat Channel settings for a source type | validate_fat_channel_for_source tool | | Know mixer capacity (inputs, aux mixes, FX buses, stagebox) | get_mixer_capabilities tool | | Validate rider capacity requirements | check_required_setup tool | | Validate expected channel names, phantom, mute | validate_channel_setup tool | | Validate an agent-provided input list against the mixer | validate_input_list_against_mixer tool | | Validate a patch sheet for conflicts and range issues | validate_patch_sheet tool | | Render a structured patch sheet for human printing | render_patch_sheet_data tool | | Monitor signal activity (silent / active / hot / clipping) | presonus://mixer/{id}/meters/summary resource | | Know which project and scene are loaded | presonus://mixer/{id}/scene/current resource | | Diagnose a single channel (mute, fader, gate, signal) | diagnose_channel tool | | Run a line-check step and observe meter results | analyze_line_check_step tool | | Detect possible patch swaps during line check | detect_possible_patch_swap tool | | Diagnose no-signal routing causes | diagnose_no_signal_routing tool | | Inspect observable routing (AUX/FX/sub/main sends) | get_routing_graph tool / presonus://mixer/{id}/routing resource | | Validate input routing (Layer A: name, meter, mute) | validate_input_routing tool | | Validate stagebox connection | validate_stagebox_routing tool | | Inspect output patch router (source index) | presonus://mixer/{id}/routing/outputs resource | | Inspect AUX mixes (master, sends, levels) | get_aux_mix tool / presonus://mixer/{id}/auxes resource | | Validate monitor send requirements | validate_monitor_requirements tool | | Find missing / muted / hot monitor sends | find_missing_monitor_sends, find_muted_monitor_sends, find_hot_monitor_sends tools | | Full aux mix audit | validate_aux_mix tool | | Inspect FX send routing | presonus://mixer/{id}/fx-sends resource | | Inspect flat channel-to-aux routing graph | presonus://mixer/{id}/monitor-routing resource | | Raw diagnostic state dump | presonus://mixer/{id}/raw/state resource | | Propose and apply an EQ change (write-enabled only) | propose_eq_change + apply_change_set tools |
By default agents cannot change mixer parameters. Write tools require writeEnabled: true and use a ProposedChangeSet workflow with 60 s TTL and operator confirmation — see ADR-006. All write tool responses include a changeSetConfidence field (observed / inferred / guessed) indicating how well-calibrated the proposed values are.
---
Hardware requirements
| Requirement | Details | |---|---| | Mixer | PreSonus StudioLive III series (16, 16R, 24R, 32SC, 32R) | | Network | Mixer and MCP server host on the same LAN segment | | Protocol | TCP port 53000 (UC Surface / Studio One Remote protocol) | | Coexistence | UC Surface, QMix-UC, and this server can connect simultaneously | | Internet | Not required |
Hardware validation status
| Model | Status | |---|---| | StudioLive 32SC firmware 3.3.0.109659 | Empirically inspected — state capture validated, routing and Fat Channel partially probed | | StudioLive 32R | Expected-compatible (same protocol); requires HIL testing to confirm | | StudioLive 24R | Expected-compatible; requires HIL testing to confirm | | StudioLive 16R | Expected-compatible; requires HIL testing to confirm | | StudioLive 16 | Expected-compatible; requires HIL testing to confirm |
See docs/release-readiness-checklist.md for gates required before any model is claimed as validated or production-ready.
---
Quick start
Prerequisites
- Node.js 20+
- pnpm 9+
Install and build
git clone https://github.com/zarfld/presonus-studiolive-mcp.git
cd presonus-studiolive-mcp
pnpm install # installs all workspace packages + applies featherbear patch
pnpm build # compiles all 4 packages
Run the MCP server (stdio transport)
pnpm mcp:server
Or without a prior build, using tsx:
pnpm mcp:server:dev
The server logs to stderr, connects to discovered mixers on startup, and accepts MCP requests on stdin/stdout.
Connect from Claude Desktop / VS Code
Add to your MCP client config:
{
"mcpServers": {
"presonus-studiolive": {
"command": "node",
"args": ["<absolute-path>/packages/presonus-mcp-server/dist/index.js"]
}
}
}
Probe the hardware first (optional but recommended)
pnpm probe:dev discover # find mixers on your LAN
pnpm probe:dev dump-state -d <ip> # dump full state tree to captures/
pnpm probe:dev probe-fat-channel -d <ip> -c LINE:1 # inspect Fat Channel state for ch1
---
Packages
This is a pnpm monorepo with four packages under packages/. Dependencies flow one way: domain ↠adapter ↠inspector | server.
@presonus-mcp/domain
Pure TypeScript/Zod schema library — the single source of truth for all data contracts.
MixerIdentitySchema— deviceId, serial, IP, port, role, controllableMixerChannelSchema— name, mute, solo, fader (dB/linear/raw), pan, color,compModelName,eqModelNameMeterSummarySchema— time-windowed channel activity classificationFatCompressorStateSchema/FatEqStateSchema— discriminated unions per modelCOMPRESSOR_MODEL_BY_CLASSID/EQ_MODEL_BY_CLASSID— full GUID → model name maps for scene file decodingdecodeCompressorModel(value)/decodeEqModel(value)— live state float → model name helpers
No runtime dependency on featherbear or the MCP SDK.
@presonus-mcp/adapter
Hardware adapter wrapping @featherbear/presonus-studiolive-api. Manages connections, state, and meters.
discoverMixers({ timeoutMs? })— UDP broadcast discoveryPresonusClientManager— connects/disconnects clients; providesgetSnapshot(),getIdentity(),getSummarizer()flattenFeatherbearState()— converts featherbear's nested_data.internal.children.*tree to flat dot-notation keysPresonusMeterSummarizer— ring-buffer of raw uint16 meter packets → time-windowedMeterSummarymapRawStateToSnapshot()— translates flat state keys to normalizedMixerSnapshot
The featherbear dependency is patched (patches/@featherbear__presonus-studiolive-api@1.8.0.patch) to add UBJSON I (int16) and D (float64) type support missing from the original.
@presonus-mcp/inspector
Probe CLI binary (presonus-probe) for hardware reconnaissance during development. See Probe CLI.
@presonus-mcp/server
MCP server that wires the adapter to the MCP SDK over stdio transport. Registers resources and tools at startup (write tools registered only when writeEnabled: true). Runs background mixer discovery.
See docs/capability-matrix.generated.md for the current generated tool and resource inventory.
---
MCP server — tools and resources
Tools
The current tool and resource inventory is generated from source — see docs/capability-matrix.generated.md.
Write tools are registered only when writeEnabled: true (default: false). All write tools use the ProposedChangeSet workflow (propose → 60 s TTL → apply) and include a changeSetConfidence field so operators know how well-calibrated the proposed values are.
| Group | Tools | |---|---| | Discovery / identity | discover_mixers, refresh_mixer_state, validate_mixer_identity | | Capabilities | get_mixer_capabilities, check_required_setup | | Channel setup | validate_channel_setup, diagnose_channel | | Input list / patch sheet | validate_input_list_against_mixer, validate_patch_sheet, render_patch_sheet_data | | Fat Channel inspection | get_fat_channel, validate_fat_channel_for_source | | Line check | analyze_line_check_step, detect_possible_patch_swap | | Routing | get_routing_graph, validate_input_routing, validate_stagebox_routing, diagnose_no_signal_routing, get_input_routing\, validate_avb_routing\, validate_output_routing†| | Monitor / aux | get_aux_mix, validate_monitor_requirements, find_missing_monitor_sends, find_muted_monitor_sends, find_hot_monitor_sends, validate_aux_mix | | Write (gated) | propose_eq_change, apply_change_set |
\* Layer B stub — returns not_verifiable_with_current_adapter with probe instructions. †Layer B partial — source index known; source name requires probe.
discover_mixers
Trigger UDP discovery of StudioLive III mixers on the local network.
{ timeoutMs?: number } // discovery window in ms; default 5000
// → [{ deviceId, serial, model, firmware, role, ipAddress, port }]
validate_mixer_identity
Verify a connected mixer matches expected serial and/or role before proceeding.
{ deviceId: string, expectedSerial?: string, expectedRole?: "FOH"|"STAGEBOX"|"MONITOR"|"UNKNOWN" }
// → { valid: boolean, reasons: string[] }
validate_input_list_against_mixer
Validate an agent-provided input list against actual mixer state. Returns name mismatches, phantom mismatches, mute issues, and printable patch rows.
{
deviceId: string,
inputList: [{ inputNo: number, sourceName: string, phantomRequired: boolean, micPreference?: string, notes?: string }]
}
// → { status: "ok"|"warning"|"error", issues[], printablePatchRows[] }
get_fat_channel
Return the Fat Channel DSP state (EQ, compressor, gate, limiter, HPF) for a single channel.
{ deviceId: string, channelId: string } // channelId e.g. "line.ch1"
// → ChannelFatState (eqModel, compModel, eqBands, comp, gate, limiter, hpfFrequencyHz)
validate_fat_channel_for_source
Check Fat Channel settings against source-type expectations (HPF engaged, gate enabled, comp enabled, limiter enabled).
{ deviceId: string, channelId: string, sourceType: "vocal"|"kick"|"snare"|"bass"|... }
// → { checks: [{ check, passed, detail }], warnings: string[], parameterConfidence }
---
Resources
presonus://mixers
All connected mixer identities.
presonus://mixer/{deviceId}/channels
Normalized channel list. Each channel includes:
{
id: string, // "line.ch1", "aux.ch3", "sub.ch1", …
selector: { type, channel },
name?: string, // label from mixer (e.g. "Kick In")
mute?: boolean,
solo?: boolean,
fader?: { db: number|null, linear: number|null },
pan?: number, // 0.0 left … 0.5 center … 1.0 right
linked?: boolean,
color?: string, // RGBA hex
// Fat Channel — decoded from live opt.compmodel / opt.eqmodel
compModelName?: string, // e.g. "FET", "BRIT_COMP", "FC_670"
eqModelName?: string // e.g. "STANDARD", "ALPINE_EQ_550", "SOLAR_69_EQ"
}
presonus://mixer/{deviceId}/meters/summary
Time-windowed meter classification (last 10 seconds):
{
windowSec: number,
computedAt: string, // ISO 8601
silentChannels: string[], // channel ids with no signal
activeChannels: string[], // signal in normal range
hotChannels: string[], // approaching clip
clippingChannels: string[], // at or above clip threshold
noSignalButExpected: string[],
signalButUnexpected: string[]
}
presonus://mixer/{deviceId}/scene/current
{
currentProject: string | null,
currentScene: string | null,
availableProjects: string[]
}
presonus://mixer/{deviceId}/routing
Per-channel AUX/FX/subgroup/main-LR send routing. parameterConfidence: 'inferred' until AUX fader de-normalization is probe-confirmed.
presonus://mixer/{deviceId}/routing/outputs
Output patch router — source index known for each analog/AVB output; sourceName: null until probe confirms source → index mapping.
presonus://mixer/{deviceId}/auxes
All aux mixes: master level/mute + per-channel send levels. prePost: 'unknown' until hardware probing confirms.
presonus://mixer/{deviceId}/fx-sends
Per-channel FX bus send state.
presonus://mixer/{deviceId}/monitor-routing
Flattened channel-to-aux routing graph for monitor mix planning.
presonus://mixer/{deviceId}/fat-channel/{channelId}
Fat Channel DSP state for a single channel (EQ, compressor, gate, limiter, HPF, model names). Use get_fat_channel tool instead when querying programmatically.
presonus://mixer/{deviceId}/raw/state
Full raw state dump (pre-normalized flat dot-notation). For diagnostics and development only — do not use for agent reasoning logic.
Layer A / Layer B routing model: Layer A resources and tools return data directly observable from confirmed state keys. Layer B tools (
get_input_routing,validate_avb_routing) returnnot_verifiable_with_current_adapterand include probe instructions — physical cable routing and AVB stream assignments cannot be verified by software alone.
---
Probe CLI
presonus-probe is the hardware reconnaissance tool used during development. Run via pnpm probe:dev <command>.
| Command | Purpose | |---|---| | discover | Find all StudioLive III mixers on the LAN; print serial/model/IP/role | | dump-state -d <ip> | Connect and dump full flat state tree as JSON to captures/ | | watch-events -d <ip> | Stream all featherbear data events as NDJSON | | watch-meters -d <ip> | Capture raw meter stream as NDJSON | | diff-state --before <f> --after <f> | Compare two state dumps; identify changed keys (workflow: change one control → dump-state → diff → name the key) | | probe-fat-channel -d <ip> -c <TYPE:N> | Dump all Fat Channel state keys for a channel (e.g. LINE:1) | | read-scene -d <ip> | List projects and scenes stored on the mixer |
Note: Scene file content (
__classidGUIDs) is not accessible over the network API — it returns 0 bytes. Fat Channel model identity must be read from live state viaopt.compmodel.value/opt.eqmodel.value.
---
Fat Channel skill
A VS Code / Copilot skill is included at .github/Skills/PresonusFatChannelSelection/SKILL.md.
It gives AI agents:
- Character, hardware archetype, best-use, and avoid-when for all 11 compressor models (STANDARD, TUBE, FET, BRIT_COMP, CLASSIC_COMPRESSOR, COMP_160, EVEREST_C100A, FC_670, RC_500_COMPRESSOR, TUBE_CB, VT_1_COMPRESSOR)
- Character and use-case guidance for all 10 EQ models (STANDARD, PASSIVE, VINTAGE, ALPINE_EQ_550, BAXANDALL_EQ, RC_500_EQ, SOLAR_69_EQ, TUBE_EQ, VINTAGE_3_BAND_EQ, VT_1_EQ)
- Quick-reference selector tables (source instrument → recommended model)
- Decision framework for choosing between transparency, punch, warmth, and bus treatment
- Full
__classidGUID reference for all models (fromclassID_Mapping.md, empirically confirmed on 32SC firmware 3.3.0.109659) - Exact MCP resource/tool schemas and accepted property lists per model
---
Development
Commands
pnpm install # install + apply featherbear patch
pnpm build # compile all packages
pnpm build:watch # watch mode
pnpm clean # remove all dist/ and tsbuildinfo
pnpm test # unit tests (no hardware needed)
pnpm test:watch # watch mode
pnpm test:coverage # with coverage report
pnpm test:hil # hardware-in-loop tests (requires HIL_PRESONUS=1 + physical mixer)
pnpm typecheck # tsc dry-run
pnpm probe:dev discover # discover mixers
pnpm probe:dev dump-state -d <mixer-ip> # capture state
pnpm probe:dev probe-fat-channel -d <ip> -c LINE:1 # inspect channel
pnpm mcp:server:dev # run MCP server (tsx, no build needed)
pnpm mcp:server # run MCP server (compiled dist/)
Test strategy
| Test type | Command | Requires hardware | |---|---|---| | Unit (schema/adapter logic) | pnpm test | No | | HIL (hardware-in-loop) | pnpm test:hil | Yes — see below |
HIL test setup
HIL tests require a physical StudioLive III mixer on the local network. Set the following environment variables before running pnpm test:hil:
# Required
export HIL_PRESONUS=1 # enables HIL test suite
export HIL_PRESONUS_IP=<mixer-ip-address> # e.g. 192.168.1.50
export HIL_PRESONUS_SERIAL=<serial-number> # e.g. SD7E21010066
# Then run
pnpm test:hil
On Windows PowerShell:
$env:HIL_PRESONUS="1"
$env:HIL_PRESONUS_IP="<mixer-ip>"
$env:HIL_PRESONUS_SERIAL="<serial>"
pnpm test:hil
Unit tests cover all domain schemas, decode functions, flattenFeatherbearState, and PresonusMeterSummarizer using captured fixture data from captures/.
GitHub issue model and traceability
All requirements, architecture decisions, and test cases are tracked as GitHub issues following this taxonomy:
| Prefix | Type | Example | |---|---|---| | StR-NNN | Stakeholder requirement | #1–#4 | | REQ-F- | Functional requirement | #15–#46 | | REQ-NF- | Non-functional requirement | #21–#24 | | ADR- | Architecture decision | #47 | | QA-SC- | Quality attribute scenario | #25–#27, #49–#50 | | TEST-* | Verification test case | #51–#60, #80–#83 |
Issues are organized into milestones v0.1–v1.0. See the GitHub Issues tab for the full traceability register.
Adding a new raw state key
pnpm probe:dev dump-state -d <ip>before and after changing a control in UC Surfacepnpm probe:dev diff-state --before <before.json> --after <after.json>— identifies the changed key- Add the constant to
packages/presonus-adapter/src/types.ts - Map it in
mapRawStateToSnapshot()or expose viarawExtra - Add a unit test with a fixture snapshot
---
Architecture
Three-layer architecture (ADR-002):
┌─────────────────────────────────────────â”
│ AI Agent / MCP Client │
│ (Claude Desktop, VS Code, custom) │
└────────────────┬────────────────────────┘
│ stdio (MCP protocol)
┌────────────────▼────────────────────────â”
│ @presonus-mcp/server │
│ See docs/capability-matrix.generated.md │
└────────────────┬────────────────────────┘
│ internal API
┌────────────────▼────────────────────────â”
│ @presonus-mcp/adapter │
│ PresonusClientManager │
│ flattenFeatherbearState() │
│ PresonusMeterSummarizer │
└────────────────┬────────────────────────┘
│ TCP 53000 (UC Surface protocol)
┌────────────────▼────────────────────────â”
│ StudioLive III mixer │
│ (32SC, 32R, 24R, 16R, 16) │
└─────────────────────────────────────────┘
Domain schemas (@presonus-mcp/domain) sit outside this stack and are imported by both adapter and server — never the other way round.
Key decisions:
- ADR-001 — TypeScript/Node.js 20+
- ADR-002 — Three-layer architecture above
- ADR-003 — pnpm monorepo, 4 packages
- ADR-004 —
@featherbear/presonus-studiolive-apiv1.8.0 pinned + patched as hardware adapter - ADR-005 — Read-only-first; write operations require a
ProposedChangeSet+ audit log + confirmation flow before being enabled
Known gaps / future work
- Write tools —
propose_eq_change+apply_change_setare available (write-enabled mode). Extended change-set framework (rename, mute, fader, aux send, comp/gate/limiter) planned. - Layer B routing — Physical input source routing and AVB stream routing require probe-diff sessions (
probe-routing diff --kind input-source/avb-stream).get_input_routingandvalidate_avb_routingreturn probe instructions. - Output patch source names —
validate_output_routingknows source indices but not names; probe-diff with--kind bus-to-outputneeded. - Stereo IEM pair model — Monitor layout and stereo-pair validation planned (Phase 4).
- Show prep layer —
ShowInputSchemais a stub (rider analysis, channel template suggestions). - Scene file access —
__classidGUIDs not accessible over network; only live state model IDs available. - HIL test coverage — tests with
*.hil.test.tsrequire a physical mixer.
---
License
MIT — see LICENSE.












