What Claude writes is a score in this format, and what the app plays is the same file. It is JSON so an agent can write it, and precise enough that a bar that does not add up is caught before it is kept.
{
"formatVersion": 1,
"metadata": {"id": "ode-to-joy", "title": "Ode to Joy", "composer": "Ludwig van Beethoven", "level": "beginner", "difficulty": 1},
"timing": {"ticksPerQuarter": 480, "tempo": [{"tick": 0, "microsecondsPerQuarter": 600000}]},
"notes": [
{"id": "m1-1", "pitch": 64, "start": 0, "duration": 450, "velocity": 80, "part": "melody", "hand": "right"},
{"id": "m1-2", "pitch": 64, "start": 480, "duration": 450, "velocity": 74, "part": "melody", "hand": "right"},
{"id": "m1-3", "pitch": 65, "start": 960, "duration": 450, "velocity": 74, "part": "melody", "hand": "right"},
…
]
}The format is defined once, in packages/score-format, and a repository check refuses a second copy anywhere else. The JSON Schema is generated from that definition, and MIDI and MusicXML are read by the same package.