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