Sonilo MCP Server
An MCP (Model Context Protocol) server that exposes Sonilo's licensed music and sound-effects API to MCP-compatible clients (Claude Code, Claude Desktop, Codex).
The flagship tool is video_to_music: hand it your finished video and it composes an original soundtrack matched to the cut — the music follows the pacing, emotion, and edits because the model saw them. Length matches the video automatically. Every track is licensed and safe for commercial use (terms apply). text_to_music is also available for fixed-length tracks with no video to match.
For sound design, video_to_sfx watches your video and generates matching sound effects, returned as a standalone audio file. text_to_sfx generates a standalone effect from a description.
▶ Example result — an AI-generated trailer with its soundtrack composed by video_to_music from the assembled cut. For recipes covering any AI-video pipeline (stitch → grade → add music → mux), see the Sonilo video-to-music cookbook.
Quickstart with Claude Code
Sign in once with the CLI, then add the server with no secret in the config:
Or hold an API key yourself — get one from the Sonilo dashboard:
Then start a session and ask, e.g. "Make background music that matches this video: ~/Desktop/promo.mp4."
Why Sonilo
- Video-to-music — give it a video and Sonilo composes a full-length score matched to its pacing, motion, and emotion. Transitions and beat drops align to your cut points, and the track matches the video's duration exactly — no prompts or manual syncing required.
- Text-to-music — generate tracks from a text description (genre, mood, tempo, instrumentation), at an exact duration (5–360s) or none at all, in which case Sonilo picks a length that suits the prompt.
- Video-to-SFX — Sonilo watches the video and generates sound effects for what it sees. You get the SFX as a standalone audio file. Optional
segments let you script effects to specific time ranges ([{start, end, prompt}]).
- Text-to-SFX — generate a standalone sound effect from a description (0.5–180s), in
wav, mp3, aac, or flac.
- Proofread before dubbing —
proofread transcribes a video and translates the transcript into editable .srt files, one per language plus the source-language transcript. Read and correct the wording, then hand the files to dubbing as subtitles so the dub speaks exactly the approved text — nothing is voiced until you have signed off on it.
- Fully licensed, commercial-safe — music licensed via Shutterstock; every generated track is cleared for commercial use on social, brand content, and advertising, with no Content ID worries.
- Video-to-sound — generate music and sound effects for the same clip in one call, mixed into a single balanced soundtrack. Get back the mixed audio, or a new video with it muxed in.
- Multiple variants per call —
text_to_music, video_to_music, video_to_video_music, video_to_sound, and video_to_video_sound accept variants_num (1–10, default 1): generate several distinct creative directions in one request instead of re-rolling one at a time. Cost scales linearly with N, and N > 1 is never covered by the free trial.
- Instrument stems —
text_to_music and video_to_music accept stems=true: get the generated track additionally split into drums, bass, vocals and other, at no extra charge. Separation adds a few minutes; on video_to_music it splits the generated music, never the video's own audio.
- Pay as you go — billed only for the seconds of music you generate. Self-serve accounts start with free runs on every endpoint, no card required: 2 each on text-to-music, text-to-sfx, audio-ducking and proofread, and 1 each on video-to-music, video-to-sfx, video-to-video-music, video-to-video-sfx, video-to-sound and video-to-video-sound. After that, calls bill at the normal rate.
dubbing gets one free run, a 15-second preview: the first single-language call without subtitles dubs only the first 15 seconds of the video. After that it bills video duration × number of languages (see Free trial below).
Audio Playback Dependencies
The play_audio tool requires PortAudio at runtime (for sounddevice). On macOS/Linux, install via:
- macOS:
brew install portaudio
- Debian/Ubuntu:
sudo apt-get install libportaudio2
uvx sonilo-mcp and pip install will pull the Python bindings, but the system PortAudio library must be installed separately. The other tools (text_to_music, video_to_music, text_to_sfx, video_to_sfx, audio_ducking, get_sfx_task, get_account_services, get_usage) work without PortAudio.
Quickstart with Claude Desktop
-
Authenticate, either way round:
- Sign in with the CLI —
npm install -g sonilo-cli && sonilo login — and leave "env": {} below. Nothing secret goes in the config file.
- Or get an API key from the Sonilo dashboard and put it in
"env".
-
Install the uv package manager (provides uvx):
See the uv repo for other install methods.
-
Go to Claude > Settings > Developer > Edit Config > claude_desktop_config.json to include the following:
That is the whole config when you have signed in with sonilo login. To
hold a key instead, or to change the defaults:
-
Restart Claude Desktop. You should see the Sonilo tools available in the tool menu.
Quickstart with Codex
Signed in with the CLI (npm install -g sonilo-cli && sonilo login), the whole
setup is one command:
Or, holding a key yourself:
To configure it by hand instead:
-
Get your API key from the Sonilo dashboard — or skip this step if you signed in with sonilo login.
-
Install the uv package manager (provides uvx):
-
**Go to Codex > Settings > MCP servers to fill out the following:
[alt text]
Or you can add the server** to ~/.codex/config.toml:
- Restart Codex (or start a new session), then run
/mcp to confirm sonilo is connected and its tools are listed.
Example usage
Once the server is connected, just ask your assistant in natural language. For example:
- "Make background music that matches this video:
~/Desktop/promo.mp4."
- "Compose music for
https://example.com/clip.mp4 with a calm, ambient style."
- "I stitched my AI-generated clips into
~/Desktop/trailer.mp4 — add a soundtrack that matches the cut."
- "Use Sonilo mcp to generate 30 seconds of upbeat lo-fi hip-hop for a study playlist and save it to my Desktop."
- "Use Sonilo to write an epic orchestral cinematic track, about 60 seconds long."
- "What Sonilo services and limits does my account have?"
- "Show my Sonilo usage for the last 7 days."
- "Play the track you just generated."
The assistant will call the matching tool (text_to_music, video_to_music, text_to_sfx, video_to_sfx, audio_ducking, get_sfx_task, get_account_services, get_usage, or play_audio) and save generated audio to your configured output directory.
Configuration
Authentication
The server takes its key from SONILO_API_KEY if that is set, and otherwise
from the credential written by sonilo login
(~/.config/sonilo/credentials.json, or $XDG_CONFIG_HOME/sonilo/). The
environment variable wins on purpose, so any config that already sets it keeps
resolving to the same account after an upgrade.
The credential is looked up by the server's own SONILO_API_URL, so a staging
server never picks up a production sign-in. This server only ever reads the
file: run sonilo login again to renew it (keys from a sign-in expire after 90
days) and sonilo logout to revoke it.
Environment Variables
Some MCP hosts impose their own tool-call timeout and may cut a call off before that wait ends. The task keeps running and is still billed: its id is in the server's stderr log as [sonilo-mcp] task submitted: <id>, and get_sfx_task("<id>") retrieves the result — nothing is lost.
File access & confinement
By default, the file tools (video_to_music input, play_audio, and any
output_directory) are confined to SONILO_MCP_BASE_PATH. Paths that
resolve outside it (after symlink resolution) are rejected. This limits the
blast radius if a client is tricked into reading or exfiltrating arbitrary
files. To opt out — e.g. to read a video from elsewhere on disk — set
SONILO_MCP_ALLOW_ANY_PATH=true.
Tools
Tools marked ✅ make API calls that incur charges on your Sonilo account.
Every tool also carries MCP tool annotations, so clients can tell the two kinds apart without reading descriptions: get_account_services, get_usage and play_audio are read-only; every other tool writes files or incurs charges, and none of them is destructive (outputs never overwrite an existing file). The server's instructions tell an agent to check get_account_services before the first paid call and to recover a timed-out call with get_sfx_task.
Free trial: self-serve accounts start with a few free runs per service — no card required. get_account_services() reports what is left as trial[service] = {granted, used, remaining}; check it before calling a ✅ tool so you can warn the user instead of failing on them. When a service's remaining hits 0, calls to it fail with trial_exhausted until a payment method is added. Dubbing's single free run is a 15-second preview: the first single-language call without subtitles translates only the first 15 seconds of the video, free, and the result carries trial_preview with what the full video would cost; proofread has 2 free runs, and like dubbing it bills per language. Accounts without a free-trial allowance simply have no trial key.
Optional: if ffprobe (part of FFmpeg) is installed, video_to_music checks a video's duration locally and rejects anything over 360s before uploading. video_to_sfx performs the same local check with its 480s cap. audio_ducking does the same for both of its inputs against its 360s cap. Without it, the same limits are still enforced by the backend.
Sound effects and ducking run as tasks
The music tools stream their result and finish in one call — with one exception: video_to_music(preserve_speech=true) submits a task and polls it internally instead, because keeping the source speech is only available in the backend's async mode. You still get the saved file paths back from a single call; you just don't see the task, and — like the SFX tools — it uses the same get_sfx_task recovery path if the call times out. The SFX tools submit a task, then poll it until it completes — text_to_sfx and video_to_sfx do this for you and return the saved file paths, so you normally never see the task. audio_ducking uses the same submit-then-poll flow and the same get_sfx_task recovery path.
If a call times out, the generation keeps running (and is already charged). The error message carries the task id, and get_sfx_task("<id>") retrieves the result once it's ready. The task id is also printed to stderr the moment a task is submitted, so it survives even a cancelled call. get_sfx_task is safe to call repeatedly: if the file is already on disk it reports that instead of downloading a second copy.
Output Format
Music is saved as .m4a (AAC in MP4 container). File names use the title returned by the backend (slugified), or a sonilo-<timestamp>.m4a fallback. When multiple parallel streams are returned, a -<index> suffix is appended.
video_to_music(preserve_speech=true) saves up to three kinds of file, each labeled in the returned text: the generated music audio (same naming as above, based on prompt or falling back to music-<first 8 chars of the task id>), the preserved speech stem as <base>-vocals.<ext>, and the mux — speech and music already mixed together, the ready-to-use combined result — as <base>-mux.<ext> (or <base>-mux-<index>.<ext> for multiple streams). The speech stem and mux extensions come from the backend's reported content_type (typically .m4a).
Sound effects are saved in the requested audio_format — wav, mp3, flac, or aac (the default, written as .m4a); video_to_sfx saves audio only, not the source video.
Combined sound (video_to_sound / video_to_video_sound) is saved as a single file: a .wav for video_to_sound, a .mp4 for video_to_video_sound. The name comes from music_prompt, falling back to sfx_prompt and then to sound-<first 8 chars of the task id> / v2v-sound-<first 8 chars of the task id>. The separate music and SFX stems stay on the backend — only the mixed result is downloaded.
Multiple variants (variants_num > 1 on text_to_music, video_to_music, video_to_video_music, video_to_sound, video_to_video_sound) save one file per variant instead of one, suffixed -0, -1, … in request order (e.g. <base>-0.m4a, <base>-1.m4a). For the music tools, each variant is its own creative direction and the returned text names it when the backend provides a title. Cost scales linearly with variants_num, and any value above 1 is billed in full even on a free-trial account. A timed-out multi-variant call recovers all of its variants through get_sfx_task, the same as a single-variant one.
Stems (stems=true on text_to_music / video_to_music) save four extra files per generated stream — <base>-stems-drums.<ext>, -stems-bass, -stems-vocals, -stems-other (with the variant index folded in as <base>-stems-<index>-<name>.<ext> when variants_num > 1) — alongside the untouched full track. Separation is free and best-effort: it typically adds 2–6 minutes (the call waits up to 40), and when it fails or is skipped the result carries a stems_error note instead of (or alongside a partial set of) stem files, while the music itself is unaffected and fully delivered.
Dubbing (dubbing) is saved as one .mp4 per requested language, named dubbing-<first 8 chars of the task id>.<language>.mp4 — there is no prompt to name the files after. With export_srt, each language's re-timed script lands beside its video as dubbing-<first 8 chars of the task id>.<language>.srt, reported with that language's export status and alignment loss (plus its issue codes and report link when the export was blocked). Every dubbed .mp4 is downloaded before the first .srt, and a script that was blocked or could not be downloaded is reported as a note, not a failure — a missing subtitle never costs you a video. Asking for four languages writes four files and costs four times as much as one. Dubbing polls for at least two hours regardless of TIME_OUT_SECONDS, matching the backend's own ceiling for the job.
Proofread (proofread) is saved as one .srt per language in the result, named proofread-<first 8 chars of the task id>.<language>.srt — there is no prompt to name the files after. The set always includes the detected source language on top of every requested target, so even a request with no languages writes one file. After the files, the returned text reports the detected source_language and the cue_count of the source script, then one note per non-blocking warning (e.g. a cue whose text runs too fast to read) — a warning never blocks a file, and a language whose .srt could not be downloaded is a note carrying the task id, not a failure. Edit the files, then pass them to dubbing as subtitles so the dub speaks the approved wording; dubbing's subtitle keys must match the languages being dubbed, so leave out the source-language file unless that language is itself a target.
File names come from the prompt (slugified, truncated to 80 characters). When there is no prompt to name a file after — video_to_sfx without one, or an SFX/ducking file recovered via get_sfx_task — the name is sfx-<first 8 chars of the task id> instead. A video_to_music(preserve_speech=true) task recovered via get_sfx_task is named music-<first 8 chars of the task id> instead (get_sfx_task detects the music envelope shape and saves audio/vocals/mux the same way as a direct video_to_music call). Existing files are never overwritten: a -1, -2, … suffix is added instead.
Ducking results are saved as a single file: a .wav, or a .mp4 when the voice input was a video (the ducked mix is muxed back into it). The file name is the voice input's name plus -ducked (e.g. interview.mp4 → interview-ducked.mp4), falling back to ducked-<first 8 chars of the task id> when there is no usable name. A ducking result recovered via get_sfx_task is named sfx-<first 8 chars of the task id> instead, since that tool has no voice file name to work from.
Common Errors