MCP
The Model Context Protocol lets an AI assistant call tools on your Mac. choruspop mcp is an MCP server, so Claude and other clients can make songs, versions and exports for you.
Set up
First install the command-line tool: the server is part of it.
Claude Code
claude mcp add choruspop -- choruspop mcp
Add --scope user to have it in every project. If choruspop isn't on your PATH, use the full path: /Applications/ChorusPop.app/Contents/Helpers/choruspop.
Claude Desktop
Settings ▸ Developer ▸ Edit Config opens ~/Library/Application Support/Claude/claude_desktop_config.json. Add ChorusPop under mcpServers, then quit and reopen Claude. Claude Desktop doesn't read your shell's PATH, so give the full path:
{
"mcpServers": {
"choruspop": {
"command": "/Applications/ChorusPop.app/Contents/Helpers/choruspop",
"args": ["mcp"]
}
}
}
Other clients
Any client that starts stdio servers takes the same two things: the command and the argument mcp. No environment variables or network ports are needed.
{
"name": "choruspop",
"transport": "stdio",
"command": "/Applications/ChorusPop.app/Contents/Helpers/choruspop",
"args": ["mcp"]
}
With the Python package installed, python3 -m choruspop.mcp (or choruspop-mcp) starts the same server.
Try it
- “Write a 30-second synthwave song about the harbor at night and make it with ChorusPop.”
- “Make three short lo-fi songs for my study playlist, one at a time, and put them in an album called Focus.”
- “Take the newest ChorusPop song, remove the vocals, and save the instrumental as FLAC on my Desktop.”
- “List my ChorusPop songs from this week and make stems of the one called Neon River.”
- “Make that last song again with the same seed, but with a male voice.”
The server hands every client the guide for AI agents when it connects, so the assistant knows songs take minutes, asks before long batches and reports where the files went.
How long jobs work
A song takes minutes, so the tools don't hold the call open by default:
make_song(orremove_vocals,vocals_only,stems) queues the job and returns its id at once.get_jobwithwait_seconds(up to 600) holds until the job ends or that time passes. If the client sent aprogressToken, the server sendsnotifications/progress(0–100, with the stage and time left) while it waits.- When the job is
done, the result hassong_idandfiles: absolute paths in~/Music/ChorusPop/<album or Singles>/, or in thedestinationyou gavemake_song.
make_song also takes wait: true to hold the call itself, up to timeout_seconds. If the client cancels a call (notifications/cancelled), the job keeps going in ChorusPop; cancel_job stops it. Jobs run one at a time, in the order they were queued.
→ {"method":"tools/call","params":{"name":"make_song","arguments":{"lyrics":"[verse]\nNeon on the river\n[chorus]\nTurn it up","genre":"synthwave","moods":["dreamy"],"length_seconds":30}}}
← {"structuredContent":{"job":{"id":"8f3c…","kind":"song","status":"queued","progress":0,"stage":"waiting","title":"Neon on the river"}}}
→ {"method":"tools/call","params":{"name":"get_job","arguments":{"job_id":"8f3c…","wait_seconds":300},"_meta":{"progressToken":7}}}
← {"method":"notifications/progress","params":{"progressToken":7,"progress":42,"total":100,"message":"Neon on the river: running 42% · performing · about 0:48 left"}}
← {"structuredContent":{"job":{…,"status":"done"},"song_id":"8f3c…","files":["/Users/me/Music/ChorusPop/Singles/Neon on the river.m4a"]}}
Every result is also plain text in content (a one-line summary and the same JSON), for clients that don't read structuredContent. A tool that fails returns isError: true and {"error": {"code": …, "message": …}} with one of the error codes.
Server
| Server name | choruspop (ChorusPop 1.1) |
| Transport | stdio, one JSON-RPC message per line |
| Protocol versions | 2025-06-18, 2025-03-26, 2024-11-05 |
| Capabilities | {"tools":{"listChanged":false}} |
| Instructions | The guide for AI agents, 9,509 characters |
Tools
| Tool | What it does |
|---|---|
make_song | Make a song |
get_job | Get a job |
list_songs | List songs |
get_song | Get a song |
remove_vocals | Remove vocals |
vocals_only | Vocals only |
stems | Stems |
export_song | Export a song |
cancel_job | Cancel a job |
make_song Make a song
Queue a new song — music and singing — from lyrics and a style, in ChorusPop on this Mac. Returns a job id at once (or, with wait, holds until it's done). Songs are made on this Mac by an on-device model and take minutes, not seconds: roughly 1–3 minutes for 0:30 and 10+ minutes for 4:50 on an Apple-silicon Mac, one at a time in ChorusPop's queue. Follow the job with get_job; when it's done the result has song_id and the file path.
Lyrics: plain lines under section tags like [verse], [chorus], [bridge], [outro]; about 2 sung words per second of length fit. Style: pick genre/moods/voice/instruments, or give a free-form style line instead (it wins over the chips).
| Input | Type | Values | What it is |
|---|---|---|---|
album | string | up to 80 characters | Add the finished song to the album with this title (made if there isn't one). |
destination | string | Optional absolute path for the file(s): an existing folder, a path ending in /, or a file path. Default: the user's Music folder (~/Music/ChorusPop/<album or Singles>/). | |
format | string | one of m4a, alac, flac; default "m4a" | File format: m4a (AAC, small), alac (Apple Lossless) or flac. Every file carries title, album, cover art, lyrics and the model credit. |
genre | string | one of pop, rock, hipHop, rnb, electronic, dance, indie, folk, country, jazz, blues, soul, metal, punk, reggae, lofi, synthwave; default "pop" | Genre chip. |
instrumental | boolean | default false | No singing: the plan's Vocal line is silenced and the tune moves to the band. Leave lyrics out. Experimental: on some seeds it still sings, which is why the app doesn't offer it yet. |
instruments | array of string | one of piano, acousticGuitar, electricGuitar, synth, strings, bass, drums, 808s, brass; up to 3 | Up to 3 featured instruments. |
key | string | The song's key, e.g. "D minor", "F# major", "Bb", "Ebm". Exact: the model writes its plan, the plan is transposed to this key (the nearest way), then sung. Naming a key in `style` does nothing reliable. A major plan asked for a minor key (or the reverse) goes to the key with that signature (D minor → F major). | |
length_seconds | integer | 30–290; steps of 10; default 60 | Upper bound on the song's length, 30–290 s (0:30–4:50) in 10 s steps. The song may end a little sooner on its own. Longer takes proportionally longer to make. |
lyrics | string | up to 3000 characters | The words to sing, with section tags: "[verse]\nline\nline\n\n[chorus]\nline". Up to 3000 characters. Required, except with instrumental (leave it out) or plan_file (optional: sung to the plan's Vocal line). |
midi_roles | string | With a MIDI plan_file: which tracks play what, as track numbers, e.g. "voice=1,lead=5,harmony=2+3" (voice is sung, lead is the instrument line, harmony gives the chords). Default: the app's own picks. | |
moods | array of string | one of upbeat, happy, chill, dreamy, romantic, sad, dark, energetic, angry, epic; up to 2 | Up to 2 moods, in order of weight. |
plan_file | string | Optional absolute path to a plan to sing from instead of the model writing one: an .abc file in YuE2's ABC dialect, or a .mid/.midi file (turned into a plan as the app's Import MIDI does). | |
seed | integer | 0–4294967295 | Same seed and inputs make the same song. Default: random. |
style | string | up to 400 characters | A free-form style line fed to the model as is, e.g. "synthwave dreamy synth female vocal". Overrides genre/moods/voice/instruments. |
timeout_seconds | integer | 1–3600; default 900 | With wait: the longest to hold the call, in seconds. The job keeps going after it. |
title | string | up to 80 characters | The song's title. Default: the first lyric line. |
voice | string | one of female, male; default "female" | Singer. |
wait | boolean | default false | Hold this call until the job is done (sending progress notifications if you passed a progressToken), up to timeout_seconds. Default false: return the job id at once and poll get_job. |
get_job Get a job
The state of a job: queued, running, paused, done, failed or cancelled, with progress (0–1), the stage and an estimate of the seconds left. When a song job is done it has song_id and files (saved in the format asked for at make_song, to its destination or the Music folder). Pass wait_seconds to hold the call until the job ends or that time passes, with progress notifications. Poll every 30–60 s rather than faster; a song takes minutes.
read-only · idempotent
| Input | Type | Values | What it is |
|---|---|---|---|
job_id required | string | A job id returned by make_song, remove_vocals, vocals_only or stems. | |
wait_seconds | integer | 0–600; default 0 | Hold the call up to this long for the job to finish (0 = answer now). |
list_songs List songs
The user's ChorusPop songs, newest first: id, title, style, length, seed, albums and the versions made (instrumental, vocals, stems).
read-only · idempotent
| Input | Type | Values | What it is |
|---|---|---|---|
album | string | Only songs in the album with this title. | |
limit | integer | 1–200; default 20 | At most this many songs. |
query | string | Matches titles, lyrics and style words. |
get_song Get a song
One song's details and lyrics, the key, tempo and metre from its plan (null without one), and the path of its audio file in the Music folder (saved there as M4A first if it isn't yet), plus any versions saved there.
idempotent
| Input | Type | Values | What it is |
|---|---|---|---|
song required | string | The song: its id (from make_song, get_job or list_songs), the first 6+ characters of one, its exact title, or "latest". |
remove_vocals Remove vocals
Queue the instrumental of a song (the singing taken out). Returns a job id; with wait, holds until done. It waits for any song being made, then takes seconds to about a minute. The finished file is saved like make_song's.
| Input | Type | Values | What it is |
|---|---|---|---|
song required | string | The song: its id (from make_song, get_job or list_songs), the first 6+ characters of one, its exact title, or "latest". | |
destination | string | Optional absolute path for the file(s): an existing folder, a path ending in /, or a file path. Default: the user's Music folder (~/Music/ChorusPop/<album or Singles>/). | |
format | string | one of m4a, alac, flac; default "m4a" | File format: m4a (AAC, small), alac (Apple Lossless) or flac. Every file carries title, album, cover art, lyrics and the model credit. |
timeout_seconds | integer | 1–3600; default 900 | With wait: the longest to hold the call, in seconds. The job keeps going after it. |
wait | boolean | default false | Hold this call until the job is done (sending progress notifications if you passed a progressToken), up to timeout_seconds. Default false: return the job id at once and poll get_job. |
vocals_only Vocals only
Queue a version of a song with only the singing. Returns a job id; with wait, holds until done. Seconds to about a minute, after any song being made.
| Input | Type | Values | What it is |
|---|---|---|---|
song required | string | The song: its id (from make_song, get_job or list_songs), the first 6+ characters of one, its exact title, or "latest". | |
destination | string | Optional absolute path for the file(s): an existing folder, a path ending in /, or a file path. Default: the user's Music folder (~/Music/ChorusPop/<album or Singles>/). | |
format | string | one of m4a, alac, flac; default "m4a" | File format: m4a (AAC, small), alac (Apple Lossless) or flac. Every file carries title, album, cover art, lyrics and the model credit. |
timeout_seconds | integer | 1–3600; default 900 | With wait: the longest to hold the call, in seconds. The job keeps going after it. |
wait | boolean | default false | Hold this call until the job is done (sending progress notifications if you passed a progressToken), up to timeout_seconds. Default false: return the job id at once and poll get_job. |
stems Stems
Queue a song's stems: four files — drums, bass, other and vocals — in a folder. Returns a job id; with wait, holds until done. Seconds to about a minute, after any song being made.
| Input | Type | Values | What it is |
|---|---|---|---|
song required | string | The song: its id (from make_song, get_job or list_songs), the first 6+ characters of one, its exact title, or "latest". | |
destination | string | Optional absolute path for the file(s): an existing folder, a path ending in /, or a file path. Default: the user's Music folder (~/Music/ChorusPop/<album or Singles>/). | |
format | string | one of m4a, alac, flac; default "m4a" | File format: m4a (AAC, small), alac (Apple Lossless) or flac. Every file carries title, album, cover art, lyrics and the model credit. |
timeout_seconds | integer | 1–3600; default 900 | With wait: the longest to hold the call, in seconds. The job keeps going after it. |
wait | boolean | default false | Hold this call until the job is done (sending progress notifications if you passed a progressToken), up to timeout_seconds. Default false: return the job id at once and poll get_job. |
export_song Export a song
Write a song, or a version already made from it, as an audio file with its tags (title, album, cover, lyrics, model credit and license). To the Music folder by default, or to destination. Returns the file paths.
idempotent
| Input | Type | Values | What it is |
|---|---|---|---|
song required | string | The song: its id (from make_song, get_job or list_songs), the first 6+ characters of one, its exact title, or "latest". | |
destination | string | Optional absolute path for the file(s): an existing folder, a path ending in /, or a file path. Default: the user's Music folder (~/Music/ChorusPop/<album or Singles>/). | |
format | string | one of m4a, alac, flac; default "m4a" | File format: m4a (AAC, small), alac (Apple Lossless) or flac. Every file carries title, album, cover art, lyrics and the model credit. |
lyrics_file | boolean | default false | Also write the synced lyrics as an .lrc beside the audio. |
version | string | one of original, instrumental, vocals, stems; default "original" | original (the song), or a version made with remove_vocals (instrumental), vocals_only (vocals) or stems. |
cancel_job Cancel a job
Stop a running job or take a queued one out of the queue. Nothing is saved for it.
destructive · idempotent
| Input | Type | Values | What it is |
|---|---|---|---|
job_id required | string | A job id returned by make_song, remove_vocals, vocals_only or stems. |
Result objects
job in a result is the same object the command line prints:
Job
| Key | Type | What it is |
|---|---|---|
id | string | The job id. A song job's id becomes the song's id; a version job is <song id>#<kind>. |
kind | string | One of song, instrumental, vocals, stems. |
status | string | One of queued, running, paused, done, failed, cancelled. |
progress | number | 0…1. |
stage | string (optional) | A song's stage, in order: preparing, writing (the plan: tempo, key, chords, melody), composing, performing, mixing, saving. A version's: separating. Either, while queued or held: waiting. |
title | string | The song's title. |
song_id | string (optional) | The finished song (a song job, once done), or the song a version is of. |
eta_seconds | integer (optional) | Seconds until it's done, when the app can tell. |
length_sec | integer (optional) | The length asked for, in seconds. |
message | string (optional) | Why it failed or was cancelled. |
Song
| Key | Type | What it is |
|---|---|---|
id | string | The song's id. |
title | string | The song's title. |
style | string | "Synthwave · Dreamy" |
genre | string | Genre id. |
moods | array of string | Mood ids. |
voice | string | female or male. |
instruments | array of string | Instrument ids. |
style_fed | string | The style line the model was given. |
length_sec | integer | The length asked for, in seconds. |
audio_sec | number | The audio's actual length, in seconds. |
seed | integer | Make it again with this seed and the same inputs. |
created_at | string | When it was made (ISO 8601). |
albums | array of string | Titles of the albums it's in. |
versions | array of string | The versions made so far: instrumental, vocals, stems. |
App
| Key | Type | What it is |
|---|---|---|
name | string | ChorusPop. |
version | string | The app's version. |
build | string | The app's build number. |
bundle_id | string | The app's bundle id. |
protocol | integer | The version of the tool-to-app protocol. |
model_ready | boolean | Whether the song model is installed. Songs queued before it is wait. |
songs | integer | How many songs the library has. |
active_jobs | integer | Jobs queued or running. |
features | array of string (optional) | What the app can do beyond the first commands: plan, midi, key, instrumental, deliver, plan_info, midi_tracks. Missing from apps before build 16. |
If it doesn't connect
- Run
choruspop versionin Terminal. It opens ChorusPop and prints both versions; scripting needs ChorusPop 1.0 (6) or later, the direct download. - Claude Desktop: check the path in the config, then Settings ▸ Developer shows the server's state and log.
- The server writes its own notes to stderr, which clients keep in their MCP logs.