osc-bridge
[Release]
[CI]
[License: AGPL-3.0-or-later]
[mcptry]
Declarative OSC bridge for music hardware and software — and an MCP server to drive it from an LLM.
Hardware synthesizers over MIDI / SysEx, plus DAWs and live-coding
environments over OSC (Ableton, Bitwig, Reaper, Sonic Pi, SuperCollider, Pure
Data, TouchDesigner, VCV Rack). Every device gets a clean, named OSC surface
described by one JSON file, its driver; the catalogue of drivers lives on
SynthHub, which hands them out one at a
time. Drive it from a live-coding client, a DAW, a
script, the CLI — or from Claude via the built-in MCP server (a MIDI MCP
and OSC MCP).
[osc-bridge — clients (OSC clients, the CLI, an LLM via MCP) feed one osc-bridge core that fans out to hardware synthesizers over MIDI/SysEx and music software (Ableton, Bitwig, Reaper, Sonic Pi) over OSC]
Install
Hosted MCP server, nothing to install — the SynthHub server, run by the
maintainer of osc-bridge, with the catalogue, trust from real use and the
relay to your own hardware:
- Claude (web, desktop): Settings → Connectors → Add custom connector →
https://mcp.synthhub.roomi-fields.com/mcp
- Claude Code:
claude mcp add --transport http synthhub https://mcp.synthhub.roomi-fields.com/mcp
- Cursor and other clients:
{"mcpServers": {"synthhub": {"url": "https://mcp.synthhub.roomi-fields.com/mcp"}}}
On your machine — the MCP server over stdio, with hardware detection and
OSC to a running bridge (each driver fetched from SynthHub when needed and
kept in a cache; you choose what it shares):
As a standalone bridge / CLI:
MCP client config and the tools → docs/MCP.md. Full
quick-start with MIDI port discovery → § Install & quick start.
Why a bridge?
Modern synths — hardware and software alike — expose themselves over MIDI,
SysEx, and OSC with proprietary, scattered protocols. Using them from
live-coding environments, a DAW, a script, or an LLM is painful:
- CC / NRPN addresses are cryptic
- SysEx byte formats are hand-rolled per device
- Bidirectional state (what the device CURRENTLY knows) is hard to track
osc-bridge gives every supported synth a clean, named OSC surface:
Each synth is described by one JSON file. No Rust recompile required to add a device.
Where the drivers come from
The catalogue lives on SynthHub, not in this repository: browse and search it
at synthhub.roomi-fields.com. osc-bridge
asks the SynthHub server for one driver at a time, when it needs it:
osc-bridge run --device korg/minilogue (a driver id) or --device minilogue
(a slug: the device's best variant) fetches that driver and keeps it in the
cache (~/.cache/osc-bridge/drivers/, or $XDG_CACHE_HOME, %LOCALAPPDATA%),
then downloads it again only when the server's copy changed;
osc-bridge detect and the MCP tool detect_hardware ask the server which
makers' identity requests to send, then send it the names of the MIDI
ports found here and what the devices answered, and it names the matching
drivers and firmware; list_devices searches the catalogue there;
- offline (
osc-bridge privacy --offline on, OSC_BRIDGE_OFFLINE=1), only
the drivers already cached are used and nothing is sent.
These requests present the anonymous account of this machine. A JSON file on your machine is used as it is
(--device my-synth.json, osc-bridge mcp --devices-dir my-drivers/).
What makes it different
- One JSON = one driver. Adding a synth is ~30 lines of declarative JSON — CC numbers, SysEx frame templates, optional parameter tables. No Rust, no bindings, no build step. Ship a PR,
osc-bridge run picks it up.
- A shared catalogue. Prophets, Oberheims, the Yamaha DX family, MatrixBrute, Hydrasynth, Virus TI, Digitone, Matrix-1000… the SynthHub catalogue covers most synths, drum machines and DAWs, and grows with what users and agents contribute. Searchable at synthhub.roomi-fields.com.
- Hardware and software. The same named OSC surface drives a hardware synth over MIDI/SysEx or a DAW / live-coding environment over OSC — Ableton, Bitwig, Reaper, Sonic Pi, SuperCollider, Pure Data, TouchDesigner, VCV Rack. See § Software targets.
- An MCP server built in.
osc-bridge mcp exposes the SynthHub catalogue to Claude (or any MCP client) — a MIDI MCP and OSC MCP. Discover devices, read a device's OSC surface, send OSC to synths and DAWs. See docs/MCP.md.
- Honest provenance. Each device declares its source in
_sources[]: ✅ hardware-verified, ✅ software-verified, 📘 vendor-doc, 📡 vendor-osc-api / 📡 third-party-osc, 🎛️ electra-preset, 📦 pencilresearch. You always know whether a mapping was tested on the device or lifted from a spec sheet.
- Bidirectional out of the box. Incoming MIDI (knob turns, SysEx replies, NRPN) is decoded and re-emitted as OSC. Multi-client fan-out is a
--osc-client flag.
- N-to-N orchestration. One process can drive several synths at once (
osc-bridge orchestrate --config bridge.toml), each reachable under its own OSC prefix — so two MatrixBrutes live side-by-side as /matrixbrute-1 and /matrixbrute-2.
- First-class reconfigurable controllers (Electra One MK2). Upload a generated preset as one OSC call; the bridge parses the JSON, reconfigures the device over SysEx, and dynamically rewires its own CC↔OSC routing so each knob on the new layout is reachable by name. Self-describing over OSC:
/bridge/docs returns the integration guide, /electra1/routes/list returns the authoritative address table, so clients (or LLM-driven integrators) never hardcode slugification or protocol details. See § Reconfigurable controllers below.
- Escape hatch when JSON isn't enough. For devices with checksums, quirky scaling, or stateful protocols, an optional per-parameter
transform / per-command script runs Lua 5.4 sandboxed (1 MiB / 10 ms caps, no os/io/loaders). Used sparingly — osc-bridge lint warns on every script use — but it lets the declarative core stay small without giving up on edge cases. See docs/scripting.md.
- Runtime you can trust. Rate-limiting per device, backpressure-aware, zero-alloc hot path, no GC. Single static binary on Windows / Linux / macOS.
Supported devices
The catalogue — synths, drum machines, controllers, DAWs and live-coding
environments, with each driver's source and how far users confirmed it — is
on SynthHub:
Each driver declares its source in _sources[]: ✅ hardware-verified, ✅ software-verified, 📘 vendor-doc, 📡 vendor-osc-api / 📡 third-party-osc, 🎛️ electra-preset, 📦 pencilresearch.
Share your own device
New here? The 30-minute tutorial builds a
working driver end to end. Otherwise — three common cases, pick yours:
Your synth isn't listed → write its driver and send it to SynthHub.
- Find a source: vendor programmer's reference, MIDI implementation PDF, or a canonical CSV.
- Follow
docs/DEVICE_JSON_SCHEMA.md; the driver of a similar device (/api/v1/devices/<id>.json on SynthHub, or the copy in your cache) is a good template.
- Check it:
osc-bridge lint my-synth.json, then osc-bridge run --device my-synth.json on the device.
- Contribute it: from an MCP client, the
submit_driver tool (it is checked, then added to the catalogue); or open an issue here with the file.
A driver is wrong, or you tested it on the hardware → tell SynthHub: report_result and report_mapping (MCP) record what works on which firmware, and submit_fix turns learned mappings into a corrected driver.
Full contributor guide (schema extensions, bug reports, Lua escape hatch) → CONTRIBUTING.md. For devices whose protocol can't be expressed declaratively, see docs/scripting.md — use sparingly; osc-bridge lint warns on every use.
Reconfigurable controllers (Electra One MK2) — the scripts stress-test
Most synths have a fixed set of controls — MiniLab 3's knobs always mean the same
thing. The Electra One MK2 is different: its 216 controls
are defined by a preset that you upload to the device, and the meaning of every
CC changes when the preset changes. A static JSON spec can't describe that; the
bridge treats it as a first-class case.
This integration is what validates the whole Lua scripting subsystem in anger.
The MK2 driver exercises every feature of the scripting.rs layer on real
hardware (firmware 4.1.4, verified 2026-04-14):
- Native JSON codec (
ob.json_decode / ob.json_encode) — parses a full
Electra preset (up to 100 KB of nested pages/devices/groups/controls/overlays)
in a single Lua block, under the 500 ms preset_ingest profile.
- Persistent per-device state (
ob.state) — the driver stores the active
preset name so /electra1/preset/current can report it on demand.
- Dynamic CC↔OSC route registration (
ob.register_cc_route /
ob.clear_routes / ob.set_current_page) — every preset upload rebuilds
the bridge's routing table from scratch, zero restart.
- Multi-block SysEx emission (
ob.emit_sysex) — the upload script emits
the wrapped SysEx preset data block directly from Lua.
- Outbound OSC emission (
ob.emit_osc) — powers the introspection
endpoints: /routes/list, /preset/current, /page/current.
- Reply scripts with matched bindings (
ctx.bindings) — when the device
reports a page switch over SysEx, a reply script updates the bridge's active
page so CC routing tracks the hardware UI automatically.
- Companion markdown auto-load — the driver's notes (
electra-one.md, next to its JSON) are
loaded at startup and served over /bridge/docs, so LLM-driven integrators
read the device-specific quirks (hex-color requirement, inputs[{potId}]
mandatory, envelope-segment cycling gesture, MIDI port selection, etc.)
without any code-side knowledge.
Every item above has a unit test, an end-to-end test against the real .json
driver, and a hardware-verified green light. If you're building a driver for
another reconfigurable controller (Faderfox custom, programmable MIDI Fighter),
the Electra driver is the template — copy its custom_commands block, adapt
the JSON→routes parsing to your device's preset format, done.
What the bridge does
Discovery from zero
Every piece of device-specific knowledge is exposed over OSC — clients never
hardcode slugification rules, page indexing, or SysEx opcodes:
An LLM-driven integrator needs exactly four steps to go from cold start to
routing knob turns: /bridge/status → /bridge/docs → /electra1/preset/upload
→ /electra1/routes/list. Everything else is in the markdown guide embedded
in the bridge itself.
Generalising to other reconfigurable controllers
The machinery (DynamicRoutes, ob.register_cc_route, ob.emit_sysex,
custom_commands, companion .md auto-loading) is device-agnostic. Adding
a reconfigurable Faderfox, a custom MIDI Fighter Twister layout, or any
programmable controller is the same recipe: a preset-parsing Lua block inside
the device JSON that calls ob.register_cc_route per control. No core changes.
Install & quick start
Prebuilt binaries
Grab the latest osc-bridge.exe (Windows), osc-bridge (Linux/macOS) from
the Releases page.
From source
Minimal MiniLab 3 workflow
Incoming MIDI events (knob turns, pads played, pitch-bend, SysEx GET replies)
are forwarded to --osc-client as OSC:
Browser clients (OSC over WebSocket)
Browsers can't speak UDP. Pass --ws-bind to expose the same OSC surface
over WebSocket:
Binary WS frames carry raw OSC packets — the exact bytes the UDP transport
uses, so a minimal dependency-free OSC encoder/decoder in JS covers both
directions. Every connected WS client behaves like an extra --osc-client:
it receives all outbound events (decoded MIDI-in, SysEx replies,
/bridge/status) and its frames go through the same dispatch as UDP.
Under orchestrate, set ws_bind = "127.0.0.1:7890" in the [osc]
section of bridge.toml. Disabled by default; keep it on 127.0.0.1
unless you really want LAN clients.
Only pages served from this machine (localhost, 127.0.0.1) and
non-browser clients may connect: any other website open in your browser is
refused (HTTP 403), so it cannot reach your synths. Allow a specific site
with --ws-allow-origin https://example.org (repeatable; null for
file:// pages; * for any), or ws_allow_origins = [...] in
bridge.toml.
OSCQuery and MIDNAM
--oscquery-bind 127.0.0.1:7891 (or oscquery_bind in the [osc] section
of bridge.toml) answers OSCQuery requests: http://127.0.0.1:7891/ lists
every OSC address the bridge answers, with its types and ranges, so OSCQuery
clients build their controls by themselves. osc-bridge export --device <driver> --format oscquery|midnam writes the same namespace, or the driver's
MIDI names as a MIDNAM file for DAWs. See docs/EXPORT.md.
Device not showing up? (Windows 11 24H2+)
Since the March 2026 cumulative update (KB5079473), Windows 11 ships
Microsoft's new USBMidi2-ACX driver which enforces stricter USB descriptor
checks. Devices that pass the old checks but fail the new ones appear in
Device Manager with Status: Unknown, or bind to a driver that exposes
them only through the new MIDI Services API — invisible to midir (and
therefore to osc-bridge) which queries the legacy MME interface.
Confirmed affected: Polyend Synth / Play / Tracker / Mess / Step
(VID 16D0). Likely affected: any older USB-MIDI device whose descriptor
doesn't meet the new spec.
Workaround — force the legacy usbaudio driver:
- Device Manager → Sound, video and game controllers → right-click the
device → Update Driver.
- Browse my computer for drivers → Let me pick from a list of
available drivers on my computer.
- Select USB Audio Device (FR: "Périphérique audio USB"). If not
visible, uncheck "Show compatible hardware" and look under the Standard
USB section.
- Install, then unplug/replug the USB cable to force MME to re-enumerate.
osc-bridge list should now show the device.
Track Microsoft's progress on KB5079473 if you want a permanent fix —
Pete Brown (Microsoft) acknowledged the packet-size issue publicly but
classified it as non-blocking. Polyend's firmware-side fix is not
announced at the time of writing.
Ergonomics
Which synth is plugged in, and which driver fits?
Lists MIDI ports, asks each device who it is (the standard MIDI Identity
Request, then the makers' own requests the drivers name — harmless), has the
SynthHub server match the answers against the drivers' identity blocks
(offline: the drivers cached here), with the firmware read from them, and
prints a ready-to-run osc-bridge run line. --no-probe matches by port
name only.
Back up and restore presets (SysEx librarian)
Ports take an index or a name fragment. backup --request "F0 … F7" --out-port …
sends a dump request first; restore --gap-ms paces older devices.
Drum machines: play sounds by name
Drivers with a drum map expose each voice: /drm1_mkiv/drum/kick 110 fires
the Vermona DRM1 MKIV kick at velocity 110 (note 36, channel 10). When a
device documents several trigger modes, the address carries the mode, e.g.
/tr_8s/drum/default_note_map/bass_drum. See
DEVICE_JSON_SCHEMA.md.
Explore a device spec without connecting anything
Live-debug the OSC traffic
Send raw SysEx (power user)
How device JSONs work
A device file declares three things:
- Framing — the SysEx header/footer bytes.
- Commands — high-level OSC routes (pad colors, display text, preset recall) that build frames by substituting OSC args into placeholder templates.
- Params — a flat table of parameter addresses for bulk GET/SET via a shared opcode.
Example snippet (the arturia/minilab3 driver):
Software targets — DAWs and live coding environments
osc-bridge isn't only for hardware synthesizers. Since version 0.10 it also
treats DAWs and live-coding environments as first-class devices, behind
the same /<env> OSC surface as a Prophet or a Matriarch. A driver JSON
declares device.transport.kind = "osc" and the bridge forwards OSC to the
target's UDP port instead of going through a MIDI cable.
Two flavors:
Declarative drivers — when the host software has a fixed, documented OSC
surface (Ableton via AbletonOSC,
Bitwig via DrivenByMoss, Reaper's
native OSC). The driver enumerates commands[] with typed args, plus
replies[] for incoming events.
Passthrough drivers — when the host software has no fixed OSC surface and
each user defines their own paths in their project (Sonic Pi, SuperCollider,
Pure Data, TouchDesigner, VCV Rack). The driver just declares
transport.passthrough_prefix and the bridge forwards anything under
/<env>/... verbatim. Replies travel the other way with the prefix added back.
Currently supported:
Each driver comes with its .md notes on SynthHub that document the
required setup on the host side (Remote Script installation, ports, conventions).
Combined with osc-bridge orchestrate, you can drive a hardware MiniLab 3,
an Ableton Live, and a Sonic Pi loop from a single process. With the
new [[routes]] section in bridge.toml, a knob turn on the MiniLab can
drive an Ableton track volume or a Sonic Pi cue, without writing any client
code:
Use from Claude via MCP
osc-bridge ships a Model Context Protocol
server — a MIDI MCP and OSC MCP. Point any MCP client at it and the
model can browse the SynthHub catalogue, read a device's OSC surface, and
send OSC to synths and DAWs.
Hosted — add https://mcp.synthhub.roomi-fields.com/mcp as a remote MCP server (see
Install). Nothing to install; your synth joins through the relay
page, synthhub.roomi-fields.com/connect.
On your machine:
Nine local tools — list_devices, get_device_docs, list_routes, export_device,
send, get_status, detect_hardware, backup_presets, restore_presets — plus
the server's: reports, trust, contributions and shared presets (find, load, take from the synth,
share). Then in conversation:
"What can the MatrixBrute do? Sweep its filter cutoff." — the model calls
list_routes("matrixbrute"), then send. No glue code.
Full setup — Claude Desktop / Cursor config, the workflow, driving software
targets → docs/MCP.md.
Project genesis
Started as a MatrixBrute OSC driver (patch format reverse-engineered from the
GPL-3 sysex-controls and the
community MBPV tool). Grew into a generic declarative bridge once the Arturia
protocol family became clear.
License
AGPL-3.0-or-later. Running a modified version as a network service obliges
you to offer its source to the users of that service. The sysex-controls
reverse engineering that seeded our MiniLab 3 parameter table is GPL-3.0,
which GPL-3.0 section 13 allows to combine with AGPL-3.0 code.
Third-party device data
The driver catalogue is SynthHub's, each driver with the credit and licence of
its sources in _sources[]. The few drivers kept here for the tests
(tests/fixtures/devices/) keep theirs: those of type pencilresearch are
adapted from MIDI Guide by Pencil Research and
contributors, licensed CC BY-SA 4.0
(source: pencilresearch/midi); each
records the licence, the attribution and the exact upstream CSV and commit;
the change made is the conversion of CSV rows to the osc-bridge JSON schema.