Skip to main content
Glama

Get clip details

get_clip
Read-only

Read one clip: its elements (positions/sizes in canvas pixels), voiceover (text, voice, duration, voiceover_volume), background and transition. Pass render to also get a PNG of the frame.

ASK FOR WHAT YOU NEED. A full read is large — on a dense clip the per-word voiceover array and the element type_data blobs dominate it, and repeated full reads are the main way a long session runs out of context. select returns exactly the parts you name:

select: ['elements.x','elements.y','elements.width','elements.height'] → geometry only, to fix a layout select: ['elements.name','elements.start_time','elements.end_time'] → a timing pass select: ['voiceover_words'] → word timings only, to sync visuals to narration select: ['elements.textdata','voiceover_words'] → rewrite copy against the VO select: ['elements'] → whole element rows, no words select: ['groups'] → group rows only, to get a group_id for update_groups select: [] → no JSON at all (pair with render for the PNG alone — smallest read) (omit select) → everything; fine for a first look, expensive to repeat

render is the other output, and it is separate from select: select shapes the JSON, render produces a PNG.

render: {} → the frame at t=0 render: { timestamps: 2.5 } → the frame 2.5s into the clip render: { timestamps: [0.5, 2, 4] } → those three moments as ONE labelled grid render: { timestamps: [...], layout:'separate'} → the same moments as full-size images (~4x the tokens) render: { save: true } → also uploads the frame and returns presigned_url render: { element_id: 'e1' } → just that animation, cropped, at four moments (a fast check) render: { filmstrip: true } → the whole clip, every element, as 12 frames in one image with a timeline of cues, actions and element starts/ends render: { preview: '' } → an edit_animation dry run, cropped, at four moments (a site placed nowhere here: alone on the clip) render: { element_id: 'e1', test: {cues: {land: 1.2}} } → that animation with other values or timeline, nothing saved (a render of one animation also returns { warnings }: a glow cut at its box, text off its frame, frames near the limits) render: { max_width: 1280 } → a sharper frame when you must read small print select: [], render: {} → the PNG alone, no JSON (the default for a render of one animation) select: ['elements'], render: {} → element rows AND the frame

A single frame renders 960px wide by default — legible for this design system and about half the tokens of a 1280 frame. A GRID defaults to 1280, because that is the width of the whole grid and a third of 960 would leave each cell unreadable. Either way max_width caps the image you get back; raise it only to read genuinely small print. For a render of one animation (element_id, preview) it sets the resolution the canvas is drawn at before the crop: each cropped cell of the sheet is at most 360px wide whatever you pass, so a higher max_width sharpens a small element but does not enlarge the sheet. To look closer, use layout:'separate' (full-size crops) or render the clip frame without element_id.

Omitting render renders nothing. timestamps, layout and save live inside it because they only mean anything for a render — there is no way to ask for them without asking for the image.

element_ids is the other axis: it picks WHICH element rows come back, independently of select. Combine them for the leanest read — e.g. element_ids: ['el_9'], select: ['elements.x','elements.y'].

Element shape: universal wrapper fields (id, element_type, name, x, y, width, height, start_time, end_time, rotation) plus type-specific data (textdata/shapedata/imagedata/videodata/zoomdata) plus an optional keyframes array when animated. Keyframes come back in the same flat wire shape add_elements takes — { timestamp, positionX?, positionY?, width?, height?, interpolation? } in canvas pixels — so you can round-trip read → edit → update_elements without reshaping.

An HTML animation element (codedata.version 2) carries an html_mograph object: its status, and the parameters, actions and cues its page declares. That is the vocabulary for update_elements' param_tracks / actions / cues, e.g. update_elements({updates:[{element_id, type_data:{cues:[{op:'set', key, wordIndex, offset}]}}]}). Its page's files are read with read_animation. An older canvas animation element carries an animation block instead; it plays and exports as it is.

Clip-level fields include transition (the current transition object — sibling of the update_clips transition arg; null if none) and voiceover_words (per-word timestamps, {word, start, end, punctuated_word} with start/end in SECONDS; null on clips with no transcription).

Input Schema

NameRequiredDescriptionDefault
renderNoRender the clip as an image. Presence of this object IS the request to render — omit it and nothing is rendered. `{}` renders at t=0. Independent of `select`, which only shapes the JSON: pair `select: []` with `render: {}` for the PNG alone (smallest read). CHECKING YOUR WORK: pass `timestamps` with SEVERAL mid-clip moments, not the t=0 default — text and image elements have entry animations (a ~0.4s slide/fade by default), so at t=0 they have not arrived yet and a correct edit renders as an empty frame, while shapes have no entry animation and do show at t=0. That mix is what makes a single t=0 render actively misleading: some elements appear and others don't. A list comes back as one grid for about a quarter of the tokens of the same frames separately, so checking several moments is the cheap option, not the expensive one. `animations: false` draws everything settled if you would rather not pick moments at all.
selectNoAsk for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none. Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here. Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, clickaction, questiondata, resultsdata, formdata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'. Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); [] alone returns just the clip's own fields (clip_id, duration, background, voiceover_text); ['elements.textdata','voiceover_words'] to rewrite copy against the VO. Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this.
clip_indexYesZero-based clip index
project_idYesThe project ID
element_idsNoWHICH element rows to return — all others are dropped, and [] returns none. Independent of `select`, which chooses the sections/keys. Use it to re-inspect just what you added or updated; most add_elements/update_elements already echo the element's resolved layout, so often you don't need this at all.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, clickaction, questiondata, resultsdata, formdata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, clickaction, questiondata, resultsdata, formdata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); [] alone returns just the clip's own fields (clip_id, duration, background, voiceover_text); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
  2. Changed12 schema fields changed
    • removedInput schema / properties / render / properties / actions
      Removed value: -{
      -  "description": "Action clips to try, [{name, start, duration?, args?}], element-local seconds; duration defaults to the action's default_duration. Replaces the element's clips for this render.",
      -  "items": {
      -    "properties": {
      -      "args": {
      -        "additionalProperties": {},
      -        "propertyNames": {
      -          "type": "string"
      -        },
      -        "type": "object"
      -      },
      -      "duration": {
      -        "type": "number"
      -      },
      -      "name": {
      -        "minLength": 1,
      -        "type": "string"
      -      },
      -      "start": {
      -        "type": "number"
      -      }
      -    },
      -    "required": [
      -      "name",
      -      "start"
      -    ],
      -    "type": "object"
      -  },
      -  "type": "array"
      -}
    • removedInput schema / properties / render / properties / animation
      Removed value: -{
      -  "description": "With entry: render that animation entry on its own on this clip at its latest revision (or rev), without placing or saving anything. Pair with parameter_values, duration and timestamps as needed.",
      -  "minLength": 1,
      -  "type": "string"
      -}
    • removedInput schema / properties / render / properties / cues
      Removed value: -{
      -  "additionalProperties": {
      -    "type": "number"
      -  },
      -  "description": "Cue times to try, key: seconds (element-local).",
      -  "propertyNames": {
      -    "type": "string"
      -  },
      -  "type": "object"
      -}
    • removedInput schema / properties / render / properties / duration
      Removed value: -{
      -  "description": "With animation, or a preview without an element: how long it plays, in seconds (default its default_duration).",
      -  "exclusiveMinimum": 0,
      -  "type": "number"
      -}
    • changedInput schema / properties / render / properties / element_id / description
      Previous value: -"Render only this element over the clip's background, each moment cropped to its box, as one small sheet: the fast way to check one animation. Without timestamps it shows four moments across the element."New value: +"Render only this element over the clip's background, each moment cropped to its box, as one small sheet: the fast way to check one animation (an HTML animation is drawn from its page, at its own values and timeline). Without timestamps it shows four moments across the element."
    • removedInput schema / properties / render / properties / entry
      Removed value: -{
      -  "description": "With animation: the entry to render.",
      -  "minLength": 1,
      -  "type": "string"
      -}
    • changedInput schema / properties / render / properties / filmstrip / description
      Previous value: -"See motion in ONE image instead of four moments: frames spaced evenly across a window (default 12 over the whole animation), in reading order, each labelled with its element-local time, with a thin timeline under each row marking the frames, cues (lines) and action clips (bars). Read pacing, easing and whether a beat lands on its cue from it. true for the defaults, or {from, to} / {around: 'cue:<key>' | 'action:<name>', span}, with frames or fps. Replaces timestamps. On get_clip without element_id, animation or preview it shows the whole clip, every element drawn, in clip seconds, and the timeline also ticks each element's start and end."New value: +"See motion in ONE image instead of four moments: frames spaced evenly across a window (default 12 over the whole animation), in reading order, each labelled with its element-local time, with a thin timeline under each row marking the frames, cues (lines) and action clips (bars). Read pacing, easing and whether a beat lands on its cue from it. true for the defaults, or {from, to} / {around: 'cue:<key>' | 'action:<name>', span}, with frames or fps. Replaces timestamps. On get_clip without element_id or preview it shows the whole clip, every element drawn, in clip seconds, and the timeline also ticks each element's start and end. HTML animations (pages) are drawn from their page in every render, the next clip's across a seam included; their cues and actions are the page's."
    • removedInput schema / properties / render / properties / param_tracks
      Removed value: -{
      -  "additionalProperties": {
      -    "items": {
      -      "properties": {
      -        "interpolation": {
      -          "type": "string"
      -        },
      -        "t": {
      -          "type": "number"
      -        },
      -        "value": {}
      -      },
      -      "required": [
      -        "t",
      -        "value"
      -      ],
      -      "type": "object"
      -    },
      -    "minItems": 1,
      -    "type": "array"
      -  },
      -  "description": "Keyframed settings to try, key: [{t, value, interpolation?}], t in element-local seconds. Replaces that key's track for this render.",
      -  "propertyNames": {
      -    "type": "string"
      -  },
      -  "type": "object"
      -}
    • removedInput schema / properties / render / properties / parameter_values
      Removed value: -{
      -  "additionalProperties": {},
      -  "description": "Settings to render with, merged over the element's own (key: value).",
      -  "propertyNames": {
      -    "type": "string"
      -  },
      -  "type": "object"
      -}
    • changedInput schema / properties / render / properties / preview / description
      Previous value: -"A preview token from edit_animation(dry_run: true): renders the unsaved code on its own as one sheet, cropped like element_id — the element the edit was about, or, for a dry run without an element, the entry alone on this clip. Nothing is saved."New value: +"A preview token from edit_animation(dry_run: true): the HTML animation it was about is drawn from the unsaved page, cropped like element_id -- the element it names, else one using that site (this clip first), else the page alone on this clip. Nothing is saved."
    • removedInput schema / properties / render / properties / rev
      Removed value: -{
      -  "description": "With animation: the revision to render.",
      -  "maximum": 9007199254740991,
      -  "minimum": 1,
      -  "type": "integer"
      -}
    • addedInput schema / properties / render / properties / test
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Try other settings on the one animation rendered (element_id or preview), nothing saved: parameter_values, param_tracks, actions, cues, in element-local (page) seconds. For an HTML animation the page is drawn with them; check a value or a cue before setting it with update_elements.",
      +  "properties": {
      +    "actions": {
      +      "description": "Action clips to try instead of the element's: [{name, start, duration?, args?}].",
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "args": {
      +            "additionalProperties": {},
      +            "propertyNames": {
      +              "type": "string"
      +            },
      +            "type": "object"
      +          },
      +          "duration": {
      +            "exclusiveMinimum": 0,
      +            "type": "number"
      +          },
      +          "name": {
      +            "minLength": 1,
      +            "type": "string"
      +          },
      +          "start": {
      +            "minimum": 0,
      +            "type": "number"
      +          }
      +        },
      +        "required": [
      +          "name",
      +          "start"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "cues": {
      +      "additionalProperties": {
      +        "minimum": 0,
      +        "type": "number"
      +      },
      +      "description": "Cue times to try: {key: seconds}.",
      +      "propertyNames": {
      +        "type": "string"
      +      },
      +      "type": "object"
      +    },
      +    "param_tracks": {
      +      "additionalProperties": {
      +        "items": {
      +          "additionalProperties": {},
      +          "properties": {
      +            "interpolation": {
      +              "type": "string"
      +            },
      +            "t": {
      +              "description": "Element-local (page) seconds.",
      +              "minimum": 0,
      +              "type": "number"
      +            },
      +            "value": {}
      +          },
      +          "required": [
      +            "t",
      +            "value"
      +          ],
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "description": "Keyframes to try for animatable parameters: {key: [{t, value, interpolation?}]}.",
      +      "propertyNames": {
      +        "type": "string"
      +      },
      +      "type": "object"
      +    },
      +    "parameter_values": {
      +      "additionalProperties": {},
      +      "description": "Values to try, over the element's own (an image or video value: a URL or upload).",
      +      "propertyNames": {
      +        "type": "string"
      +      },
      +      "type": "object"
      +    }
      +  },
      +  "type": "object"
      +}
  3. Changed2 schema fields changed
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, clickaction, questiondata, resultsdata, formdata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "busy",
      -  "geometry",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.avatardata",
      -  "elements.parent_id",
      -  "elements.dropShadows",
      -  "elements.animation"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "geometry",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.clickaction",
      +  "elements.questiondata",
      +  "elements.resultsdata",
      +  "elements.formdata",
      +  "elements.avatardata",
      +  "elements.parent_id",
      +  "elements.dropShadows",
      +  "elements.animation"
      +]
  4. Changed1 schema field changed
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "busy",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.avatardata",
      -  "elements.parent_id",
      -  "elements.dropShadows",
      -  "elements.animation"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "geometry",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.avatardata",
      +  "elements.parent_id",
      +  "elements.dropShadows",
      +  "elements.animation"
      +]
  5. Changed16 schema fields changed
    • changedInput schema / properties / element_ids / description
      Previous value: -"WHICH element rows to return — all others are dropped. Independent of `select`, which chooses the sections/keys. Use it to re-inspect just what you added or updated; most add_elements/update_elements already echo the element's resolved layout, so often you don't need this at all."New value: +"WHICH element rows to return — all others are dropped, and [] returns none. Independent of `select`, which chooses the sections/keys. Use it to re-inspect just what you added or updated; most add_elements/update_elements already echo the element's resolved layout, so often you don't need this at all."
    • addedInput schema / properties / render / properties / actions
      Added value: +{
      +  "description": "Action clips to try, [{name, start, duration?, args?}], element-local seconds; duration defaults to the action's default_duration. Replaces the element's clips for this render.",
      +  "items": {
      +    "properties": {
      +      "args": {
      +        "additionalProperties": {},
      +        "propertyNames": {
      +          "type": "string"
      +        },
      +        "type": "object"
      +      },
      +      "duration": {
      +        "type": "number"
      +      },
      +      "name": {
      +        "minLength": 1,
      +        "type": "string"
      +      },
      +      "start": {
      +        "type": "number"
      +      }
      +    },
      +    "required": [
      +      "name",
      +      "start"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • addedInput schema / properties / render / properties / animation
      Added value: +{
      +  "description": "With entry: render that animation entry on its own on this clip at its latest revision (or rev), without placing or saving anything. Pair with parameter_values, duration and timestamps as needed.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / render / properties / cues
      Added value: +{
      +  "additionalProperties": {
      +    "type": "number"
      +  },
      +  "description": "Cue times to try, key: seconds (element-local).",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / render / properties / duration
      Added value: +{
      +  "description": "With animation, or a preview without an element: how long it plays, in seconds (default its default_duration).",
      +  "exclusiveMinimum": 0,
      +  "type": "number"
      +}
    • addedInput schema / properties / render / properties / element_id
      Added value: +{
      +  "description": "Render only this element over the clip's background, each moment cropped to its box, as one small sheet: the fast way to check one animation. Without timestamps it shows four moments across the element.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / render / properties / entry
      Added value: +{
      +  "description": "With animation: the entry to render.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / render / properties / filmstrip
      Added value: +{
      +  "anyOf": [
      +    {
      +      "const": true,
      +      "type": "boolean"
      +    },
      +    {
      +      "additionalProperties": false,
      +      "properties": {
      +        "around": {
      +          "description": "Center the window on a cue ('cue:land') or an action clip ('action:hop', by name or id) instead of from/to. On a whole clip, a cue several animations share is 'cue:land@<element name>'.",
      +          "pattern": "^(cue|action):.+",
      +          "type": "string"
      +        },
      +        "fps": {
      +          "description": "Frames per second instead of a count, e.g. 8 over 2 s is 17, capped at 16.",
      +          "exclusiveMinimum": 0,
      +          "maximum": 60,
      +          "type": "number"
      +        },
      +        "frames": {
      +          "description": "How many frames across the window, first and last included (default 12, max 16).",
      +          "maximum": 16,
      +          "minimum": 2,
      +          "type": "integer"
      +        },
      +        "from": {
      +          "description": "Window start, element-local seconds, or clip seconds for a whole clip (default 0).",
      +          "minimum": 0,
      +          "type": "number"
      +        },
      +        "seam": {
      +          "const": true,
      +          "description": "get_clip of a whole clip only: the cut from this clip into the next, as the player shows it: this clip's last second, its transition, and the next clip's first second, in this clip's seconds; takes frames, no window. Every cut of the film at once: get_project({render: {seams: true}}).",
      +          "type": "boolean"
      +        },
      +        "span": {
      +          "description": "With around: the window's length in seconds (default 2 around a cue; the clip plus 0.5 around an action).",
      +          "exclusiveMinimum": 0,
      +          "type": "number"
      +        },
      +        "to": {
      +          "description": "Window end, element-local seconds, or clip seconds for a whole clip (default the end).",
      +          "exclusiveMinimum": 0,
      +          "type": "number"
      +        }
      +      },
      +      "type": "object"
      +    }
      +  ],
      +  "description": "See motion in ONE image instead of four moments: frames spaced evenly across a window (default 12 over the whole animation), in reading order, each labelled with its element-local time, with a thin timeline under each row marking the frames, cues (lines) and action clips (bars). Read pacing, easing and whether a beat lands on its cue from it. true for the defaults, or {from, to} / {around: 'cue:<key>' | 'action:<name>', span}, with frames or fps. Replaces timestamps. On get_clip without element_id, animation or preview it shows the whole clip, every element drawn, in clip seconds, and the timeline also ticks each element's start and end."
      +}
    • changedInput schema / properties / render / properties / max_width / description
      Previous value: -"Width in px of the IMAGE you get back — for a grid that is the whole grid, not each cell, so a cell is roughly a third of it. Default 960 for a single frame, 1280 for a grid. An image costs roughly (w×h)/750 tokens, so 1280 is ~1229 and 960 ~691; above 1568 nothing is gained because the model downscales to that anyway. The default is legible for this design system's type scale — a 40px canvas caption lands at ~20px. Raise it only to read genuinely small print, such as UI text inside a pasted screenshot."New value: +"Width in px of the image you get back: default 960 for a single frame, 1280 for a grid (the whole grid; a cell is about a third). An image costs about (w×h)/750 tokens and nothing is gained above 1568. On a render of one animation it is the resolution drawn before the crop; sheet cells stay at most 360px wide."
    • addedInput schema / properties / render / properties / param_tracks
      Added value: +{
      +  "additionalProperties": {
      +    "items": {
      +      "properties": {
      +        "interpolation": {
      +          "type": "string"
      +        },
      +        "t": {
      +          "type": "number"
      +        },
      +        "value": {}
      +      },
      +      "required": [
      +        "t",
      +        "value"
      +      ],
      +      "type": "object"
      +    },
      +    "minItems": 1,
      +    "type": "array"
      +  },
      +  "description": "Keyframed settings to try, key: [{t, value, interpolation?}], t in element-local seconds. Replaces that key's track for this render.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / render / properties / parameter_values
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Settings to render with, merged over the element's own (key: value).",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / render / properties / preview
      Added value: +{
      +  "description": "A preview token from edit_animation(dry_run: true): renders the unsaved code on its own as one sheet, cropped like element_id — the element the edit was about, or, for a dry run without an element, the entry alone on this clip. Nothing is saved.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / render / properties / rev
      Added value: +{
      +  "description": "With animation: the revision to render.",
      +  "maximum": 9007199254740991,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • addedInput schema / properties / render / properties / video
      Added value: +{
      +  "description": "For people: also a short low-resolution MP4 of the same window (the filmstrip's, or the animation's first 10 s), returned as video_url, a link valid for an hour. You can't watch it: judge motion from the filmstrip.",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, avatardata, parent_id, dropShadows. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, avatardata, parent_id, dropShadows, animation. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "busy",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.avatardata",
      -  "elements.parent_id",
      -  "elements.dropShadows"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.avatardata",
      +  "elements.parent_id",
      +  "elements.dropShadows",
      +  "elements.animation"
      +]
  6. Changed2 schema fields changed
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id, dropShadows. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, avatardata, parent_id, dropShadows. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "busy",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.parent_id",
      -  "elements.dropShadows"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.avatardata",
      +  "elements.parent_id",
      +  "elements.dropShadows"
      +]
  7. Changed2 schema fields changed
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id, dropShadows. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "busy",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.parent_id"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.parent_id",
      +  "elements.dropShadows"
      +]
  8. Changed4 schema fields changed
    • removedInput schema / properties / context
      Removed value: -{
      -  "description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"",
      -  "type": "string"
      -}
    • removedInput schema / properties / conversation_id
      Removed value: -{
      -  "description": "Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.",
      -  "type": "string"
      -}
    • removedInput schema / properties / llm_model
      Removed value: -{
      -  "description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.",
      -  "type": "string"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "project_id",
      -  "clip_index",
      -  "context",
      -  "llm_model"
      -]New value: +[
      +  "project_id",
      +  "clip_index"
      +]
  9. Changed4 schema fields changed
    • addedInput schema / properties / context
      Added value: +{
      +  "description": "Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"",
      +  "type": "string"
      +}
    • addedInput schema / properties / conversation_id
      Added value: +{
      +  "description": "Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.",
      +  "type": "string"
      +}
    • addedInput schema / properties / llm_model
      Added value: +{
      +  "description": "The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.",
      +  "type": "string"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "project_id",
      -  "clip_index"
      -]New value: +[
      +  "project_id",
      +  "clip_index",
      +  "context",
      +  "llm_model"
      +]
  10. Changed5 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • removedInput schema / additionalProperties
      Removed value: -false
    • addedInput schema / properties / clip_index / maximum
      Added value: +9007199254740991
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "voiceover_words",
      -  "groups",
      -  "elements.name",
      -  "elements.element_type",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.parent_id"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "busy",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.parent_id"
      +]
  11. Changed9 schema fields changed
    • changedInput schema / properties / render / description
      Previous value: -"Render a PNG of the frame. Presence of this object IS the request to render — omit it and nothing is rendered. `{}` renders at t=0. Independent of `select`, which only shapes the JSON: pair `select: []` with `render: {}` for the PNG alone (smallest read). CHECKING YOUR WORK: pass a mid-clip `timestamp`, not the t=0 default — text and image elements have entry animations (a ~0.4s slide/fade by default), so at t=0 they have not arrived yet and a correct edit renders as an empty frame. Shapes have no entry animation and do show at t=0, which makes a t=0 render especially misleading: some elements appear and others don't."New value: +"Render the clip as an image. Presence of this object IS the request to render — omit it and nothing is rendered. `{}` renders at t=0. Independent of `select`, which only shapes the JSON: pair `select: []` with `render: {}` for the PNG alone (smallest read). CHECKING YOUR WORK: pass `timestamps` with SEVERAL mid-clip moments, not the t=0 default — text and image elements have entry animations (a ~0.4s slide/fade by default), so at t=0 they have not arrived yet and a correct edit renders as an empty frame, while shapes have no entry animation and do show at t=0. That mix is what makes a single t=0 render actively misleading: some elements appear and others don't. A list comes back as one grid for about a quarter of the tokens of the same frames separately, so checking several moments is the cheap option, not the expensive one. `animations: false` draws everything settled if you would rather not pick moments at all."
    • addedInput schema / properties / render / properties / animations
      Added value: +{
      +  "description": "Whether entry/exit animations play at this timestamp. Default true — the frame as the video will show it, which is what you want to judge motion. Pass false to draw every element settled at its final position and full opacity: use that to check COMPOSITION, because with staggered starts and short windows there may be no single timestamp at which nothing is mid-flight, and an element part-way through a slide reads as mis-positioned when it is fine.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / render / properties / layout
      Added value: +{
      +  "description": "Only meaningful when `timestamps` holds several. 'sheet' (default) tiles them into one image; 'separate' returns them full-size, which costs ~4x more — use it only when a grid cell is too small to judge.",
      +  "enum": [
      +    "sheet",
      +    "separate"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / render / properties / max_width
      Added value: +{
      +  "description": "Width in px of the IMAGE you get back — for a grid that is the whole grid, not each cell, so a cell is roughly a third of it. Default 960 for a single frame, 1280 for a grid. An image costs roughly (w×h)/750 tokens, so 1280 is ~1229 and 960 ~691; above 1568 nothing is gained because the model downscales to that anyway. The default is legible for this design system's type scale — a 40px canvas caption lands at ~20px. Raise it only to read genuinely small print, such as UI text inside a pasted screenshot.",
      +  "maximum": 1568,
      +  "minimum": 320,
      +  "type": "integer"
      +}
    • changedInput schema / properties / render / properties / save / description
      Previous value: -"Also upload the rendered PNG to S3 and return presigned_url, which you can pass as source_url to update_clueprint (files=[{path, source_url}])."New value: +"Also upload the rendered image to S3 and return presigned_url, which you can pass as source_url to update_clueprint (files=[{path, source_url}]). Uploads ONE image, so it works with a single frame or a grid but not with layout:'separate' — there is one URL and several frames."
    • removedInput schema / properties / render / properties / timestamp
      Removed value: -{
      -  "description": "The moment within the clip to render, in seconds (default 0).",
      -  "minimum": 0,
      -  "type": "number"
      -}
    • addedInput schema / properties / render / properties / timestamps
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 0,
      +      "type": "number"
      +    },
      +    {
      +      "items": {
      +        "minimum": 0,
      +        "type": "number"
      +      },
      +      "maxItems": 15,
      +      "minItems": 1,
      +      "type": "array"
      +    }
      +  ],
      +  "description": "Which moment(s) of the clip to render, in seconds (default 0). Pass several — [0.5, 2, 4] — and they come back as one labelled grid costing about a quarter of what those frames cost individually. Prefer several whenever you are checking rather than reading: entry animations mean no single moment shows everything, so one frame is usually the wrong question. A grid cell is a third of the width, so for small print pass a single number. Max 15; duplicates dropped, sorted into time order."
      +}
    • changedInput schema / properties / select / description
      Previous value: -"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'words' (per-word VO timings). Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, geo, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['words'] to sync visuals to narration; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."New value: +"Ask for exactly the JSON you want, GraphQL-style. Omit for everything; pass [] for none.\n  Sections: 'elements' (whole element rows), 'voiceover_words' (per-word VO timings; returns the key of the same name, holding `{word, start, end, punctuated_word}` with start/end in SECONDS — null on a clip with no transcription), 'groups' (group rows: id, name, parent_id, bounds_px, anchor_px, keyframes). Each section returns the key it is named after. Rendering is `render`, not a value here.\n  Per-key: 'elements.<key>' projects element rows to just those keys (id is always kept). Keys: name, element_type, x, y, width, height, start_time, end_time, rotation, keyframes, textdata, shapedata, imagedata, videodata, zoomdata, codedata, parent_id.\n  Examples: ['elements.x','elements.y','elements.width','elements.height'] to read geometry; ['elements.name','elements.start_time','elements.end_time'] for a timing pass; ['voiceover_words'] to sync visuals to narration; ['groups'] to resolve a group id for update_groups; [] with render:{} returns the PNG with no JSON (smallest read); ['elements.textdata','voiceover_words'] to rewrite copy against the VO.\n  Mixing 'elements' with 'elements.<key>' returns whole rows. Use element_ids to choose WHICH rows — that is independent of this."
    • changedInput schema / properties / select / items / enum
      Previous value: -[
      -  "elements",
      -  "words",
      -  "elements.name",
      -  "elements.geo",
      -  "elements.x",
      -  "elements.y",
      -  "elements.width",
      -  "elements.height",
      -  "elements.start_time",
      -  "elements.end_time",
      -  "elements.rotation",
      -  "elements.keyframes",
      -  "elements.textdata",
      -  "elements.shapedata",
      -  "elements.imagedata",
      -  "elements.videodata",
      -  "elements.zoomdata",
      -  "elements.codedata",
      -  "elements.parent_id"
      -]New value: +[
      +  "elements",
      +  "voiceover_words",
      +  "groups",
      +  "elements.name",
      +  "elements.element_type",
      +  "elements.x",
      +  "elements.y",
      +  "elements.width",
      +  "elements.height",
      +  "elements.start_time",
      +  "elements.end_time",
      +  "elements.rotation",
      +  "elements.keyframes",
      +  "elements.textdata",
      +  "elements.shapedata",
      +  "elements.imagedata",
      +  "elements.videodata",
      +  "elements.zoomdata",
      +  "elements.codedata",
      +  "elements.parent_id"
      +]
  12. First observed

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare this is a read-only, non-destructive, non-open-world operation, so the safety profile is covered. The description adds useful behavior beyond that: it warns that full reads are expensive, explains token implications of rendering choices, states that omitting `render` renders nothing, and notes that rendering one animation can return `warnings`. However, much of this behavioral detail is duplicated in the nested schema descriptions rather than being uniquely carried by the top-level description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a clear purpose and organized into useful sections, but it is very long and repeatedly restates details already present in the input schema, especially for `select`, `render`, and `max_width`. The examples are helpful, but the definition would be stronger if it removed duplicated schema content and kept only the highest-value guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the burden of explaining return values, and it does so thoroughly: it describes element wrapper fields, type-specific data, keyframes, HTML animation metadata, clip-level fields, `transition`, `voiceover_words`, and render `warnings`. Given the tool's complexity and nested parameters, the definition is complete enough for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the nested `select` and `render` schemas are already highly descriptive, so the baseline is 3. The description does add value through scenario-oriented examples such as using `select` to fix a layout, run a timing pass, or sync visuals to narration, but many of its parameter examples repeat the schema almost verbatim.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource: 'Read one clip' and lists the main returned components (elements, voiceover, background, transition). It also clearly distinguishes the optional render behavior from the JSON read, so an agent can tell what this tool is for without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit guidance on when to use `select` versus `render`, when to omit `select`, when to use single timestamps versus grids, and when `layout:'separate'` is worth the cost. It also names alternatives and related tools such as `read_animation`, `update_elements`, `edit_animation`, `update_groups`, and `get_project`, reducing the chance of using the wrong sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.