gradeNot graded
music_get_stateAnswer
SAFETY: read-only, changes nothing. Call this first, and again before giving advice or building a proposal, to see the actual current state of the page. Takes no arguments. Returns an object with: schemaVersion (2), capabilities (instruments supported and the guitar catalogue/tuning/capo/event bounds), revision (an integer that increments on every human or applied-agent change — pass the current value as baseRevision on music_propose_exercise), view, phase (player state machine), instrument (drums/keys/guitar), sound, bpm, exercise (the open lesson/exercise object, or null if none is open), catalog (this instrument's note names and indices), input, audioReady (whether Web Audio is already running), lastCompletedTake (the most recent completed human "Try alone" round, or null until one finishes — never a demo or partial round), pendingProposal (an escrowed proposal awaiting the human's Apply, or null), canUndoExercise (whether Undo agent exercise is available), limits (plain-text notes on what this tool never exposes), and live — the player right now: hands ({count, tracking: live/paused/off}), chordHand (guitar only: {fingers, chord, mode}, null off-guitar), instrument, pedals (guitar only: [{id, label, on, foot}]), latency ({handToSoundMs: {p50, p95, n}}), lastHits (last 8 {instrument, note|pad, source, atMs}), controller ({lastAction, lastCode, atMs, mapVersion} — the last action any programmable keypad drove through the input router, or all null before the first press), phonePedals ({connected}) when available, and round ({active, lesson, hitsCounted, hitsExpected, startedAtMs}) — while round.active is true, music_open_lesson and music_play (other than mode "stop") return a refusal instead of acting, so check it before calling either. Call this before every proposal, and read live.chordHand.fingers or live.hands after a music_focus to check whether the player is actually holding the count or chord you asked for — do not assume a focus call was followed. Guitar catalogue indices are stringIndex × 13 + relativeFret (0–77); a capo changes pitch, never the index. No camera images, no audio recordings, no cloud requests.
View tool JSON
{
"name": "music_get_state",
"kind": "answer",
"impl": "imperative",
"description": "SAFETY: read-only, changes nothing. Call this first, and again before giving advice or building a proposal, to see the actual current state of the page. Takes no arguments. Returns an object with: schemaVersion (2), capabilities (instruments supported and the guitar catalogue/tuning/capo/event bounds), revision (an integer that increments on every human or applied-agent change — pass the current value as baseRevision on music_propose_exercise), view, phase (player state machine), instrument (drums/keys/guitar), sound, bpm, exercise (the open lesson/exercise object, or null if none is open), catalog (this instrument's note names and indices), input, audioReady (whether Web Audio is already running), lastCompletedTake (the most recent completed human \"Try alone\" round, or null until one finishes — never a demo or partial round), pendingProposal (an escrowed proposal awaiting the human's Apply, or null), canUndoExercise (whether Undo agent exercise is available), limits (plain-text notes on what this tool never exposes), and live — the player right now: hands ({count, tracking: live/paused/off}), chordHand (guitar only: {fingers, chord, mode}, null off-guitar), instrument, pedals (guitar only: [{id, label, on, foot}]), latency ({handToSoundMs: {p50, p95, n}}), lastHits (last 8 {instrument, note|pad, source, atMs}), controller ({lastAction, lastCode, atMs, mapVersion} — the last action any programmable keypad drove through the input router, or all null before the first press), phonePedals ({connected}) when available, and round ({active, lesson, hitsCounted, hitsExpected, startedAtMs}) — while round.active is true, music_open_lesson and music_play (other than mode \"stop\") return a refusal instead of acting, so check it before calling either. Call this before every proposal, and read live.chordHand.fingers or live.hands after a music_focus to check whether the player is actually holding the count or chord you asked for — do not assume a focus call was followed. Guitar catalogue indices are stringIndex × 13 + relativeFret (0–77); a capo changes pitch, never the index. No camera images, no audio recordings, no cloud requests.",
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"page": "/air-instruments/"
}music_list_lessonsAnswer
SAFETY: read-only, changes nothing. Call this to find a lesson's stable ID before calling music_open_lesson, or to see what is available to suggest. Takes no arguments. Returns an array of lesson summary objects, each with id (pass this to music_open_lesson), instrument, level, title, and goal. Does not fabricate data and makes no cloud requests.
View tool JSON
{
"name": "music_list_lessons",
"kind": "answer",
"impl": "imperative",
"description": "SAFETY: read-only, changes nothing. Call this to find a lesson's stable ID before calling music_open_lesson, or to see what is available to suggest. Takes no arguments. Returns an array of lesson summary objects, each with id (pass this to music_open_lesson), instrument, level, title, and goal. Does not fabricate data and makes no cloud requests.",
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
},
"page": "/air-instruments/"
}music_open_lessonAction
SAFETY: changes the UI immediately — no human confirmation step. Switches the visible instrument to match the lesson and loads it as the open exercise. Call this after music_list_lessons has given you a lessonId, before music_play or music_propose_exercise. While a round is active this returns {refused: true, reason: "round_active", hint} instead of acting — wait for music_wait_for("take_completed") or call music_play("stop") first. Returns the new state, in the same shape as music_get_state.
View tool JSON
{
"name": "music_open_lesson",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: changes the UI immediately — no human confirmation step. Switches the visible instrument to match the lesson and loads it as the open exercise. Call this after music_list_lessons has given you a lessonId, before music_play or music_propose_exercise. While a round is active this returns {refused: true, reason: \"round_active\", hint} instead of acting — wait for music_wait_for(\"take_completed\") or call music_play(\"stop\") first. Returns the new state, in the same shape as music_get_state.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"lessonId"
],
"properties": {
"lessonId": {
"type": "string",
"minLength": 1,
"maxLength": 80,
"description": "Stable lesson ID from a music_list_lessons entry's id field. Required."
}
}
},
"page": "/air-instruments/"
}music_playAction
SAFETY: changes the UI immediately — starts or stops audio playback in the browser tab, no human confirmation step. Call demo to play a scripted example, guided for untimed practice, take to start a scored "Try alone" round the human plays, or stop to cancel whatever round is active. Requires a lesson already open (music_open_lesson). Any mode other than stop returns {refused: true, reason: "round_active", hint} instead of acting while a round (guided or take) is already active; stop always goes through. Returns immediately without waiting for the round to finish — call music_wait_for with event "take_completed" instead of polling, and only trust lastCompletedTake once phase has returned to idle. Starting sound may need a prior human click on Enable sound; if so the return has needsUserAction: true and started: false instead of starting playback. Demonstration and guided notes are never a human take — only a fully completed take round updates lastCompletedTake.
View tool JSON
{
"name": "music_play",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: changes the UI immediately — starts or stops audio playback in the browser tab, no human confirmation step. Call demo to play a scripted example, guided for untimed practice, take to start a scored \"Try alone\" round the human plays, or stop to cancel whatever round is active. Requires a lesson already open (music_open_lesson). Any mode other than stop returns {refused: true, reason: \"round_active\", hint} instead of acting while a round (guided or take) is already active; stop always goes through. Returns immediately without waiting for the round to finish — call music_wait_for with event \"take_completed\" instead of polling, and only trust lastCompletedTake once phase has returned to idle. Starting sound may need a prior human click on Enable sound; if so the return has needsUserAction: true and started: false instead of starting playback. Demonstration and guided notes are never a human take — only a fully completed take round updates lastCompletedTake.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"mode"
],
"properties": {
"mode": {
"enum": [
"demo",
"guided",
"take",
"stop"
],
"description": "demo: play a scripted demonstration of the phrase, not counted as a human take. guided: start untimed guided practice, not scored. take: start a scored \"Try alone\" round; only a fully completed one becomes lastCompletedTake. stop: cancel whatever round is currently active. Required."
}
}
},
"page": "/air-instruments/"
}music_propose_exerciseAction
SAFETY: proposal-only — never applies anything by itself. This tool only escrows a candidate exercise for human review; the human must press Apply exercise (or Keep mine) in the visible panel before the exercise, tempo, or instrument state actually changes, and Undo agent exercise reverses an applied one afterward. Call this after music_get_state, using its revision as baseRevision, once you have a lesson open on the target instrument. Guitar proposals require a standard-tuning guitar snapshot (capo 0–5, a preset, six frets, and reviewed shapes); guitar events are plucks (one note) or directed down/up strums (with spread, at most one fret per string). See each schema property for its own constraints and defaults. Returns {status: "awaiting_human_apply", revision, proposal} — the proposal is not yet in effect.
View tool JSON
{
"name": "music_propose_exercise",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: proposal-only — never applies anything by itself. This tool only escrows a candidate exercise for human review; the human must press Apply exercise (or Keep mine) in the visible panel before the exercise, tempo, or instrument state actually changes, and Undo agent exercise reverses an applied one afterward. Call this after music_get_state, using its revision as baseRevision, once you have a lesson open on the target instrument. Guitar proposals require a standard-tuning guitar snapshot (capo 0–5, a preset, six frets, and reviewed shapes); guitar events are plucks (one note) or directed down/up strums (with spread, at most one fret per string). See each schema property for its own constraints and defaults. Returns {status: \"awaiting_human_apply\", revision, proposal} — the proposal is not yet in effect.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"description": "A proposed exercise, escrowed for human review. Never auto-applied: the human sees title, reason, tempo, and the full note pattern, then presses Apply exercise (or Keep mine) before anything changes on screen.",
"required": [
"baseRevision",
"title",
"reason",
"instrument",
"beats",
"bpm",
"events"
],
"properties": {
"baseRevision": {
"type": "integer",
"minimum": 0,
"description": "The `revision` value from the most recent music_get_state call. Detects state drift between reading state and proposing; call music_get_state first if unsure. Required."
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 80,
"description": "Short human-facing title for the proposed exercise, 1-80 characters. Required."
},
"reason": {
"type": "string",
"minLength": 1,
"maxLength": 300,
"description": "One or two sentences explaining why this exercise is proposed; shown to the human before they can apply it. 1-300 characters. Required."
},
"instrument": {
"enum": [
"drums",
"keys",
"guitar"
],
"description": "Instrument the proposal is for: drums, keys, or guitar. Must match the instrument of the lesson currently open (from music_get_state). Required."
},
"beats": {
"enum": [
4,
8,
16
],
"description": "Length of the exercise loop in quarter-note beats: 4, 8, or 16. Required."
},
"bpm": {
"type": "integer",
"minimum": 40,
"maximum": 120,
"description": "Tempo in beats per minute, 40-120. Required."
},
"guitar": {
"type": "object",
"additionalProperties": false,
"description": "Guitar configuration snapshot. Required when instrument is \"guitar\"; must be omitted for drums or keys proposals.",
"properties": {
"tuning": {
"type": "array",
"minItems": 6,
"maxItems": 6,
"items": {
"type": "integer"
},
"description": "Standard guitar tuning as six MIDI note numbers, low string first. Must exactly equal [40,45,50,55,59,64] — alternate tunings are not supported. Required inside guitar."
},
"capo": {
"type": "integer",
"minimum": 0,
"maximum": 5,
"description": "Capo position in frets, 0-5. 0 means no capo. Required inside guitar."
},
"preset": {
"enum": [
"acoustic",
"clean",
"overdrive"
],
"description": "Guitar tone preset: acoustic, clean, or overdrive. Required inside guitar."
},
"frets": {
"type": "array",
"minItems": 6,
"maxItems": 6,
"items": {
"type": [
"integer",
"null"
],
"minimum": 0,
"maximum": 12
},
"description": "Six entries, one per string low to high: the fretted position 0-12, or null if that string does not sound. At least one entry must be non-null. Required inside guitar."
},
"shapes": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"items": {
"type": "string"
},
"uniqueItems": true,
"description": "Names of reviewed chord shapes available for this proposal, 1-10 unique entries drawn from the app's reviewed shape library. Optional — defaults to every reviewed shape when omitted."
}
}
},
"events": {
"type": "array",
"minItems": 1,
"maxItems": 64,
"description": "Ordered list of 1-64 note events making up the exercise loop, strictly increasing by beat. Required.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"beat",
"notes"
],
"properties": {
"beat": {
"type": "number",
"minimum": 0,
"description": "Zero-based quarter-note position within the loop, from 0 up to (but excluding) beats. Must be strictly greater than the previous event's beat. Required."
},
"notes": {
"type": "array",
"minItems": 1,
"maxItems": 6,
"items": {
"type": "integer"
},
"uniqueItems": true,
"description": "1-6 unique catalogue indices sounded together at this beat. Range depends on instrument: 0-11 for drums, 0-24 for keys, 0-77 for guitar (stringIndex × 13 + fret; a capo shifts pitch, never the index). Required."
},
"duration": {
"type": "number",
"minimum": 0.05,
"maximum": 4,
"description": "Note length in beats, 0.05-4. Optional — defaults to 0.65 when omitted."
},
"velocity": {
"type": "number",
"minimum": 0.1,
"maximum": 1,
"description": "Note loudness from 0.1 (quiet) to 1 (loud). Optional — defaults to 0.75 when omitted."
},
"kind": {
"enum": [
"pluck",
"strum"
],
"description": "Guitar only: pluck sounds exactly one note, strum sounds a chord across up to 6 notes with one fret per string. Required for guitar events; must be omitted for drums or keys."
},
"direction": {
"enum": [
"down",
"up"
],
"description": "Guitar strum only: down or up, the order strings ring out. Required when kind is strum; must be omitted for a pluck or for drums/keys."
},
"spread": {
"type": "number",
"minimum": 0,
"maximum": 0.12,
"description": "Guitar strum only: seconds between adjacent strings ringing out, 0-0.12. Required when kind is strum; must be omitted for a pluck or for drums/keys."
}
}
}
}
}
},
"page": "/air-instruments/"
}music_explainAction
SAFETY: changes the UI immediately — writes your text into the visible, attributed agent-advice panel, no human confirmation step (the text itself has no other effect: it does not alter the lesson, exercise, or player state). Call this to surface advice you have already formed from music_get_state and, if relevant, lastCompletedTake; this tool does not call a model or generate the advice itself, it only displays text you supply. Ground the explanation in evidence: cite a concrete field from music_get_state's live block (e.g. "you held up 3 fingers") or from lastCompletedTake (e.g. "3 of 4 notes were in time"), not a generic tip. Rendered as plain text, never HTML. Returns {displayed: true}.
View tool JSON
{
"name": "music_explain",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: changes the UI immediately — writes your text into the visible, attributed agent-advice panel, no human confirmation step (the text itself has no other effect: it does not alter the lesson, exercise, or player state). Call this to surface advice you have already formed from music_get_state and, if relevant, lastCompletedTake; this tool does not call a model or generate the advice itself, it only displays text you supply. Ground the explanation in evidence: cite a concrete field from music_get_state's live block (e.g. \"you held up 3 fingers\") or from lastCompletedTake (e.g. \"3 of 4 notes were in time\"), not a generic tip. Rendered as plain text, never HTML. Returns {displayed: true}.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"message"
],
"properties": {
"message": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "Advice text to display verbatim, as plain text (never HTML), in the agent-advice panel. Cite a specific live or lastCompletedTake fact rather than generic encouragement. 1-500 characters. Required."
}
}
},
"page": "/air-instruments/"
}music_focusAction
SAFETY: changes the UI immediately — highlights something on screen, no human confirmation step (visual only: it does not play sound, change the exercise, or affect scoring). Call this to point at a specific step of the currently open exercise, or — guitar only — at a finger count, a chord, or a pedal, for example while explaining what to practise next. Requires a lesson open and the player idle (no active round). Pass exactly one target. Returns an object describing the target just highlighted.
View tool JSON
{
"name": "music_focus",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: changes the UI immediately — highlights something on screen, no human confirmation step (visual only: it does not play sound, change the exercise, or affect scoring). Call this to point at a specific step of the currently open exercise, or — guitar only — at a finger count, a chord, or a pedal, for example while explaining what to practise next. Requires a lesson open and the player idle (no active round). Pass exactly one target. Returns an object describing the target just highlighted.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"eventIndex": {
"type": "integer",
"minimum": 0,
"maximum": 63,
"description": "Zero-based index into the currently open exercise's events array, 0-63. Any instrument. Exactly one of eventIndex, fingers, chord, or pedal is required."
},
"fingers": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Highlights the chord box that finger count maps to on the chord strip and shows \"hold up N fingers\" on stage; 5 means palm mute. Guitar only. Exactly one of eventIndex, fingers, chord, or pedal is required."
},
"chord": {
"type": "string",
"minLength": 1,
"maxLength": 8,
"description": "Chord name to highlight on the chord strip and chord list, e.g. \"G\". Guitar only. Exactly one of eventIndex, fingers, chord, or pedal is required."
},
"pedal": {
"type": "string",
"minLength": 1,
"maxLength": 40,
"description": "Pedal id to pulse on the stomp strip, from a live.pedals entry's id in music_get_state. Guitar only. Exactly one of eventIndex, fingers, chord, or pedal is required."
}
}
},
"page": "/air-instruments/"
}music_wait_forAction
SAFETY: read-only, changes nothing. Blocks until the requested event happens or timeoutMs elapses, whichever comes first — use this instead of polling music_get_state in a loop. take_completed: a human "Try alone" round finished scoring (resolves with the take). finger_count_stable: the chord hand's held finger count settled on a new value (resolves with {count}), guitar only. hit: any instrument note sounded (resolves with the hit). chord_changed: the guitar's selected chord/frets changed (resolves with {instrument, chord}), guitar only. pedal_toggled: a pedal was switched on or off (resolves with {id, label, on}), guitar/keys only. Returns {timedOut: true} instead if nothing happened in time — that is a normal result, not an error.
View tool JSON
{
"name": "music_wait_for",
"kind": "act",
"impl": "imperative",
"description": "SAFETY: read-only, changes nothing. Blocks until the requested event happens or timeoutMs elapses, whichever comes first — use this instead of polling music_get_state in a loop. take_completed: a human \"Try alone\" round finished scoring (resolves with the take). finger_count_stable: the chord hand's held finger count settled on a new value (resolves with {count}), guitar only. hit: any instrument note sounded (resolves with the hit). chord_changed: the guitar's selected chord/frets changed (resolves with {instrument, chord}), guitar only. pedal_toggled: a pedal was switched on or off (resolves with {id, label, on}), guitar/keys only. Returns {timedOut: true} instead if nothing happened in time — that is a normal result, not an error.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"event"
],
"properties": {
"event": {
"enum": [
"take_completed",
"finger_count_stable",
"hit",
"chord_changed",
"pedal_toggled"
],
"description": "Which event to wait for: take_completed, finger_count_stable, hit, chord_changed, or pedal_toggled. Required."
},
"timeoutMs": {
"type": "integer",
"minimum": 1,
"maximum": 20000,
"description": "Milliseconds to wait before giving up, 1-20000. Optional — defaults to 5000."
}
}
},
"page": "/air-instruments/"
}