Tool tree
gradeNot graded
get_synth_stateAnswer
Get live synth state: runtime, FX order, modulation routes, and preset identity. One call with `format: "compact"` also returns every parameter that differs from its default as `id=formatted` lines — the cheapest way to verify a patch. `patch.preset` is `{name, source, dirty}`; `dirty: true` means the patch was edited since that preset was loaded or saved, so read it before deciding whether to save. Routes ship by default in `patch.modulations` (with `total`, and `nextOffset` when more exist — raise `modulationLimit`). `lfo: 1` returns one shape as `patch.lfoShape`.
View tool JSON
{
"name": "get_synth_state",
"kind": "answer",
"impl": "imperative",
"description": "Get live synth state: runtime, FX order, modulation routes, and preset identity. One call with `format: \"compact\"` also returns every parameter that differs from its default as `id=formatted` lines — the cheapest way to verify a patch. `patch.preset` is `{name, source, dirty}`; `dirty: true` means the patch was edited since that preset was loaded or saved, so read it before deciding whether to save. Routes ship by default in `patch.modulations` (with `total`, and `nextOffset` when more exist — raise `modulationLimit`). `lfo: 1` returns one shape as `patch.lfoShape`.",
"inputSchema": {
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"full",
"compact"
]
},
"group": {
"type": "string",
"maxLength": 100,
"description": "Exact group id from `groups`, case-insensitive; an unknown name is an error."
},
"search": {
"type": "string",
"maxLength": 100,
"description": "Case-insensitive substring over parameter id, name, and group."
},
"parameterOffset": {
"type": "integer",
"minimum": 0,
"description": "Full-format paging cursor; omit it with `format: \"compact\"`."
},
"parameterLimit": {
"type": "integer",
"minimum": 1,
"maximum": 60,
"description": "Full-format page size (default 5, max 60); omit it with `format: \"compact\"`, which needs none and returns every non-default parameter in one response."
},
"modulationOffset": {
"type": "integer",
"minimum": 0
},
"modulationLimit": {
"type": "integer",
"minimum": 1,
"maximum": 60
},
"lfo": {
"type": "integer",
"minimum": 1,
"maximum": 8
},
"lfoPointOffset": {
"type": "integer",
"minimum": 0
},
"lfoPointLimit": {
"type": "integer",
"minimum": 1,
"maximum": 60
}
},
"additionalProperties": false
}
}get_parameter_schemaAnswer
Discover parameter units, ranges, defaults, curves, choices, and modulation capabilities. One unfiltered call with `format: "compact"` returns all 224 parameters, one line each (`filter1.cutoff Hz 20..20000 exp =8000 mod`). env1 is the amplitude envelope (VCA) and the only hardwired modulator: env2-env6 and lfo1-lfo8 do nothing until routed with `set_modulation`; `groupNotes` repeats such facts for the groups on the page. Add `sourceLimit` for the modulation source ids `set_modulation` accepts.
View tool JSON
{
"name": "get_parameter_schema",
"kind": "answer",
"impl": "imperative",
"description": "Discover parameter units, ranges, defaults, curves, choices, and modulation capabilities. One unfiltered call with `format: \"compact\"` returns all 224 parameters, one line each (`filter1.cutoff Hz 20..20000 exp =8000 mod`). env1 is the amplitude envelope (VCA) and the only hardwired modulator: env2-env6 and lfo1-lfo8 do nothing until routed with `set_modulation`; `groupNotes` repeats such facts for the groups on the page. Add `sourceLimit` for the modulation source ids `set_modulation` accepts.",
"inputSchema": {
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"full",
"compact"
]
},
"group": {
"type": "string",
"maxLength": 100,
"description": "Exact group id from `groups`, case-insensitive; an unknown name is an error, not an empty page. Groups are per instance (`filter1`, `filter2`, `env1`..`env6`), so `filter` is only the routing group. Usually skip it: omit `group` and `search`, and one call with `format: \"compact\"` returns all 224 parameters."
},
"search": {
"type": "string",
"maxLength": 100,
"description": "Case-insensitive substring over parameter id, name, and group; unlike `group` it spans instances (`filter` matches every filter1/filter2 parameter). Omit it too and one call with `format: \"compact\"` returns all 224."
},
"offset": {
"type": "integer",
"minimum": 0,
"description": "Full-format paging cursor; omit it with `format: \"compact\"`."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 60,
"description": "Full-format page size (default 5, max 60); omit it with `format: \"compact\"`, which needs none and returns all 224 parameters in one response."
},
"sourceOffset": {
"type": "integer",
"minimum": 0
},
"sourceLimit": {
"type": "integer",
"minimum": 1,
"maximum": 60
}
},
"additionalProperties": false
}
}update_parametersAction
Atomically validate and apply a batch of raw-unit parameter values or textual choice labels. Example: {"updates":[{"id":"filter1.cutoff","value":1200},{"id":"filter1.type","value":"LP 24"}]}
View tool JSON
{
"name": "update_parameters",
"kind": "act",
"impl": "imperative",
"description": "Atomically validate and apply a batch of raw-unit parameter values or textual choice labels. Example: {\"updates\":[{\"id\":\"filter1.cutoff\",\"value\":1200},{\"id\":\"filter1.type\",\"value\":\"LP 24\"}]}",
"inputSchema": {
"type": "object",
"properties": {
"updates": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"value": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
}
]
}
},
"required": [
"id",
"value"
],
"additionalProperties": false
}
}
},
"required": [
"updates"
],
"additionalProperties": false
}
}set_modulationAction
Add, update, remove, or clear modulation routes in the shared 32-slot matrix. `add` takes `source`+`destination` and replaces any route on that pair; `update`/`remove` address a route by `slot` or by that same pair — never both, and a pair with no route is an error, not a new route. `depth` is bipolar (-1..1): added to the destination's normalized 0..1 value, then clamped, so depth 0.5 on a parameter at 0.5 sweeps it to 1.0. Example: {"action":"add","source":"lfo1","destination":"filter1.cutoff","depth":0.4}
View tool JSON
{
"name": "set_modulation",
"kind": "act",
"impl": "imperative",
"description": "Add, update, remove, or clear modulation routes in the shared 32-slot matrix. `add` takes `source`+`destination` and replaces any route on that pair; `update`/`remove` address a route by `slot` or by that same pair — never both, and a pair with no route is an error, not a new route. `depth` is bipolar (-1..1): added to the destination's normalized 0..1 value, then clamped, so depth 0.5 on a parameter at 0.5 sweeps it to 1.0. Example: {\"action\":\"add\",\"source\":\"lfo1\",\"destination\":\"filter1.cutoff\",\"depth\":0.4}",
"inputSchema": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"add",
"update",
"remove",
"clear"
]
},
"source": {
"type": "string",
"description": "Source id: env1, env2, env3, env4, env5, env6, lfo1, lfo2, lfo3, lfo4, lfo5, lfo6, lfo7, lfo8, velocity, keytrack, random, macro1, macro2, macro3, macro4, modwheel, pitchwheel, aftertouch. Required by `add`; on `update`/`remove` it pairs with `destination` instead of `slot`."
},
"destination": {
"type": "string",
"description": "Moddable parameter id (`mod` in the compact schema). Required by `add`; pairs with `source` on `update`/`remove`."
},
"depth": {
"type": "number",
"minimum": -1,
"maximum": 1
},
"enabled": {
"type": "boolean"
},
"slot": {
"type": "integer",
"minimum": 0,
"maximum": 31,
"description": "Matrix slot from `route.slot`; the alternative to a `source`+`destination` pair on `update`/`remove` — passing both is ambiguous."
}
},
"required": [
"action"
],
"additionalProperties": false
}
}set_fx_orderSensitive Action
Reorder the effects chain — the one patch edit with no parameter behind it, so until now an agent could switch every effect on without being able to say which came first. `order` is all 8 effect ids exactly once, first processed first; whether an effect does anything is `<id>.enabled` in update_parameters. Example: {"order":["fxdist","chorus","phaser","flanger","delay","reverb","eq","comp"]}
View tool JSON
{
"name": "set_fx_order",
"kind": "transact",
"impl": "imperative",
"description": "Reorder the effects chain — the one patch edit with no parameter behind it, so until now an agent could switch every effect on without being able to say which came first. `order` is all 8 effect ids exactly once, first processed first; whether an effect does anything is `<id>.enabled` in update_parameters. Example: {\"order\":[\"fxdist\",\"chorus\",\"phaser\",\"flanger\",\"delay\",\"reverb\",\"eq\",\"comp\"]}",
"inputSchema": {
"type": "object",
"properties": {
"order": {
"type": "array",
"minItems": 8,
"maxItems": 8,
"items": {
"type": "string",
"enum": [
"chorus",
"phaser",
"flanger",
"delay",
"reverb",
"eq",
"comp",
"fxdist"
]
},
"description": "All 8 effect ids exactly once, in processing order (default chorus, phaser, flanger, delay, reverb, eq, comp, fxdist). A missing, repeated or unknown id is an error, not a partial reorder."
}
},
"required": [
"order"
],
"additionalProperties": false
}
}apply_patchAction
Build a whole sound in one call: parameters, modulation routes and FX order applied together, then optionally saved and auditioned. Everything is validated before anything is applied and the patch lands all or nothing, using the same validation `update_parameters` and `set_modulation` use. `dryRun: true` reports what would change and touches nothing. `auditionNotes` RENDERS the result rather than playing it, so it needs no Start gesture. `rollbackId` in the result is the history version that undoes the whole call.
View tool JSON
{
"name": "apply_patch",
"kind": "act",
"impl": "imperative",
"description": "Build a whole sound in one call: parameters, modulation routes and FX order applied together, then optionally saved and auditioned. Everything is validated before anything is applied and the patch lands all or nothing, using the same validation `update_parameters` and `set_modulation` use. `dryRun: true` reports what would change and touches nothing. `auditionNotes` RENDERS the result rather than playing it, so it needs no Start gesture. `rollbackId` in the result is the history version that undoes the whole call.",
"inputSchema": {
"type": "object",
"properties": {
"parameters": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"value": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
}
]
}
},
"required": [
"id",
"value"
],
"additionalProperties": false
},
"description": "What `update_parameters` takes: raw-unit values or choice labels."
},
"modulations": {
"type": "object",
"description": "Routes as `set_modulation`'s `add` resolves them. `replace: true` clears the matrix first, so `routes` becomes all of it; on its own it just clears.",
"properties": {
"replace": {
"type": "boolean"
},
"routes": {
"type": "array",
"maxItems": 32,
"items": {
"type": "object",
"properties": {
"source": {
"type": "string",
"description": "One of: env1, env2, env3, env4, env5, env6, lfo1, lfo2, lfo3, lfo4, lfo5, lfo6, lfo7, lfo8, velocity, keytrack, random, macro1, macro2, macro3, macro4, modwheel, pitchwheel, aftertouch."
},
"destination": {
"type": "string",
"description": "Moddable parameter id."
},
"depth": {
"type": "number",
"minimum": -1,
"maximum": 1
},
"enabled": {
"type": "boolean"
}
},
"required": [
"source",
"destination",
"depth"
],
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"fxOrder": {
"type": "array",
"minItems": 8,
"maxItems": 8,
"items": {
"type": "string",
"enum": [
"chorus",
"phaser",
"flanger",
"delay",
"reverb",
"eq",
"comp",
"fxdist"
]
},
"description": "All 8 effect ids exactly once, in processing order (default chorus, phaser, flanger, delay, reverb, eq, comp, fxdist). A missing, repeated or unknown id is an error, not a partial reorder."
},
"presetName": {
"type": "string",
"minLength": 1,
"maxLength": 80,
"description": "Save the applied patch under this name, as save_preset would."
},
"auditionNotes": {
"type": "array",
"minItems": 1,
"maxItems": 128,
"items": {
"type": "object",
"properties": {
"midi": {
"type": "integer",
"minimum": 0,
"maximum": 127,
"description": "Pitch as a number. Pass this or `note`, never both."
},
"note": {
"type": "string",
"minLength": 2,
"maxLength": 5,
"description": "Pitch as a name with an octave: \"C4\" is MIDI 60, \"D2\" is 73.4 Hz. Sharps \"#\", flats \"b\". Pass this or `midi`, never both."
},
"velocity": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"start": {
"type": "number",
"minimum": 0
},
"duration": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"velocity",
"start",
"duration"
],
"oneOf": [
{
"required": [
"midi"
]
},
{
"required": [
"note"
]
}
],
"additionalProperties": false
},
"description": "Render these against the new patch and return its metrics: the render_audio step without a second round trip."
},
"dryRun": {
"type": "boolean",
"description": "Validate and report what would change, applying, saving and rendering nothing."
}
},
"additionalProperties": false
}
}play_notesAction
Play a bounded note sequence live; start/duration relative, in seconds. Each note names its pitch as `note` ("D2") or `midi` (38), never both, and `pitches` in the result echoes back what each resolved to. Runs only once a human clicks CLICK TO START AUDIO (runtime.running=true); before that use `render_audio`, which needs no gesture. A repeated pitch retriggers. Example: {"notes":[{"note":"C4","velocity":0.8,"start":0,"duration":0.5}]}
View tool JSON
{
"name": "play_notes",
"kind": "act",
"impl": "imperative",
"description": "Play a bounded note sequence live; start/duration relative, in seconds. Each note names its pitch as `note` (\"D2\") or `midi` (38), never both, and `pitches` in the result echoes back what each resolved to. Runs only once a human clicks CLICK TO START AUDIO (runtime.running=true); before that use `render_audio`, which needs no gesture. A repeated pitch retriggers. Example: {\"notes\":[{\"note\":\"C4\",\"velocity\":0.8,\"start\":0,\"duration\":0.5}]}",
"inputSchema": {
"type": "object",
"properties": {
"notes": {
"type": "array",
"minItems": 1,
"maxItems": 128,
"items": {
"type": "object",
"properties": {
"midi": {
"type": "integer",
"minimum": 0,
"maximum": 127,
"description": "Pitch as a number. Pass this or `note`, never both."
},
"note": {
"type": "string",
"minLength": 2,
"maxLength": 5,
"description": "Pitch as a name with an octave: \"C4\" is MIDI 60, \"D2\" is 73.4 Hz. Sharps \"#\", flats \"b\". Pass this or `midi`, never both."
},
"velocity": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"start": {
"type": "number",
"minimum": 0
},
"duration": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"velocity",
"start",
"duration"
],
"oneOf": [
{
"required": [
"midi"
]
},
{
"required": [
"note"
]
}
],
"additionalProperties": false
}
}
},
"required": [
"notes"
],
"additionalProperties": false
}
}render_audioAction
Render a bounded note sequence and return `metrics` for it (plus `metricNotes`, which says how to read them). Offline by default: repeatable scheduling, far faster than real time, and available before the human starts audio. Each note names its pitch as `note` ("D2") or `midi` (38), never both. The fundamental is MEASURED from the render, never assumed from the note asked for, so a transposed or detuned patch shows up as a real number: `pitchCheck` puts the requested note and the measured one side by side.
View tool JSON
{
"name": "render_audio",
"kind": "act",
"impl": "imperative",
"description": "Render a bounded note sequence and return `metrics` for it (plus `metricNotes`, which says how to read them). Offline by default: repeatable scheduling, far faster than real time, and available before the human starts audio. Each note names its pitch as `note` (\"D2\") or `midi` (38), never both. The fundamental is MEASURED from the render, never assumed from the note asked for, so a transposed or detuned patch shows up as a real number: `pitchCheck` puts the requested note and the measured one side by side.",
"inputSchema": {
"type": "object",
"properties": {
"notes": {
"type": "array",
"minItems": 1,
"maxItems": 128,
"items": {
"type": "object",
"properties": {
"midi": {
"type": "integer",
"minimum": 0,
"maximum": 127,
"description": "Pitch as a number. Pass this or `note`, never both."
},
"note": {
"type": "string",
"minLength": 2,
"maxLength": 5,
"description": "Pitch as a name with an octave: \"C4\" is MIDI 60, \"D2\" is 73.4 Hz. Sharps \"#\", flats \"b\". Pass this or `midi`, never both."
},
"velocity": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"start": {
"type": "number",
"minimum": 0
},
"duration": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"velocity",
"start",
"duration"
],
"oneOf": [
{
"required": [
"midi"
]
},
{
"required": [
"note"
]
}
],
"additionalProperties": false
}
},
"duration": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 15
},
"mode": {
"type": "string",
"enum": [
"offline",
"realtime"
],
"description": "Default \"offline\". \"realtime\" captures the live AudioWorklet output and needs running audio; with no OfflineAudioContext an offline request falls back to real time via `renderModeFallback`."
},
"format": {
"type": "string",
"enum": [
"metrics",
"url"
],
"description": "Payload beside the metrics: \"metrics\" (default) or \"url\", a page-local blob URL of the rendered WAV a human can click to hear it. The URL costs a few dozen bytes; the samples themselves are never returned."
}
},
"required": [
"notes"
],
"additionalProperties": false
}
}analyze_audioAction
Re-analyze the last render (default), the live output, or the uploaded reference. `source: "recent"` reads the rolling 4 s capture — what a human just played, still there after they released the key — where `source: "scope"` holds 21 ms and reports silence for a note that has ended. `source: "reference"` re-analyzes the retained reference PCM, so a corrected `f0Hz` or a higher `windows` count costs no re-upload and replaces the active reference. render_audio already returns these metrics, so call this only for a fresh look without re-rendering.
View tool JSON
{
"name": "analyze_audio",
"kind": "act",
"impl": "imperative",
"description": "Re-analyze the last render (default), the live output, or the uploaded reference. `source: \"recent\"` reads the rolling 4 s capture — what a human just played, still there after they released the key — where `source: \"scope\"` holds 21 ms and reports silence for a note that has ended. `source: \"reference\"` re-analyzes the retained reference PCM, so a corrected `f0Hz` or a higher `windows` count costs no re-upload and replaces the active reference. render_audio already returns these metrics, so call this only for a fresh look without re-rendering.",
"inputSchema": {
"type": "object",
"properties": {
"source": {
"type": "string",
"enum": [
"scope",
"recent",
"last-render",
"reference"
]
},
"seconds": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 4,
"description": "Only for \"recent\": seconds of capture to analyze, ending now. Default and max 4."
},
"f0Hz": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 20000,
"description": "Fundamental for the partials, overriding detection. Only for \"scope\", \"recent\" and \"reference\": a render's PCM is not retained."
},
"windows": {
"type": "integer",
"minimum": 4,
"maximum": 32,
"description": "Spectral windows, default 4."
},
"format": {
"type": "string",
"enum": [
"json",
"text"
],
"description": "Default \"json\"; \"text\" adds the same metrics as a compact table."
}
},
"additionalProperties": false
}
}capture_audioAction
The one tool that WAITS for a human to play: `waitForSignal: true` blocks up to `maxWaitSeconds` for the live output to rise above -60 dBFS, then measures the `captureSeconds` that follow. Left off, it reads the rolling 4 s buffer and returns at once, as analyze_audio({source:"recent"}) does. A window nobody played into comes back as `silent: true`. Offline renders bypass the live graph, so render_audio's output never appears here.
View tool JSON
{
"name": "capture_audio",
"kind": "act",
"impl": "imperative",
"description": "The one tool that WAITS for a human to play: `waitForSignal: true` blocks up to `maxWaitSeconds` for the live output to rise above -60 dBFS, then measures the `captureSeconds` that follow. Left off, it reads the rolling 4 s buffer and returns at once, as analyze_audio({source:\"recent\"}) does. A window nobody played into comes back as `silent: true`. Offline renders bypass the live graph, so render_audio's output never appears here.",
"inputSchema": {
"type": "object",
"properties": {
"captureSeconds": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 4,
"description": "Window length, ending at the capture. Default 3, max 4 — the whole buffer."
},
"waitForSignal": {
"type": "boolean",
"description": "Wait for output above -60 dBFS, then capture `captureSeconds` from there. Default false: read the buffer and return at once."
},
"maxWaitSeconds": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 10,
"description": "Wait budget, default 5, capped at 10. Waiting in vain returns `signalDetected: false`, not an error."
}
},
"additionalProperties": false
}
}analyze_reference_audioAction
Step 1 of matching a target sound: send the target as Base64 audio, decoded in memory and analyzed with the same metrics as synth output. `pitch` is detected automatically and `harmonics`/`harmonicShape` come with it, so the target's partials are visible on the same terms as your own. Its PCM is retained (one at a time) for analyze_audio({source:"reference"}). Then call compare_audio (step 2), which renders the candidate for you.
View tool JSON
{
"name": "analyze_reference_audio",
"kind": "act",
"impl": "imperative",
"description": "Step 1 of matching a target sound: send the target as Base64 audio, decoded in memory and analyzed with the same metrics as synth output. `pitch` is detected automatically and `harmonics`/`harmonicShape` come with it, so the target's partials are visible on the same terms as your own. Its PCM is retained (one at a time) for analyze_audio({source:\"reference\"}). Then call compare_audio (step 2), which renders the candidate for you.",
"inputSchema": {
"type": "object",
"properties": {
"audioBase64": {
"type": "string",
"minLength": 1,
"maxLength": 16777216
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"mimeType": {
"type": "string",
"minLength": 1,
"maxLength": 127,
"pattern": "^[aA][uU][dD][iI][oO]/"
},
"f0Hz": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 20000,
"description": "Known target fundamental; overrides detection when it reports the wrong octave."
},
"trimStartMs": {
"type": "number",
"minimum": 0,
"description": "Milliseconds dropped from the start (leading silence)."
},
"trimEndMs": {
"type": "number",
"minimum": 0,
"description": "Milliseconds dropped from the end. What survives is the reference."
},
"windows": {
"type": "integer",
"minimum": 4,
"maximum": 32,
"description": "Spectral windows, default 4."
},
"format": {
"type": "string",
"enum": [
"json",
"text"
],
"description": "Default \"json\"; \"text\" adds the same metrics as a compact table."
}
},
"required": [
"audioBase64"
],
"additionalProperties": false
}
}compare_audioAction
Step 2 of matching a target sound: the signed, per-dimension error between the reference and your sound (`diff`). It also returns ranked moves in this synth's parameter ids (`diff.actions`) and the `comparison` score. It renders the candidate first by default, at the reference's own detected pitch and duration, releasing the note a tail before the buffer ends so its decay is measurable — never an octave-off render whose scalars still look plausible. Falls back to the last render or the live scope with `autoRender: false`, and refuses a silent scope rather than scoring against silence.
View tool JSON
{
"name": "compare_audio",
"kind": "act",
"impl": "imperative",
"description": "Step 2 of matching a target sound: the signed, per-dimension error between the reference and your sound (`diff`). It also returns ranked moves in this synth's parameter ids (`diff.actions`) and the `comparison` score. It renders the candidate first by default, at the reference's own detected pitch and duration, releasing the note a tail before the buffer ends so its decay is measurable — never an octave-off render whose scalars still look plausible. Falls back to the last render or the live scope with `autoRender: false`, and refuses a silent scope rather than scoring against silence.",
"inputSchema": {
"type": "object",
"properties": {
"autoRender": {
"type": "boolean",
"description": "Default true when the reference has a pitch: render at that pitch and duration first. False compares the last render, or the live scope."
},
"notes": {
"type": "array",
"minItems": 1,
"maxItems": 128,
"items": {
"type": "object",
"properties": {
"midi": {
"type": "integer",
"minimum": 0,
"maximum": 127,
"description": "Pitch as a number. Pass this or `note`, never both."
},
"note": {
"type": "string",
"minLength": 2,
"maxLength": 5,
"description": "Pitch as a name with an octave: \"C4\" is MIDI 60, \"D2\" is 73.4 Hz. Sharps \"#\", flats \"b\". Pass this or `midi`, never both."
},
"velocity": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"start": {
"type": "number",
"minimum": 0
},
"duration": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": [
"velocity",
"start",
"duration"
],
"oneOf": [
{
"required": [
"midi"
]
},
{
"required": [
"note"
]
}
],
"additionalProperties": false
},
"description": "Notes for that render, when the reference's own pitch is not what you want."
},
"duration": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 15,
"description": "Render seconds; defaults to the reference's duration."
},
"format": {
"type": "string",
"enum": [
"json",
"text"
],
"description": "Default \"text\": the diff as a compact table, and a summary in the strict sense — the arrays that table restates (`envelopeDb`, `bandsDb`, `spectralWindows`, `metricNotes`) are dropped from both metrics objects, cutting about a third off the response and more as `windows` rises. \"json\" returns everything whole."
},
"maxActions": {
"type": "integer",
"minimum": 0,
"maximum": 20,
"description": "Ranked moves, default 5."
}
},
"additionalProperties": false
}
}suggest_patchAction
Re-aims the last compare_audio's ranked moves at the patch as it is NOW: every from/to recomputed against current values, nothing rendered. Call it AFTER applying moves, or for a `focus` (timbre/envelope/level/space) compare_audio lacks. On an unchanged patch with no `focus` it just repeats compare_audio's `diff.actions`; `basedOn.addsNothing` says so. `basedOn.stale` means `basedOn.similarity` scores the patch you replaced.
View tool JSON
{
"name": "suggest_patch",
"kind": "act",
"impl": "imperative",
"description": "Re-aims the last compare_audio's ranked moves at the patch as it is NOW: every from/to recomputed against current values, nothing rendered. Call it AFTER applying moves, or for a `focus` (timbre/envelope/level/space) compare_audio lacks. On an unchanged patch with no `focus` it just repeats compare_audio's `diff.actions`; `basedOn.addsNothing` says so. `basedOn.stale` means `basedOn.similarity` scores the patch you replaced.",
"inputSchema": {
"type": "object",
"properties": {
"maxActions": {
"type": "integer",
"minimum": 0,
"maximum": 20,
"description": "Default 5."
},
"focus": {
"type": "string",
"enum": [
"timbre",
"envelope",
"level",
"space"
]
}
},
"additionalProperties": false
}
}save_presetAction
Save the complete current patch under a validated name, replacing that name if present. It goes to this browser's localStorage and shows up in the preset select under `User` — no file, no folder. The result says so; `list_presets` confirms it.
View tool JSON
{
"name": "save_preset",
"kind": "act",
"impl": "imperative",
"description": "Save the complete current patch under a validated name, replacing that name if present. It goes to this browser's localStorage and shows up in the preset select under `User` — no file, no folder. The result says so; `list_presets` confirms it.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 80
}
},
"required": [
"name"
],
"additionalProperties": false
}
}load_presetAction
Load a preset into the live engine: the factory patches the UI dropdown lists, and the user presets saved to this browser's localStorage. `list_presets` names both. On a name held by both, the USER preset wins — it is the only copy of deliberate work, and save_preset followed by load_preset must return the patch just saved — and the result says a factory preset was shadowed. Pass `source: "factory"` for the built-in one by name.
View tool JSON
{
"name": "load_preset",
"kind": "act",
"impl": "imperative",
"description": "Load a preset into the live engine: the factory patches the UI dropdown lists, and the user presets saved to this browser's localStorage. `list_presets` names both. On a name held by both, the USER preset wins — it is the only copy of deliberate work, and save_preset followed by load_preset must return the patch just saved — and the result says a factory preset was shadowed. Pass `source: \"factory\"` for the built-in one by name.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"source": {
"type": "string",
"enum": [
"factory",
"user"
],
"description": "Which space to load from. Omitted, a user preset of that name wins over a factory one; \"factory\" asks for the built-in patch by name even when a user preset shadows it."
}
},
"required": [
"name"
],
"additionalProperties": false
}
}list_presetsAnswer
Every preset this page can load, as ONE ordered list: the 35 factory patches first, in the dropdown's own order, then this browser's user presets in the order they were saved (oldest first). Each row carries `source`, and `total`/`factory`/`user` count them. A name held by both spaces appears twice, once per source.
View tool JSON
{
"name": "list_presets",
"kind": "answer",
"impl": "imperative",
"description": "Every preset this page can load, as ONE ordered list: the 35 factory patches first, in the dropdown's own order, then this browser's user presets in the order they were saved (oldest first). Each row carries `source`, and `total`/`factory`/`user` count them. A name held by both spaces appears twice, once per source.",
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
}
}delete_presetSensitive Action
Delete a preset saved in this browser's localStorage. Factory presets are compiled into the page and cannot be deleted, so a factory name is refused unless a user preset shadows it — then the USER copy goes and the built-in patch is loadable again under that name. The patch on screen is never touched; only the entry in storage.
View tool JSON
{
"name": "delete_preset",
"kind": "transact",
"impl": "imperative",
"description": "Delete a preset saved in this browser's localStorage. Factory presets are compiled into the page and cannot be deleted, so a factory name is refused unless a user preset shadows it — then the USER copy goes and the built-in patch is loadable again under that name. The patch on screen is never touched; only the entry in storage.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 80
}
},
"required": [
"name"
],
"additionalProperties": false
}
}export_presetAnswer
Serialize a patch as the JSON a coSynth import takes back. No argument exports the LIVE patch as it sounds right now; `name` exports a stored preset instead, factory or user, on load_preset's shadowing rule. Reading is all it does: nothing is loaded and `get_synth_state`'s `patch.preset` is the same after. Returns `json`, validated on the way out, and a `filename` to suggest. No file is written; the page cannot save one.
View tool JSON
{
"name": "export_preset",
"kind": "answer",
"impl": "imperative",
"description": "Serialize a patch as the JSON a coSynth import takes back. No argument exports the LIVE patch as it sounds right now; `name` exports a stored preset instead, factory or user, on load_preset's shadowing rule. Reading is all it does: nothing is loaded and `get_synth_state`'s `patch.preset` is the same after. Returns `json`, validated on the way out, and a `filename` to suggest. No file is written; the page cannot save one.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 80,
"description": "A stored preset to export. Omit it to export the live patch."
},
"source": {
"type": "string",
"enum": [
"factory",
"user"
],
"description": "Which space `name` is in. Only meaningful with `name`; omitted, a user preset of that name wins over a factory one."
}
},
"additionalProperties": false
}
}get_ui_targetsAnswer
Step 1 of teaching, for when someone asks how to do something themselves: find the control to point at instead of changing it for them. Returns every panel, tab, parameter, source and button by semantic ID, label, type and visibility. Pass a returned ID to show_ui_guide, or give that tool a precise CSS selector scoped to the app and its overlays.
View tool JSON
{
"name": "get_ui_targets",
"kind": "answer",
"impl": "imperative",
"description": "Step 1 of teaching, for when someone asks how to do something themselves: find the control to point at instead of changing it for them. Returns every panel, tab, parameter, source and button by semantic ID, label, type and visibility. Pass a returned ID to show_ui_guide, or give that tool a precise CSS selector scoped to the app and its overlays.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"format": {
"type": "string",
"enum": [
"full",
"compact"
],
"description": "`compact` returns every match, one `id type label` line each, in one call - no limit, no paging. `(hidden)` or `(not mounted)` is off screen; with `revealable`, the guide opens it for you. `full` (the default) returns objects, 5 per page."
},
"search": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "Case-insensitive substring over id, label, and type; usually skip it."
},
"offset": {
"type": "integer",
"minimum": 0
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"description": "Full-format page size (default 5); `format: \"compact\"` needs none."
}
}
}
}show_ui_guideAction
Step 2 of teaching: when someone asks how to do something themselves, highlight the real controls on their own screen instead of changing the patch for them or describing it in words. Up to 20 steps of interactive highlights and safe CommonMark. Returns immediately; the human uses Next/Previous/Done/Close. Never changes sound; opens the panel or tab a step's target needs, unless `reveal` is false. Replaces the active guide; `steps: []` clears it.
View tool JSON
{
"name": "show_ui_guide",
"kind": "act",
"impl": "imperative",
"description": "Step 2 of teaching: when someone asks how to do something themselves, highlight the real controls on their own screen instead of changing the patch for them or describing it in words. Up to 20 steps of interactive highlights and safe CommonMark. Returns immediately; the human uses Next/Previous/Done/Close. Never changes sound; opens the panel or tab a step's target needs, unless `reveal` is false. Replaces the active guide; `steps: []` clears it.",
"inputSchema": {
"type": "object",
"required": [
"steps"
],
"additionalProperties": false,
"properties": {
"reveal": {
"type": "boolean"
},
"steps": {
"type": "array",
"maxItems": 20,
"description": "Sequential steps, each with at most ONE target. Omit title and markdown for a highlight only; for an action spanning two elements, use two linked steps that name the next target.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"target": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"maxProperties": 1,
"description": "One semantic ID from get_ui_targets, or one CSS selector matching a single element in the app or its overlays. Omit for text only; an unreachable target still shows its text, with a warning.",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 160
},
"selector": {
"type": "string",
"minLength": 1,
"maxLength": 512
}
}
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"markdown": {
"type": "string",
"minLength": 1,
"maxLength": 4000
}
},
"minProperties": 1
}
}
}
}
}get_historyAnswer
Read retained sound versions or replayable AI performances and walkthroughs. Undo skips replays. Use the returned revision with navigate_history. Earlier alternatives remain recoverable until retention eviction.
View tool JSON
{
"name": "get_history",
"kind": "answer",
"impl": "imperative",
"description": "Read retained sound versions or replayable AI performances and walkthroughs. Undo skips replays. Use the returned revision with navigate_history. Earlier alternatives remain recoverable until retention eviction.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"view": {
"type": "string",
"enum": [
"sounds",
"replays"
]
},
"offset": {
"type": "integer",
"minimum": 0
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 20
}
}
}
}navigate_historyAnswer
Undo, redo, or restore a retained sound version. Requires the revision from get_history; stale requests fail without restoring. Stops active performances first. Replays and alternative sound versions are preserved. Never starts audio.
View tool JSON
{
"name": "navigate_history",
"kind": "answer",
"impl": "imperative",
"description": "Undo, redo, or restore a retained sound version. Requires the revision from get_history; stale requests fail without restoring. Stops active performances first. Replays and alternative sound versions are preserved. Never starts audio.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"action",
"expectedRevision"
],
"properties": {
"action": {
"type": "string",
"enum": [
"undo",
"redo",
"restore"
]
},
"entryId": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"expectedRevision": {
"type": "integer",
"minimum": 0
}
}
}
}replay_historyAnswer
Play saved AI notes with the CURRENT sound, or restart a saved walkthrough at step one against the current UI. Does not restore a patch or duplicate history. Performance replay requires audio to be started.
View tool JSON
{
"name": "replay_history",
"kind": "answer",
"impl": "imperative",
"description": "Play saved AI notes with the CURRENT sound, or restart a saved walkthrough at step one against the current UI. Does not restore a patch or duplicate history. Performance replay requires audio to be started.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"required": [
"entryId"
],
"properties": {
"entryId": {
"type": "string",
"minLength": 1,
"maxLength": 100
}
}
}
}stop_performanceAction
Stop the active AI performance or history audition and wait for owned notes and recording tasks to clean up. Keeps the saved note sequence available for replay.
View tool JSON
{
"name": "stop_performance",
"kind": "act",
"impl": "imperative",
"description": "Stop the active AI performance or history audition and wait for owned notes and recording tasks to clean up. Keeps the saved note sequence available for replay.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}