28fa27b6bd
Covers DrivenByMoss OSC configuration, architecture explanation, all 204 tools with parameters and descriptions, usage examples, and troubleshooting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
662 lines
27 KiB
Markdown
662 lines
27 KiB
Markdown
# bitwig-mcp
|
||
|
||
A full-featured [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for controlling **Bitwig Studio** via the [DrivenByMoss](https://github.com/git-moss/DrivenByMoss) OSC extension. Exposes **204 MCP tools** covering every OSC command DrivenByMoss supports — transport, tracks, devices, clips, scenes, browser, project, layout, MIDI, and more.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Prerequisites](#prerequisites)
|
||
- [Installation](#installation)
|
||
- [DrivenByMoss OSC Setup](#drivenbymoss-osc-setup)
|
||
- [Configuration](#configuration)
|
||
- [Running the Server](#running-the-server)
|
||
- [Registering with Claude Code](#registering-with-claude-code)
|
||
- [Architecture](#architecture)
|
||
- [Tool Reference](#tool-reference)
|
||
- [Transport](#transport)
|
||
- [Track](#track)
|
||
- [Master Track](#master-track)
|
||
- [Device](#device)
|
||
- [Clip](#clip)
|
||
- [Scene](#scene)
|
||
- [Browser](#browser)
|
||
- [Project](#project)
|
||
- [Layout & Panels](#layout--panels)
|
||
- [MIDI (Virtual Keyboard)](#midi-virtual-keyboard)
|
||
- [Miscellaneous](#miscellaneous)
|
||
- [Usage Examples](#usage-examples)
|
||
- [Troubleshooting](#troubleshooting)
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
- **Bitwig Studio** 4.x or later
|
||
- **DrivenByMoss** installed as a Bitwig controller extension ([download](https://github.com/git-moss/DrivenByMoss/releases))
|
||
- **Python 3.10+**
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
# Clone or navigate to the project
|
||
cd bitwig-mcp
|
||
|
||
# Create a virtual environment
|
||
python -m venv .venv
|
||
|
||
# Activate it (Windows)
|
||
.venv\Scripts\activate
|
||
|
||
# Install the package and all dependencies
|
||
pip install -e .
|
||
```
|
||
|
||
**Dependencies installed:**
|
||
- `mcp[cli]` — Model Context Protocol SDK (FastMCP server)
|
||
- `python-osc` — OSC send/receive over UDP
|
||
|
||
---
|
||
|
||
## DrivenByMoss OSC Setup
|
||
|
||
1. Open **Bitwig Studio** → Preferences → **Controllers**
|
||
2. Click **Add controller** → search for **DrivenByMoss** → select **Open Sound Control (OSC)**
|
||
3. Configure the OSC extension settings:
|
||
|
||
| DrivenByMoss Setting | Value | Meaning |
|
||
|---|---|---|
|
||
| **Port to receive on** | `8000` | DrivenByMoss listens here — bitwig-mcp sends to this port |
|
||
| **Host to send to** | `127.0.0.1` | Where DrivenByMoss sends state updates |
|
||
| **Port to send to** | `9000` | DrivenByMoss sends here — bitwig-mcp listens on this port |
|
||
|
||
4. Enable the controller and restart Bitwig if prompted.
|
||
|
||
> **Important:** "Port to receive on" is where DrivenByMoss accepts commands (bitwig-mcp sends TO it). "Port to send to" is where DrivenByMoss pushes state updates (bitwig-mcp listens ON it). These are from DrivenByMoss's perspective — the opposite of what you might expect.
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
All settings are controlled via environment variables with sensible defaults:
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `BITWIG_HOST` | `127.0.0.1` | Host where Bitwig is running |
|
||
| `BITWIG_SEND_PORT` | `8000` | Port to send OSC commands to (DrivenByMoss receive port) |
|
||
| `BITWIG_RECEIVE_PORT` | `9000` | Port to listen for state updates on (DrivenByMoss send port) |
|
||
| `BITWIG_TIMEOUT` | `5.0` | Seconds to wait when using `wait_for_state()` |
|
||
| `BITWIG_REFRESH_WAIT` | `0.5` | Seconds to wait after sending `/refresh` for state to populate |
|
||
|
||
Example — connecting to Bitwig on another machine:
|
||
```bash
|
||
BITWIG_HOST=192.168.1.10 BITWIG_SEND_PORT=8000 bitwig-mcp
|
||
```
|
||
|
||
---
|
||
|
||
## Running the Server
|
||
|
||
```bash
|
||
# With venv activated
|
||
bitwig-mcp
|
||
|
||
# Or directly via the venv executable (Windows)
|
||
.venv\Scripts\bitwig-mcp.exe
|
||
|
||
# Or via Python
|
||
python -m bitwig_mcp.server
|
||
```
|
||
|
||
The server starts and listens for MCP connections on stdio (standard MCP transport). It also starts the OSC listener thread in the background immediately.
|
||
|
||
---
|
||
|
||
## Registering with Claude Code
|
||
|
||
### Global (available in all sessions)
|
||
|
||
```bash
|
||
claude mcp add bitwig-mcp "C:\path\to\bitwig-mcp\.venv\Scripts\bitwig-mcp.exe"
|
||
```
|
||
|
||
### Project-scoped (`.claude/settings.json`)
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"bitwig-mcp": {
|
||
"command": "C:\\path\\to\\bitwig-mcp\\.venv\\Scripts\\bitwig-mcp.exe",
|
||
"args": []
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Verify it's working
|
||
|
||
Once registered, ask Claude:
|
||
> *"Check if Bitwig is connected"* → calls `ping_bitwig`
|
||
> *"Get the current transport state"* → calls `transport_get_state`
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
### Push-Based OSC Protocol
|
||
|
||
DrivenByMoss uses a **push-based** (publish-subscribe) model rather than request/response. Bitwig proactively sends OSC messages to notify about every state change. This means:
|
||
|
||
- **No polling needed** — state arrives automatically when anything changes in Bitwig
|
||
- **State store** — `osc_client.py` maintains an in-memory dict (`address → last received args`) updated by every incoming OSC message
|
||
- **`/refresh`** — sending this special command triggers Bitwig to dump its full current state immediately (used on startup and by `refresh_state`)
|
||
|
||
### Data Flow
|
||
|
||
```
|
||
Claude / LLM
|
||
│
|
||
│ MCP tool calls
|
||
▼
|
||
bitwig-mcp (FastMCP server)
|
||
│ ▲
|
||
│ cmd(addr, *args) │ OSC state updates
|
||
│ (UDP to port 8000) │ (UDP from port 9000)
|
||
▼ │
|
||
DrivenByMoss OSC extension (inside Bitwig Studio)
|
||
│ ▲
|
||
│ Bitwig Java API │
|
||
▼ │
|
||
Bitwig Studio ──────────┘
|
||
```
|
||
|
||
### Key Components
|
||
|
||
| File | Purpose |
|
||
|---|---|
|
||
| `src/bitwig_mcp/server.py` | FastMCP server entry point; registers all tool modules |
|
||
| `src/bitwig_mcp/config.py` | Environment variable configuration |
|
||
| `src/bitwig_mcp/osc_client.py` | Thread-safe OSC singleton: send, receive, state store |
|
||
| `src/bitwig_mcp/tools/` | One file per domain, each exports `register(mcp)` |
|
||
|
||
### OSC Client API
|
||
|
||
```python
|
||
from bitwig_mcp.osc_client import get_client
|
||
|
||
c = get_client()
|
||
|
||
# Send a command to Bitwig (fire-and-forget)
|
||
c.cmd("/play", 1)
|
||
c.cmd("/track/1/volume", 100)
|
||
|
||
# Read cached state (returns tuple or None)
|
||
state = c.get_state("/tempo/raw") # e.g. (128.0,)
|
||
value = c.get_state_value("/play") # e.g. 1
|
||
|
||
# Wait for a state update (blocks up to timeout)
|
||
c.wait_for_state("/track/1/volume", timeout=2.0)
|
||
|
||
# Trigger full state dump from Bitwig
|
||
c.refresh()
|
||
|
||
# Get all known state as a dict
|
||
all_state = c.get_all_state()
|
||
|
||
# Check connection
|
||
connected = c.ping()
|
||
```
|
||
|
||
### Value Ranges
|
||
|
||
DrivenByMoss uses the following conventions (default resolution: 128 steps):
|
||
|
||
| Parameter | Range | Notes |
|
||
|---|---|---|
|
||
| Volume | `0–127` | ~100 = unity gain (0 dB) |
|
||
| Pan | `0–127` | 64 = center |
|
||
| Send volume | `0–127` | ~100 = unity |
|
||
| Boolean (on/off) | `0` or `1` | Omitting triggers a toggle |
|
||
| Normalized floats | `0.0–1.0` | Device parameters, EQ settings |
|
||
| MIDI note | `0–127` | 60 = C4 (middle C) |
|
||
| MIDI velocity | `0–127` | 0 = note off |
|
||
| Track/clip indices | `1–8` | 1-indexed in OSC paths |
|
||
| Colors | `"rgb(r,g,b)"` | e.g. `"rgb(255,128,0)"` |
|
||
|
||
---
|
||
|
||
## Tool Reference
|
||
|
||
### Transport
|
||
|
||
Tools for controlling playback, recording, tempo, loop, and automation.
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `transport_play` | `value=1` | Start/stop playback. `1` = play, `0` = stop |
|
||
| `transport_stop` | — | Stop playback (rewinds if already stopped) |
|
||
| `transport_restart` | — | Restart playback from the beginning |
|
||
| `transport_toggle_record` | `value?` | Toggle/set arranger record mode |
|
||
| `transport_toggle_overdub` | `value?` | Toggle/set arranger overdub |
|
||
| `transport_toggle_launcher_overdub` | `value?` | Toggle/set clip launcher overdub |
|
||
| `transport_toggle_loop` | `value?` | Toggle/set loop (repeat) mode |
|
||
| `transport_toggle_punch_in` | `value?` | Toggle/set punch-in recording |
|
||
| `transport_toggle_punch_out` | `value?` | Toggle/set punch-out recording |
|
||
| `transport_toggle_click` | `value?` | Toggle/set metronome |
|
||
| `transport_set_click_volume` | `value` (0–100) | Set metronome volume |
|
||
| `transport_toggle_click_ticks` | `value?` | Toggle/set metronome tick marks |
|
||
| `transport_toggle_click_preroll` | `value?` | Toggle/set pre-roll click |
|
||
| `transport_quantize` | — | Quantize the cursor clip |
|
||
| `transport_set_tempo` | `bpm` (float) | Set tempo in BPM |
|
||
| `transport_tap_tempo` | — | Tap tempo (call repeatedly) |
|
||
| `transport_adjust_tempo` | `amount`, `direction` ("+"/"-") | Nudge tempo up or down |
|
||
| `transport_set_position` | `value` (float) | Set timeline position in beats |
|
||
| `transport_step_position` | `direction` ("+"/"-"), `size` ("small"/"large") | Step position forward/backward |
|
||
| `transport_goto_start` | — | Jump to beginning of arrangement |
|
||
| `transport_set_crossfade` | `value` (0–127) | Set crossfader position (64 = center) |
|
||
| `transport_reset_crossfade` | — | Reset crossfader to center |
|
||
| `transport_toggle_autowrite` | `value?` | Toggle/set arranger automation write |
|
||
| `transport_toggle_launcher_autowrite` | `value?` | Toggle/set launcher automation write |
|
||
| `transport_set_automation_mode` | `mode` ("OFF"/"LATCH"/"TOUCH") | Set automation write mode |
|
||
| `transport_set_preroll` | `measures` (int) | Set pre-roll measure count |
|
||
| `transport_set_post_recording_action` | `action` (string) | Set launcher post-recording action |
|
||
| `transport_set_post_recording_offset` | `beats` (float) | Set post-recording time offset in beats |
|
||
| `transport_set_launch_quantization` | `quantization` (string) | Set default clip launch quantization |
|
||
| `transport_get_state` | — | Get complete transport state snapshot |
|
||
|
||
**Post-recording action values:** `"OFF"`, `"PLAY_RECORDED"`, `"RECORD_NEXT_FREE_SLOT"`, `"STOP"`, `"RETURN_TO_ARRANGEMENT"`
|
||
|
||
**Launch quantization values:** `"NONE"`, `"1/32"`, `"1/16"`, `"1/8"`, `"1/4"`, `"1/2"`, `"1"`, `"2"`, `"4"`, `"8"`
|
||
|
||
---
|
||
|
||
### Track
|
||
|
||
Tools for mixer controls, clips, sends, and navigation across the 8-track bank window. All `track_index` parameters are **1-indexed** (1–8).
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `track_set_volume` | `track_index`, `value` (0–127) | Set track volume |
|
||
| `track_reset_volume` | `track_index` | Reset volume to unity gain |
|
||
| `track_set_pan` | `track_index`, `value` (0–127) | Set track pan (64 = center) |
|
||
| `track_reset_pan` | `track_index` | Reset pan to center |
|
||
| `track_toggle_mute` | `track_index`, `value?` | Toggle/set mute |
|
||
| `track_toggle_solo` | `track_index`, `value?` | Toggle/set solo |
|
||
| `track_toggle_recarm` | `track_index`, `value?` | Toggle/set record arm |
|
||
| `track_toggle_monitor` | `track_index`, `value?` | Toggle/set input monitoring |
|
||
| `track_toggle_auto_monitor` | `track_index`, `value?` | Toggle/set auto-monitoring |
|
||
| `track_select` | `track_index` | Select/focus a track |
|
||
| `track_set_crossfade_mode` | `track_index`, `mode` ("A"/"B"/"AB") | Set crossfade assignment |
|
||
| `track_set_send_volume` | `track_index`, `send_index`, `value` (0–127) | Set a send level |
|
||
| `track_reset_send_volume` | `track_index`, `send_index` | Reset send to default |
|
||
| `track_toggle_send` | `track_index`, `send_index`, `value?` | Toggle/set send active |
|
||
| `track_clip_launch` | `track_index`, `clip_index` | Launch a clip slot |
|
||
| `track_clip_launch_alt` | `track_index`, `clip_index` | Launch clip (alternative mode) |
|
||
| `track_clip_record` | `track_index`, `clip_index` | Record into a clip slot |
|
||
| `track_clip_create` | `track_index`, `clip_index`, `length_beats=4.0` | Create empty clip |
|
||
| `track_clip_duplicate` | `track_index`, `clip_index` | Duplicate a clip |
|
||
| `track_clip_remove` | `track_index`, `clip_index` | Delete a clip |
|
||
| `track_clip_set_name` | `track_index`, `clip_index`, `name` | Rename a clip |
|
||
| `track_clip_set_color` | `track_index`, `clip_index`, `color` | Set clip color (rgb string) |
|
||
| `track_navigate_next` | — | Select next track |
|
||
| `track_navigate_prev` | — | Select previous track |
|
||
| `track_bank_next` | — | Scroll track bank forward one page |
|
||
| `track_bank_prev` | — | Scroll track bank backward one page |
|
||
| `track_navigate_parent` | — | Navigate to parent group track |
|
||
| `track_enter_group` | `track_index` | Enter a group track |
|
||
| `track_add_audio` | — | Add a new audio track |
|
||
| `track_add_instrument` | — | Add a new instrument track |
|
||
| `track_add_effect` | — | Add a new effect/FX track |
|
||
| `track_stop_clips` | `track_index` | Stop all clips on a track |
|
||
| `track_toggle_bank` | — | Toggle between main and effect track bank |
|
||
| `track_set_param` | `param_index` (1–8), `value` (0.0–1.0) | Set a remote control parameter |
|
||
| `track_reset_param` | `param_index` | Reset remote control parameter |
|
||
| `track_navigate_params` | `direction` ("+"/"-"/"page+"/"page-") | Navigate remote control parameters |
|
||
| `track_indicate_volume` | `all_tracks=True` | Enable volume indication |
|
||
| `track_indicate_pan` | `all_tracks=True` | Enable pan indication |
|
||
| `track_indicate_send` | `send_index` | Enable send indication |
|
||
| `track_toggle_vu_meters` | `value?` | Toggle/set VU meter feedback |
|
||
| `track_get_info` | `track_index` | Get full state for one track |
|
||
| `track_get_all` | — | Get state for all 8 tracks in bank |
|
||
| `track_selected_get_state` | — | Get cursor (selected) track state |
|
||
| `track_selected_set_volume` | `value` (0–127) | Set cursor track volume |
|
||
| `track_selected_set_pan` | `value` (0–127) | Set cursor track pan |
|
||
| `track_selected_toggle_mute` | `value?` | Toggle/set cursor track mute |
|
||
| `track_selected_toggle_solo` | `value?` | Toggle/set cursor track solo |
|
||
| `track_selected_toggle_recarm` | `value?` | Toggle/set cursor track record arm |
|
||
|
||
---
|
||
|
||
### Master Track
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `master_set_volume` | `value` (0–127) | Set master track volume |
|
||
| `master_reset_volume` | — | Reset master volume to unity |
|
||
| `master_set_pan` | `value` (0–127) | Set master track pan |
|
||
| `master_reset_pan` | — | Reset master pan to center |
|
||
| `master_toggle_mute` | `value?` | Toggle/set master mute |
|
||
| `master_select` | — | Select/focus the master track |
|
||
| `master_get_state` | — | Get complete master track state |
|
||
|
||
---
|
||
|
||
### Device
|
||
|
||
Tools for the cursor device (the currently focused device in Bitwig's device chain). Also covers the primary instrument (`primary_*`) and EQ device (`eq_*`).
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `device_toggle_bypass` | `value?` | Toggle/set device bypass |
|
||
| `device_toggle_expand` | `value?` | Toggle/set device expanded view |
|
||
| `device_toggle_parameters` | `value?` | Toggle/set parameters panel |
|
||
| `device_toggle_window` | `value?` | Toggle/set plugin window |
|
||
| `device_toggle_pinned` | `value?` | Toggle/set cursor pin |
|
||
| `device_navigate_next` | — | Move cursor to next device in chain |
|
||
| `device_navigate_prev` | — | Move cursor to previous device |
|
||
| `device_set_param` | `param_index` (1–8), `value` (0.0–1.0) | Set device parameter |
|
||
| `device_reset_param` | `param_index` | Reset parameter to default |
|
||
| `device_navigate_params` | `direction` ("+"/"-"/"page+"/"page-") | Navigate parameter pages |
|
||
| `device_select_page` | `page_index` | Select a parameter page |
|
||
| `device_select_sibling` | `sibling_index` | Select a sibling device in chain |
|
||
| `device_layer_select` | `layer_index` | Select a device layer |
|
||
| `device_layer_set_volume` | `layer_index`, `value` (0–127) | Set layer volume |
|
||
| `device_layer_set_pan` | `layer_index`, `value` (0–127) | Set layer pan |
|
||
| `device_layer_toggle_mute` | `layer_index`, `value?` | Toggle/set layer mute |
|
||
| `device_layer_set_send_volume` | `layer_index`, `send_index`, `value` (0–127) | Set layer send volume |
|
||
| `device_layer_enter` | `layer_index` | Enter a layer to view its devices |
|
||
| `device_layer_navigate` | `direction` ("+"/"-"/"page+"/"page-") | Navigate layers |
|
||
| `device_drumpad_select` | `pad_index` | Select a drum pad |
|
||
| `device_drumpad_set_volume` | `pad_index`, `value` (0–127) | Set drum pad volume |
|
||
| `device_drumpad_set_pan` | `pad_index`, `value` (0–127) | Set drum pad pan |
|
||
| `device_drumpad_toggle_mute` | `pad_index`, `value?` | Toggle/set drum pad mute |
|
||
| `device_drumpad_toggle_solo` | `pad_index`, `value?` | Toggle/set drum pad solo |
|
||
| `primary_set_param` | `param_index` (1–8), `value` (0.0–1.0) | Set primary instrument parameter |
|
||
| `primary_toggle_bypass` | `value?` | Toggle/set primary instrument bypass |
|
||
| `primary_navigate_params` | `direction` ("+"/"-"/"page+"/"page-") | Navigate primary instrument params |
|
||
| `eq_set_type` | `band_index`, `eq_type` (string) | Set EQ band filter type |
|
||
| `eq_set_gain` | `band_index`, `value` (0.0–1.0) | Set EQ band gain |
|
||
| `eq_set_frequency` | `band_index`, `value` (0.0–1.0) | Set EQ band frequency (normalized) |
|
||
| `eq_set_q` | `band_index`, `value` (0.0–1.0) | Set EQ band Q factor (normalized) |
|
||
| `eq_add` | — | Add an EQ+ device to the selected track |
|
||
| `device_get_state` | — | Get full device state (params, pages, layers, siblings) |
|
||
|
||
**EQ band types:** `"OFF"`, `"LP1"`, `"LP2"`, `"HP1"`, `"HP2"`, `"LS"` (low shelf), `"HS"` (high shelf), `"BELL"`, `"NOTCH"`
|
||
|
||
---
|
||
|
||
### Clip
|
||
|
||
Tools for the **cursor clip** — the clip currently in focus in Bitwig's clip editor.
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `clip_quantize` | — | Quantize the cursor clip |
|
||
| `clip_set_name` | `name` | Rename the cursor clip |
|
||
| `clip_set_color` | `color` (rgb string) | Set cursor clip color |
|
||
| `clip_toggle_pinned` | `value?` | Toggle/set clip pin (keeps focus on this clip) |
|
||
| `clip_launch` | — | Launch the cursor clip |
|
||
| `clip_launch_alt` | — | Launch cursor clip (alternative mode) |
|
||
| `clip_record` | — | Record into the cursor clip |
|
||
| `clip_create` | `length_beats=4.0` | Create a new empty clip |
|
||
| `clip_stop` | — | Stop clips on the cursor track |
|
||
| `clip_stop_alt` | — | Stop clips (alternative mode) |
|
||
| `clip_stop_all` | — | Stop all playing clips globally |
|
||
| `clip_stop_all_alt` | — | Stop all clips (alternative mode) |
|
||
| `clip_navigate` | `direction` ("+"/"-") | Navigate to next/previous clip |
|
||
| `clip_get_state` | — | Get cursor clip state (name, color, pinned) |
|
||
|
||
---
|
||
|
||
### Scene
|
||
|
||
Tools for the clip launcher scene bank. All `scene_index` parameters are **1-indexed**.
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `scene_launch` | `scene_index` | Launch a scene (triggers all clips in that row) |
|
||
| `scene_launch_alt` | `scene_index` | Launch scene (alternative mode) |
|
||
| `scene_select` | `scene_index` | Select a scene |
|
||
| `scene_duplicate` | `scene_index` | Duplicate a scene |
|
||
| `scene_remove` | `scene_index` | Delete a scene |
|
||
| `scene_set_name` | `scene_index`, `name` | Rename a scene |
|
||
| `scene_set_color` | `scene_index`, `color` (rgb string) | Set scene color |
|
||
| `scene_navigate` | `direction` ("+"/"-"/"bank+"/"bank-") | Scroll scene bank |
|
||
| `scene_add` | — | Add a new empty scene |
|
||
| `scene_create_from_playing` | — | Create scene from all currently playing clips |
|
||
| `scene_get_all` | — | Get state of all 8 scenes in current bank |
|
||
|
||
---
|
||
|
||
### Browser
|
||
|
||
Tools for navigating Bitwig's content browser (presets, devices, samples).
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `browser_open_preset` | — | Open browser to replace current device with a preset |
|
||
| `browser_insert_device_after` | — | Open browser to insert device after cursor |
|
||
| `browser_insert_device_before` | — | Open browser to insert device before cursor |
|
||
| `browser_navigate_tab` | `direction` ("+"/"-") | Navigate content type tabs |
|
||
| `browser_navigate_filter` | `column_index`, `direction` ("+"/"-") | Navigate a filter column |
|
||
| `browser_reset_filter` | `column_index` | Reset a filter column to show all |
|
||
| `browser_navigate_results` | `direction` ("+"/"-") | Navigate results list |
|
||
| `browser_commit` | — | Load the selected browser item |
|
||
| `browser_cancel` | — | Close browser without loading |
|
||
| `browser_get_state` | — | Get browser state (tab, filters, results) |
|
||
|
||
---
|
||
|
||
### Project
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `project_save` | — | Save the current project |
|
||
| `project_navigate` | `direction` ("+"/"-") | Switch between open projects |
|
||
| `project_toggle_engine` | `value?` | Toggle/set the Bitwig audio engine |
|
||
| `project_set_param` | `param_index` (1–8), `value` (0.0–1.0) | Set a project remote control parameter |
|
||
| `project_reset_param` | `param_index` | Reset project parameter to default |
|
||
| `project_navigate_params` | `direction` ("+"/"-"/"page+"/"page-") | Navigate project parameters |
|
||
| `project_select_page` | `page_index` | Select a project parameter page |
|
||
| `project_get_state` | — | Get project state (name, engine, params, pages) |
|
||
|
||
---
|
||
|
||
### Layout & Panels
|
||
|
||
Tools for controlling Bitwig's UI layout and panel visibility.
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `layout_set` | `layout_name` | Set the panel layout (e.g. `"SINGLE"`, `"DOUBLE"`, `"THREE"`) |
|
||
| `panel_toggle_note_editor` | `value?` | Toggle/set note editor panel |
|
||
| `panel_toggle_automation_editor` | `value?` | Toggle/set automation editor panel |
|
||
| `panel_toggle_devices` | `value?` | Toggle/set devices panel |
|
||
| `panel_toggle_mixer` | `value?` | Toggle/set mixer panel |
|
||
| `panel_toggle_fullscreen` | `value?` | Toggle/set fullscreen mode |
|
||
| `arranger_toggle_cue_markers` | `value?` | Toggle cue marker visibility in arranger |
|
||
| `arranger_toggle_playback_follow` | `value?` | Toggle playback follow (auto-scroll) |
|
||
| `arranger_toggle_track_height` | `value?` | Toggle double track row height |
|
||
| `arranger_toggle_clip_launcher` | `value?` | Toggle clip launcher section in arranger |
|
||
| `arranger_toggle_timeline` | `value?` | Toggle timeline visibility |
|
||
| `arranger_toggle_io_section` | `value?` | Toggle I/O section in arranger |
|
||
| `arranger_toggle_effect_tracks` | `value?` | Toggle effect tracks visibility |
|
||
| `mixer_toggle_clip_launcher` | `value?` | Toggle clip launcher in mixer |
|
||
| `mixer_toggle_crossfade_section` | `value?` | Toggle crossfade section in mixer |
|
||
| `mixer_toggle_device_section` | `value?` | Toggle device section in mixer |
|
||
| `mixer_toggle_sends_section` | `value?` | Toggle sends section in mixer |
|
||
| `mixer_toggle_io_section` | `value?` | Toggle I/O section in mixer |
|
||
| `mixer_toggle_meter_section` | `value?` | Toggle meter/VU section in mixer |
|
||
| `layout_get_state` | — | Get full layout and visibility state |
|
||
|
||
---
|
||
|
||
### MIDI (Virtual Keyboard)
|
||
|
||
Tools for sending MIDI messages through DrivenByMoss's virtual keyboard. Channels are **1-indexed** (1–16).
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `midi_note` | `channel`, `note` (0–127), `velocity` (0–127) | Send a note on/off message |
|
||
| `midi_note_with_array` | `channel`, `note`, `velocity` | Send note using array format |
|
||
| `midi_note_octave` | `channel`, `direction` ("+"/"-") | Shift note keyboard octave |
|
||
| `midi_drum` | `channel`, `note` (0–127), `velocity` (0–127) | Send a drum pad note |
|
||
| `midi_drum_octave` | `channel`, `direction` ("+"/"-") | Shift drum pad octave |
|
||
| `midi_cc` | `channel`, `cc` (0–127), `value` (0–127) | Send a MIDI CC message |
|
||
| `midi_channel_aftertouch` | `channel`, `pressure` (0–127) | Send channel aftertouch |
|
||
| `midi_poly_aftertouch` | `channel`, `note` (0–127), `pressure` (0–127) | Send polyphonic aftertouch |
|
||
| `midi_pitchbend` | `channel`, `value` (0–127) | Send pitch bend (64 = center) |
|
||
| `midi_set_fixed_velocity` | `velocity` (0–127) | Set fixed accent velocity (0 = disable) |
|
||
| `midi_note_repeat_toggle` | `value?` | Toggle/set note repeat mode |
|
||
| `midi_note_repeat_period` | `period` (string) | Set note repeat period (e.g. `"1/16"`) |
|
||
| `midi_note_repeat_length` | `length` (string) | Set note repeat length (e.g. `"1/16"`) |
|
||
| `midi_get_state` | — | Get MIDI/note repeat state |
|
||
|
||
**Note repeat period/length values:** `"1/32"`, `"1/16"`, `"1/8T"`, `"1/8"`, `"1/4T"`, `"1/4"`, `"1/2"`, `"1"`
|
||
|
||
---
|
||
|
||
### Miscellaneous
|
||
|
||
| Tool | Parameters | Description |
|
||
|---|---|---|
|
||
| `undo` | — | Undo the last action |
|
||
| `redo` | — | Redo the last undone action |
|
||
| `action_execute` | `action_index` (1–20) | Execute a DrivenByMoss assignable custom action |
|
||
| `marker_launch` | `marker_index` | Jump to and play from a cue marker |
|
||
| `marker_navigate` | `direction` ("bank+"/"bank-") | Navigate marker bank pages |
|
||
| `marker_get_all` | — | Get all markers in current bank |
|
||
| `refresh_state` | — | Send `/refresh` and return full state dump |
|
||
| `state_dump` | — | Return current state store without refreshing |
|
||
| `ping_bitwig` | — | Check if Bitwig+DrivenByMoss is reachable |
|
||
|
||
---
|
||
|
||
## Usage Examples
|
||
|
||
### Check connection and get initial state
|
||
|
||
```
|
||
ping_bitwig
|
||
→ {"connected": true, "message": "Bitwig + DrivenByMoss OSC is active"}
|
||
|
||
refresh_state
|
||
→ {"/play": (0,), "/tempo/raw": (128.0,), "/track/1/name": ("Drums",), ...}
|
||
```
|
||
|
||
### Transport control
|
||
|
||
```
|
||
transport_set_tempo(bpm=120.0)
|
||
transport_toggle_loop(value=1)
|
||
transport_play()
|
||
|
||
transport_get_state()
|
||
→ {"playing": 1, "tempo_bpm": 120.0, "loop": 1, ...}
|
||
|
||
transport_stop()
|
||
```
|
||
|
||
### Mixing
|
||
|
||
```
|
||
track_get_all()
|
||
→ [{"index": 1, "name": "Drums", "volume": 100, "mute": 0, ...}, ...]
|
||
|
||
track_set_volume(track_index=1, value=90)
|
||
track_toggle_mute(track_index=2)
|
||
track_toggle_solo(track_index=1)
|
||
|
||
master_set_volume(value=110)
|
||
```
|
||
|
||
### Launching clips and scenes
|
||
|
||
```
|
||
scene_get_all()
|
||
→ [{"index": 1, "name": "Verse", ...}, {"index": 2, "name": "Chorus", ...}]
|
||
|
||
scene_launch(scene_index=2)
|
||
|
||
track_clip_launch(track_index=3, clip_index=1)
|
||
|
||
clip_stop_all()
|
||
```
|
||
|
||
### Device control
|
||
|
||
```
|
||
device_get_state()
|
||
→ {"name": "Polysynth", "bypass": 0, "params": [...], "pages": [...]}
|
||
|
||
device_set_param(param_index=1, value=0.75)
|
||
device_select_page(page_index=2)
|
||
device_toggle_bypass()
|
||
```
|
||
|
||
### MIDI input
|
||
|
||
```
|
||
# Play a C major chord on channel 1
|
||
midi_note(channel=1, note=60, velocity=100) # C4
|
||
midi_note(channel=1, note=64, velocity=100) # E4
|
||
midi_note(channel=1, note=67, velocity=100) # G4
|
||
|
||
# Release
|
||
midi_note(channel=1, note=60, velocity=0)
|
||
midi_note(channel=1, note=64, velocity=0)
|
||
midi_note(channel=1, note=67, velocity=0)
|
||
|
||
# Drum hit on pad 1 (usually kick drum)
|
||
midi_drum(channel=10, note=36, velocity=127)
|
||
```
|
||
|
||
### Browser
|
||
|
||
```
|
||
browser_open_preset() # Opens browser for current device
|
||
browser_navigate_tab("+") # Switch to next content type
|
||
browser_navigate_results("+") # Move to next result
|
||
browser_commit() # Load the selection
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### `ping_bitwig` returns `connected: false`
|
||
|
||
1. **Is Bitwig running?** Make sure Bitwig Studio is open and a project is loaded.
|
||
2. **Is DrivenByMoss active?** Go to Preferences → Controllers and confirm the OSC controller is enabled (green checkmark).
|
||
3. **Check ports:** DrivenByMoss "receive port" must be `8000` and "send to port" must be `9000` (or set `BITWIG_SEND_PORT`/`BITWIG_RECEIVE_PORT` to match your custom values).
|
||
4. **Firewall:** Ensure UDP ports 8000 and 9000 are not blocked by a firewall.
|
||
5. **Port conflict:** Check nothing else is using those ports: `netstat -ano | findstr :8000` (Windows).
|
||
|
||
### State values all return `None`
|
||
|
||
The state store is empty, meaning no messages have been received from Bitwig. Call `refresh_state` to trigger a full dump:
|
||
```
|
||
refresh_state()
|
||
```
|
||
If it still returns mostly empty, see the connectivity steps above.
|
||
|
||
### `track_get_all` shows only 8 tracks but I have more
|
||
|
||
DrivenByMoss uses a **track bank** window of 8 tracks. Use `track_bank_next` / `track_bank_prev` to scroll to other groups of tracks.
|
||
|
||
### Changes not reflected in state after a command
|
||
|
||
DrivenByMoss sends state updates asynchronously. For fast sequences of commands, wait briefly or call `refresh_state` to re-sync. The `BITWIG_REFRESH_WAIT` env var controls how long `refresh()` waits (default 0.5 s).
|
||
|
||
### Server exits immediately
|
||
|
||
Make sure you are running the server directly, not piping to it. The MCP server communicates over stdio — it is meant to be launched by a MCP client (like Claude Code), not run interactively in a terminal. To test it manually, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
MIT
|