Skip to main content
Glama
README.md
# Synthesizer V Studio 2 MCP Server (`mcp-svstudio`)

A production-grade [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for **Dreamtonics Synthesizer V Studio 2 Pro**, enabling Generative AI and LLM agents to safely, structurally, and effectively manipulate notes, lyrics, phonemes, vocal attributes, parameters, and playback transport via the official Dreamtonics Scripting API.

---

## Architecture Overview

Synthesizer V Studio 2 Pro executes scripts within an embedded Lua 5.4 / Duktape JS environment without external network sockets. To achieve high performance, low latency, and zero C-library dependencies, this MCP server uses an **Atomic File-Mailbox IPC Protocol**:

```
+--------------------------------------+
|       LLM / MCP Client               |
|   (Antigravity / Claude / Cursor)    |
+------------------+-------------------+
                   | JSON-RPC over Stdio
                   v
+--------------------------------------+
|       Node.js MCP Server             |
|  - Tool Schema & Validation (Zod)    |
|  - Stable Note Locator Resolver      |
|  - Safe Diff & Dry Run Engine        |
|  - Mailbox IPC Client                |
+------------------+-------------------+
                   | Atomic Mailbox IPC (.req / .res)
                   | Live Heartbeat Monitor (heartbeat.json)
                   v
+--------------------------------------+
|  Synthesizer V Studio 2 Pro (Lua 5.4)|
|  `StartMCPServerRequestHandler.lua`  |
|  - Non-blocking SV:setTimeout loop   |
|  - Dreamtonics Official Scripting API|
|  - Automatic Snapshot Rollback & Undo|
+--------------------------------------+
```

### IPC Protocol Highlights
- **Atomic File Renames**: Writes to `<id>.tmp` and atomically renames to `<id>.req` / `<id>.res` to prevent race conditions and partial file reads.
- **Unique Request IDs**: Guarantees request-response pairing even during rapid sequential commands.
- **Instant Heartbeat Liveness**: The Lua script updates `heartbeat.json` every 500ms. The MCP server checks heartbeat freshness and instantly reports offline state (<50ms) instead of hanging on timeouts.
- **Automatic Garbage Collection**: Auto-cleans stale temporary files older than 60 seconds on startup and during polling.

---

## Installation & Setup

### Prerequisites
- Node.js (v18 or higher; tested on v22 & v26)
- Synthesizer V Studio Pro (Version 2.0 or 2.1+)

### 1. Build the MCP Server
```bash
git clone https://github.com/shotarokawade/SV-MCP.git
cd SV-MCP
npm install
npm run build
```

### 2. Install Lua Scripts to Synthesizer V Studio
Run the automated installer:
```bash
npm run install-scripts
```
Or manually copy the files in `sv-scripts/` to your Synthesizer V Studio scripts folder:
- **macOS**: `~/Library/Application Support/Dreamtonics/Synthesizer V Studio 2/scripts/MCP/`
- **Windows**: `%APPDATA%\Dreamtonics\Synthesizer V Studio 2\scripts\MCP\`
- **Linux**: `~/.local/share/Dreamtonics/Synthesizer V Studio 2/scripts/MCP/`

### 3. Start the Server Handler in Synthesizer V Studio
1. Launch **Synthesizer V Studio 2 Pro**.
2. Open or create a project with vocal tracks.
3. In the top menu bar, select:
   **Scripts > MCP > Start MCP Server Request Handler**
4. The background handler is now running and responsive. (To stop it, select **Scripts > MCP > Stop MCP Server Request Handler**).

---

## MCP Client Configuration

### Antigravity (`~/.gemini/config/mcp_config.json` or project configuration)
```json
{
  "mcpServers": {
    "synthv": {
      "command": "node",
      "args": ["/absolute/path/to/SV-MCP/build/index.js"],
      "env": {
        "MCP_SVSTUDIO_IPC_DIR": "/absolute/path/to/.mcp-svstudio/ipc"
      }
    }
  }
}
```

### Claude Desktop (`claude_desktop_config.json`)
```json
{
  "mcpServers": {
    "synthv": {
      "command": "node",
      "args": ["/path/to/SV-MCP/build/index.js"]
    }
  }
}
```

---

## MCP Tool Reference

| Tool Name | Description |
| :--- | :--- |
| `get_server_status` | Returns connection status, script heartbeat timestamp, and current project info. |
| `get_project_info` | Retrieves project filename, duration (in blicks), track count, group count, tempo & measure marks. |
| `list_tracks` | Lists tracks with names, group reference counts, display colors, and mixer settings (gain, pan, mute, solo). |
| `list_groups` | Lists all note groups in the project library with UUIDs and note counts. |
| `get_notes` | Retrieves notes for a track and group (0-based indices) including pitch, onset, duration, lyrics, phonemes, and note attributes. |
| `find_notes` | Searches notes matching onset range, pitch range, lyrics substring/regex, or phonemes. |
| `add_notes` | Adds one or more notes to a group. Supports `dry_run: true`. |
| `update_notes` | Updates existing notes by index or locator (`{ onset, pitch }`). Supports `dry_run: true`. |
| `delete_notes` | Deletes notes by indices or locator. Supports `dry_run: true`. |
| `get_phonemes` | Retrieves user-specified phonemes for note(s). |
| `set_phonemes` | Directly sets formal space-separated phoneme strings (`Note.setPhonemes()`). |
| `get_computed_phonemes` | Queries the internal text-to-phoneme engine results and computed attributes (`SV.getComputedAttributesForGroup`). |
| `get_singing_project_snapshot` | Reads every track/group/note with group offsets, project-absolute onset, visible lyrics, direct phonemes, and computed phonemes in one call. |
| `begin_fresh_musicxml_choir_job` | Starts a verifiable from-scratch MusicXML choir job, rejects a pre-existing output path, and records the source hash and baseline project fingerprint. |
| `audit_musicxml_lyrics` | Compares an entire MusicXML authority against the active SynthV project and returns a deterministic pass/fail plus every structural, lyric, phoneme, `sil`, and placeholder defect. |
| `repair_musicxml_lyrics` | Dry-runs or atomically applies the complete MusicXML-derived pronunciation plan, rejects stale audits/structural mismatches, and returns a post-repair audit. |
| `get_note_attributes` | Gets note attributes (detune, languageOverride, phonesetOverride, musicalType, rapAccent, per-phoneme timing/strength). |
| `set_note_attributes` | Modifies note attributes and per-phoneme attributes (`phonemes: [{ leftOffset, position, activity, strength }]`). |
| `get_voice` | Gets voice parameters on `NoteGroupReference` (loudness, tension, breathiness, gender, toneShift, vocalModeParams). |
| `set_voice` | Modifies track/group voice parameters and vocal modes. |
| `get_parameters` | Reads automation curve points for parameters (`pitchDelta`, `loudness`, `tension`, `breathiness`, `voicing`, `gender`, `vocalMode_*`). |
| `set_parameters` | Adds, replaces, or removes automation points with range validation. |
| `play` | Starts playback transport. |
| `pause` | Pauses playback without resetting playhead. |
| `stop` | Stops playback and resets playhead to start position. |
| `seek` | Moves playhead to position in seconds. |
| `get_playhead` | Reads playhead position and status (`"playing"`, `"looping"`, `"stopped"`). |
| `loop` | Sets loop playback region between `tBegin` and `tEnd` in seconds. |
| `batch_edit` | Executes multiple operations atomically in a single undo transaction with pre-validation and diff preview. |

---

## Phoneme Manipulation & German Multi-syllabic Lyrics Fix

### The Problem
When importing MusicXML from MuseScore into Synthesizer V Studio, German multi-syllabic words split across notes (e.g. `schö-` and `-ne`) with `syllabic=begin/end` often get merged with raw phoneme text in lyrics:
- Intended Note 1: `.sh er`
- Intended Note 2: `.n ax`
- Result in SynthV if placed in lyrics: `.sh er.n ax` (causing pronunciation warnings and phonetic errors).

### The Solution: Direct Phoneme Injection via MCP
Using this MCP server, the LLM sets lyrics and phonemes directly via official APIs:

```json
{
  "trackIndex": 0,
  "groupIndex": 0,
  "assignments": [
    { "noteIndex": 0, "phonemes": ".sh er" },
    { "noteIndex": 1, "phonemes": ".n ax" }
  ]
}
```

### Round-Trip Pronunciation Verification
1. Call `set_phonemes` to apply the target phonemes.
2. Call `get_computed_phonemes` to re-query Synthesizer V's internal synthesizer engine.
3. Compare the computed phonemes against expected pronunciation to verify exact match.

---

## MuseScore MCP Integration Pipeline

```
[ MuseScore MCP ]
       │ 1. Extract note pitches, onset blicks, measure positions, and lyric syllables
       ▼
[ LLM Agent ]
       │ 2. Perform German grapheme-to-phoneme (G2P) conversion to Synthesizer V phonemes
       │    (e.g., "Freude" -> [".f r oy", "d ax"])
       ▼
[ Synthesizer V MCP ]
       │ 3. `find_notes` or `get_notes` matching onset and measure range
       │ 4. `batch_edit` with `dry_run: true` to inspect diff
       │ 5. `batch_edit` with `dry_run: false` to apply notes and `set_phonemes`
       │ 6. `get_computed_phonemes` to verify synthesis pronunciation
```

## Lunaでも省略できない全件歌詞監査

合唱案件では、個別の`get_notes`や画面の目視だけで完了判定しない。新規案件は既存SVPを流用せず、次の4段階を固定手順にする。

1. `begin_fresh_musicxml_choir_job`を、まだ存在しない新規SVP保存先で呼ぶ。
2. 空のボイス先行テンプレートを作り、MusicXMLを`New Project`、テンプレートを`New Tracks`として読み込み、ノートグループ全体だけを移す。
3. 返された`freshJobId`付きで`audit_musicxml_lyrics`を呼び、`auditId`と全不具合を取得する。
4. `repair_musicxml_lyrics`を同じIDでdry-run後に一括適用し、`postAudit.passed`と`postAudit.freshStart.verified`が両方`true`であることを確認する。

```json
{
  "musicxmlPath": "/absolute/path/De_profundis.musicxml",
  "freshJobId": "begin_fresh_musicxml_choir_jobが返したjobId",
  "trackMap": {
    "P1": 0,
    "P2": 1,
    "P3": 2,
    "P4": 3
  },
  "profile": "ecclesiastical-latin",
  "requireDirectPhonemes": true,
  "requireDirectLyricLabels": true,
  "verifyComputedPhonemes": true
}
```

実適用には直前の監査が返した`auditId`が必要である。MusicXML、ノート配置、歌詞、直接音素のいずれかが変わると監査IDが古くなり、`STALE_AUDIT`として拒否される。音符数、音高、開始位置、音価などの構造不一致がある場合も自動補正せず、`requiresSourceReimport: true`を返す。

現行実装のMusicXML入力は、非圧縮の`.musicxml`または`.xml`、`score-partwise`、原則1声部1パートを対象とする。複数の歌詞付きvoiceを同一パートへ含める場合は、声部ごとに分けて書き出す。

---

## Safety, Dry Run, and Rollback Guarantees

1. **`dry_run: true`**:
   All mutation tools support `dry_run: true`. The server returns the predicted changes and diff without modifying project state.
2. **One-Step In-App Undo (`project.newUndoRecord()`)**:
   Every mutating MCP operation registers a project undo record. The user can press `Cmd+Z` / `Ctrl+Z` inside Synthesizer V Studio to instantly revert the entire operation.
3. **Transaction Rollback in Batch**:
   If an error occurs during `batch_edit`, the script captures the pre-mutation state and automatically rolls back modified items before returning the error.
4. **Boundary & Range Validation**:
   - MIDI pitch: `0` - `127`
   - Loudness: `-48` dB to `+12` dB
   - Tension / Breathiness / Gender: `-1.0` to `+1.0`
   - Voicing: `0.0` to `+1.0`
   - Pitch Delta: `-1200` to `+1200` cents
   - Vocal Mode: `0` to `150`

---

## References & Official API Compliance

- **Official Scripting Manual**: [https://resource.dreamtonics.com/scripting/index.html](https://resource.dreamtonics.com/scripting/index.html)
- **Key Official APIs Used**:
  - `Note.getPhonemes()` / `Note.setPhonemes(phonemes)`
  - `SV.getPhonemesForGroup(groupRef)`
  - `SV.getComputedAttributesForGroup(groupRef)` (SynthV 2.1.1+)
  - `Note.getAttributes()` / `Note.setAttributes(attributes)`
  - `NoteGroupReference.getVoice()` / `NoteGroupReference.setVoice(voice)`
  - `NoteGroup.getParameter(name)` / `Automation`
  - `PlaybackControl` (`play`, `pause`, `stop`, `seek`, `loop`, `getPlayhead`)
  - `Project.newUndoRecord()`

---

## License

MIT License.

TDQS

B3.4/5.0

Scored across 26 tools

Disambiguation4/5

Tools are generally organized by resource (notes, phonemes, voice, parameters, transport) and the descriptions clarify distinctions such as get_phonemes vs get_computed_phonemes and get_voice vs get_parameters. Minor overlap remains between get_project_info/get_server_status and between note attribute and phoneme tools, but selection should be reliable.

Naming Consistency4/5

Most tools follow a consistent verb_noun pattern (get_notes, set_voice, list_tracks, batch_edit), with list_ reserved for collections and get_/set_ for individual properties. The transport controls (play, pause, stop, seek, loop) are bare verbs and batch_edit is a compound, so the pattern is not perfectly uniform but remains predictable.

Tool Count4/5

26 tools is high but defensible for a Synthesizer V Studio integration that spans project status, note editing, phonemes, attributes, voice settings, automation curves, and transport. The count is at the upper edge of reasonable and every tool maps to a distinct operation rather than redundant duplicates.

Completeness4/5

The core note workflow is well covered: create/read/search/update/delete notes, phoneme handling, attribute editing, voice settings, parameter automation, and transport control. Missing group/track creation or rendering/export are notable gaps, but they appear outside the intended editing scope and can be worked around.

Maintenance

ActivityMaintained
ResponsivenessNo issues