ChorusPop for AI agents
For agents and the people who set them up. The same text is AGENTS.md (plain Markdown), choruspop.agents_guide() in the Python package, and the MCP server's instructions.
You are driving ChorusPop, a Mac app that makes songs (music and singing) from lyrics and a style, on the user's Mac, with an on-device model. You reach it through its MCP server (choruspop mcp), its command-line tool (choruspop) or its Python package (choruspop). All three put work into the app's own queue. Nothing leaves the Mac.
What you can do
- Make a song from lyrics and a style, 0:30 to 4:50 long, in an exact key if you ask for one.
- Make a song from a plan: an ABC plan or a MIDI file (melody, chords).
- Make an instrumental (no singing). Experimental: on some seeds it still sings, which is why the app itself doesn't offer it yet.
- Make versions of a finished song: the instrumental, the vocals alone, or four stems.
- Find the user's songs, read their details and lyrics.
- Export a song or a version as M4A, Apple Lossless or FLAC, with an optional synced-lyrics
.lrcfile. - Follow and cancel jobs.
MCP tools: when to use each
| Tool | Use it to |
|---|---|
make_song | Start a song. Returns a job id at once. key, instrumental and plan_file here too. |
get_job | Follow a job. Pass wait_seconds (up to 600) to hold until it ends. |
list_songs | Find songs by title, lyrics or style words, newest first. |
get_song | Read one song's details, lyrics, key, tempo and metre, and get its file path. |
remove_vocals | Make the instrumental of a finished song. |
vocals_only | Make a version with only the singing. |
stems | Split a song into drums, bass, other and vocals. |
export_song | Write a song or a version already made to a file. |
cancel_job | Stop a running job or remove a queued one. |
A song argument takes its id, the first 6+ characters of the id, its exact title, or "latest". The CLI and Python use the same words: make, strip, export, list, show, status, cancel, and tracks for a MIDI file's tracks.
Jobs take minutes
make_songreturns a job id at once. Don't passwait: trueunless you passed aprogressTokenand your client shows progress.- Call
get_jobwithwait_seconds: 300(or poll every 30–60 s without it) untilstatusisdone,failedorcancelled. With aprogressTokenthe server sendsnotifications/progress(0–100) while it waits. - When it's
done, the result hassong_idandfiles(absolute paths).
Timing on an M-series Mac: roughly 1–2 minutes per 40 s of audio (a 0:30 song about 1–1.5 minutes, a 3:30 song about 11 minutes). Versions take seconds to a minute, after any song being made.
The app renders ONE job at a time. Never start a second song expecting it to run in parallel; it waits in the queue behind the first. The queue holds 10 songs. status values: queued, running, paused, done, failed, cancelled. eta_seconds is an estimate when the app has one.
If your call is cancelled or times out, the job keeps going in the app. Only cancel_job stops it.
Lyrics
[verse]
Neon on the river
Lights across the bay
[chorus]
Turn it up
Turn it up tonight
- Section tags on their own line:
[verse],[chorus],[bridge],[outro]. A blank line between sections. - One line per sung phrase. Plain words: no chords, stage directions or speaker names.
- About 2 sung words per second of length fit. Short lyrics end the song early:
length_secondsis an upper bound, not a promise. - Length: 30–290 s in 10 s steps (0:30–4:50), default 60. Longer takes proportionally longer.
- Up to 3,000 characters of lyrics, 80 for a title (default: the first line).
Style
Pick chips, or write one style line. A style line wins over the chips.
genre: pop, rock, hipHop, rnb, electronic, dance, indie, folk, country, jazz, blues, soul, metal, punk, reggae, lofi, synthwave.moods(up to 2, strongest first): upbeat, happy, chill, dreamy, romantic, sad, dark, energetic, angry, epic.voice: female or male.instruments(up to 3): piano, acousticGuitar, electricGuitar, synth, strings, bass, drums, 808s, brass.style(up to 400 characters): the line the model reads, in the order genre, mood, instruments, voice, for examplesynthwave dreamy synth female vocalorrock energetic electric guitar drums male vocal 120 bpm. Keep it to a few plain words; more words blur it.
Key
Naming a key in style does nothing reliable: the model picks its own. Pass key instead ("D minor", "F# major", "Bb", "Ebm"; CLI --key). The model writes its plan, the plan is moved to that key the nearest way (up to 6 semitones), then sung, so the key is exact. Transposing can't change a mode: a plan in a major key asked for a minor one (or the reverse) goes to the key with that signature (asked D minor, a major plan comes out in F major). get_song (CLI show --json) reports the key, tempo and metre of the plan the song was sung from, and the plan's path (plan.abc).
From a plan
plan_file (CLI --plan song.abc or --midi song.mid) sings a plan instead of one the model writes, as the app's Import MIDI and Edit plan do.
- An
.abcplan must be in the model's dialect: headerX,T,M,L,Q, voicesV: VocalthenV: Ins, thenK; one phrase per voice under each% section; every bar full. Write the Vocal melody an octave above the pitch you want sung: the model sings it an octave lower. A song's ownplan.abcis a good template. - A MIDI file becomes a plan as Import MIDI makes it.
midi_roles(CLI--roles) picks the tracks:voice=1,lead=5,harmony=2+3. Without it, the app's own picks;choruspop tracks song.midlists the tracks and those picks. - Lyrics are optional; given, they're sung to the plan's Vocal line, one syllable per note.
keymoves the plan too. The song is made at least as long as the plan.- An invalid plan is refused with
invalidand the line that's wrong.
Instrumental
instrumental: true (CLI --instrumental) makes a song with no singing: the plan's Vocal line is silenced and the tune moves to the band. Leave the lyrics out. Experimental: on some seeds it still sings, which is why the app doesn't offer it yet. Tell the user that if they ask for one.
Seeds
The same seed (0–4294967295) with the same lyrics, style and length makes the same song. Leave it out for a random one. To remake or vary a song the user liked, read its seed from get_song or list_songs and reuse it, changing one thing at a time.
Versions
remove_vocals: one file, the song without singing (versioninstrumental).vocals_only: one file, only the singing (versionvocals).stems: a folder<title> (Stems)with four files: drums, bass, other, vocals.
Each returns a job id like <song_id>#instrumental; follow it with get_job. Once made, export_song with version writes it again in any format.
Where files land
- MCP:
~/Music/ChorusPop/<album or Singles>/unless you passdestination(an absolute path: an existing folder, a path ending in/, or a file path). - CLI and Python: the current folder, or
--out PATH/out=, or--music/music=Truefor the Music folder. If the command stops waiting (--timeout, ^C, or--no-waitwith--out/--music), the job keeps going and its files still go there when it's done. - Versions wait for every song queued or being made first: songs go ahead of strips.
- Formats:
m4a(default, small),alac(Apple Lossless),flac. - Every file carries the title, album, cover, lyrics and the model's credit and license tags.
Errors
| What you see | What to do |
|---|---|
| ChorusPop isn't running | Nothing: the tool opens it in the background and waits for it. |
unavailable "didn't answer" | Scripting needs the direct download of ChorusPop, build 6 or later, from choruspop.com. Tell the user. |
queue_full | 10 songs are waiting. Wait for one to finish, then retry. |
not_supported | This Mac can't make songs. Tell the user. |
not_found | No such song or job. Use list_songs to find the id. |
invalid | An argument is out of range; the message says which. Fix it. |
A job stays queued with stage waiting | Another job is running, or the model isn't ready yet. Keep waiting; don't cancel and resubmit. |
status: failed | Tell the user. Retry once with a different seed at most. |
status: cancelled | Someone stopped it. Nothing was saved. Don't restart it unasked. |
Etiquette
- Before a batch (more than 3 songs, or more than about 10 minutes of rendering), tell the user how many songs and roughly how long, and ask.
- Don't cancel jobs you didn't start.
- Don't overwrite the user's files: a
destination(or--out) that names an existing file replaces it. Leavedestinationout so the app names the file in the Music folder, or give a new folder. - When a job is done, report the file path(s) and the song's title.
- While a long job runs, report progress sparingly: once a minute is plenty.
License
The song model is YuE2-3B by M-A-P, licensed CC BY-NC 4.0: non-commercial use only (https://creativecommons.org/licenses/by-nc/4.0/). Generated files carry the credit and license in their tags. Don't strip or rewrite those tags, and don't help anyone present the songs as commercially licensed. Stems use htdemucs by Meta (MIT). The CLI, MCP server and Python wrapper code are MIT.
Docs: https://choruspop.com/docs/ · Help: support@choruspop.com