cavalry-mcp
Summary: This MCP server automates Cavalry through three schema-exposed tools: atomic helper-script execution, scene inspection, and procedural preset rig generation.
Run batched Cavalry JavaScript with injected
_mghelpers in one Undo group, returning JSON-serializable results.Inspect compact layer summaries (
id,name,niceName,type) for the current selection or active composition roots.Build parameterized motion-graphics rigs from 120 shipped presets, optionally passing preset-specific params.
Preset coverage includes typography, UI mockups, data viz, camera/backgrounds, AI, devtools, collaboration, workflows, fintech, security, cloud, media, onboarding, and CTA/social-proof scenes.
The README describes additional server capabilities (batch ops, frame render/verify, API probing, diagnostics, telemetry, CLI, legacy tools), but the provided schema exposes only the three tools above.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cavalry-mcpcreate a 1920x1080 rectangle layer and set its fill color to #101828"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cavalry-mcp
Low-token Cavalry automation through Stallion. Build whole motion rigs in one atomic script, call one of 120 procedural presets, or bypass MCP schemas entirely with the bundled CLI.
Agent / IDE <--MCP stdio / CLI--> cavalry-mcp <--HTTP--> Stallion <---> Cavalry 2.8The server is deliberately small. Every tool is a thin, schema-checked wrapper around one
Cavalry execution, and every execution is one Undo group. The value is in what runs inside
that execution: the _mg helper DSL, the batch compiler, the preset library, and the
render/verify/readback loops that keep an agent from paying a one-second round trip per
attribute.
This repository is the middle layer of a three-part stack:
Layer | Repository | Role |
| evidence-gated video-to-Cavalry pipeline; optional consumer of this server | |
| this repository | MCP server, |
Stallion | Cavalry extension that exposes the script host over HTTP |
Contents
Related MCP server: cavalry-mcp
Requirements
Requirement | Why |
Cavalry 2.8.x | the build the preset library and attribute paths are verified against. |
Stallion from akspro9866-art/stallion | the HTTP bridge this server talks to. Enable it in Cavalry with Scripts → Stallion; it must be re-run after every Cavalry start and after a Save As. |
Node.js >= 18.17 | runs the MCP server and the CLI. |
| promo encoding ( |
An MCP-capable client | Qoder, Claude Code/Desktop, Cursor, ZCode, Antigravity, Codex, or any client that can spawn a stdio server. |
Cavalry is proprietary software and is not part of this repository. Stallion is a separate MIT project; this server verifies the bridge's native result channel and falls back to the legacy result file when an older build is listening.
Install
git clone https://github.com/akspro9866-art/cavalry-mcp.git
cd cavalry-mcp
npm ci
npm run build
npm testnpm run build runs tsc and then scripts/build-preset-manifest.mjs, which writes
dist/presets/preset_manifest.json. The build fails if that manifest exceeds its 1500-token
budget, because it is meant to be readable by an agent.
The package declares two binaries:
Binary | Entry point | Purpose |
|
| the stdio MCP server |
|
| the zero-MCP CLI (imports |
Connect an MCP client
Z.ai ZCode
%USERPROFILE%\.zcode\cli\config.json (or <project>\.zcode\config.json) nests servers under
mcp.servers:
{
"mcp": {
"servers": {
"cavalry": {
"command": "node",
"args": ["C:/Users/<you>/tools/cavalry-mcp/dist/index.js"]
}
}
}
}Claude Desktop / Claude Code / Cursor
{
"mcpServers": {
"cavalry": {
"command": "node",
"args": ["C:/Users/<you>/tools/cavalry-mcp/dist/index.js"]
}
}
}Tools appear to the client as mcp__cavalry__<tool_name>. The server writes nothing to stdout
except the MCP protocol stream; diagnostics go to stderr.
Core tools
Eight tools are exposed by default. They are intentionally few: every additional schema is context the model pays for on every turn, and every additional round trip costs about a second of bridge latency.
Tool | Purpose |
| batched JavaScript with |
| bounded |
| build a parameterized procedural rig in one execution |
| compile many scene operations into one atomic round trip |
| render a few frames; report collisions, unseen layers, missing previews |
| render a whole range in deadline-sized, resumable chunks |
| ask the live build which functions, types and attributes actually exist |
| one-shot diagnosis of bridge, runtime, binaries and workspace |
Set ENABLE_LEGACY_TOOLS=true to add the original 44 granular tools described
below.
cavalry_run_script
The workhorse. The code string is wrapped in one isolated scope and one
api.startUndoGroup("AI Motion Batch") / api.endUndoGroup() pair with try/finally; the
_mg helpers are injected into that scope. Use return to send a JSON-serialisable value back:
const title = _mg.text("MOTION", 110, "#FFFFFF");
const rig = _mg.subMesh(title, 1, 8);
_mg.kf(title, "scale", [[0, 0], [30, 100]]);
_mg.ease(title, "opacity", { inFrame: 0, outFrame: 24, from: 0, to: 100 });
return { title, ...rig };The result is the JSON text of the returned value. A thrown error is returned as an MCP error
with the stack, and the applied edits remain — Cavalry has no transactional rollback. The
response also carries the telemetry snapshot in _meta.telemetry when one was captured
(status, errorStack, createdLayerIds, activeSelection, compInfo, world-space
boundingBoxes, durationMs). The same snapshot is written to disk, outside the Undo group, so
a failed script still leaves usable spatial evidence behind.
Rules that matter:
One script is one Undo step. Do not try to make half of it undoable separately.
Prefer returning
{ id, ... }maps over printing; the text payload is what the model reads.Renders are asynchronous inside Cavalry. Change the scene and render in separate calls.
A script that overruns the result deadline may still be running. The deadline stops the wait, not the bridge.
cavalry_inspect
Returns a compact summary of the current selection or the active composition's roots. It is the cheap way to learn ids before a patch, without dumping the scene.
Parameter | Default | Meaning |
|
|
|
| — | up to 16 integer frames; adds world-space bbox, transform, opacity and in/out connections per sampled layer, then restores the playhead |
| — | up to 50 layer ids to sample; defaults to whatever |
The per-frame sample is evaluated telemetry, not a keyframe listing. It tells you where a layer will be at a frame, which is what a collision check or a layout correction needs.
cavalry_run_preset
{
"name": "payment_card_checkout",
"params": { "amount": "$99.00", "accentColor": "#22D3EE" }
}name is constrained to the 120 shipped preset ids. params is optional; each preset runs
with its declared defaults when it is omitted. Parameter keys are checked against that preset's
declared parameters — an unknown key is reported instead of silently ignored.
This is the only preset entry point needed from MCP. The CLI can list presets and print their rig contract without a model in the loop; see Procedural presets.
cavalry_batch
Many ordered operations, one atomic round trip. Each operation returns its own ok/error;
one failure does not abort the batch, and the result reports executed, failed, refs and
errors.
Name a new layer with "as": "bg" and reference it later as "$bg". Both forms resolve inside
the same execution.
Op | Fields |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
easingType is one of the 20 names Cavalry 2.8 accepts: SlowIn, SlowOut,
SlowInSlowOut, VerySlowIn, VerySlowOut, VerySlowInVerySlowOut, SpringIn,
SpringOut, SpringInSpringOut, SmallSpringIn, SmallSpringOut,
SmallSpringInSmallSpringOut, AnticipateIn, OvershootOut, AnticipateInOvershootOut,
BounceIn, BounceOut, BounceInBounceOut, None, Custom. An expression is accepted only
with Custom or None. There is no Linear or EaseInOut name in this build.
[
{ "op": "create", "layerType": "basicShape", "name": "BG", "as": "bg" },
{ "op": "set", "layerId": "$bg", "attributes": { "material.materialColor": "#101828", "generator.dimensions": [1920, 1080] } },
{ "op": "create", "layerType": "textShape", "name": "Title", "as": "title" },
{ "op": "set", "layerId": "$title", "attributes": { "fontSize": 120, "text": "HELLO" } },
{ "op": "keyframe", "layerId": "$title",
"keyframes": [
{ "frame": 0, "attributes": { "position.y": -200, "opacity": 0 } },
{ "frame": 30, "attributes": { "position.y": 0, "opacity": 100 } }
] },
{ "op": "easing", "layerId": "$title", "attrPath": "position.y", "frame": 0, "easingType": "SpringOut" },
{ "op": "easing", "layerId": "$title", "attrPath": "opacity", "frame": 0, "easingType": "Custom", "expression": "1 - (1-t)*(1-t)*0.4" },
{ "op": "order", "layerId": "$title", "to": "front" },
{ "op": "gradient", "layerId": "$bg", "attrPath": "generator.gradient", "colors": ["#101828", "#2d3a8c"] },
{ "op": "expression", "layerId": "$title", "attrPath": "position.x", "expression": "value * 2" },
{ "op": "timing", "layerId": "$bg", "inFrame": 0, "outFrame": 90 }
]One call builds the whole scene. The same work as separate tools would cost roughly a second per operation in bridge latency.
cavalry_verify_frames
Renders a small set of frames and answers the questions a layout pass actually has: did the PNG arrive, did non-background layers overlap, and did anything requested stay invisible?
Parameter | Default | Meaning |
| required |
|
|
| preview directory |
| active comp | comp id to activate, restored afterwards |
|
| PNG scale percentage (1–400) |
|
| preview file prefix |
|
| also inspect hidden and out-of-range layers |
It restores the original playhead and active composition, returns a warning list, and marks missing previews. Treat a collision as a question, not a verdict — overlapping title cards and intentional layering are real.
cavalry_render_frames
api.renderPNGFrame renders whatever frame the playhead is on, and a Stallion request that
overruns its result deadline does not stop running. The frame-by-frame route therefore pays
about a second of bridge latency per frame, and the "send 36 frames at once" route loses the
whole result when the deadline expires. This tool sits between the two:
chunk size is derived from a per-frame cost estimate against the deadline:
chunkFrames = floor((timeoutMs - overheadMs) / perFrameMs), clamped to 1–40. Both the result deadline and the HTTP deadline are raised per call instead of relying onCAVALRY_RESULT_TIMEOUT_MS;each chunk is one atomic script that restores the playhead and active comp afterwards;
the files on disk are the progress signal. After an unanswered chunk it waits
graceMsfor late files, counts what arrived, and stops rather than re-sending work the bridge may still be doing;render-progress.jsonin the output directory recordscavalry-mcp/render-progress@1— status, budget, completed and pending frames — andresume(default on) reuses non-empty frames, so a long render is continued by calling the same tool again.
Parameter | Default | Meaning |
| required | ranges, lists, or an array |
|
| output directory |
|
| file name template with one padded token |
|
| PNG scale percentage (1–400) |
| active comp | comp to activate, restored afterwards |
| auto | frames per bridge call, overriding the budget estimate (1–40) |
|
| measured cost of one frame, used to size chunks (50–60000) |
| per-call | result deadline for a single chunk (1000–1800000) |
|
| extra wait for files that land after a chunk stops answering (0–600000) |
|
| reuse non-empty files already on disk |
|
| max entries per frame list in the result |
{ "frames": "0-215", "outDir": "C:/renders/hero", "timeoutMs": 45000, "perFrameMs": 300 }Measured against Cavalry 2.8.0 and a 736×414 comp: 216 frames in 6 chunks in 40 s (~175 ms per frame), with the playhead returned to frame 211.
cavalry_probe_api
Documentation and the live build disagree about names. One call settles it: the function keys of
Object.keys(api), a yes/no per watched name, one representative layer per type with its full
attribute list, and api.getAllLayerTypes().
Parameter | Default | Meaning |
| — | up to 20 ids whose full attribute list to read |
| built-in watch list | function names to test |
|
| read one representative layer per type in the active comp |
|
| include |
|
| return the full function-name list, not just the file path |
|
| report path |
The tool payload stays a summary; the full report is written to disk. For the 2.8.0 build used
in development: 320 API functions, 256 layer types, 82 attributes on a textShape, and seven
watched names absent (getName, getCompositionList, duplicateSelection, startUndoGroup,
endUndoGroup, save, open).
cavalry_doctor
One call, four groups of checks, with a concrete next step for every failure:
Check | What it proves |
|
|
|
|
| the binaries resolve and print a version |
| the telemetry state directory is writable and the result/timeout configuration is readable |
Parameter | Default | Meaning |
|
| per-probe ceiling (250–60000); raise it while Cavalry is mid-render |
|
| set |
|
| Cavalry minor to compare against |
|
|
|
A slow runtime probe is usually a busy Cavalry: the script host is single-threaded, so a modal dialog or a running render looks like a timeout. The doctor says so instead of treating it as a dead bridge.
The _mg helper DSL
_mg is injected into every cavalry_run_script and preset execution. It is a plain object of
functions that compile down to api.* calls — no hidden state except the alias registry and the
preset-root registry, both held on globalThis so they survive across executions in one Cavalry
session.
Everything below also runs through types/cavalry.d.ts, which is the
TypeScript contract for the injected API and helpers.
Scene construction and handles
Helper | Purpose |
| create and activate a composition (defaults |
| apply a dot-path attribute map to one layer |
| create a shape layer |
| create any layer type |
| create a text layer (defaults |
| register a stable |
| every registered alias as |
| resolve a layer id, semantic id, slug or |
| read or assign a semantic id on a layer |
| list semantic ids in the active comp |
| the current selection as ids / the first id |
| run a function inside one named Undo group; nesting collapses to the outermost pair |
Aliases are the recommended way to address layers across calls. A live id like textShape#7
changes when a scene is recreated; @hero_title does not.
Structure and rigging
Helper | Purpose |
| connect one attribute to another |
| add a deformer to a layer |
| sub-mesh a text layer and add a stagger; returns |
| build a duplicator rig (defaults |
| editable vector from a VTracer SVG file or an inline path |
| apply |
| set a blend mode |
|
|
| switch a sub-mesh between characters, words and lines ( |
|
|
| corner radius on a shape |
Animation and timing
Helper | Purpose |
| write keyframes from |
| declarative two-boundary move: |
| multi-point curve: |
| boundary move with |
| deterministic per-layer delay: |
| bake an analytic damped spring to per-frame keyframes: |
| retime animation to beat frames |
| trim start/end/offset and travel |
| procedural modulation: |
| dynamic connector between layers |
Easing names are the 20 Cavalry names above. _mg.ease also accepts the friendly aliases that
refclone emits — ExpoOut, CubicInOut, BackOut, QuarticIn, Linear and friends — and
maps a raw cubic-bezier to the nearest accepted preset by RMS over 11 samples. It never sends
Custom to api.magicEasing; that API needs a separate expression argument and is not
equivalent.
_mg.rig(spec) is a pure descriptor helper: give it stiffness, mass, damping/
dampingRatio or overshoot, and it returns the implied frequencies, overshoot, suggested
easing and hold/stagger terms without writing anything. It is what a refinement loop uses to
translate spring language into Cavalry's vocabulary.
Patching, layout and convergence
Helper | Purpose |
| idempotent in-place adjustments; never creates or recreates layers |
| restyle/retime the selection or the last preset's roots |
| write values at one exact frame without stacking keys or rewriting neighbour tangents |
| fit a layer's measured world bounds to a target box synchronously |
| convert top-left, y-down video pixels to Cavalry-centred, y-up comp space |
| read the bound theme / bind a palette, typography and radius for future layers |
_mg.patch is the correction primitive. A spec targets layers by any of alias, target,
ids, semantic or semantics, and applies any of:
Field | Effect |
| absolute dot-path values |
| additive values — read the current value, then add |
| multiplicative factor applied after |
| write at that frame through |
| slide existing keys by frames (optionally only for named attrs) |
| retune existing keyframe easing |
|
|
Static writes are compared with the evaluated attribute first, so re-applying the same spec is a
no-op and reports the value under skipped rather than writing a duplicate. A frame-targeted
write always lands, because Cavalry 2.8 has no value-at-frame getter. patch returns
{ patched, changes, skipped, targets } per spec (or an array of those when given an array).
_mg.fitToBounds resizes the intrinsic geometry first — generator.dimensions or
generator.radius for shapes, fontSize for text — then closes the residual with scale, and
corrects position from the freshly measured box after every pass. Options are space
(comp or raster), compWidth/compHeight, tolerance (default 0.5), maxPasses
(default 5, max 10), intrinsic (default true), preserveAspect and fitWidth. It
returns { ok, layerId, type, space, target, box, passes, error, applied }. layerId accepts
an @alias, a semantic id or a slug; the resolved live id is reported.
Procedural presets
The registry ships 120 presets across 14 category packs — a self-contained SaaS
motion-graphics library. Every preset is an isolated IIFE inside its own Undo group, with
try/finally. It returns a map of role → layer id plus preset: "<id>", so you can keep
animating the pieces it built.
# | Category | Label | Presets |
1 |
| Hero Typography & Messaging | 5 |
2 |
| UI Components & Product Mockups | 5 |
3 |
| Data Visualization & KPI | 5 |
4 |
| Backgrounds, Transitions & Camera | 5 |
5 |
| AI Agents, LLMs & Copilot | 10 |
6 |
| Developer Tools, Code & Terminal | 10 |
7 |
| Multiplayer Collaboration & Presence | 10 |
8 |
| Workflow Automation & Node Graphs | 10 |
9 |
| Fintech, Payments & Billing | 10 |
10 |
| Security, Auth & Identity | 10 |
11 |
| Cloud, DevOps & Infrastructure | 10 |
12 |
| Voice, Audio & Video | 10 |
13 |
| Onboarding & Product-Led Growth | 10 |
14 |
| Social Proof & Call-to-Action | 10 |
Presets are stored as category packs in src/presets/packs/cat01…cat14 and composed from
shared _rb factories in src/presets/rig_builders.ts. Every preset accepts {} and runs with
its declared defaults. Parameters declare the types string, int, double, bool, color
(hex string) and json (object or array).
Accepted aliases:
Alias | Preset |
|
|
|
|
|
|
Categories 9–14 and presets 59–60 extend the original 58-preset brief; they follow the same
conventions — _rb factories, verified Cavalry 2.8 attribute paths, floor-pinned bars, one
guarded Undo group — but their layouts and parameters were designed to be consistent with the
brief rather than transcribed from it.
Preset manifest
npm run build writes dist/presets/preset_manifest.json. Its declared format is:
id|category|motionSignature|requiredParams|defaultDurationEach entry is one line under presets, with abbreviated category codes resolved by the adjacent
categories map. The manifest is generated from the same definitions the server ships, never
hand-maintained, and the build enforces a 1500-token budget. refclone consumes it to choose a
preset without loading the whole library into context; it looks for
REFCLONE_PRESET_MANIFEST, then CAVALRY_MCP_PRESET_MANIFEST, then a sibling checkout.
Zero-MCP CLI
bin/stallion-cli.mjs exposes the same runtime without an MCP client. It imports dist/, so run
npm run build after changing source. Installing the package exposes the stallion command.
node bin/stallion-cli.mjs -e 'return _mg.text("FAST", 96);'
node bin/stallion-cli.mjs -f ./rig.js
node bin/stallion-cli.mjs -p kinetic_text '{"text":"HELLO"}'
node bin/stallion-cli.mjs --list
node bin/stallion-cli.mjs --list fintech_billing
node bin/stallion-cli.mjs --rig payment_card_checkout
node bin/stallion-cli.mjs --sel
node bin/stallion-cli.mjs --verify-frames 0,60,120 --scale 50
node bin/stallion-cli.mjs --render-frames 0-215 --out ./frames --chunk 20
node bin/stallion-cli.mjs --probe --capabilities-out ./capabilities.jsonMode | Flags |
Script |
|
Preset |
|
Selection |
|
Beats |
|
Promo |
|
Verify |
|
Render |
|
Probe |
|
Global |
|
--state prints the execution snapshot to stderr on success; on failure the snapshot is always
printed. --rig prints the preset's spring/easing contract (derived from its script and
parameter defaults), which is what a refinement loop reads before retuning it. --probe writes
its report to ~/.cavalry-mcp/cavalry-api-capabilities.json unless --capabilities-out is
given.
Promo rendering needs a local ffmpeg (pass --ffmpeg <path>); beat analysis honours
FFMPEG_PATH. A pre-analysed JSON beat file lets --audio-beats run without ffmpeg.
Execution state and telemetry
After every atomic execution the wrapper snapshots the active comp before the script, then writes
a compact state file after the Undo group has closed — in a finally, outside the group:
{
"seq": "…",
"status": "ok",
"errorStack": null,
"createdLayerIds": ["textShape#7"],
"activeSelection": ["textShape#7"],
"compInfo": { "id": "comp#1", "name": "Main", "fps": 60, "startFrame": 0, "endFrame": 180, "resolution": [1920, 1080] },
"boundingBoxes": { "textShape#7": { "x": -300, "y": -60, "width": 600, "height": 120, "centre": { "x": 0, "y": 0 } } },
"durationMs": 123,
"writtenAt": "2026-11-01T00:00:00.000Z"
}The MCP server returns the same object as
_meta.telemetrywithout changing the text payload that scripts and existing clients parse.The CLI prints it to stderr with
--state, and always on a failed execution.The path is
~/.cavalry-mcp/last-execution-state.json(override withCAVALRY_STATE_PATH). If the first write fails, a smallerwriteFailedpayload is attempted so the node side still learns what happened.Bounding boxes cover up to 50 created or selected layers and are measured in world space at the playhead after the script ran.
Environment variables
All are optional.
Variable | Default | Purpose |
|
| Stallion base URL; must be |
| empty | bearer token when |
|
| legacy URL configuration used when |
|
| abort timeout for every HTTP request; long render callers override it per call |
|
| overall structured-result wait; per-call |
| (legacy) | sets both timeouts at once when the specific variables are unset |
|
| legacy fallback result file |
|
| telemetry snapshot path |
|
|
|
|
|
|
|
|
|
|
| the Cavalry minor |
|
| ffmpeg binary for beats and promo encoding |
|
| ffprobe binary used by |
|
| register the original 44 granular MCP tools |
Legacy tools (ENABLE_LEGACY_TOOLS=true)
The original surface remains available for existing clients. It is off by default to keep MCP schema tokens and round trips down. All 44 tools run through the same atomic wrapper and the same state capture.
Scene building and data
Tool | Notes |
| reachability check plus Cavalry version |
| arbitrary JS; assign |
| returns the new layer id, e.g. |
|
|
| returns the value |
| connect one attribute to another (the single-operation form of the batch |
| structured JSON: id, type, name, visible, selected, position, in/out, parent |
| every attribute path on a layer, optionally with definitions |
| incoming connections per attribute plus outgoing connections per attribute and id |
| delete verifies what was actually removed |
|
|
| rename in place |
| z-order without a second layer id: |
| local (default) or world space |
| image/sequence/video/audio → asset and layer ids |
| add a dynamic attribute to a layer |
| every layer type in this build ( |
|
|
Animation
Tool | Notes |
| one keyframe or a whole curve ( |
| delete keys at frames, wipe an attribute's (or a layer's) animation, set/clear per-keyframe velocity |
| schema-validated enum of the 20 real easing names; |
| keyframe frames per attribute |
| presets in one call: |
| set or read a per-attribute JavaScript expression |
Style and colour
Tool | Notes |
| one-call gradient from hex stops ( |
| harmonious palettes: monochromatic, analogous, complementary, triadic, goldenRatio, … |
Composition and timing
Tool | Notes |
| resolution, fps, frame range, current frame |
| width/height, fps, inFrame/outFrame |
| list/create/set-active comps, list a comp's layers, preCompose, new scene (confirm-gated) |
| playhead control |
| play/stop the timeline so the user can watch |
| trim in/out points or slide a layer by N frames |
| stack order and hierarchy |
Render and export
Tool | Notes |
| renders and returns the image inline ( |
| N frames across the timeline → one contact sheet image |
| starts a background render-queue video render of the active comp |
| manage renders |
| Save As verified by watching the file on disk |
| experimental, feature-detected; a clear error if this build lacks it |
Tips and known behaviour
Batch, always. One bridge round trip costs about a second. Use
cavalry_batch, onecavalry_run_script, or the CLI instead of a loop of single-attribute calls.Renders are asynchronous inside Cavalry. The PNG reflects the scene state when the render completes. Make scene changes and renders in separate calls.
api.saveSceneAstears down the Stallion script context. After a Save As, re-run Scripts → Stallion before further tool calls.Layer ids are strings like
textShape#3, numbered per type across the scene. Treat them as ephemeral; register an_mg.aliasfor anything you will address again.cavalry_render_videofollows the render queue item's settings. Configure defaults once in Cavalry's Render window;outputPathsets the item's filename on a best-effort basis.Customis not a magic-easing preset.api.magicEasingneeds a separate expression argument, so helpers map raw beziers to the nearest accepted curve rather than sending it.A timed-out script is not a cancelled script. The deadline stops the wait; the bridge may still be executing. Inspect the state file and reconcile before re-sending.
The Cavalry scripting reference is the authority on attribute paths: https://cavalry.studio/docs/tech-info/scripting/api-module. When it disagrees with the build,
cavalry_probe_apiwins.
Development
npm ci
npm run build # tsc -> dist/ and the preset manifest
npm test # 235 tests, no Cavalry requiredThe suite runs the real server against an in-memory MCP client and a mock Stallion: an HTTP
server that executes scripts in a node:vm sandbox with a modelled api. Bridge behaviour —
seq matching, the write-failure retry, queue serialization, timeouts, the friendly connection
error — is therefore covered without Cavalry installed. test/presets/library.test.ts compiles and
executes all 120 presets with defaults and typed overrides against the same mock. Theme,
telemetry, render-chunking, verify, probe and MCP schema tests run in the same suite.
CI runs npm ci, npm run build and npm test on Ubuntu and Windows with Node 20, 22 and 24.
Useful scripts:
Command | Effect |
|
|
| regenerate |
| run the built server directly |
| watch-mode test run through |
Repository layout
src/
index.ts MCP server, core registration, legacy gate
server/
core-tools.ts core MCP tools: batch, verify, render, probe, doctor
legacy/
index.ts the 44 opt-in granular tools
animate-presets.ts one-call animation presets for cavalry_animate_in
filmstrip.ts PNG contact sheets for the legacy preview tools
tools/
batch.ts batch operation compiler
beats.ts audio-beat analysis
doctor.ts bridge/runtime/binary/workspace diagnosis
probe.ts live API capability report
promo.ts multi-scene MP4 promo renderer
render-frames.ts chunked, resumable PNG sequence render
verify.ts frame previews, collisions, missing files
bridge/
stallion.ts serialized HTTP client, native + legacy result channels
shared/
lib.ts string, outcome and JSON helpers
fs-safe.ts Windows-safe tree removal
state.ts execution-state snapshots
runtime/
atomic.ts one Undo group, result envelope, state capture
execute.ts script execution and result formatting
inspect.ts bounded selection/roots summaries
mg_helpers.ts the `_mg` DSL
rig_terms.ts spring/easing contract derivation
presets/
cavalry_snippets.ts compilePreset, listPresets, PRESET_NAMES
rig_builders.ts shared `_rb` factories
manifest.ts preset manifest builder
packs/cat01…cat14 the 120 presets
bin/stallion-cli.mjs the zero-MCP CLI
scripts/ build-time preset manifest
types/cavalry.d.ts API and `_mg` TypeScript declarations
test/
bridge/ cli/ e2e/ helpers/ legacy/ presets/ runtime/ server/ shared/ tools/
grouped unit, mock-bridge, MCP e2e and preset-library testsCredits
kacperchlebowicz/Cavalry-mcp — the original Claude Desktop version this project rebuilt
scenery-io/stallion — the upstream Stallion project; this stack is developed against the akspro9866-art fork, which adds the seq-matched result channel, status heartbeat, readback message types and mock bridge this server uses
Scene Group — Cavalry
License
MIT
Available Tools
3 toolscavalry_inspectA
Inspect compact layer id/name/niceName/type summaries for the selection or active composition roots.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | selection |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden. "Inspect" and "summaries" correctly signal a non-mutating, read-only operation and pre-declare the returned fields (id/name/niceName/type), but nothing is said about permissions, cost, or behavior when nothing is selected or no composition is active.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with the verb and the returned payload, with the scope qualifier at the end. No filler and nothing that could be cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-optional-param read tool with no output schema, the description names the exact fields returned and the two scopes it can inspect, which is enough to call it correctly. Only the empty-selection/no-active-composition edge case is unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the single parameter is an enum whose two values are explicitly mapped by the phrase "selection or active composition roots," so an agent can choose the default or override with intent. The description effectively compensates for the undocumented schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Inspect) and resource (compact layer id/name/niceName/type summaries) plus the scope it operates on (selection or active composition roots). It implicitly separates itself from cavalry_run_script and cavalry_run_preset, which execute things rather than read layer metadata, though it never names those siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"for the selection or active composition roots" implies the situations where the tool is relevant, but there is no explicit when/when-not guidance and no direct routing to or away from cavalry_run_script and cavalry_run_preset. Usage has to be inferred from the scope phrase.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cavalry_run_presetC
Build a parameterized procedural motion rig in one atomic Cavalry execution.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| params | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully says the build happens in one atomic execution, but it does not disclose mutation side effects, required Cavalry state, failure behavior, idempotency, or what is returned after the rig is built.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no wasted words. It is appropriately concise, though the extreme terseness contributes to the missing usage and parameter details elsewhere.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a complex mutation tool with a large enum, a free-form nested params object, no annotations, and no output schema. One sentence stating that it builds a parameterized rig is not enough to tell an agent how to invoke it correctly or what to expect from the atomic execution.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, but it only says the rig is 'parameterized'. It does not explain the 118-value name enum, what the free-form params object can contain, how params override preset defaults, or whether any params are required beyond name.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Build a parameterized procedural motion rig' in 'one atomic Cavalry execution'. It is clear what the tool does and implies preset-driven rig creation, but it does not explicitly differentiate itself from cavalry_run_script or cavalry_inspect.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no exclusions, and no mention of the sibling tools cavalry_run_script or cavalry_inspect. The agent is left to infer that this tool should be chosen when a named preset is desired rather than a custom script.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cavalry_run_scriptB
Run batched Cavalry JavaScript with _mg helpers and one Undo group. Return a value for JSON readback.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose two real traits: all effects collapse into one Undo group (atomicity), and a returned value is read back as JSON (serialization requirement). It says nothing about permissions, error/failure behavior, whether the script can leave partial state on failure, or what _mg helpers actually are.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the operation and followed by the two constraints that matter. No filler, though the phrasing is terse enough to be slightly cryptic.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter scripting tool with no annotations or output schema, the description covers the return path ('JSON readback') and undo semantics, which is more than most. Gaps remain around the _mg helper surface and failure behavior, which matter for arbitrary code execution.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single `code` parameter has 0% schema description coverage, so the description must compensate. It does add guidance — the code may use _mg helpers and should return a JSON-serializable value — but does not clarify the string format, expected structure, or any length/context constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource: run batched Cavalry JavaScript. Adds the distinguishing traits (with _mg helpers, a single Undo group), which separates it from a plain script runner. It does not explicitly contrast with cavalry_inspect or cavalry_run_preset, so sibling differentiation is only implicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No statement of when to use this rather than cavalry_run_preset or cavalry_inspect, and no prerequisites or exclusions. 'Batched' hints that this suits multiple operations, but the agent must infer that from a single adjective.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v3.0.0- First observed
cavalry_inspect - First observed
cavalry_run_preset - First observed
cavalry_run_script
TDQS
Scored across 3 tools
cavalry_inspect is clearly the read path, while cavalry_run_script and cavalry_run_preset both execute code and could initially be confused. The descriptions resolve this (preset = one atomic parameterized rig build, script = general JS escape hatch), so boundaries are workable but not perfectly crisp.
All three share a consistent cavalry_ prefix and use verb-style names (run_script, run_preset, inspect). The only minor deviation is that inspect has no object noun while the other two follow verb_noun, but the pattern remains readable.
Three tools is on the lean side, but the design is deliberate: one general execution tool, one readback tool, one high-level preset builder. Each earns its place rather than being a thin, trivial surface.
The general script runner plus inspect covers most read/write workflows, but there is no discovery tool for available presets, _mg helper documentation, or broader scene/composition enumeration beyond selection and roots. Agents must guess at APIs, which will cause some dead ends.
Maintenance
Related MCP Connectors
Build editable 3D scenes, direct characters and cameras, and export AI video references with MCP.
An MCP connector for Adobe After Effects. Real, editable layers and keyframes, not scripts.
- CueFrameDeprecatedai.cueframe.api
Compose, edit and render video through CueFrame’s hosted x402 MCP endpoint: content-aware reframing, word-timed captions and motion graphics. Paid tool calls require an x402-capable client and wallet payment. For OAuth browser sign-in, use the CueFrame OAuth connector.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables natural language control of Cavalry animation software for creating layers, setting attributes, keyframing, rendering frames, and more via an MCP server.2131MIT
- FlicenseBqualityDmaintenanceMCP server for driving Cavalry's 2D motion-graphics engine from LLMs, enabling scene creation and animation via natural language.283-
- AlicenseBqualityDmaintenanceEnables AI assistants to control Cavalry, a procedural 2D motion graphics application, through natural language commands for creating and animating scenes.182MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to control Cavalry motion graphics projects through its local JavaScript API, supporting scene and layer inspection, creation, editing, keyframing, expressions, saving, and rendering with validated structured results.MIT