Skip to main content
Glama
unicornwhodev-afk

FL Studio MCP Server

README.md
# mcp-flstudio

Official release label: Pré-BETA Complete MCP-serveur-FL-Studio2025

A canonical **local-core** MCP (Model Context Protocol) server for FL Studio on Windows: no cloud relay for the published core surface, explicit extension boundaries for non-local capabilities, and a public CLI/runtime that can be packaged and shipped cleanly.

Works with Claude Desktop, VS Code Copilot, Codex, and any other MCP-compatible client.

---

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│  MCP Client  (Claude Desktop / VS Code / Codex)         │
└────────────────────┬────────────────────────────────────┘
                     │ JSON-RPC over stdio
┌────────────────────▼────────────────────────────────────┐
│            MCP Server  (Node.js / TypeScript)            │
│                                                         │
│  ┌──────────────────┐  ┌──────────────────────────────┐ │
│  │ WindowsDesktop   │  │  LocalMailbox Bridge          │ │
│  │ Bridge           │  │                               │ │
│  │ PowerShell COM   │  │  inbox/  ←── requests (.json) │ │
│  │ (fallback only)  │  │  outbox/ ──→ responses (.json)│ │
│  └──────────────────┘  └──────────────┬───────────────┘ │
│  ┌──────────────────────────────────┐  │                 │
│  │ LocalHelper Backend (optional)   │  │                 │
│  │ PowerShell — render / playlist   │  │                 │
│  └──────────────────────────────────┘  │                 │
└────────────────────────────────────────┼────────────────┘
                                         │ file IPC
┌────────────────────────────────────────▼────────────────┐
│         FL Studio  (Python MIDI script companion)        │
│         FLMcpBridge.py — runs inside FL Studio process   │
│         Reads inbox → executes FL API → writes outbox    │
└─────────────────────────────────────────────────────────┘
```

### Three-tier bridge — fallback flow

```mermaid
flowchart TD
    Client([MCP Client]) -->|stdio JSON-RPC| Server[MCP Server]

    Server --> D{FL Studio running?}
    D -- No --> ERR1[Error: FL_NOT_RUNNING]

    D -- Yes --> C{Companion active?\nheartbeat fresh}

    C -- Yes --> MB[LocalMailbox Bridge\ninbox → outbox file IPC]
    MB -->|timeout| CACHE[Return cached state\nfrom last heartbeat]

    C -- "No, desktop-capable cmd" --> DB[WindowsDesktop Bridge\nPowerShell SendKeys]
    DB --> FL[FL Studio process]

    C -- "No, mailbox-only cmd" --> ERR2[Error: COMPANION_NOT_RUNNING]

    MB --> HELPER{Helper configured?}
    HELPER -- Yes --> HP[LocalHelper Backend\nPowerShell render / playlist]
    HELPER -- No --> MB2[Mailbox result only]
    HP --> ENRICH[Enriched response]
```

### Mailbox IPC — request lifecycle

```mermaid
sequenceDiagram
    participant S as MCP Server
    participant FS as File System
    participant C as FL Companion

    S->>FS: write inbox/<uuid>.json (atomic)
    loop OnIdle / OnMidiIn callback
        C->>FS: scan inbox/ for .json files
        FS-->>C: inbox/<uuid>.json found
        C->>C: execute FL Studio API
        C->>FS: write outbox/<uuid>.json (atomic)
    end
    loop poll every 150 ms
        S->>FS: check outbox/<uuid>.json
        FS-->>S: response found
    end
    S->>FS: delete outbox/<uuid>.json
    S-->>S: return response to client
```

---

## Components

| Component | Runtime | Location |
|-----------|---------|----------|
| **MCP Server** | Node.js ≥ 20 | `src/` → `dist/` |
| **Python Companion** | Python 3.x (FL Studio built-in) | `companion/fl_studio/FLMcpBridge.py` |
| **Helper Script** *(optional)* | PowerShell | user-defined, set via `FL_MCP_HELPER_SCRIPT` |

---

## Installation

### Prerequisites

- Windows 10 / 11
- FL Studio (any recent version with MIDI scripting)
- Node.js ≥ 20
- PowerShell 5+ (pre-installed on Windows)

### 1 — Build the MCP server

```bash
git clone https://github.com/your-org/mcp-flstudio
cd mcp-flstudio
npm install
npm run build
```

The compiled public CLI is:

```bash
node dist/cli/index.js help
```

or, once installed from npm:

```bash
mcp-flstudio help
```

### 2 — Install the Python companion in FL Studio

Deploy the companion into the FL Studio MIDI scripts folder:

```powershell
npm run deploy:companion
```

or with the public CLI:

```powershell
node dist/cli/index.js deploy-companion
```

This installs:

```
Documents\Image-Line\FL Studio\Settings\Hardware\FLMcpBridge\device_FLMcpBridge.py
Documents\Image-Line\FL Studio\Settings\Hardware\FLMcpBridge\FLMcpBridge.py
```

The deployed bootstrap now uses this runtime root by default:

```
Documents\Image-Line\FL Studio\Settings\Hardware\FLMcpBridge\runtime
```

The Node server auto-detects that deployed runtime before falling back to `%LOCALAPPDATA%\FLStudioMcpBridge`, so `FL_MCP_BRIDGE_DIR` is now an override rather than a requirement.

Then in FL Studio:

1. Open **Options → MIDI Settings**
2. Create or select a virtual MIDI port named **FLMcpBridge** (loopMIDI is the recommended local setup)
3. Set **Input = FLMcpBridge**, **Controller type = FLMcpBridge**, **Port = 10**
4. Prefer **Output = (none)** for the FLMcpBridge script device. If you need an FL Studio MIDI output, use a second dedicated loopMIDI port instead of reusing the same one.
5. Click **Update MIDI scripts** / **Mettre a jour scripts MIDI**
6. Restart FL Studio if the script does not reload immediately

The companion now keeps MIDI master sync disabled by default to avoid transport feedback loops when the same `loopMIDI` port is used for both input and output.

If playback makes the BPM dip or fluctuate, inspect the FL Studio MIDI routing first: repeated `OnMidiIn` activity while transport starts usually means the loopback port is still receiving transport or clock data. The most reliable setup is one input-only script port for `FLMcpBridge`, with no FL output assigned to that same virtual port.

The play/pause loop reported during validation is now resolved in the default deployed runtime: inbound MIDI callbacks are swallowed by the script, and mailbox servicing stays on `OnIdle` / `OnUpdateMeters`.

The companion writes its mailbox state and heartbeat to:

```
Documents\Image-Line\FL Studio\Settings\Hardware\FLMcpBridge\runtime\state.json
```

### 3 — Configure your MCP client

**Claude Desktop** (`%APPDATA%\Claude\claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "flstudio-local": {
      "command": "node",
      "args": ["C:/path/to/mcp-flstudio/dist/cli/index.js", "serve"]
    }
  }
}
```

**VS Code** (`.vscode/mcp.json` — already included in this repo):

```json
{
  "servers": {
    "flstudio-local": {
      "type": "stdio",
      "command": "node",
      "args": ["${workspaceFolder}/dist/cli/index.js", "serve"]
    }
  }
}
```

See `config/` for additional examples (Codex, PowerShell helper).

---

## Configuration

All settings are environment variables. Copy `.env.example` to `.env` and adjust. The server and CLI now load `.env` automatically from the working directory, or from `FL_MCP_ENV_FILE` when set.

| Variable | Default | Description |
|----------|---------|-------------|
| `FL_MCP_BRIDGE_DIR` | auto-detect deployed `Hardware\FLMcpBridge\runtime`, then `%LOCALAPPDATA%\FLStudioMcpBridge` | Root directory for all bridge files |
| `FL_MCP_HELPER_DIR` | `<bridge_dir>\helper` | Root directory for helper request and snapshot files |
| `FL_MCP_LOG_FILE` | `<bridge_dir>\bridge.log` | Structured JSON log path |
| `FL_MCP_LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
| `FL_MCP_SAFE_MODE` | `true` | Enables operation guards |
| `FL_MCP_RESPONSE_TIMEOUT_MS` | `8000` | Default mailbox timeout (ms) |
| `FL_MCP_HEARTBEAT_TIMEOUT_MS` | `15000` | Companion considered dead after (ms) |
| `FL_MCP_STALE_REQUEST_AGE_MS` | `300000` | Auto-cleanup age for orphaned requests |
| `FL_MCP_WINDOW_TITLE` | `FL Studio` | Window title for focus automation |
| `FL_MCP_HELPER_SCRIPT` | *(unset)* | Path to optional PowerShell helper script |
| `FL_MCP_HELPER_TIMEOUT_MS` | `45000` | Helper script timeout (ms) |

---

## Available Tools

The live server only registers implemented tools. The canonical catalog remains the source of truth, and any non-local command stays out of the published local-core registry.

Compatibility aliases kept across `1.x`:

- `fl_save_project` -> `session_save_project`
- `fl_undo` -> `session_undo`
- `fl_redo` -> `session_redo`

`session_clear_undo_history` is retained as an extension-only compatibility utility and is not part of the canonical published surface.

### Status & diagnostics

| Tool | Description |
|------|-------------|
| `fl_ping` | Check whether FL Studio and the companion are reachable |
| `fl_get_status` | Full status: process, companion, health, transport |
| `fl_get_version` | FL Studio version and build number |
| `fl_get_project_name` | Current project name |
| `fl_get_project_path` | Current project file path |
| `fl_get_project_modified_state` | Whether there are unsaved changes |
| `fl_get_active_window` | Targeted FL Studio window title |
| `fl_get_focus_state` | Whether FL Studio is focused |
| `fl_get_last_error` | Last recorded bridge error |
| `fl_get_environment_info` | Runtime environment summary |
| `fl_get_timebase` | Project PPQ / timebase |
| `fl_get_ui_state` | Current FL Studio UI visibility summary |

### Project overview

| Tool | Description |
|------|-------------|
| `project_get_info` | Project name, tempo, channel/mixer/playlist counts |
| `project_get_summary` | Full project state snapshot |
| `project_get_stats` | Derived statistics (muted channels, clip counts…) |
| `project_get_structure` | Derived arrangements / sections / markers |
| `project_get_selection` | Current playlist selection |
| `project_get_current_arrangement` | Active arrangement metadata |
| `project_get_open_windows` | Active FL window + UI state |
| `project_get_markers` | Playlist markers |
| `project_get_channel_inventory` | All channels with state |
| `project_get_mixer_inventory` | All mixer tracks with state |
| `project_get_playlist_inventory` | Playlist clips, tracks, and markers |
| `project_get_recent_actions` | Latest MCP actions in this session |

### Transport

| Tool | Inputs | Description |
|------|--------|-------------|
| `transport_play` | — | Start playback |
| `transport_stop` | — | Stop playback |
| `transport_pause` | — | Pause playback |
| `transport_get_status` | — | Playing / paused / stopped / recording |
| `transport_get_song_position` | — | Position in ticks |
| `transport_set_song_position` | `ticks` | Move playhead |
| `transport_jump_to_bar` | `bar` | Jump to bar number |
| `transport_jump_to_tick` | `tick` | Jump to exact tick |
| `transport_get_tempo` | — | Current BPM |
| `transport_set_tempo` | `bpm` (10–300) | Set tempo |
| `transport_nudge_tempo` | `delta_bpm` | Offset current BPM |
| `transport_rewind` | `amount_ticks?` | Move playhead backward |
| `transport_fast_forward` | `amount_ticks?` | Move playhead forward |
| `transport_start_from_beginning` | `play_after?` | Reset to bar 1 and optionally play |
| `transport_get_time_signature` | — | Current time signature |

### Channels

| Tool | Inputs | Description |
|------|--------|-------------|
| `channel_list` | — | All channels with name, volume, mute state |
| `channel_get` | `channel_index` | Single channel summary |
| `channel_find_by_name` | `name` | Case-insensitive channel lookup |
| `channel_set_volume` | `channel_index`, `value` (0–1) | Set channel volume |
| `channel_mute` | `channel`, `muted` | Mute / unmute |
| `channel_unmute` | `channel_index` | Unmute channel |

### Mixer

| Tool | Inputs | Description |
|------|--------|-------------|
| `mixer_list_tracks` | — | All mixer tracks |
| `mixer_get_track` | `track_index` | Single mixer track summary |
| `mixer_set_volume` | `track_index`, `value` (0–1) | Set track volume |
| `mixer_mute` | `track`, `muted` | Mute / unmute |

### Playlist

| Tool | Inputs | Description |
|------|--------|-------------|
| `playlist_get_clips` | — | Playlist inventory enriched by the helper backend when available |
| `playlist_list_tracks` | — | Derived playlist track list |
| `playlist_get_track` | `track_index` | Single playlist track summary |

### Piano Roll

| Tool | Inputs | Description |
|------|--------|-------------|
| `pianoroll_get_notes` | `channel?` | All notes in the current piano roll |
| `pianoroll_add_note` | `pitch`, `start`, `length`, `velocity?`, `channel?` | Add a note |
| `pianoroll_quantize` | `channel?`, `amount?` (0–1) | Quantize notes |

### Plugins

| Tool | Inputs | Description |
|------|--------|-------------|
| `plugin_set_parameter` | `target`, `parameter`, `value` (0–1) | Set a plugin parameter |

### Session management

| Tool | Inputs | Description |
|------|--------|-------------|
| `fl_save_project` / `session_save_project` | — | Save the current project |
| `fl_undo` / `session_undo` | — | Undo last action |
| `fl_redo` / `session_redo` | — | Redo |
| `session_save_checkpoint` | `label` | Named state snapshot |
| `session_list_checkpoints` | — | All checkpoints |
| `session_delete_checkpoint` | `checkpoint_id` | Delete a checkpoint |
| `session_get_undo_history` | — | Recent undo/redo entries |
| `session_clear_undo_history` | — | Remove undo/redo from audit log |
| `session_get_audit_log` | `limit?` | Full audit log |
| `session_clear_audit_log` | — | Clear audit log |
| `session_begin_transaction` | `label?` | Open a logical transaction |
| `session_commit_transaction` | `transaction_id` | Commit |
| `session_abort_transaction` | `transaction_id` | Abort |
| `session_get_transaction_state` | `transaction_id` | Transaction status |

### Render

| Tool | Inputs | Description |
|------|--------|-------------|
| `render_preview_wav` | `outputPath?` | Trigger a local WAV render via the helper backend |

---

## Resources (MCP)

| URI | Description |
|-----|-------------|
| `fl://status/current` | Current FL Studio status (JSON) |
| `fl://project/current` | Current project summary (JSON) |
| `fl://channels/list` | Channel list (JSON) |
| `fl://mixer/{track}` | Mixer track detail (JSON) |
| `fl://catalog/tools` | Full tool catalog |
| `fl://catalog/domain/{domain}` | Tools for one domain |
| `fl://catalog/tool/{name}` | Single tool definition |
| `fl://catalog/coverage` | Generated coverage report: canonical / published / alias / missing |

---

## Prompts (MCP)

| Name | Args | Description |
|------|------|-------------|
| `arrange-trap-beat` | `style`, `bars` | Inspect session and propose an arrangement |
| `audit-session-before-render` | `focus` | Pre-render checklist |
| `clean-up-low-end` | `bassBus` | Low-end cleanup workflow |

---

## Health states

```
fl_get_status → health field
```

| State | Meaning |
|-------|---------|
| `ready` | FL Studio running + companion active and sending heartbeats |
| `degraded` | FL Studio running, companion detected but heartbeat stale (> 15 s) |
| `offline` | FL Studio process not found |

---

## Diagnostics

```bash
# Print the resolved configuration
npm run debug:config
# or
node dist/cli/index.js debug-config

# Inspect current bridge state (companion heartbeat, pending requests)
npm run debug:state
# or
node dist/cli/index.js debug-state

# Print canonical catalog coverage vs live registry
npm run catalog:coverage

# Verify the companion end-to-end
npm run verify

# Generate the full tool-by-tool checklist
npm run functional:checklist

# Run the scripted smoke scorecard and publish a Markdown report
npm run evaluate:functional

# Run tests
npm test

# Run tests with coverage
npm run test:coverage
```

Log output is written to `bridge.log` in newline-delimited JSON:

```powershell
# Tail the live log for the resolved bridgeDir (PowerShell)
$bridgeDir = (node dist/cli/index.js debug-config | ConvertFrom-Json).bridgeDir
Get-Content (Join-Path $bridgeDir 'bridge.log') -Wait -Tail 40
```

---

## Functional validation

Public validation assets now live under `docs/validation/`:

- `docs/validation/bridge-runtime-changes.md` documents the runtime and deployment fixes
- `docs/validation/mcp-tool-test-checklist.md` lists every registered MCP call and now includes the dated manual validation snapshot from the guided FL Studio campaign
- `docs/validation/functional-evaluation.plan.json` defines the scripted smoke suite
- `docs/validation/functional-evaluation.latest.md` is the latest generated Markdown scorecard
- `docs/validation/functional-evaluation.latest.html` is the visual dashboard
- `docs/validation/functional-evaluation.latest.summary.svg` is the release summary visual
- `docs/validation/functional-evaluation.latest.domains.svg` is the domain score visual
- `docs/validation/functional-evaluation.latest.coverage.svg` is the domain coverage visual

The manual snapshot currently records confirmed validation for selection, mute/unmute, solo/unsolo, pan, volume, color, rename, channel-to-mixer routing, mixer enable/disable, and safe `transport_play` / `transport_stop`, along with known failures such as `channel_set_pitch`, `transport_toggle_metronome`, and the still-inconsistent `transport_toggle_loop`.

Public command-reference assets now live under `docs/reference/`:

- `docs/reference/tool-catalog.en.md` is the English command catalog
- `docs/reference/tool-catalog.fr.md` is the French command catalog
- `docs/reference/tool-catalog.csv` is the spreadsheet-friendly export of all commands
- `docs/reference/tool-catalog.json` is the machine-readable command inventory
- `docs/reference/tool-catalog.domains.svg` is the command count by domain graph
- `docs/reference/tool-catalog.coverage.svg` is the automated coverage by domain graph
- `docs/reference/tool-status-board.md` is the working vs incoming/planned status board
- `docs/reference/tool-status-board.fr.md` is the dedicated French status board
- `docs/reference/tool-status-board.csv` is the spreadsheet export of the status board
- `docs/reference/tool-status-board.json` is the machine-readable status board
- `docs/reference/tool-status-board.svg` is the status distribution graph

Recommended workflow:

```bash
npm run functional:checklist
npm run evaluate:functional
npm run docs:catalog
npm run docs:status
```

The automated plan now covers:

- companion health and transport smoke checks
- `channel_mute` with automatic state restoration
- `mixer_set_volume` with automatic state restoration
- safe `plugin_*` inspection calls driven by live target discovery
- safe `session_*` checkpoint and transaction flows

---

## Limitations

- **Windows only** — requires PowerShell and FL Studio
- **`transport_stop` desktop fallback** is a play/pause toggle; companion required for exact stop
- **`transport_get_song_position`** may return `UNSUPPORTED_COMMAND` on FL Studio builds that do not expose bar/beat conversion in the MIDI scripting API
- **Piano roll quantize** exposes full-amount quantize only (FL scripting API limitation)
- **Mixer effect plugins** are not individually addressable (channel plugins only)
- **`render_preview_wav`** requires the optional helper backend script

---

## Development

```bash
npm run dev           # Run from TypeScript source (tsx, no build needed)
npm run build         # Compile TypeScript → dist/
npm run check         # Type-check only (no emit)
npm run typecheck     # Type-check gate used by CI/prepack
npm run lint          # ESLint
npm run lint:fix      # ESLint with auto-fix
npm run format        # Prettier
npm run functional:checklist
npm run evaluate:functional
npm test              # Vitest
npm run test:coverage # With coverage report
npm run ci:check      # build + typecheck + lint + test + npm pack --dry-run
```

### Project structure

```
mcp-flstudio/
├── src/
│   ├── index.ts               # MCP server entry point — tool/resource registration
│   ├── config.ts              # Environment variable parsing
│   ├── configValidation.ts    # Config validation with detailed errors
│   ├── logger.ts              # Structured JSON logger (stderr + file)
│   ├── errors.ts              # BridgeRuntimeError + toBridgeError()
│   ├── types.ts               # Shared TypeScript types
│   ├── env.ts                 # Optional .env loading
│   ├── publicApi.ts           # snake_case public payload normalization
│   ├── server.ts              # MCP bootstrap
│   ├── serverRuntime.ts       # Shared runtime helpers / tool execution
│   ├── bridge/
│   │   ├── flStudioBridge.ts      # Orchestrator — routes across the three tiers
│   │   ├── localMailboxBridge.ts  # File-based IPC (inbox/outbox)
│   │   ├── windowsDesktopBridge.ts # PowerShell COM fallback
│   │   └── helperBackend.ts       # Optional PowerShell helper (render, playlist)
│   ├── catalog/
│   │   └── loadCatalog.ts     # YAML catalog loader + coverage report builder
│   ├── registry/
│   │   ├── definitions.ts     # Canonical tool definitions by domain
│   │   └── register.ts        # MCP registration factory
│   ├── services/
│   │   └── sessionState.ts    # Audit log, checkpoints, transactions
│   ├── cli/
│   │   └── index.ts           # Public CLI: serve / debug / verify / deploy
│   └── __tests__/
│       ├── configValidation.test.ts
│       ├── env.test.ts
│       ├── publicApi.test.ts
│       ├── catalog.test.ts
│       ├── packageManifest.test.ts
│       ├── logger.test.ts
│       └── sessionState.test.ts
├── companion/
│   └── fl_studio/
│       ├── device_FLMcpBridge.py   # Publishable bootstrap, no hardcoded repo path
│       └── FLMcpBridge.py          # FL Studio MIDI script companion runtime
├── config/                    # Client configuration examples
├── .github/workflows/ci.yml   # Required build/test/package gates
├── dist/                      # Compiled output (git-ignored)
└── tools_scoop_mvp_v3         # YAML tool catalog (V3)
```

---

## License

MIT