Files

7.0 KiB

MCP server

SoundCraft speaks the Model Context Protocol over stdio, so Claude and other agents can make music with it.

soundcraft-cli mcp                    # headless engine (no window, no audio device)
soundcraft-cli mcp --demo             # headless, starting from the demo session
soundcraft-cli mcp --automation-read-root DIR --automation-write-root DIR   # headless, confined to folders
soundcraft --control 7801 &           # the desktop app…
soundcraft-cli mcp --connect 7801     # …driven over MCP: same tools plus screenshots and clicks

Claude Code:

claude mcp add soundcraft -- soundcraft-cli mcp --demo

Confining file access

soundcraft-cli mcp --automation-read-root /work/project --automation-write-root /work/project/out
{"mcpServers": {"soundcraft": {"command": "soundcraft-cli",
  "args": ["mcp", "--automation-read-root", "/work/project", "--automation-write-root", "/work/project/out"]}}}

The two flags (VectorCraft and the other Craft apps take the same ones, so one client config works across the suite) keep the agent to the folders you give it:

  • Every file a command reads must lie inside the read root: open_session, session.open, import_audio and file.import_audio, file.import_midi, file.import_video, file.import_clip_groups, file.import_session_data, track.load_preset and mix.sampler_load. The media a session file names is read too, so an opened session whose audio paths lead outside the read root loads without that audio and lists it under missing.
  • Every file a command writes must lie inside the write root: save_session, session.save, session.save_as, session.save_copy, session.save_template, bounce_mix and file.bounce_mix, file.bounce_stems, file.export_clips, file.export_midi, file.export_session_text, file.export_selected_tracks_as_session, file.export_sibelius, file.send_to_sibelius, file.print_score, file.export_clip_groups and track.save_preset, together with what a session save writes beside the session file (Audio Files/, Backups/) and its temporary file. A score command given no path writes to the temp folder, which is outside the write root unless you made it the root, so it is refused.
  • Read and write access are separate. Give the same folder twice for both; a root left out grants none of its access (with only --automation-read-root, nothing can be written to disk). Without either flag nothing changes.
  • A path is made absolute against the server's working directory, then its links are followed: a file (or folder) link that leads out of a root is outside it. For a file that doesn't exist yet, its nearest existing folder's links are followed and the rest must be plain names, so .. below a folder that doesn't exist is refused, as is a link that leads nowhere (writing through it would create its target). The result is compared with the root by whole folder names: /work2/x is not inside /work. On Windows case is ignored, UNC paths (\\server\share\...) are resolved like drive-letter ones, and device names (CON, NUL, COM1...) are refused.
  • A refused path is an ordinary tool error the agent can read and correct, for example automation path rejected: outside the write root /work/project/out: /etc/x.wav or automation filesystem access is not granted: write authority is absent: /work/project/out/x.wav. A refused save path is reported as refused, never as "already exists".
  • The roots must be existing folders; a missing one stops the server with an error. Each flag is given once (--flag <dir> or --flag=<dir>), and a misspelled --automation-... flag is an error rather than an unconfined server.

The flags confine the in-process server, so --connect together with them is an error: the server would otherwise talk to an app that isn't confined. SoundCraft's desktop app has no equivalent flags yet, so a running app (soundcraft --control) is not confined. --demo is the operator's own choice and is built before the roots apply.

Playback and bounce need no check of their own. SoundCraft decodes audio into the session when it enters it (import, open, sampler load), so rendering reads nothing from disk afterwards. Movies are the exception: a session only references them by path and the desktop app decodes pictures from that file, but the headless server never does, and the movie's path is checked when it is imported and when a session naming it is opened.

What the roots do not cover: commands that take no path still read and change the open session, and inspect_session returns it. Plug-in discovery scans the system's plug-in folders and loads plug-in binaries, which are fixed locations and native code, not agent paths. Recording is only reachable in the desktop app. Anything that reads or writes a path goes through one place in the engine (io::read_file, io::write_file and the helpers beside them in crates/engine/src/io.rs); a test keeps the rest of the engine from touching the file system on its own.

The checks hold for every thread of the server process. A path is checked, then opened: another program that swaps a link in between can still win that race, so the roots keep an agent to the files it was given rather than guarding against other software on the computer.

Tools

Tool Purpose
list_commands Discover every command (id, label, menu path, shortcut, params doc, enabled).
execute Run one command with JSON params.
batch Run several commands in order (keep_going optional). Any failed call makes the tool result isError: true; with keep_going the remaining calls still run and every result is listed.
inspect_session Verify work: tracks, clips, mixer, plugins, sends, automation, markers, selection; each sources[] entry includes loaded (decoded audio in the pool).
new_session, open_session, save_session Session files (.scraft, audio in Audio Files/). open_session returns missing: the media files that could not be loaded (empty when all are found). save_session won't save over another existing file unless replace is true.
import_audio WAV, AIFF, FLAC, MP3, OGG, AAC/M4A, ALAC, CAF onto a new or existing track.
bounce_mix Render to WAV/AIFF/FLAC; returns peak dBFS and integrated LUFS.
parity Feature-parity report.
screenshot, ui_inspect, ui_set, menu_invoke, click, drag, key App only (with --connect).

Resources: soundcraft://session (full session JSON), soundcraft://commands.

Conventions

  • Programmatic calls never open dialogs; empty params act on the current selection.
  • Positions: samples, {"seconds": x}, or "bars_beats:5|1|000"-style strings.
  • Tracks by name or id; clips by id.
  • Every command is undoable through edit.undo unless it is a query or a view setting.

The acceptance test crates/automation/tests/agent_tasks.rs drives a realistic session end to end through MCP only (mixing, plugins, routing, editing with undo, markers, tempo, automation, bounce, save and reopen).