frapea
Registry code: 8d3261c98b838fef
Frapea is a video editor that runs in the user's browser; their footage never leaves their machine. Start with connect: if Frapea is already open, ask them for the pairing code (the Connect MCP button); if not, call connect with no arguments and send them the link it gives you. Then pass the token on every call. read_skill {topic:'editing'} explains how to cut; motion compositions have their own guides (read_skill {topic:'motion'}) — aim well beyond a PowerPoint of titles and bullets, and build UI-like parts on a parts board first, checked in one large frame. Media itself never travels over…
- endpoint
- https://app.frapea.com/mcp
- protocol
- streamable-http ·2025-06-18
- authentication
- none observed
- public key
- none — nobody has proven they own this listing · is it yours? claim it
- karma
- 0 · newcomer
- Is frapea live?
- Yes — it answered the hub's last check (checked 1h ago). It answered 100% of checks over the last 30 days.
- Is frapea free to use?
- No — it asks for a key or a login before it will serve.
- What tools does frapea have?
- 62 tools: create_motion_composition, new_project, track_faces, remove_clips, undo, read_skill, close_project, list_motion_compositions, ….
- Is frapea safe to connect?
- The hub found no text in its card or tool descriptions aimed at the agent reading them. It measures what the server answers, not its code — grant it only the access its tools need.
90 days 100%· all time 100%
last good check
of 62 tools
- unknown → live
Calls placed through this hub's router, from its own receipts. Every caller and every payer counts the same; the chain total is counted from three payers.
through this hub
successful
what callers paid
Price is per tool, not per server. An agent whose handshake is open can hold tools that demand a key or a payment, and one figure for the whole agent sends callers into a wall.
create_motion_composition auth-required never probed
Creates a MOTION COMPOSITION — an animated graphic (infographic, title, promo end-card, chart…) written as a React TSX component. Read the 'motion' skill (read_skill {topic:'motion'}) for the exact API surface, available fonts/icons and golden examples before writing one. files maps file names to TSX source; the entry must `export default` the component. schema declares the editable props (type string|number|color|boolean|select|mediaRef + default) — they become Inspector controls the user can tweak, so parameterize colors, copy and media slots. The composition is compiled and test-rendered BEFORE saving; on failure the error returns verbatim — fix the code and retry. Returns the mediaRef to place with add_clips. Unless asked for plain, aim well beyond a PowerPoint of titles and bullets — the story, its moments and the look are yours to decide. With UI-like parts (buttons, pills, progress bars, cards), write them in their own file with a partsBoard prop (default false) and look at the board first: preview:true, previewProps:{partsBoard:true} returns one large frame with the reply.
{ "type": "object", "required": [ "projectId", "name", "files", "token" ], "properties": { "fps": { "type": "number", "description": "Composition fps (default 30)." }, "name": { "type": "string", "description": "Display name, e.g. 'Pink promo end card'." }, "entry": { "type": "string", "description": "Entry file (default composition.tsx); must export default." }, "files": { "type": "object", "description": "File name → TSX source. Relative imports between them work." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "width": { "type": "number", "description": "Composition width px (default 1920)." }, "height": { "type": "number", "description": "Composition height px (default 1080)." }, "schema": { "type": "object", "description": "Editable props: {key: {type, default, label?, min?, max?, options?, multiline?}}. type mediaRef makes a media-pool slot." }, "preview": { "type": "boolean", "description": "Return one large frame (≤1560 px) with the reply. Media is not loaded in a preview: media slots draw empty and the reply says how many." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "previewFrame": { "type": "number", "description": "Frame to preview (default 0)." }, "previewProps": { "type": "object", "description": "Merged over the schema defaults for the preview picture only, e.g. {partsBoard:true}. Nothing saved changes." }, "durationInFrames": { "type": "number", "description": "Length in frames (default 150)." } } }arguments 63 linesnew_project auth-required never probed
Creates a project. By default it is created in the browser's own storage, which needs no permission and no click: the project id comes back immediately and you can start working. The user can turn it into a real folder on their disk whenever they want (Cmd/Ctrl+S, or File → Save to a Folder) without losing anything. Pass storage:'folder' only when the user asked for a folder up front — that needs a browser security gesture, so it opens a dialog in Frapea with a 'Choose folder' button, returns an actionId, and you must tell the user IN YOUR REPLY to click it, then call await_user_action; on 'granted' the project appears in get_projects.
{ "type": "object", "required": [ "name", "token" ], "properties": { "name": { "type": "string", "description": "Project name, e.g. from the user's brief." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "storage": { "enum": [ "browser", "folder" ], "type": "string", "description": "Where the project lives. 'browser' (default) needs no user action. 'folder' asks the user to pick a directory on their disk." } } }arguments 25 linestrack_faces auth-required never probed
Finds every FACE in a video clip and stores one independent path per person — position, size and head rotation (nod/turn/tilt) per frame. Queued, not immediate: poll list_face_tracks until state is 'ready'. Re-running on the same clip REPLACES its previous run and keeps the same trackId, so clips already bound stay bound. A person who leaves the shot and returns much later becomes a SECOND face on purpose — use merge_faces if they are the same person.
{ "type": "object", "required": [ "projectId", "clipId", "token" ], "properties": { "name": { "type": "string", "description": "Display name for the run." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "The video clip to scan, from get_timeline." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "holdSeconds": { "type": "number", "description": "How long a vanished face is still 'the same face' when it comes back (default 1)." }, "travelFaces": { "type": "number", "description": "How far it may reappear from where it vanished, in face widths (default 0.8)." }, "scoreThreshold": { "type": "number", "description": "Detection confidence floor, 0.3–0.95 (default 0.6)." } } }arguments 38 linesremove_clips auth-required never probed
Deletes clips (lift — leaves a gap). Linked partners are NOT removed automatically; pass both ids of a pair to delete it whole. To close the gap too, use ripple_delete_ranges instead.
{ "type": "object", "required": [ "projectId", "timelineId", "clipIds", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipIds": { "type": "array", "description": "Clip ids to delete." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clips." } } }arguments 27 linesundo auth-required never probed
Reverts the most recent edit THIS agent session made to the project (one step per call; repeat to go further). Cannot revert other tabs' or the user's edits.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesread_skill auth-required never probed
Returns Frapea's agent guide: editing conventions, tool workflow tips, the current feature surface, and the CREATIVE MANDATE — how far to run with a terse brief (source stock footage, sounds, voiceover, motion graphics and deliver a finished video vs. when to hand control back to the user). Read once at the start of an editing session. MOTION topics: 'motion' is the API surface (imports, fonts, props schema, the parts-board-first golden example) and routes to the rest — read before create_motion_composition; 'motion-examples' (two finished pieces that differ in look); 'motion-craft' (anticipation, overshoot, stagger, the tells of machine-made motion — read with 'motion', always); 'motion-text' (size floors, fitting copy that cannot clip, per-word animation, annotations); 'motion-effects' (grain, duotone, light leaks, blurs, glow); 'motion-transitions' (TransitionSeries, presentations, cut overlays); 'motion-sound' (where sound lives, the built-in SFX pack, placement and levels); 'motion-review' (the same loop as 'review', from a motion job). The CRAFT guides teach how to actually cut: 'core' (rhythm, b-roll, audio hierarchy, restraint — read first), then the format you are making — 'shorts' | 'longform' | 'podcast' | 'tutorial' | 'montage'. Also 'voiceover' (read BEFORE generate_voiceover: narration is generated in segments, not one block) and 'verify' (how to prove the cut is right — read before every export), and 'review' (for any piece with an audience: an opponent argues with your rendered frames in rounds until it is finished — read before you start).
{ "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "topic": { "type": "string", "description": "Optional. Motion: motion | motion-examples | motion-craft | motion-text | motion-effects | motion-transitions | motion-sound | motion-review. Craft: core | shorts | longform | podcast | tutorial | montage | voiceover | verify | review." } } }arguments 16 linesclose_project auth-required never probed
Navigates the user's Frapea tab back to the project manager and releases this session's headless runtime for the project.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 lineslist_motion_compositions auth-required never probed
Lists the project's motion compositions: id, mediaRef (for add_clips), name, size, fps, duration and the editable props schema.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesget_analysis_status auth-required 1h ago
How far the project's media analysis has got. Analysis runs BY ITSELF whenever media is connected — never start it. Returns percent (0-100) — which reaching 100 only means nothing is OUTSTANDING, so always read `failed` beside it and call retry_analysis on anything that failed — done/total/pending item counts, runningItem {mediaRef, kind, progress}, etaSeconds (null until something has finished), blockedOnModels, and models {visual, speech}. Poll this while you wait; when blockedOnModels is above zero, call request_ai_models.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesget_bug_report auth-required 1h ago
Prepare a BUG REPORT for the user: it opens in Frapea (Problems → Create bug report) for them to read and save. The report itself is NOT returned to you — it would leave the user's machine before they have seen it. You get a summary: open problems, media counts, and how many of the user's own words remain in composition code. It contains versions, problems with engine details, the project's structure with names and texts replaced, provider files by public id, the user's own files only as encoding fingerprints, and motion code. timelineIds limits it to those timelines and what they nest.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects (must be open in the editor)." }, "timelineIds": { "type": "array", "description": "Only these timelines and the timelines they nest (default: all)." } } }arguments 21 linesget_job_status auth-required 1h ago
Status of a background job started by a job-returning tool (generate_voiceover, verify_timeline): {state: queued|running|done|failed, progress: 0-1, detail, ...result fields such as mediaRef when done; verify_timeline puts its report in `result`}. Poll every few seconds while state is 'queued' or 'running' (queued = waiting for the analysis master tab, which loads the models once for the whole browser). For analysis started by start_transcription or start_visual_indexing, poll get_analysis_status instead.
{ "type": "object", "required": [ "jobId", "token" ], "properties": { "jobId": { "type": "string", "description": "Id returned by the start_* tool." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." } } }arguments 17 linesconnect auth-required never probed
Pairs you with the user's Frapea browser tab. TWO WAYS ROUND. (1) If they already have Frapea open: ask them for the pairing code (the Connect MCP button) and call this with it. (2) If they do not, or you are not sure: call this with NO arguments and you get back a link — send them the link, then call this again with the code it contains every few seconds until it returns a token (they have to accept it in their browser first). Either way you end up with a session token to pass on every later call. Codes are single-use and expire in ten minutes; a token lasts until the user disconnects.
{ "type": "object", "properties": { "code": { "type": "string", "description": "The 8-character code — from the user's screen, or from the link this tool gave you. Omit it to get a link to send them." } } }arguments 9 linesget_projects auth-required never probed
Lists the projects Frapea knows on this machine: id, name, lastOpenedAt, and for a project stored in a folder on disk its `folder` (the directory's name — files you write into its `media/` subfolder join the pool by themselves). Call first to find the projectId every other tool needs. A project the browser has no permission for will surface PERMISSION_REQUIRED when used — relay the returned instruction to the user, then retry. ALSO RETURNS `providers` — Pexels (stock video/photos), Freesound (music, ambience, SFX) and ElevenLabs (voiceover), each 'connected' | 'checking' | 'missing' | 'invalid'. These are the user's OWN API keys and they cannot be set from here. When any is missing, `providersAdvice` is present and carries the exact thing to say. SAY IT ONCE, in your first visible reply of the session, and then get on with the work you can do — the user cannot see tool calls, so silence here reads as the editor simply not being able to fetch b-roll or narrate a script. When everything is connected there is no `providersAdvice` and you say NOTHING about it: telling someone to connect what they already connected is nagging. SAY IT AGAIN ONLY WHEN IT IS SOMETHING NEW. The rule is one general nudge at the start, and after that only a CONCRETE consequence for the piece in hand — "I can cut what you shot, but with Pexels connected I'd cover the gaps at 0:14 and 0:37 instead of holding on a static frame" is new information and worth saying once before you build; "remember you have no Pexels key" is nagging. Never as a reminder, never twice for the same reason. SEPARATELY: when a CALL actually fails with PERMISSION_REQUIRED, always relay that error — it is a thing that just went wrong, not a repeat of the nudge, and swallowing it makes a failed fetch look like a choice you made.
{ "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." } } }arguments 12 linesget_timeline auth-required never probed
Reads a project's timeline structure: timelines (id, name, fps, resolution) with tracks and clips. THE USER CAN COPY A CLIP'S ID from the editor's right-click menu, so a bare UUID in their message names exactly one clip — call this with NO timelineId to search every timeline and match `clip.id`, rather than guessing from a name. Clips carry id, name, mediaRef, kind, startFrame, endFrame (exclusive), trimStartFrame and linkGroupId (linked A/V pairs share it), plus `look` (opacity, blend, transform, crop, keyframes) whenever any of it differs from the defaults — an untouched clip has no `look` key. Omit timelineId to get every timeline; frames are in that timeline's fps.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Optional single timeline to read." } } }arguments 21 linesget_media auth-required never probed
Lists the project's media library: mediaRef (the id clips use), name, kind (video/audio/image), durationSeconds, resolution, and sceneSamples — timestamps (seconds) where the visual indexer detected distinct moments. sceneSamples is a ready-made shot map: inspect or cut at those times first. Each asset also carries visualIndex and transcript objects: {phase: 'ready'|'pending'|'queued'|'running'|'failed'|'no-audio'|'disabled', error, attempts, sealed}. Analysis starts by itself and retries a failure a few times before sealing it. If a phase is 'failed', read `error` and decide: call retry_analysis to run just that one analysis again, or leave it. If 'disabled', call request_ai_models.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesget_transcript auth-required never probed
Returns the word-level speech transcript of one media asset: language and words [{text, startSec, endSec}]. Only assets the user has transcribed have one — NOT_FOUND means ask the user to run Transcribe on the clip in Frapea. Use the timestamps to align cuts with what is being said.
{ "type": "object", "required": [ "projectId", "mediaRef", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "mediaRef": { "type": "string", "description": "Asset reference from get_media." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 22 linesupdate_motion_composition auth-required never probed
Updates a motion composition's TSX files and/or manifest (name, duration, schema…). Same compile-and-test gate as create; files given here MERGE over the existing set. Every clip playing the composition re-renders. preview:true returns one large frame of the result with the reply.
{ "type": "object", "required": [ "projectId", "compositionId", "token" ], "properties": { "fps": { "type": "number" }, "name": { "type": "string" }, "files": { "type": "object", "description": "File name → TSX source (merged)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "width": { "type": "number" }, "height": { "type": "number" }, "schema": { "type": "object" }, "preview": { "type": "boolean", "description": "Return one large frame (≤1560 px) with the reply. Media is not loaded in a preview: media slots draw empty and the reply says how many." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "previewFrame": { "type": "number", "description": "Frame to preview (default 0)." }, "previewProps": { "type": "object", "description": "Merged over the schema defaults for the preview picture only, e.g. {partsBoard:true}. Nothing saved changes." }, "compositionId": { "type": "string", "description": "From create/list_motion_compositions." }, "durationInFrames": { "type": "number" } } }arguments 56 linesget_motion_composition auth-required never probed
Returns a motion composition's manifest and full TSX sources.
{ "type": "object", "required": [ "projectId", "compositionId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "compositionId": { "type": "string", "description": "Composition id." } } }arguments 22 linescreate_timeline auth-required never probed
Creates a new timeline in the project and returns its id. Set fps, width and height for the target format (e.g. 1080×1920 @30 for a vertical short). The new timeline starts with one video and one audio track; spare tracks appear automatically as clips land.
{ "type": "object", "required": [ "projectId", "name", "token" ], "properties": { "fps": { "type": "number", "description": "Optional frames per second (default 30)." }, "name": { "type": "string", "description": "Display name, e.g. 'TikTok short'." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "width": { "type": "number", "description": "Optional width in px (default 1920)." }, "height": { "type": "number", "description": "Optional height in px (default 1080)." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 34 linesadd_clips auth-required never probed
Places media segments on a timeline. Each clip: mediaRef (from get_media, 'timeline:<id>' to NEST another timeline as a clip, 'solid:#rrggbb' , 'motion:<id>' for a MOTION composition (create_motion_composition) for a generated solid-colour matte, 'adjustment:' for an adjustment clip — no picture of its own, its effects/masks/opacity grade everything below it — or 'text:' for a title, restyled via set_clip_properties' `text`), atFrame (timeline position), sourceInSec/sourceOutSec (the segment of the source to use — align these with transcript words or sceneSamples), and optional trackId (from get_timeline) to target a specific same-kind track — without it clips land on V1/A1 with overwrite semantics. A spare empty track always exists above the highest used one, so V2/A2 is available before anything sits on it. Video sources with audio get a linked audio clip automatically (on the matching A track: V2 ⇒ A2). Returns the created clip ids in order. Frames are in the target timeline's fps.
{ "type": "object", "required": [ "projectId", "timelineId", "clips", "token" ], "properties": { "clips": { "type": "array", "description": "[{mediaRef, atFrame, sourceInSec, sourceOutSec, trackId?}] — segments in playback order; trackId targets a specific track (default V1/A1)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline id from get_timeline." } } }arguments 27 linessplit_clips auth-required never probed
Blades clips at a timeline frame. Splits every listed clip (and its linked partner) into two independent clips at atFrame. Frames are in the target timeline's fps. Use with get_timeline to find clip ids first.
{ "type": "object", "required": [ "projectId", "timelineId", "clipIds", "atFrame", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "atFrame": { "type": "number", "description": "Timeline frame to cut at (must lie inside each clip)." }, "clipIds": { "type": "array", "description": "Clip ids to split." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clips." } } }arguments 32 linesripple_delete_ranges auth-required never probed
Extracts [fromFrame, toFrame) on one track and shifts everything after it left to close the gap (sync-locked tracks follow). The go-to tool for tightening a cut or removing a bad take.
{ "type": "object", "required": [ "projectId", "timelineId", "trackId", "fromFrame", "toFrame", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "toFrame": { "type": "number", "description": "Range end (exclusive)." }, "trackId": { "type": "string", "description": "Track id from get_timeline." }, "fromFrame": { "type": "number", "description": "Range start (inclusive)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." } } }arguments 37 linesset_clip_properties auth-required never probed
Adjusts a clip. Only the provided fields change. Mix: volume (0–2, 1 = unity), fadeInFrames / fadeOutFrames (gain or opacity ramps at the edges). Look: opacity (0–1), blend (normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | add), transform, crop and speed. transform.fit picks how the picture meets the frame before scaling: contain (letterbox, default) | cover (fill + crop overflow) | fill (stretch, ignores aspect). Identity = contain fit; position is offset from centre in timeline pixels (y down), scale multiplies the fitted size, rotationDeg is clockwise about the anchor (a fraction of the cropped picture, 0.5/0.5 = centre). Crop takes fractions off each edge of the source. Retiming: `speed` (constant rate/direction) and `speedKeyframes` (source-anchored rate curve; reflows length). Shape masks: `masks` (full-list replace). `disabled` parks a clip (it stays but neither renders nor sounds — DaVinci's D). `text` restyles text clips (mediaRef 'text:'). `motion` customizes MOTION clips (mediaRef 'motion:<id>') per clip.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "token" ], "properties": { "crop": { "type": "object", "properties": { "top": { "type": "number" }, "left": { "type": "number" }, "right": { "type": "number" }, "bottom": { "type": "number" } }, "description": "Optional partial crop, fractions 0–1 of the SOURCE off each edge (the inspector shows source pixels). Crop is a window: it cuts pixels away without moving or re-fitting the picture; edges may meet (the picture vanishes)." }, "text": { "type": "object", "properties": { "box": { "type": "object", "properties": { "h": { "type": "number" }, "w": { "type": "number" }, "x": { "type": "number" }, "y": { "type": "number" } } }, "bold": { "type": "boolean" }, "align": { "type": "string" }, "color": { "type": "string" }, "italic": { "type": "boolean" }, "content": { "type": "string" }, "animation": { "type": "object", "properties": { "type": { "type": "string" }, "perWordFrames": { "type": "number", "description": "Frames each word takes to enter (word-timed styles)." }, "highlightColor": { "type": "string", "description": "#rrggbb the highlight styles paint the active word with." } }, "description": "Text animation. type: none | fadeIn | popIn | slideUp | typewriter | wordReveal | wordSlide | wordPop | wordCycle | highlightPop | highlightBlock. Word-timed styles (word*/highlight*) follow caption word timings when present, else perWordFrames. Partial patch — {type} alone keeps the other values." }, "fontFamily": { "type": "string" }, "outlineColor": { "type": "string", "description": "#rrggbb; empty string = no outline." }, "outlineWidth": { "type": "number" }, "sizeFraction": { "type": "number" }, "backgroundFill": { "type": "string", "description": "#rrggbb[aa]; empty string = no fill." }, "wholeCaptionGroup": { "type": "boolean", "description": "Restyle the clip's whole caption group, not just this clip." } }, "description": "Optional TEXT clip restyle (clips with mediaRef 'text:'; create them with add_clips). Partial patch: content (\n breaks lines), fontFamily (Inter | DM Sans | Space Grotesk | Bebas Neue | Playfair Display | Caveat), sizeFraction (of frame height), bold, italic, color (#rrggbb), align (left|center|right), outlineColor (#rrggbb or null), outlineWidth, backgroundFill (#rrggbb[aa] or null), box {x,y,w,h in frame fractions, centre-based}, animation. wholeCaptionGroup: true restyles every caption sharing the clip's caption group in one edit (content stays per-clip)." }, "blend": { "type": "string", "description": "Optional blend mode (see description)." }, "masks": { "type": "array", "items": { "type": "object", "required": [ "shape" ], "properties": { "size": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } }, "shape": { "type": "string", "description": "rect | ellipse" }, "center": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } }, "invert": { "type": "boolean" }, "feather": { "type": "number" }, "opacity": { "type": "number" }, "rotationDeg": { "type": "number" } } }, "description": "Optional shape masks, FULL-LIST replace (empty array removes all). Masks UNION (Resolve's additive Power Windows); `invert` flips one mask's coverage. Geometry in fractions of the SOURCE picture, centre 0.5/0.5 — masks ride the clip's transform. Each: {shape: rect|ellipse, center{x,y}, size{x,y}, rotationDeg, feather (fraction of source width), opacity 0–1, invert}." }, "speed": { "type": "object", "properties": { "rate": { "type": "number" }, "direction": { "type": "string", "description": "forward | reverse | freeze" }, "freezeFrame": { "type": "number" }, "pitchCorrection": { "type": "boolean" } }, "description": "Optional retiming (Resolve's Speed Change). rate 1 = 100 % (0.01–50); direction forward | reverse | freeze; freezeFrame = held source frame (offset from trim-in) while frozen; pitchCorrection keeps the audio's pitch. Keyframes stay glued to the pictures (they are source-anchored). Combine with speedRipple: true to rescale the clip so the same source range plays at the new rate and shift everything downstream." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "bypass": { "type": "object", "properties": { "crop": { "type": "boolean" }, "lens": { "type": "boolean" }, "masks": { "type": "boolean" }, "speed": { "type": "boolean" }, "denoise": { "type": "boolean" }, "composite": { "type": "boolean" }, "stabilize": { "type": "boolean" }, "transform": { "type": "boolean" } }, "description": "Optional section switches, true = section OFF (its values are kept but not applied) — the inspector's per-section toggles." }, "clipId": { "type": "string", "description": "Clip to modify." }, "motion": { "type": "object", "properties": { "props": { "type": "object", "description": "Prop values keyed by the composition schema's keys." } }, "description": "Optional MOTION clip customization (clips with mediaRef 'motion:<id>'). props: partial patch over the composition's schema-declared props ({key: value}; a key set to null resets it to the composition default). get_motion_composition lists the schema; per-clip values override its defaults." }, "volume": { "type": "number", "description": "Optional new volume (0–2)." }, "denoise": { "type": "object", "properties": { "mix": { "type": "number", "description": "Wet share 0-1 (default 0.6)." } }, "description": "AI voice denoise (dry/wet). Setting it (or enabling the section) queues the cached background render for the clip's source file." }, "opacity": { "type": "number", "description": "Optional static opacity 0–1." }, "disabled": { "type": "boolean", "description": "Optional. True disables the clip (and its A/V link partners): it stays on the timeline but neither renders nor sounds. False re-enables. The multicam angle switch is built on this." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "transform": { "type": "object", "properties": { "fit": { "type": "string", "description": "contain | cover | fill" }, "yaw": { "type": "number", "description": "3D turn, same units; positive brings the left edge forward and pushes the right away." }, "flipH": { "type": "boolean" }, "flipV": { "type": "boolean" }, "pitch": { "type": "number", "description": "3D tilt, Resolve units −1.5…1.5: a projective keystone — the near edge stretches toward the camera, the far edge shrinks but never folds edge-on. Positive tips the top away." }, "scale": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } }, "anchor": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } }, "position": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } }, "rotationDeg": { "type": "number" } }, "description": "Optional partial transform; provided keys replace, others stay." }, "cropRetain": { "type": "boolean", "description": "Optional. True = Resolve's 'Retain Image Position': the crop window stays fixed in the frame while transforms move the picture underneath it." }, "solidColor": { "type": "string", "description": "Optional, solid-colour matte clips only: recolour the matte (#rrggbb). Mattes are created by add_clips/insert_clips with mediaRef 'solid:#rrggbb'." }, "timelineId": { "type": "string", "description": "Timeline containing the clip." }, "dynamicZoom": { "type": "object", "properties": { "end": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" }, "scale": { "type": "number" } } }, "ease": { "type": "string", "description": "linear | ease-in | ease-out | ease-in-out" }, "start": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" }, "scale": { "type": "number" } } }, "enabled": { "type": "boolean" } }, "description": "Optional Ken Burns move (Resolve's Dynamic Zoom): animates the framing from `start` to `end` over the clip's WHOLE duration (retimes when the clip is trimmed). Rects: centre x/y as picture fractions (0.5/0.5 = middle) and scale (1 = whole picture, 0.5 = half → 2× zoom). Setting framing or ease switches it on." }, "speedRipple": { "type": "boolean", "description": "Ripple Timeline for this speed edit (see `speed`)." }, "cropSoftness": { "type": "number", "description": "Optional crop-edge feather, fraction of the source width 0–0.5 (the inspector shows source pixels). Keyframable as 'cropSoftness'." }, "fadeInFrames": { "type": "number", "description": "Optional fade-in length in frames." }, "fadeOutFrames": { "type": "number", "description": "Optional fade-out length in frames." }, "stabilization": { "type": "object", "properties": { "mode": { "type": "string", "description": "perspective | similarity | translation" }, "zoom": { "type": "boolean" }, "smooth": { "type": "number" }, "strength": { "type": "number" }, "cameraLock": { "type": "boolean" }, "croppingRatio": { "type": "number" } }, "description": "Optional stabilization knobs (Resolve's Stabilization). The expensive motion tracking is a per-media analysis — run it with the stabilizer; these values post-process its cached path instantly. mode perspective | similarity | translation; cameraLock kills ALL motion (ignores ratio/smooth); zoom scales up to hide blanking; croppingRatio 0.25–1 (1 allows no correction); smooth 0.25–1; strength -1–1 (negative inverts)." }, "lensDistortion": { "type": "number", "description": "Optional Lens Correction Distortion, -1–1. Positive straightens wide-angle barrel distortion, negative adds it. 0 = none." }, "speedKeyframes": { "type": "array", "items": { "type": "object", "properties": { "rate": { "type": "number" }, "atSourceFrame": { "type": "number" } } }, "description": "Optional source-anchored speed keyframes (forward-direction clips only): [{atSourceFrame, rate}], rate 1 = 100 %. Rate is CONSTANT between points (held outside). Present, it replaces `speed.rate` AND REFLOWS the clip's timeline length to keep the same source window (slower ⇒ longer); the A/V link group retimes together. EMPTY array clears the curve (back to the constant rate)." } } }arguments 425 linesgenerate_captions auth-required never probed
Generates animated captions from a spoken clip's transcript (the same generator as Frapea's UI): transcript words group into caption text clips over the clip's span, word-timed so the animation follows speech. One undo, and they share a captionGroupId — restyle them all via set_clip_properties with text.wholeCaptionGroup. Needs a transcript (get_transcript tells you). Returns the created clip ids. PLACEMENT: they JOIN the existing caption track whenever they fit in its gaps, and only open a new one when they would overlap (a second language, a second layer). So captioning four narration segments in four calls leaves ONE caption track, not four — no tidying needed afterwards for this. Call manage_tracks {action:'tidy'} at the end of the build for the empty tracks left by everything else. They join the LOWEST caption track, so a picture layer you add ABOVE it after an earlier pass will cover them — captions render behind b-roll rather than looking wrong, which is the hardest kind of mistake to notice. Move the whole lane with manage_tracks {action:'merge'} if you want the captions on top.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "The spoken clip to caption." }, "preset": { "type": "string", "description": "Style preset: clean (default) | boldPop | karaoke | highlight | minimal | editorial." }, "position": { "type": "string", "description": "bottom (default) | top." }, "textCase": { "type": "string", "description": "original (default) | upper | lower." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the spoken clip." }, "censorProfanity": { "type": "boolean", "description": "Star out profane words." }, "maxWordsPerCaption": { "type": "number", "description": "Words per caption before a new one starts (default 4)." } } }arguments 47 linesmanage_effects auth-required never probed
Edits a clip's effect stack (ordered, per-effect enable). action: 'add' (type; returns the new effect's id) | 'remove' (effectId) | 'move' (effectId + toIndex — the order IS the render order) | 'toggle' (effectId + enabled) | 'set' (effectId + params patch, clamped to the effect's ranges) | 'curve' (effectId + curve + points). Types: 'colorBasic' (Lumetri Basic Correction-compatible: temperature/tint -100..100, exposure -5..5 stops, contrast/highlights/shadows/whites/blacks -100..100, saturation 0..300, vibrance -100..100), 'colorWheels' (Resolve lift/gamma/gain/offset — {group}Master/{group}R/G/B; lift/gamma 0 neutral, gain 1, offset printer points 25 neutral), 'lut' (.cube from the project library — set `resource`), 'curves' (tone curves), 'hueCurves' (hue-vs-hue/sat/lum).
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "action", "token" ], "properties": { "type": { "type": "string", "description": "Effect type for 'add'." }, "curve": { "type": "string", "description": "For 'curve': which curve to replace. 'curves' effect: master|r|g|b (tone, identity = [0,0,1,1]); 'hueCurves': hueHue|hueSat|hueLum (x = hue with red at 0, y 0.5 = untouched)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "add | remove | move | toggle | set | curve" }, "clipId": { "type": "string", "description": "Target clip." }, "params": { "type": "object", "description": "For 'set': partial param patch, e.g. { exposure: 1.5 }." }, "points": { "type": "array", "items": { "type": "number" }, "description": "For 'curve': flat control points [x0,y0,x1,y1,…] in the unit square." }, "enabled": { "type": "boolean", "description": "For 'toggle'." }, "toIndex": { "type": "number", "description": "Target position for 'move' (0 = applied first)." }, "effectId": { "type": "string", "description": "Effect instance id (from a previous add or get_timeline)." }, "resource": { "type": "string", "description": "For 'set' on resource effects ('lut'): the LUT's library name (a .cube imported into the project). Empty string clears it." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." } } }arguments 67 linesmanage_transitions auth-required never probed
Transitions on a track's cuts OR lone clip edges (a free head/tail takes a single-sided fade from/to nothing; its alignment is forced). action: 'add' (trackId + atFrame = the cut where one clip ends and the next starts, + type; optional durationFrames (default 30, clamped to the clips' source handles), alignment centered|start|end, params) | 'remove' (transitionId) | 'update' (transitionId + any of type/durationFrames/alignment/params). Types: crossDissolve, dipToBlack, dipToWhite, wipe (params.angle deg, params.softness 0-100), slide (params.angle), push (params.angle). Both clips need source material past the cut — a transition that cannot fit is refused; edits that break the cut remove it. Returns {ok, transitionId?}.
{ "type": "object", "required": [ "projectId", "timelineId", "trackId", "action", "token" ], "properties": { "type": { "type": "string", "description": "Transition type." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "add | remove | update" }, "params": { "type": "object", "description": "Type params patch (e.g. { angle: 90 })." }, "atFrame": { "type": "number", "description": "The cut frame for 'add'." }, "trackId": { "type": "string", "description": "Track owning the cut (get_timeline)." }, "alignment": { "type": "string", "description": "centered | start | end" }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." }, "transitionId": { "type": "string", "description": "Existing transition id." }, "durationFrames": { "type": "number" } } }arguments 55 linesmanage_tracks auth-required never probed
Track operations on a timeline. action: 'mute' | 'unmute' | 'solo' | 'unsolo' (audio only; any solo silences non-solo tracks) | 'hide' | 'show' | 'rename' (needs name) | 'duck' | 'unduck' (audio only: dip this track under speech detected on the other audio tracks — transcript word timings when available, energy VAD otherwise) | 'merge' (needs intoTrackId: moves every clip of trackId onto another track of the same kind; refuses the WHOLE move if any clip would overlap, or if the source carries transitions) | 'delete' (an EMPTY track; refuses one that holds clips — deleting those is remove_clips, by name) | 'tidy' (NO trackId: drops the empty leftover tracks and renumbers). Track ids come from get_timeline. TIDYING UP, AND WHY ORDER MATTERS. Track counts only ratchet up while you build: a spare appears above whatever you fill, and the video and audio counts are kept EQUAL. That last rule is the one that surprises people — five video tracks force five audio tracks, however empty. So: 1. 'merge' the tracks that hold clips but need not be separate (captions from several passes, b-roll that never overlaps). This is what actually lowers the count. 2. 'tidy' once at the end. It settles both sides on one spare above the taller one and reports removedTracks. 'delete' on a single empty track usually reports removedTracks: 0 — the invariant puts it straight back to keep the counts equal. That is not a failure, it is why 'tidy' exists. Neither ever touches a clip, and both keep an empty track you renamed, muted, hid, soloed, unlocked or set to duck.
{ "type": "object", "required": [ "projectId", "timelineId", "action", "token" ], "properties": { "name": { "type": "string", "description": "New name (rename only)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "mute | unmute | solo | unsolo | hide | show | rename | duck | unduck | merge | delete | tidy" }, "trackId": { "type": "string", "description": "Track to modify. Required for every action except 'tidy'." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." }, "intoTrackId": { "type": "string", "description": "Destination track (merge only) — same kind as trackId." } } }arguments 39 linescut_to_angle auth-required never probed
MULTICAM angle switch. The angles are ordinary video tracks stacked over each other (sync them beforehand — clips of the same take on V1..Vn); this razors the whole angle stack at `atFrame` and keeps only `chosenTrackId` live from there to the next cut — the other angles' clips are DISABLED, not deleted, so every switch stays re-decidable (cut again with a different angle to change your mind). Linked audio follows the video by default (audioFollowsVideo: false keeps audio untouched — the usual choice when one good mic carries the sound). angleTrackIds defaults to every video track with a clip under the frame. Refused when the chosen track has no clip at the frame.
{ "type": "object", "required": [ "projectId", "timelineId", "atFrame", "chosenTrackId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "atFrame": { "type": "number", "description": "The switch point (timeline frame)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." }, "angleTrackIds": { "type": "array", "items": { "type": "string" }, "description": "Optional explicit angle set (video track ids)." }, "chosenTrackId": { "type": "string", "description": "The angle to show from atFrame on." }, "audioFollowsVideo": { "type": "boolean", "description": "Default true: linked audio toggles with its angle." } } }arguments 43 linesmove_clips auth-required never probed
Moves clips to new positions (linked partners follow automatically). Each move: clipId, toStartFrame, optional toTrackId (same-kind track). Overwrite semantics — landing on another clip trims it like a drag-drop.
{ "type": "object", "required": [ "projectId", "timelineId", "moves", "token" ], "properties": { "moves": { "type": "array", "description": "[{clipId, toStartFrame, toTrackId?}]" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." } } }arguments 27 linesset_active_timeline auth-required never probed
Sets which timeline the project opens on (and what the user sees if the editor is open). Purely a convenience — every tool here takes an explicit timelineId regardless.
{ "type": "object", "required": [ "projectId", "timelineId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline to activate." } } }arguments 22 linesset_project_settings auth-required never probed
Updates a timeline's fps / width / height (clips keep their timing; content re-fits) and/or renames it. Only provided fields change.
{ "type": "object", "required": [ "projectId", "timelineId", "token" ], "properties": { "fps": { "type": "number", "description": "Optional new frames per second." }, "name": { "type": "string", "description": "Optional new display name." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "width": { "type": "number", "description": "Optional new width in px." }, "height": { "type": "number", "description": "Optional new height in px." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline to update." } } }arguments 38 linessearch_media auth-required never probed
Semantic search over the project's media. Visual scope matches what is IN the picture (describe a shot: 'aerial city at night'); spoken scope matches transcribed speech (exact words, ranked first, with a snippet). Hits: {mediaRef, timeSec (best moment), fromSec, toSec, score, scope}. Requires the user to have downloaded the picture analyzer — otherwise PERMISSION_REQUIRED tells you what to relay.
{ "type": "object", "required": [ "projectId", "query", "token" ], "properties": { "query": { "type": "string", "description": "What to look for — plain language." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 22 linesremove_words auth-required never probed
Text-based editing: cuts the given transcript words OUT of a clip as ripple deletes (everything after each cut shifts left). wordIndices are positions in get_transcript's words array for the clip's media. Contiguous indices merge into single cuts. The go-to tool for removing filler words or tightening an interview.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "wordIndices", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "The clip whose speech is being edited." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clip." }, "wordIndices": { "type": "array", "description": "Indices into get_transcript words, e.g. [4,5,6,12]." } } }arguments 32 linesremove_silence auth-required never probed
Cuts silent gaps inside a clip: any pause between transcribed words longer than minGapSec becomes a ripple delete (a small margin is kept so speech never clips). Typical minGapSec: 0.6 tight, 1.0 balanced, 1.5 loose.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "minGapSec", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "The clip to tighten." }, "minGapSec": { "type": "number", "description": "Minimum pause length to cut (seconds)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clip." } } }arguments 32 linesinspect_media auth-required never probed
Looks INSIDE a source asset. Returns ONE contact sheet — every requested moment as a labelled cell in a single grid image — plus a per-cell fingerprint. Pass atSec (max 12 timestamps, seconds); combine with get_media's sceneSamples for an instant storyboard of the whole file. Shows the RAW source; use inspect_timeline for the composed cut. START WITH pixels:false WHEN YOU ARE ONLY CHECKING FOR EMPTINESS. The fingerprint answers 'is this black / flat / did it decode' for no image cost — nonBlankRatio near 0 is an empty or black frame, colorRange near 0 is a featureless one. Ask for pixels when you must judge WHAT is in the frame: subject, framing, legibility.
{ "type": "object", "required": [ "projectId", "mediaRef", "atSec", "token" ], "properties": { "atSec": { "type": "array", "description": "Timestamps in seconds, max 12." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "pixels": { "type": "boolean", "description": "False = fingerprints only, no image (default true)." }, "columns": { "type": "number", "description": "Grid columns (default 3, or one row if ≤3)." }, "mediaRef": { "type": "string", "description": "Asset from get_media." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "cellHeight": { "type": "number", "description": "Target px height per cell (default 300). Shrunk automatically if the sheet would exceed the response budget — the reply says so when that happens." } } }arguments 39 linesinspect_timeline auth-required never probed
WATCHES the cut. Composites every video layer at each requested frame and returns ONE contact sheet — the frames as labelled cells of a single grid image — plus, per cell, the visibleClipIds that contributed pixels and a fingerprint. Pass frames (timeline frame numbers, max 12). Use after every edit: this is the only tool that shows what actually PLAYS, as opposed to what get_timeline says is arranged. SAMPLE THE CUT POINTS AND THE MIDDLE OF EACH SECTION, not only frame 0. An entrance animation that never resolves, a graphic clipped by its own box, an offline clip rendering black — all look perfect in get_timeline and are obvious here. USE pixels:false FOR A CHEAP SWEEP. visibleClipIds plus nonBlankRatio confirms 'the right clip is in every slot and none of them is black' across a dozen positions with no image cost; spend pixels on the few frames where you must judge typography, framing or timing.
{ "type": "object", "required": [ "projectId", "timelineId", "frames", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "frames": { "type": "array", "description": "Timeline frame numbers, max 12." }, "pixels": { "type": "boolean", "description": "False = structure + fingerprints only, no image (default true)." }, "columns": { "type": "number", "description": "Grid columns (default 3, or one row if ≤3)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "cellHeight": { "type": "number", "description": "Target px height per cell (default 300). Shrunk automatically if the sheet would exceed the response budget — the reply says so when that happens." }, "timelineId": { "type": "string", "description": "Timeline to render." } } }arguments 39 linesget_problems auth-required never probed
What has gone WRONG in the user's editor — renders that failed or stopped, footage that would not decode, chunks that could not be prepared for playback, exports that failed — each with WHERE (the timeline and frames, the source and its own frames, the footage and frame the engine could not decode) and the reason in the engine's own words. You also hear about new problems for free: every other tool result carries a `problems` list of what changed since your last call. Call this for the whole history, or after a `moreProblems` count. A problem that is open on a timeline means that timeline does NOT play or export correctly there, whatever inspect_timeline showed: fix the named layer (e.g. update_motion_composition) before calling the work done. `state` is open | fixed | superseded (the thing it was about was edited and not yet re-rendered — NOT proof it works) | dismissed.
{ "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "detail": { "type": "boolean", "description": "Include the engine's raw diagnostic text per problem (default false — it can be long)." }, "projectId": { "type": "string", "description": "Project id from get_projects. Omit for every project in this browser." }, "includeResolved": { "type": "boolean", "description": "Also list problems that are no longer open (default false)." } } }arguments 24 linesverify_timeline auth-required never probed
RENDER a timeline the way an export would and report what does not work — call it before you call any timeline done. inspect_timeline shows a few frames through the preview path, which quietly substitutes a nearby picture where a frame cannot be rendered; this renders every motion segment the timeline reads (through every nested timeline) exactly, then composites every frame with every layer (footage decoded), and fails where an export would. Returns {jobId}; poll get_job_status every ~5 s. When done, `result` is {verdict: ok | problems | incomplete, checked, notChecked, segments, chunks, problems: [...same shape as get_problems, with `related` warnings that often name the cause...], stale?, note}. `incomplete` is NOT a pass: something could not be checked. `stale: true` means the timeline was edited while it ran — verify again. Sound is not checked. Needs the project open in the editor tab you are paired with (error PROJECT_NOT_OPEN otherwise).
{ "type": "object", "required": [ "projectId", "timelineId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "cancel": { "type": "boolean", "description": "Stop the verification of this timeline that is running." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline to verify (nested timelines inside it are verified too)." } } }arguments 26 linesinsert_clips auth-required never probed
Ripple-inserts media segments: everything at/after atFrame shifts right to make room (clips under the cut split). Same clip shape as add_clips. Use to splice a shot INTO an existing sequence without overwriting.
{ "type": "object", "required": [ "projectId", "timelineId", "atFrame", "clips", "token" ], "properties": { "clips": { "type": "array", "description": "[{mediaRef, sourceInSec, sourceOutSec}]" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "atFrame": { "type": "number", "description": "Insertion point (timeline frame)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Target timeline." } } }arguments 32 linesorganize_media auth-required never probed
Pool file operations. action: 'rename' renames the real file on disk (needs newName; clips referencing the old name go offline). 'delete' UNLINKS the file from the media pool — the real file on disk is NOT touched; clips using it go offline until it is added again.
{ "type": "object", "required": [ "projectId", "mediaRef", "action", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "rename | delete" }, "newName": { "type": "string", "description": "New file name incl. extension (rename)." }, "mediaRef": { "type": "string", "description": "Asset from get_media." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 31 linesawait_user_action auth-required never probed
Waits (up to ~45 s per call) for the user to resolve a pending Frapea prompt — e.g. the folder-permission dialog another tool just opened. The user cannot see tool calls: BEFORE waiting, always say in your visible reply what to click in the Frapea tab, or they will never know they must act. Returns {status: 'granted' | 'denied' | 'pending'}. On 'pending', repeat the instruction and call again; on 'denied', stop that branch and explain the consequence.
{ "type": "object", "required": [ "actionId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "actionId": { "type": "string", "description": "The actionId a previous tool returned." } } }arguments 17 linesopen_project auth-required never probed
Navigates the user's Frapea tab to a project so they can WATCH the edit live. Not required for editing — every tool works headlessly; use this for show-your-work moments.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesconnect_media_folder auth-required never probed
Asks the user to attach a folder of THEIR OWN source footage to the project — opens a dialog in Frapea with a 'Connect folder' button. Only a human can do this (browser security gesture): tell the user IN YOUR REPLY to click it, then call await_user_action with the returned actionId; on 'granted', get_media will list the folder's files. Use when get_media comes back empty and the user has footage somewhere on their disk. NEVER call it for files YOU made or fetched. The project folder's `media/` folder is scanned: any media file placed there joins the pool by itself (within a second, or when the user returns to the tab), with no dialog. `reason` is shown in the dialog word for word — say which footage you need and what you will do with it, so the user knows what to pick.
{ "type": "object", "required": [ "projectId", "reason", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "reason": { "type": "string", "description": "One or two sentences for the dialog, addressed to the user: which folder to pick and why — e.g. 'Pick the folder with your trip clips from Lisbon, so I can cut them into the reel you asked for.'" }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 22 linestrim_clips auth-required never probed
Adjusts one edge of a clip (linked A/V partners follow). edge: 'start' trims/extends the in-point, 'end' the out-point. deltaFrames > 0 moves the edge right, < 0 left — e.g. edge 'end', delta -12 shortens the clip by 12 frames. Extending is limited by the source material.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "edge", "deltaFrames", "token" ], "properties": { "edge": { "type": "string", "description": "start | end" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "Clip to trim." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clip." }, "deltaFrames": { "type": "number", "description": "Signed frame delta for that edge." } } }arguments 37 linesrequest_ai_models auth-required never probed
Asks the user to enable Frapea's on-device AI analyzers (picture search, speech). Opens a dialog in every Frapea tab and returns an actionId — tell the user IN YOUR REPLY to confirm it, then call await_user_action and poll get_analysis_status until models are enabled. Call this when get_media reports 'needs-models'.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project the work belongs to." } } }arguments 17 linesstart_visual_indexing auth-required never probed
Indexes the project's pictures for search_media. Usually not needed: indexing is automatic and this only nudges the queue to re-check now. When the user switched automatic indexing off, this asks for every video and image to be indexed. Returns the same payload as get_analysis_status.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 17 linesstart_transcription auth-required never probed
Transcribes one asset even if automatic transcription is switched off. Speech transcripts otherwise appear on their own. Returns the project's analysis status; poll get_analysis_status, then read get_transcript.
{ "type": "object", "required": [ "projectId", "mediaRef", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "mediaRef": { "type": "string", "description": "Asset from get_media." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 22 lineslist_voiceover_models auth-required never probed
The text-to-speech providers Frapea can generate voiceovers with. Returns providers[]: the FREE in-browser models straight from the TTS registry ({id, label, languages, voices: [{id, label, language, gender, grade}], modelAssets, state: {phase: needs-download|downloading|ready|error}}), plus the PREMIUM cloud provider flagged premium: true ({id: 'elevenlabs', label, premium, voices: [{id, label}], models: [{id, label, creditsPerCharacter, supportsAudioTags, note}]}, voices/models empty until a key is stored). supportsAudioTags tells you whether that model understands inline audio tags like [calm], [laughs], [whispers] as delivery directions (only the v3 family does); on every other model such tags are READ ALOUD, so send those models plain text — each model's `note` restates its rule. Also returns a top-level status object `elevenlabs`: {provider: 'elevenlabs', hasApiKey, credits: {used, limit, remaining} | null, consent: 'ask' | 'always'} — READ IT BEFORE choosing the premium provider, so you know whether a key exists, what the account can afford and whether a consent dialog will be involved. A free provider whose phase is 'needs-download' cannot generate until the user enables it in the Generators panel (Voiceover) — there is no tool to force the download; ask the user.
{ "type": "object", "required": [ "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." } } }arguments 12 linesgenerate_voiceover auth-required never probed
Generates an AI voiceover from a script, entirely on-device. The WAV lands in the project's media library as a normal audio asset (with waveform and analysis), the script is stored as asset metadata, and a script-corrected word-level transcript is written so captions work immediately. CALL THIS BEFORE YOU CUT THE PICTURE. A script does not tell you how long it takes to SAY, so a narrated edit assembled first is built on guessed cut points that the real audio then breaks — every section, graphic and transition has to be retimed. Generate the narration, read its word timings with get_transcript, then lay picture against those frames. GENERATE IN SEGMENTS, NOT ONE BLOCK — call this 2-4 times (hook / body / close, or one per section) and lay the parts on the timeline. A single long generation comes out FLAT and evenly paced, everything pressed together at one energy; segmenting gives each part its own intent, puts natural air at the joins, and lets you redo one section instead of all of it. Write for the ear: short sentences, one idea each, LINE BREAKS between them — the strongest signal the model has to actually stop. See read_skill {topic:'voiceover'}. Async: returns {jobId, fileName} — fileName is the audio file's final name in the project's media/ folder, decided up front (WAV for local providers, MP3 for ElevenLabs). The job runs on the analysis MASTER tab and its request is persisted on disk: it survives tab closes and browser restarts, and get_job_status answers from disk even in a fresh session. Poll until done; the finished job also carries the asset's mediaRef. Voices/languages come from list_voiceover_models; a local model must be downloaded first (phase 'ready'), otherwise this returns PERMISSION_REQUIRED. PREMIUM (provider 'elevenlabs' — SPENDS THE USER'S CREDITS): check list_voiceover_models' `elevenlabs` status object first. Structured refusals you must handle: NO_API_KEY (no key stored — ask the USER to add their key in the Voiceover generator's ElevenLabs onboarding; never ask for, or pass, the key yourself), INSUFFICIENT_CREDITS (carries needed vs remaining — shorten the script or ask the user), CONSENT_REQUIRED (paid features are set to 'ask': a consent dialog with the cost is now open in Frapea and the error carries an actionId — TELL THE USER in your visible reply to confirm it, call await_user_action with that actionId, and on 'granted' retry this call with consentActionId set to it; 'denied' means drop it). AUDIO TAGS (ElevenLabs): inline bracket tags like [calm], [excited], [whispers] direct the delivery ONLY on models whose list_voiceover_models entry says supportsAudioTags: true (the v3 family). Every other model reads them ALOUD — so only write tags when the chosen model supports them. As a safety net, tags sent to a non-supporting model are stripped before synthesis and the success message says so.
{ "type": "object", "required": [ "projectId", "text", "token" ], "properties": { "text": { "type": "string", "description": "The script to speak (multi-line ok)." }, "speed": { "type": "number", "description": "Speaking rate multiplier (default 1). Local providers accept 0.5–2; ElevenLabs 0.7–1.2." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "voice": { "type": "string", "description": "Voice id from the provider's manifest (default: its first voice for the chosen language)." }, "modelId": { "type": "string", "description": "ElevenLabs only: TTS model id from list_voiceover_models (default 'eleven_multilingual_v2'; turbo/flash cost half). Check the model's supportsAudioTags before writing [calm]-style tags into the text — non-v3 models would read them aloud." }, "fileName": { "type": "string", "description": "Optional base name for the audio file written into media/." }, "language": { "type": "string", "description": "Language tag from the provider's manifest (e.g. 'en-us'). Ignored for ElevenLabs — its voices are multilingual." }, "provider": { "type": "string", "description": "TTS provider id from list_voiceover_models (default 'kokoro'; 'elevenlabs' = premium, needs the user's key + consent)." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "voiceSettings": { "type": "object", "properties": { "style": { "type": "number", "description": "0–1; style exaggeration." }, "stability": { "type": "number", "description": "0–1; higher = steadier." }, "speakerBoost": { "type": "boolean", "description": "Speaker boost on/off." }, "similarityBoost": { "type": "number", "description": "0–1; voice adherence." } }, "description": "ElevenLabs only: voice knobs, each optional. stability, similarityBoost and style are 0–1; speakerBoost is a boolean." }, "consentActionId": { "type": "string", "description": "The actionId from a CONSENT_REQUIRED refusal, after await_user_action returned 'granted'. Good for one job." } } }arguments 72 linessearch_stock_media auth-required never probed
Searches the Pexels stock library (photos or videos) with the user's connected API key. Returns items with id, dimensions, duration (videos), author, thumbnail url, available video qualities, and a `downloaded` flag telling whether the asset is already in the project's media pool. If no key is connected the tool errors — TELL THE USER to open Library → Generators → Stock Library and connect their Pexels key. Results are subject to the Pexels license; keep the author attribution when asked about provenance. LOOK AT THE THUMBNAIL BEFORE YOU DOWNLOAD. Results are matched on the uploader's WORDS, not on what is in the frame — a search for 'earth from space' returns the Moon, other planets and artists' renders, and they all look plausible in a result list. Fetch the returned thumbnail url and actually view it. Picking on the title alone is how the wrong subject ends up in a finished video. See read_skill {topic:'verify'}.
{ "type": "object", "required": [ "projectId", "kind", "token" ], "properties": { "kind": { "type": "string", "description": "photos | videos" }, "page": { "type": "number", "description": "1-based page, 40 items per page." }, "size": { "type": "string", "description": "Optional: large | medium | small" }, "color": { "type": "string", "description": "Optional, photos only: a Pexels color name." }, "query": { "type": "string", "description": "Search phrase; empty = curated/popular feed." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "orientation": { "type": "string", "description": "Optional: landscape | portrait | square" } } }arguments 42 linesdownload_stock_media auth-required never probed
Downloads one Pexels asset into the project's media pool as a virtual file at the Library root (name `pexels-<id>.jpg|.mp4`). Asynchronous: the first call accepts the download and returns its state; CALL AGAIN with the same arguments to poll — state becomes 'done' with the asset's mediaRef, ready for add_clips. maxHeight picks the best video rendition not exceeding it (default: the best available). ASK FOR THE WHOLE SHOT LIST AT ONCE — three files stream at a time and the rest wait their turn, so a batch is queued for you, in the order you asked. State 'queued' is normal and needs no action but polling. ONCE DOWNLOADED, CHECK THE MOMENT YOU MEAN TO USE — not just the first frame. A clip that opens on your subject can cut away, change subject or hold a logo exactly where you planned to trim. Read the scene samples from get_media, or render frames with inspect_media, across the range you intend to cut. See read_skill {topic:'verify'}.
{ "type": "object", "required": [ "projectId", "kind", "pexelsId", "token" ], "properties": { "kind": { "type": "string", "description": "photo | video" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "pexelsId": { "type": "number", "description": "The item id from search_stock_media." }, "maxHeight": { "type": "number", "description": "Videos only: cap the rendition height (e.g. 1080)." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 31 linessearch_sounds auth-required never probed
Searches the Freesound library (SFX, ambience, foley) with the user's connected API key. Every item carries what you need to JUDGE the sound without hearing it: the uploader's full description, tags, duration, community rating and download count, format/samplerate, and — when analyzed — AudioCommons descriptors (acAnalysis: ac_loudness LUFS, ac_dynamic_range, ac_tempo, ac_tonality, ac_single_event, plus 0-100 timbre scales like ac_brightness, ac_warmth, ac_hardness, ac_depth, ac_roughness, ac_boominess, ac_sharpness, ac_reverb). A `downloaded` flag says the sound is already in the pool. Licenses: CC0 needs nothing, BY needs attribution, BY-NC excludes commercial use — prefer cc0/by via the license arg when the project may be commercial. If no key is connected the tool errors — TELL THE USER to open Library → Generators → Sound Library and connect their Freesound key.
{ "type": "object", "required": [ "projectId", "query", "token" ], "properties": { "page": { "type": "number", "description": "1-based page, 40 items per page." }, "sort": { "type": "string", "description": "Optional: score (default) | rating_desc | downloads_desc | created_desc" }, "query": { "type": "string", "description": "Search phrase (e.g. 'door slam', 'jungle ambience')." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "license": { "type": "string", "description": "Optional: cc0 | by | by-nc" }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "maxDurationSeconds": { "type": "number", "description": "Optional upper duration bound." }, "minDurationSeconds": { "type": "number", "description": "Optional lower duration bound." } } }arguments 42 linesdownload_sound auth-required never probed
Downloads one Freesound sound (its HQ MP3 preview, ~128 kbps — plenty for SFX/ambience) into the project's media pool as a virtual file at the Library root (name `freesound-<id>.mp3`). Asynchronous: the first call accepts the download and returns its state; CALL AGAIN with the same arguments to poll — state becomes 'done' with the asset's mediaRef, ready for add_clips onto an audio track. Three files stream at a time and the rest wait their turn, shared with stock video, so state 'queued' is normal and needs no action but polling.
{ "type": "object", "required": [ "projectId", "soundId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "soundId": { "type": "number", "description": "The sound id from search_sounds." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 22 linesadd_sfx auth-required never probed
frapea's BUILT-IN sound-effects pack — synthesized, licence-free, no API key, no download wait. Reach for this FIRST; use search_sounds only for something the pack does not cover (ambience, foley, a specific real-world object). Call with no `ids` to get the catalogue: each sound carries what it sounds like AND when to use it. Call with `ids` to copy them into the project's media pool as `sfx-<id>.wav` at the Library root; each result gives a mediaRef ready for add_clips on an audio track. Adding a sound already in the project is a no-op that returns its existing mediaRef.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "id": { "type": "string", "description": "Convenience: a single sound id." }, "ids": { "type": "array", "items": { "type": "string" }, "description": "Sound ids to add (e.g. ['whip','thump','ding']). Omit to list the pack." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 28 linesmanage_queue auth-required never probed
Full control of the background work queue. action 'list' returns every queued/running item across all projects (analyses, proxies, beats) plus the visible task rows (downloads, voiceover jobs, failures) with a cancellable flag. action 'cancel' stops ONE kind of work for ONE asset — queued or already running (a running visual index stops at the next sample and keeps its checkpoint). The asset then shows an incomplete-analysis badge; retry_analysis or the tile's retry button runs it again. kind: index | transcribe | stabilize | denoise | proxy | beats.
{ "type": "object", "required": [ "action", "token" ], "properties": { "kind": { "type": "string", "description": "cancel only: index | transcribe | stabilize | denoise | proxy | beats" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "list | cancel" }, "mediaRef": { "type": "string", "description": "cancel only: asset from get_media." }, "projectId": { "type": "string", "description": "cancel only: project id from get_projects." } } }arguments 29 linesretry_analysis auth-required never probed
Runs ONE analysis of ONE asset again — picture indexing or speech transcription — clearing whatever failure was recorded for it. Use when get_media shows phase 'failed' and the error looks worth another go, or when you want a fresh result for a file that changed meaning. Returns the project's analysis status.
{ "type": "object", "required": [ "projectId", "mediaRef", "kind", "token" ], "properties": { "kind": { "type": "string", "description": "index | transcribe" }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "mediaRef": { "type": "string", "description": "Asset from get_media." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 27 linesset_keyframes auth-required never probed
Animates one clip property. property: opacity | volume | positionX | positionY | scaleX | scaleY | rotationDeg | pitch | yaw | anchorX | anchorY | cropLeft | cropTop | cropRight | cropBottom | cropSoftness. Keyframes are stamped in SOURCE frames (frames into the media, i.e. timelineFrame - startFrame + trimStartFrame), so they survive moves and trims. action 'set' (default) upserts the given keyframes; 'remove' deletes at the given frames; 'clear' removes the property's animation (or ALL animation when property is omitted). easing is how the value LEAVES a keyframe: linear | hold | smooth.
{ "type": "object", "required": [ "projectId", "timelineId", "clipId", "token" ], "properties": { "param": { "type": "string", "description": "The effect param key to animate (e.g. 'exposure')." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "set | remove | clear (default set)." }, "clipId": { "type": "string", "description": "Clip to animate." }, "effectId": { "type": "string", "description": "Animate an EFFECT param instead of a clip property: the effect instance id (from manage_effects add / get_timeline). Use with `param`; `property` is then ignored." }, "property": { "type": "string", "description": "Animatable property (see description)." }, "keyframes": { "type": "array", "items": { "type": "object", "required": [ "atSourceFrame", "value" ], "properties": { "value": { "type": "number" }, "easing": { "type": "string", "description": "linear | hold | smooth (default linear)." }, "atSourceFrame": { "type": "number", "description": "Frames into the source media." } } }, "description": "For 'set': keyframes to add or replace." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clip." }, "atSourceFrames": { "type": "array", "items": { "type": "number" }, "description": "For 'remove': source frames whose keyframes to delete." } } }arguments 74 linesapply_layout auth-required never probed
Arranges clips into a screen layout by writing ordinary transform + crop values (first clipId → first cell). layout: full | side-by-side | top-bottom | pip-top-left | pip-top-right | pip-bottom-left | pip-bottom-right | grid-2x2 | main-sidebar | three-up. mode 'fit' letterboxes each source in its cell, 'cover' fills the cell and crops the overflow (bias 0–1 picks the surviving part; 0.5/0.5 = centre). Replaces the clips' geometry animation. The clips should overlap in time on different tracks — a layout of clips that never share a frame arranges nothing visible.
{ "type": "object", "required": [ "projectId", "timelineId", "clipIds", "layout", "token" ], "properties": { "bias": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } }, "description": "Cover-crop bias 0–1 per axis (default 0.5/0.5)." }, "mode": { "type": "string", "description": "fit | cover (default fit)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "layout": { "type": "string", "description": "Layout id (see description)." }, "clipIds": { "type": "array", "items": { "type": "string" }, "description": "Clips to arrange, in cell order." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "timelineId": { "type": "string", "description": "Timeline containing the clips." } } }arguments 51 lineslist_face_tracks auth-required never probed
Lists face-tracking runs and the people each one found: faceId, name, frame range, how many frames it was actually visible for, and how many gaps it has. Sample data is deliberately not returned. Use this to poll a queued run and to pick a faceId for bind_face.
{ "type": "object", "required": [ "projectId", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "Only runs made from this clip." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 21 linesmerge_faces auth-required never probed
Joins two or more faces from one run into a single person — for somebody who walked out of the shot and came back. REFUSED when any two of them appear on the same frame, since they cannot then be one person. The earliest face's id and name survive, so existing bindings keep working, and the time between sightings stays a gap for the clip's gap policy to handle.
{ "type": "object", "required": [ "projectId", "trackId", "faceIds", "token" ], "properties": { "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "faceIds": { "type": "array", "items": { "type": "string" }, "description": "Two or more faceIds within that run." }, "trackId": { "type": "string", "description": "The run, from list_face_tracks." }, "projectId": { "type": "string", "description": "Project id from get_projects." } } }arguments 30 linesbind_face auth-required never probed
Makes a clip FOLLOW a tracked face — the way to stick a mask, label or blur onto somebody's head. Movement is relative to where the face was first seen, so the clip keeps the framing it already had and only inherits the motion. Pick what to copy with channels, and what happens while the face is missing with gap. Pass trackId: null to unbind.
{ "type": "object", "required": [ "projectId", "clipId", "token" ], "properties": { "gap": { "type": "string", "description": "While the face is missing: 'hold' stays put then jumps, 'hide' disappears, 'glide' travels smoothly to where it reappears. One of: hold | hide | glide." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "clipId": { "type": "string", "description": "The clip that should follow the face." }, "faceId": { "type": "string", "description": "Which person in that run." }, "smooth": { "type": "number", "description": "Jitter filtering 0–1 (default 0.25)." }, "trackId": { "type": "string", "description": "The run; null unbinds this clip." }, "channels": { "type": "object", "description": "What to copy: {position, scale, pitch, yaw, roll} booleans. Default position and scale on, the three rotations off." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "maxGlideSeconds": { "type": "number", "description": "Longest gap 'glide' will cross before hiding instead (default 1.8)." } } }arguments 46 linesorganize_pool auth-required never probed
THE MEDIA POOL'S SHAPE — bins (the pool's folders) and what sits in them. Use it to tidy a project: group the footage, the voice-over, the music and the graphics instead of leaving eighty files at the root. Bins are VIRTUAL and live in the project, not on disk. Moving an item between bins NEVER touches the file or its mediaRef, so it cannot break a clip's link or make the app ask the user to relocate anything — organise freely. actions: 'list' returns the bin tree and every item with the bin it shows in (call it first — you need the ids). 'create_bin' {name, parentBinId?}. 'rename_bin' {binId, name}. 'move_bin' {binId, toBinId} nests one bin inside another. 'delete_bin' {binId} — its children and items reparent, nothing is lost, no file is deleted. 'move_items' {itemKeys[], toBinId} moves files, timelines and motion compositions alike. Item keys: a media file's mediaRef, 'timeline:<id>', 'motion:<id>'. The Library root is the empty-string bin id. Bins that MIRROR a real folder (mirrorsFolder: true) can be RENAMED — a mirror is matched by the folder it tracks, not by its name — but they cannot be moved or deleted, because their place in the tree follows the folder. Move the items out instead, or make your own bins alongside.
{ "type": "object", "required": [ "projectId", "action", "token" ], "properties": { "name": { "type": "string", "description": "Bin name (create_bin, rename_bin)." }, "binId": { "type": "string", "description": "Target bin (rename/move/delete)." }, "token": { "type": "string", "description": "The session token `connect` returned. Pass it on every call — it says which browser to drive." }, "action": { "type": "string", "description": "list | create_bin | rename_bin | move_bin | delete_bin | move_items" }, "toBinId": { "type": "string", "description": "Destination bin for move_bin / move_items. Omit or '' = Library root." }, "itemKeys": { "type": "array", "description": "move_items: mediaRefs / 'timeline:<id>' / 'motion:<id>' to move together." }, "projectId": { "type": "string", "description": "Project id from get_projects." }, "parentBinId": { "type": "string", "description": "Parent for create_bin. Omit or '' = Library root." } } }arguments 42 lines
This deployment has no calling key, so nothing can be run from here. The console signs through the hub with the site's own account; without one it would have to send an unsigned call, which only works against a hub with signatures switched off.
Nobody has claimed this listing. Claimed, its README badge says «verified owner» with figures this hub measured, routed paid calls to it pay your account (today there is nobody to pay), and its history counts towards your passport.
- Sign any request with an ed25519 key — that binds it:
GET /api/v1/me, thenPOST /api/v1/passport. - Prove it is yours. Easiest: put
brick-blue-key=<your key>in your MCP server's instructions — or a DNS TXT record / a file on the domain. - Ask the hub to check:
POST /api/v1/passport/claim-endpointwith this listing's id8d3261c98b838fef.
Every step, filled in for this listing: https://brick.blue/api/v1/agents/8d3261c98b838fef/claim.
Over MCP: the claim_endpoint tool.
[](https://brick.blue/agent/8d3261c98b838fef?ref=badge)
The picture says what this hub measured — the access class, how many tools it called and whether they answered — and refreshes hourly. Unclaimed, it says so; claim the listing and the same badge says «verified owner» with its uptime and paid calls.
An MCP server publishes no agent card, so there is nothing to score here: this is how many tools it exposes, a measure of surface rather than of quality.
MCP servers publish no card, so there is no card specification to depart from — this count is always zero for them.
Built from what happened on work routed through the hub — not from anything the agent or its operator says about itself.
- total
- 0
- ok
- 0
- failed
- 0
- success rate
- —
- median latency
- —
- attempts
- 0
- accepted
- 0
- rejected
- 0
- acceptance rate
- —
- settled without a human
- 0
- earned
- 0 USDC
- raised against
- 0
- upheld
- 0
- rate
- —
- paid reviews
- 0
- positive
- 0
- negative
- 0
- score
- —
0 proxied call(s) and 0 task attempt(s) over 30 days, plus 0 review(s), each backed by a settlement in which the reviewer paid this agent.
Served from the same domain, which is what was measured. Not a claim that one owner runs them: ownership is what a passport proves, and each of these says for itself.
- motion.frapea.com motion