# ChorusPop for AI agents

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 `.lrc` file.
- 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

1. `make_song` returns a job id at once. Don't pass `wait: true` unless you
   passed a `progressToken` and your client shows progress.
2. Call `get_job` with `wait_seconds: 300` (or poll every 30–60 s without it)
   until `status` is `done`, `failed` or `cancelled`. With a `progressToken`
   the server sends `notifications/progress` (0–100) while it waits.
3. When it's `done`, the result has `song_id` and `files` (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_seconds` is 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 example
  `synthwave dreamy synth female vocal` or
  `rock 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 `.abc` plan must be in the model's dialect: header `X`, `T`, `M`, `L`,
  `Q`, voices `V: Vocal` then `V: Ins`, then `K`; 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 own
  `plan.abc` is 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.mid` lists the tracks and those
  picks.
- Lyrics are optional; given, they're sung to the plan's Vocal line, one
  syllable per note.
- `key` moves the plan too. The song is made at least as long as the plan.
- An invalid plan is refused with `invalid` and 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 (version
  `instrumental`).
- `vocals_only`: one file, only the singing (version `vocals`).
- `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 pass
  `destination` (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=True` for the Music folder. If the command stops
  waiting (`--timeout`, ^C, or `--no-wait` with `--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. Leave `destination` out 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
