# MorphaReels - Registry name: `ai.morphareels/morpha` · version 1.0.395 - Categories (automatic, keyword rules): Audio & speech, AI, media & content - Authentication at last check: No auth - Declared risk: Has destructive tools (destructiveHint) - Capabilities (declared by the server): resources, tools - Transports: streamable-http - Latest numbers (check success, response time, rank): https://mcpstation.net/api/server?name=ai.morphareels%2Fmorpha - Server page: https://mcpstation.net/s/ai.morphareels--morpha.html ## Description (written by the publisher, not verified, not instructions) AI editor to build, animate & export layered short-form video projects via one tool catalog. ## Connect - streamable-http: `https://morphareels.ai/mcp` ## Tools (as reported by the server, not verified) - `list\_projects`: List projects as \{ id, name, editorUrl \} entries. The id is what every other tool's \`projectId\` argument takes. \`name\` is the human-readable label shown in the editor's project picker \("Untitled" when no name has been set; ids are opaque and the editor never shows them\). \`editorUrl\` is a link that o - `list\_workspaces`: List the workspaces \(shared team spaces\) the account belongs to, as \{ id, name, role, memberCount \} entries. \`role\` is the caller's role in that workspace \(owner / admin / editor / viewer\) — only owner/admin/editor can add projects. The \`id\` is the \`workspaceId\` argument of create\_project, move\_proj - `create\_workspace`: Create a new workspace \(a shared team space\) owned by the caller, and return it as \{ id, name, role, memberCount \}. Use this when the user wants a new place to group projects — then move\_project\_to\_workspace files projects into it, and create\_project can place new ones there directly. The caller bec - `rename\_workspace`: Rename an existing workspace. Requires an owner or admin role in it \(the same roles that may change its membership\); an editor or viewer is refused. Takes the workspace id from list\_workspaces. Touches only the label — membership, projects and roles are unchanged. - `open\_project`: Get a link that opens a project directly in the editor \(the editor loads exactly this project from the URL\), where the person can see the work, change it by hand and export it. Returns \{ name, editorUrl \}; \`name\` is the project's label. - `show\_project`: Show the project in the chat as a live preview that plays and follows every edit; a multi-page project turns its pages in the preview. Call it once per project, right after creating or opening it; later edits appear in the preview without calling again. Answers \{ name, pages, placards, durationSecon - `create\_project`: Create a new project and return its server-minted id. The id is an opaque v4 UUID assigned automatically, not chosen by the caller; people see the project's \`name\`. By default this mints a fresh BLANK project \(empty canvas, no layers\) — creation is independent from cloning. Pass \`fromProjectId\` ONLY - `move\_project\_to\_workspace`: Move a project into a workspace, or back to the caller's personal space. Pass \`workspaceId\` \(from list\_workspaces\) to place the project in that workspace — every member then sees it, with their workspace role deciding edit vs view. Pass \`workspaceId: null\` to detach it back to personal. Requires an - `list\_collection`: List the user's reusable Collection — layers they added with add\_to\_collection \(lower-thirds, logo stings, brand intros, or any single layer\). Returns \{ items: \[\{ scope, workspaceId, sourceProjectId, sourceProjectName, ownerEmail, elementId, name, kind, childCount \}\] \}. \`scope\` is "personal" \(from t - `add\_from\_collection`: Add an item from the user's Collection into a project as a self-contained COPY. Pass the destination \`projectId\`, plus the \`sourceProjectId\` + \`elementId\` of an item from list\_collection. Copies that layer \(a whole group brings its children\) plus its image/clip/font bytes into the destination at fre - `duplicate\_project`: Duplicate an existing project into a brand-new one — a full copy of its layers, animations, styles, uploaded assets, and clips. Pass the SOURCE project's \`projectId\`; the copy gets a fresh id automatically \(ids are opaque; people see the name\). Defaults the copy's name to "\ copy" unless - `rename\_project`: Update a project's human-readable name \(shown in the editor's project picker\). Pass an empty string to clear the name and fall back to the id. Doesn't touch any layers, animations, or styles — only the picker label. - `reid\_project`: Change a project's id LOSSLESSLY — re-keys its JSON, every saved version, and all assets + clips, then deletes the old id. Unlike create\_project \(clone\), version history is preserved. Enforces the id policy: the new id is always a v4 UUID — pass \`newId\` to choose one, or omit to mint a fresh UUID. A - `delete\_project`: Move a project to Recently deleted. It leaves every project list at once and can be restored for 30 days, with restore\_project or from the person's projects page; after that it is deleted for good, with its saved versions, uploaded images and clips. Returns \{ name, purgeAt, restorable: true \}, where - `list\_trash`: List the projects in Recently deleted as \{ id, name, trashedAt, purgeAt, deletedBy, canEdit \} entries. Each can be restored with restore\_project until purgeAt \(epoch ms\), 30 days after it was deleted. \`deletedBy\` is \{ kind: "you" \| "person" \| "agent", label \}: the caller, a teammate \(label is their - `restore\_project`: Bring a project back from Recently deleted, with its versions, files, workspace and sharing as they were. Returns \{ projectId, name \}. Scheduled social posts cancelled by the delete stay cancelled. Requires edit access, as delete\_project does; take the id from list\_trash. - `save\_version`: Save a version of the current project state. The person flicks between versions in the editor to compare or roll back, so one version per logical change-set \(not one per tool call\) is what they can revisit. \`name\` is shown verbatim in the version picker, so a short description reads best \(e.g. "add - `list\_versions`: List every saved version of a project, newest-first. Returns summaries \(id, name, timestamp, source, kind, version\_number, cp\) — not the inner project JSON. \`kind\` is \`"bookmark"\` \(deliberate save, gets a v\ label, partner-pinnable in the \ embed\) or \`"auto"\` \(editor auto-snapshot eve - `restore\_version`: Load a saved version over the live project. Accepts either the version's UUID \`id\` or the \`v\\` shorthand \(e.g. \`"v6"\` finds the bookmark with version\_number=6\); auto-snapshots are addressable by UUID only. Without \`page\_index\` this replaces the ENTIRE project \(every page\) — an auto checkpoint name - `rename\_version`: Rename a saved version's human-readable label \(what the user sees in the picker\). The \`v\\` partner-facing identifier is unaffected — only the display name changes. Accepts the version's UUID \`id\` or the \`v\\` shorthand. Empty / whitespace-only names are rejected. - `delete\_version`: Permanently delete one saved version \(bookmark or auto-snapshot\). Accepts the version's UUID \`id\` or the \`v\\` shorthand. The bookmark v\ sequence keeps gaps — numbers never re-shuffle — so any external snippet referencing the deleted \`v\\` will fail to resolve afterwards. Idempotent: deleting a - `upload\_image`: Upload an image into a project from a public http\(s\) URL or a file attached in the chat, fetched server-side. Pass \`url\` for a public http\(s\) link, or \`file\` for a file the person attached in the chat \(a host that supports file inputs, such as ChatGPT, fills \`file\` with a temporary download link its - `upload\_audio`: Upload an audio track into a project from a public http\(s\) URL or a file attached in the chat, fetched server-side — the ONLY way to get an audio file's bytes into a project over MCP/HTTP \(add\_audio\_overlay / update\_audio\_overlay only reference a file that is already uploaded\). Pass \`url\` for a publ - `detect\_text\_regions`: Return the OCR text regions detected in a clip's video frames OR a still image asset. Pass either \`clip\` \(a video.\.clip filename\) or \`image\` \(an image layer's filename\) — not both. Video clips are sampled every 0.3s at upload time; images are OCR'd once. Results are cached in R2 next to the sour - `safe\_zones`: Return horizontal 'safe band' y-ranges of the canvas where a clip's burned-in video text \(subtitles, lower thirds, titles\) does NOT appear, plus the inverse 'text bands' \(where it does\). Reads the OCR cache built at clip-upload time \(see detect\_text\_regions\). Coordinates returned in BOTH source pixe - `transcribe\_clip`: Return the cached transcript of a clip's audio. Generated client-side in the Morpha editor \(transformers.js Whisper\) and cached in R2 next to the clip; if absent, it is produced when the clip is opened in the editor. Independent of the clip's video codec — runs on the audio track only, so it works o - `clip\_processing\_status`: Report whether a clip — or every video clip in the project, if \`clip\` is omitted — has been through the browser-side video-processing pipeline: proxy build, audio split, transcription, OCR \(text regions\). Processing never runs on the server. The preview proxy is mandatory: every processing run build - `render\_video`: Render the project to an MP4 on Morpha's servers, for an agent that has no browser to drive \(a chat app, a Linux box\). Returns \{ renderId, status: 'rendering', next \} — call render\_status with that renderId after about 30 seconds. It needs a subscription and draws on the account's render allowance, - `render\_status`: Check a server render started by render\_video, and get its download link. Returns \{ ok: true, status: 'not-ready' \} while it is still rendering \(wait and call again\), or \{ ok: true, status: 'ready', data: \{ downloadUrl, editorUrl, expiresAt, videoSeconds, scale \} \} once the MP4 is ready. \`downloadUr - `describe\_video`: Structural OVERVIEW of the composition \(the table of contents\) — canvas size, duration, platform\_safe\_area \(the part of the canvas TikTok / Reels UI and Instagram's feed crop leave visible: keep text, captions, logos and buttons inside it; null when nothing is covered or cropped\), the backdrop summa - `inspect\_layers`: Full per-element drill-in — the 'open this layer' half of the browser. Returns each named element's COMPLETE record: all of its own fields plus its animation tracks \(every keyframe\), colour/fill tracks, track-loop \(extrapolation\) modes, and style. Pass the elementIds you read from describe\_video; pu - `move\_layer`: Set a layer's static base transform. Writes x/y/w/h/rotation directly on image.\, video.\, and shapes.\; for group.\ sets pivotX/pivotY \(no width/height/rotation — use add\_keyframe for group rotation\). Also sets \`scale\` and \`opacity\`, which every layer kind carries. Note: when a layer ha - `set\_pivot`: Set the rotation / scale pivot anchor for an image, video, shape, or text leaf. Picks one of the 9 standard bbox anchors — corners, edge midpoints, or centre — so the layer rotates and scales around that point instead of its centre. The pivot is normalized to the bbox, so resizing the layer keeps th - `add\_keyframe`: Add or overwrite a keyframe on a layer's animation track. For leaves \(image/video/shape\), x/y/rotation keyframes are ABSOLUTE canvas-space values: x and y are the layer centre's pixel position \(canvas is 1080×1920\), rotation is degrees. For groups, x/y keyframes are translation offsets applied aroun - `set\_keyframes\_batch`: Add or overwrite MANY keyframes across MANY layers in ONE call — functionally equivalent to N add\_keyframe calls but with one HTTP/MCP round-trip. Pass an array of entries; each entry has the same fields as add\_keyframe. Validated atomically: any invalid entry rejects the whole batch. Use this whene - `add\_keyframes`: Add many keyframes to ONE element's ONE property in a single call, with an optional loop mode applied in the same call. The idiomatic form when every layer in a multi-element animation gets its own track \(ripple dot pulse, snowflake fall, equaliser-bar wave\). Factors elementId + property out of the - `remove\_keyframe`: Remove the keyframe at frame N on a layer's track. Removing the last keyframe from a track restores the layer's static base value across the timeline. - `shift\_track`: Bulk-shift every keyframe's VALUE on one property of one layer by \`delta\`. Mirrors the 'select all keyframes + nudge layer' gesture in After Effects / Premiere / FCP — preserves the relative spacing of the animation but slides the whole curve. Keyframe TIMES are untouched. Use for 'move all x by -30 - `set\_track\_loop`: Set the extrapolation mode for one property's animation track. Modes: "hold" \(default — holds the boundary keyframe's value past the ends\), "loop" \(wraps frames past the last keyframe back to the first, restarting the animation\), "ping-pong" \(alternates direction each cycle, bouncing back and forth\) - `add\_image\_layer`: Add an image layer. The image must already be uploaded: upload\_image takes a web URL or a file attached in the chat, and a file from the person's disk is uploaded in the editor. Every upload returns \{ filename, name, width, height \}: pass \`filename\` \(the stored file's id\) here, and \`name\` as the lay - `add\_video\_layer`: Add a video layer. The clip must already be uploaded; clips are uploaded in the MorphaReels editor. An upload returns \{ filename, name \}: pass \`filename\` \(the stored clip's id\) as \`clip\`, and \`name\` as the layer's label. To duplicate an existing layer, reuse its clip filename — the editor auto-assig - `add\_shape`: Add a shape layer. \`kind\` selects the primitive: rect \| ellipse \| triangle \| star \| pentagon \| hexagon \| arrow \| heart \| rounded-rect \| diamond \| parallelogram \| trapezoid \| semicircle \| ring \| pill \| cross \| heptagon \| octagon \| star-4 \| star-6 \| sparkle \| burst \| arrow-left \| double-arrow \| chevro - `add\_curve`: Draw an editable arrow / curved line — a stroked quadratic bezier with an arrowhead. Specify the two endpoints \(x1,y1\)→\(x2,y2\) in canvas pixels; \`bend\` pushes the midpoint perpendicular \(px, 0 = straight line, positive/negative curves either way\). \`color\` \#rrggbb, \`stroke\_width\` px, \`arrow\_head\` non - `duplicate\_layer`: Composition primitive: clone a leaf \(image.\ / video.\ / shapes.\ / text.\\) \`count\` times, applying a cumulative per-step transform — copy i is offset by i·\(dx,dy\) px, rotated by i·d\_rotation°, and scaled by d\_scale^i. One call instead of dozens: a circle of stars \(dx/dy + d\_rotation\), a - `remove\_layer`: Delete a video, image, text, or shape LEAF layer. Errors on a group.\ \(dissolve it with ungroup\_layers instead\) and on a pinned layer such as the canvas backdrop. Deleting a video layer also takes everything welded to that clip: its welded audio overlay\(s\) and its welded caption lines, both of wh - `reorder\_layer`: Move a layer within its parent's siblings. newIndex is 0-based among siblings \(root list when ungrouped, or the parent group's children when nested\). 0 = bottom of that subtree; last = top. - `set\_style`: Set style fields on a layer. Only the fields you pass are changed; omit a field to leave it untouched. Covers border/radius/shadow \(including borderAlign — inner\|center\|outer border position\) plus image-only fields: fit \(stretch\|cover\|contain\), anchorX/anchorY \(0..1, where the source anchors when cr - `set\_layer\_fill`: Set a layer's fill. The canvas backdrop is the pinned is\_background image\_layer \(its element id is exposed via describe\_video as background.elementId; the literal 'background.canvas' is also accepted as a synonym\); null is rejected on the backdrop. Shapes require a Fill \(null/missing is rejected\). I - `set\_text\_background`: Add or update the rounded background box behind a TEXT layer \(text.\\) in one call — sets the backdrop fill plus the box's padding, corner radius, and optional stroke. Pass only the fields you want to change. New text layers are already text\_autofit "hug", so padding alone shrink-wraps the box to - `set\_group\_box`: Set a group's backdrop rect size. The rect is centred on \(pivotX, pivotY\) in group-local space and transforms with the group. Either dimension at 0 hides the backdrop entirely. - `add\_color\_keyframe`: Add or overwrite a colour keyframe on a fill track. Targets a leaf \(shapes.\, image.\, video.\, group.\\). The canvas backdrop is the pinned is\_background image\_layer \(the literal 'background.canvas' is accepted as a synonym for its element id\). The value is a Fill — adjacent keyframes cr - `remove\_color\_keyframe`: Remove the colour keyframe at an exact frame on a fill track. No-op when no track or no matching keyframe exists. Removing the last keyframe drops the track entry. - `fade\_layer`: Fade a layer's opacity between two frames in one call. - `set\_layer\_visible`: Show or hide a layer instantly by writing a single opacity keyframe \(1 or 0\) at frame 0. - `apply\_preset`: Apply a canned animation preset to a layer. - `apply\_preset\_stagger`: Apply the same preset to a LIST of layers with a per-element startFrame offset — one call instead of N apply\_preset calls. For diagonal pop-in grids, sequential list reveals, ring-pulse sweeps. The startFrame for entry i is \`startFrame + i \* stagger\`. Order the elementIds in the visual order you wan - `group\_layers`: Wrap sibling elements in a new group, which the Layers panel shows as one named row the person can select, move and animate as one thing \(a butterfly assembled from wings + body + antennae, an icon built from primitives, a card + its title + badge, a lower-third\). \`name\` is that row's label. The gro - `ungroup\_layers`: Dissolve a group: its children are spliced into the group's parent at the group's old position. The group's animation tracks are discarded — children survive at their last positions but inherit none of the group's keyframes. - `set\_group\_parent`: Move an element into a group \(or out to root\). Refuses to place a group inside its own descendants. - `rename\_group`: Rename a group. Pure cosmetic — labels appear in the Inspector and describe\_video output. - `add\_to\_collection`: Add a layer to the user's reusable Collection. Pass ANY element id — a leaf \(text.\, image.\, …\) or a whole group.\ \(a lower-third, logo sting, brand intro\). It then appears in the user's Collection \(list\_collection\), where they — and, if this project is in a workspace, every teammate — can - `remove\_from\_collection`: Remove a layer from the user's Collection so it's no longer offered for reuse. Pass the element id that was added with add\_to\_collection. Copies already placed in other projects are unaffected \(they're self-contained\). No-op if the id isn't in the collection. - `embed\_project`: Embed another of the user's projects \("a morpha"\) inside this one as a version-pinned band. The source's layers are inlined into the host as a collapsible group, re-keyed to fresh ids, pinned to one immutable version of the source. Pass the source project's id as source\_morpha\_id \(and optionally a v - `add\_audio\_overlay`: Add an audio overlay \(mp3/m4a/wav/ogg\) scheduled at a frame-aligned start. The asset must already be uploaded \(upload\_audio, or the editor for a file from the person's disk\); pass the upload's returned \`filename\`, and its \`name\` as the track's label. 30 fps; convert seconds with frames = round\(s \* 3 - `remove\_audio\_overlay`: Delete an audio overlay by id. - `update\_audio\_overlay`: Patch an existing audio overlay. Only the fields you pass are changed. Pass endFrame:null to clear it \(revert to natural-length playback\). - `set\_video\_layer\_trim`: Patch a video layer's trim window: source\_in\_frame \(frame in source to start\), source\_out\_frame \(frame in source to stop, or null for natural end\), timeline\_start\_frame \(where on the project timeline the slice begins\). Only the fields you pass are changed. Use this to clip out a segment of a source - `set\_layer\_block`: Set \(or replace\) a layer's timeline BLOCK — the \[start, start+duration\) window it exists for. The layer is drawn ONLY inside that window, and its animation keyframes are sampled RELATIVE to the block start, so moving or trimming the block re-anchors its intro instead of leaving it behind. This is ho - `set\_layer\_transition`: Set how a layer ENTERS at the start of its on-timeline window and LEAVES at the end, instead of popping. The transition is EDGE-RELATIVE — only a length and a look are stored — so it rides the edge through every later trim, slide or clip retime. Prefer this over fade\_layer / apply\_preset whenever th - `set\_embed\_start`: Place an embedded morpha band on the host timeline: set its TIME ORIGIN \(the frame it starts\). The band's whole inner reel plays relative to this frame, so its intro animations fire when the band appears instead of at 0:00 \(the fix for 'the embedded intro doesn't animate'\). Keeps the band's current - `shift\_group`: MOVE a group and everything inside it along the timeline, keeping its internal timing intact — the 'slide this whole section later' operation. A plain group is a relative CONTAINER: it has no window of its own, so on the timeline it spans the hull of its contents, and moving it slides the whole subt - `set\_group\_window`: TRIM a group's own visible window — the \[start, start+duration\) range over which the group and its subtree are drawn. The contents are NOT moved or deleted: this clips what is shown, so use it to hide the head or tail of a whole section. Writing a window overrides the group's derived contents-hull f - `set\_duration`: Author an EXPLICIT composition length in seconds, pinning it \(duration\_authored=true\) so the auto-fit no longer drives it. Morpha normally DERIVES the comp length from content \(the furthest keyframe / video window / audio end\); this overrides that with a fixed length — the stage becomes a fixed canv - `fit\_duration\_to\_content`: Clear an authored composition length and return to AUTO-FIT — the comp length tracks the furthest content \(keyframe / video window / audio end\) again, with a 1-second floor. The inverse of set\_duration. NOTE: headless \(no loaded media\) this can UNDER-fit when a video layer's source\_out\_frame is null - `cut\_range`: Ripple-delete a time window \[startFrame, endFrame\) — remove that span and pull all later content earlier by delta = endFrame - startFrame \(the NLE 'ripple delete' / 'close gap'\). Shifts every keyframe, colour keyframe, marker, audio overlay, loop region, and start\_at through the cut \(a speed ramp is - `set\_embed\_origins`: Replace the project's embed allowlist — the hostnames permitted to load this project through the public \ embed. Pass the full desired list; it overwrites the previous one. An empty array turns embedding OFF \(the public embed endpoint 404s the project\). Each entry is normalized to a bar - `add\_embed\_origin`: Add one hostname to the project's embed allowlist \(the hostnames permitted to load the public \ embed\). Idempotent — re-adding an existing entry is a no-op. The origin is normalized to a bare lowercased hostname \(scheme/port/path stripped\). Morpha sends that website no request; any page - `remove\_embed\_origin`: Remove one hostname from the project's embed allowlist. Idempotent — removing an entry that isn't present is a no-op. Removing the last entry turns embedding OFF \(the public embed endpoint 404s the project\). Morpha sends that website no request; a page on it stops loading the project. Reference: htt - `set\_custom\_font`: Register a typeface Morpha does NOT ship, so text layers can use it by family name via font\_family \(exactly like a built-in family\). Families already in the built-in catalogs \(anything list\_fonts returns from google/bunny/fontshare/fontsource/velvetyne\) are REJECTED — they need no registration; just - `list\_fonts`: List available font families across every source the editor knows about \(Google + Bunny + Fontshare + Fontsource + Velvetyne\) PLUS the project's user-uploaded custom\_fonts \(surfaced as source: "custom"\). Use this to discover families before set\_layer\_text / add\_text\_layer when you don't know what to - `set\_layer\_text`: Edit an existing text layer \(text.\\). Patches its text content, font, size, colour, and full type styling — pass only the fields you want to change. Does NOT create layers and does NOT touch image layers; use add\_text\_layer to make a new one. \`font\_family\` is a Google Fonts family name \(e.g. "Ant - `add\_text\_layer`: Create a new text layer — a first-class leaf that animates, groups, and z-orders exactly like an image or shape. The renderer draws live typeset text \(multi-line, auto-fit to the box\). Defaults: x/y = canvas centre, width 900, height 320, font\_family "Hanken Grotesk", text\_size derived from existing - `add\_caption\_track`: Build a caption track from pre-timed lines \(e.g. derived from transcribe\_clip's word timings\). mode "line-sync" \(default\) creates one text layer per line, each shown only during its \[startFrame, endFrame\) window via hold-eased opacity keyframes — the active-line karaoke read; mode "static" makes a s - `split\_caption\_line`: Split one caption line into two at a COMPOSITION frame strictly inside its window \(in the editor this is the playhead\). The right half is a full clone — style, band geometry, weld — and the text divides at the word gap nearest the split point \(a single-word line keeps its text on the left; the right - `merge\_caption\_lines`: Merge two or more caption lines into one. The earliest line survives with the union window and the time-ordered texts joined by spaces; the others are removed. All lines must share one flavour — every one welded to the SAME clip, or every one standalone — and no other caption line on that track may - `rename\_layer`: Set the human-readable name of a video / image / shape / text layer — the label shown in the Inspector, and the basis for the layer's auto-derived \ embed attribute \(so renaming a layer to "caption" makes the embed attribute \`caption\`\). Pass an empty string to clear the name. For groups - `set\_loop`: Set the project's loop section: the whole composition repeats once per value, with one field of one layer varying across the repeats. Builds one pass per value, each setting \`field\` of \`elementId\` to that value — e.g. a caption text layer cycling through several strings. Pass an empty \`values\` array - `set\_canvas\_size`: Resize the ACTIVE page's canvas to width × height pixels. The composition is scaled UNIFORMLY to fit the new frame \(a single factor s = min\(newW/oldW, newH/oldH\), so nothing distorts — a circle stays a circle\) and then re-centred so the old composition centre maps to the new canvas centre. Every lay - `set\_image\_filename`: Repoint an existing image layer at a different uploaded asset — keeps the layer's id, position, size, animations, and styles; only the bitmap changes. The image must already be uploaded \(upload\_image for a web URL or a file attached in the chat; the editor for a file from the person's disk\); pass th - `set\_video\_clip`: Repoint an existing video layer at a different uploaded clip — keeps the layer's id, position, size, animations, styles, and trim window; only the source mp4 changes. The clip must already be uploaded \(the editor's Add video\); pass the \`filename\` the upload returned, and its \`name\` as the label. Use - `set\_matte\_source`: Set \(or clear\) a track matte — the host shows only where the matte source is opaque. The HOST can be a leaf \(image.\, video.\, shapes.\, text.\\) OR a group.\ \(a group is a layer of sorts\): a group host clips ALL its composited children to the source shape's path — e.g. a marching chev - `freeze\_frame`: Freeze the picture at a frame: the clip is CUT there and a still of that frame is inserted between the halves, pushing everything after it later \(the NLE 'frame hold'\). This is how you hold a moment — play, freeze, continue. The still is an ordinary IMAGE layer showing that frame for \`holdFrames\` \(d - `set\_clip\_speed`: Play a clip slower or faster at a CONSTANT rate — the normal way to retime a clip. 1 = source speed, 0.5 = half speed, 2 = double speed; range \[0.1, 8\]. The trim is unchanged, so the clip's length on the timeline changes to suit: at 0.5x it occupies twice as many frames, at 2x half as many. Audio is - `add\_speed\_keyframe`: Add or overwrite a speed-ramp \(time-remap\) keyframe on a video layer, for a rate that CHANGES over the clip. \`frame\` is a PROJECT-timeline frame and must sit on the clip \(at or after its timeline\_start\_frame\); the curve itself is anchored to the clip, so moving the clip carries the ramp with it and - `remove\_speed\_keyframe`: Remove the speed-ramp keyframe at \`frame\` \(a PROJECT-timeline frame — the same value add\_speed\_keyframe and inspect\_layers report\) on a video layer. Removing the last keyframe clears the speed\_keyframes array entirely \(restoring 1× playback\). - `set\_video\_layer\_muted`: Mute or unmute a video layer's baked audio \(silenced in both preview and export\). The processing pipeline's audio-split step sets this true after demuxing the clip's audio into a standalone overlay track \(NLE-style linked A/V\), so the source audio doesn't double with the overlay. Pass muted:false to - `add\_page`: Append a page to the project — works on any project, turning a single-page video into a multi-page one. Without duplicate\_index a blank page is appended, sized to the project's canvas. With duplicate\_index the page at that position is deep-copied \(a fresh id is minted\). There is no limit on page cou - `delete\_page`: Remove the page at \`index\`. Fails on an out-of-range index or when only one page remains — a project must keep at least one page. The active page stays active; when the active page itself is deleted, active\_index falls to the neighbouring page \(the one that slid into its position, or the new last pa - `reorder\_pages`: Move a page from \`from\_index\` to \`to\_index\`. The remaining pages shift to fill the gap; active\_index is rewritten so it keeps pointing at the same page it did before the move. Fails on out-of-range indices. - `select\_page`: Switch which page is ACTIVE — the page the content tools target. Subsequent describe\_video / inspect\_layers / all content tools read and write this page until the active page changes again. Pages are addressed by 0-based index from describe\_video's pages block. Selecting the already-active page is a Measured by MCP Station. Methods: https://mcpstation.net/methods.html · Terms: https://mcpstation.net/terms.html