Skip to main content
Glama

caption_style

DestructiveIdempotent

Read or update the caption styling for a video project: set colors, font, position, karaoke highlights, and cue grouping, with validation before applying.

Instructions

Read or change the caption look this project keeps.

The style is project state and the captions are derived from it, so a restyle survives every later cut: regenerating re-reads this. Call it with no arguments to read the current look and learn the field names; any argument sets that field and leaves the others alone. reset drops every override first — reset plus preset starts clean from a preset.

preset is the base look ("clean", "karaoke" for per-word highlight, or "boxed"); everything else overrides one of its fields, and only the overrides are stored.

Colours take "#rrggbb", "#rrggbbaa", a name ("yellow", "white", "red", …) or an ASS "&H…" value. text is the word's colour and highlight what it turns as it is spoken, which only shows with karaoke on. position is named: "bottom", "top", "top-right", and so on. Both come back resolved, because ASS quotes colours backwards and alpha-inverted.

plan validates and resolves without writing. Use caption_view to see the result on the actual timeline.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
boxNoDraw an opaque box behind the words. It buys legibility over light footage — white captions over the film's own light cards measure 1.10:1 without one — and costs clean edges, since libass draws one box per override block.
boldNoDraw bold.
fontNoFamily name to draw with. Whether it actually draws is a different question from whether it is installed — `fonts` measures a render, and libass substitutes silently at exit 0.
holdNoHow long a cue lingers after its last word, in seconds.
pathNoThe project directory to act on. Omit it — the usual case — when this server is bound to a project (started as `proofcut -C DIR mcp`, or inside a project; `ping` says which): it then resolves to that one bound project, a relative path resolves against it, and a path outside it is refused by name. Unbound, `path` is the whole address and omitting it refuses rather than guessing.
planNoResolve the whole call and report what it would do, writing nothing. Prefer it over doing the thing and undoing it.
sizeNoType size, against the project's canvas as the reference frame.
textNoThe word's own colour: `#rrggbb`, `#rrggbbaa`, a name, or an ASS `&H…` value. It comes back resolved, because ASS quotes colours backwards and alpha-inverted.
resetNoDrop every override first. `reset` together with `preset` starts clean from that preset.
marginNoDistance from the frame edge, in canvas pixels.
presetNoThe base look: `clean`, `karaoke` (per-word highlight) or `boxed`. Everything else overrides one of its fields, and only the overrides are stored.
shadowNoDrop-shadow distance.
karaokeNoFill each word as it is spoken. The fill is left-to-right within a line rather than a per-word step, which is what the grouping fields below shape.
max_gapNoStart a new cue when the silence between two words exceeds this many seconds.
positionNoWhere captions sit, named: `bottom`, `top`, `top-right` and so on.
highlightNoWhat a word turns as it is spoken. It only shows with `karaoke` on.
max_wordsNoMost words in one caption cue. Grouping is part of the look, which is why it is stored with it.
box_colourNoColour of the box behind the type, when `box` is on.
max_durationNoLongest a single cue stays on screen, in seconds.
outline_widthNoOutline thickness. With no box this is what holds the words apart from the picture.
outline_colourNoColour of the outline around the type.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed81 schema fields changedv0.25.0
    • removedInput schema / properties / bold / anyOf
      Removed value: -[
      -  {
      -    "type": "boolean"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / bold / description
      Added value: +"Draw bold."
    • removedInput schema / properties / bold / title
      Removed value: -"Bold"
    • addedInput schema / properties / bold / type
      Added value: +[
      +  "boolean",
      +  "null"
      +]
    • removedInput schema / properties / box / anyOf
      Removed value: -[
      -  {
      -    "type": "boolean"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / box / description
      Added value: +"Draw an opaque box behind the words. It buys legibility over light footage — white captions over the film's own light cards measure 1.10:1 without one — and costs clean edges, since libass draws one box per override block."
    • removedInput schema / properties / box / title
      Removed value: -"Box"
    • addedInput schema / properties / box / type
      Added value: +[
      +  "boolean",
      +  "null"
      +]
    • removedInput schema / properties / box_colour / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / box_colour / description
      Added value: +"Colour of the box behind the type, when `box` is on."
    • removedInput schema / properties / box_colour / title
      Removed value: -"Box Colour"
    • addedInput schema / properties / box_colour / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / properties / font / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / font / description
      Added value: +"Family name to draw with. Whether it actually draws is a different question from whether it is installed — `fonts` measures a render, and libass substitutes silently at exit 0."
    • removedInput schema / properties / font / title
      Removed value: -"Font"
    • addedInput schema / properties / font / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / properties / highlight / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / highlight / description
      Added value: +"What a word turns as it is spoken. It only shows with `karaoke` on."
    • removedInput schema / properties / highlight / title
      Removed value: -"Highlight"
    • addedInput schema / properties / highlight / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / properties / hold / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / hold / description
      Added value: +"How long a cue lingers after its last word, in seconds."
    • removedInput schema / properties / hold / title
      Removed value: -"Hold"
    • addedInput schema / properties / hold / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedInput schema / properties / karaoke / anyOf
      Removed value: -[
      -  {
      -    "type": "boolean"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / karaoke / description
      Added value: +"Fill each word as it is spoken. The fill is left-to-right within a line rather than a per-word step, which is what the grouping fields below shape."
    • removedInput schema / properties / karaoke / title
      Removed value: -"Karaoke"
    • addedInput schema / properties / karaoke / type
      Added value: +[
      +  "boolean",
      +  "null"
      +]
    • removedInput schema / properties / margin / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / margin / description
      Added value: +"Distance from the frame edge, in canvas pixels."
    • removedInput schema / properties / margin / title
      Removed value: -"Margin"
    • addedInput schema / properties / margin / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedInput schema / properties / max_duration / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / max_duration / description
      Added value: +"Longest a single cue stays on screen, in seconds."
    • removedInput schema / properties / max_duration / title
      Removed value: -"Max Duration"
    • addedInput schema / properties / max_duration / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedInput schema / properties / max_gap / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / max_gap / description
      Added value: +"Start a new cue when the silence between two words exceeds this many seconds."
    • removedInput schema / properties / max_gap / title
      Removed value: -"Max Gap"
    • addedInput schema / properties / max_gap / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedInput schema / properties / max_words / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / max_words / description
      Added value: +"Most words in one caption cue. Grouping is part of the look, which is why it is stored with it."
    • removedInput schema / properties / max_words / title
      Removed value: -"Max Words"
    • addedInput schema / properties / max_words / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedInput schema / properties / outline_colour / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / outline_colour / description
      Added value: +"Colour of the outline around the type."
    • removedInput schema / properties / outline_colour / title
      Removed value: -"Outline Colour"
    • addedInput schema / properties / outline_colour / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / properties / outline_width / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / outline_width / description
      Added value: +"Outline thickness. With no box this is what holds the words apart from the picture."
    • removedInput schema / properties / outline_width / title
      Removed value: -"Outline Width"
    • addedInput schema / properties / outline_width / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedInput schema / properties / path / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / path / description
      Added value: +"The project directory to act on. Omit it — the usual case — when this server is bound to a project (started as `proofcut -C DIR mcp`, or inside a project; `ping` says which): it then resolves to that one bound project, a relative path resolves against it, and a path outside it is refused by name. Unbound, `path` is the whole address and omitting it refuses rather than guessing."
    • removedInput schema / properties / path / title
      Removed value: -"Path"
    • addedInput schema / properties / path / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • addedInput schema / properties / plan / description
      Added value: +"Resolve the whole call and report what it would do, writing nothing. Prefer it over doing the thing and undoing it."
    • removedInput schema / properties / plan / title
      Removed value: -"Plan"
    • removedInput schema / properties / position / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / position / description
      Added value: +"Where captions sit, named: `bottom`, `top`, `top-right` and so on."
    • removedInput schema / properties / position / title
      Removed value: -"Position"
    • addedInput schema / properties / position / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / properties / preset / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / preset / description
      Added value: +"The base look: `clean`, `karaoke` (per-word highlight) or `boxed`. Everything else overrides one of its fields, and only the overrides are stored."
    • removedInput schema / properties / preset / title
      Removed value: -"Preset"
    • addedInput schema / properties / preset / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • addedInput schema / properties / reset / description
      Added value: +"Drop every override first. `reset` together with `preset` starts clean from that preset."
    • removedInput schema / properties / reset / title
      Removed value: -"Reset"
    • removedInput schema / properties / shadow / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / shadow / description
      Added value: +"Drop-shadow distance."
    • removedInput schema / properties / shadow / title
      Removed value: -"Shadow"
    • addedInput schema / properties / shadow / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedInput schema / properties / size / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / size / description
      Added value: +"Type size, against the project's canvas as the reference frame."
    • removedInput schema / properties / size / title
      Removed value: -"Size"
    • addedInput schema / properties / size / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedInput schema / properties / text / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedInput schema / properties / text / description
      Added value: +"The word's own colour: `#rrggbb`, `#rrggbbaa`, a name, or an ASS `&H…` value. It comes back resolved, because ASS quotes colours backwards and alpha-inverted."
    • removedInput schema / properties / text / title
      Removed value: -"Text"
    • addedInput schema / properties / text / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedInput schema / title
      Removed value: -"caption_styleArguments"
  2. First observedv0.24.0

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already mark this as destructive and idempotent, and the description aligns: it says changes persist ('restyle survives every later cut') and that `reset` drops overrides. It adds context beyond annotations by explaining how colour values are resolved (ASS quoting) and that only overrides are stored, giving an agent a clear mental model of the state mutation. Slight gap: it doesn't explicitly state that the call is idempotent, but the description implies it.

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

Conciseness4/5

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

The description is long (roughly 300 words) but well-structured with clear paragraphs. It front-loads the purpose and usage, then details colour formats, then plan/view. Every sentence contributes to the agent's decision-making. It could arguably be tighter, but for a tool with 21 parameters and a destructive mutation, the length is justified.

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?

Given the tool's complexity—21 parameters, destructive behavior, and interaction with sibling tools—the description is remarkably complete. It covers read vs write semantics, persistence, colour handling, preset/override relationships, and points to `caption_view` for verification. An agent has all the information needed to call it correctly without additional assumptions. The output schema exists, so return-value details are not required in the description.

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

Parameters5/5

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

The input schema has 100% parameter coverage with per-field descriptions, but the tool description goes far beyond: it explains the preset options ('clean', 'karaoke', 'boxed'), how overrides work, colour format options, the relationship between `text` and `highlight` with karaoke, and the meaning of `plan` and `reset`. This adds substantial semantic value that the schema alone does not provide, especially for the 21 parameters.

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 description opens with 'Read or change the caption look this project keeps,' a specific verb pair and resource. It distinguishes itself from the sibling caption_view ('Use caption_view to see the result on the actual timeline') and explains that captions are derived from this style, so an agent immediately knows this tool is the source of truth for caption appearance, not a renderer.

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?

Explicit usage rules are given: 'Call it with no arguments to read the current look and learn the field names; any argument sets that field and leaves the others alone.' It also explains the `reset` and `preset` interaction, and directs to `plan` for dry-run validation and `caption_view` for visual confirmation. No ambiguity about when to use it.

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