cuttingfloor
REMOTE · API.CUTTINGFLOOR.AI
Hosted video editing for AI agents: cut, caption, mix and export real video. Sign in with OAuth.
Available components
Last scanned (11 hours ago)
Scores higher than 84% of remote servers
Reachable. Last reached 5 Nov 2026.
Category Design and Media
How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score → Why this is hard to score →
Endpoint Security92
- The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
- Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token. View diagnostics → Pass
- HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
- HSTS check failed: the Strict-Transport-Security header is absent. See how to fix → View diagnostics → Fail
- DNSSEC check failed: this domain isn't protected by DNSSEC. See how to fix → View diagnostics → Fail
- The authorisation server supports Client ID Metadata Documents, the current MCP client-registration mechanism. View diagnostics → Pass
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability70
- 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
- AI-judged instruction clarity (good).Pass
- Context-footprint check failed: tool/resource definitions use about 21134 tokens (~245/item across 86 items; 72 tools + 14 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management33
- Stability observed for 10 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage92
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 73% of tool parameters carry a description.Partial
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 8 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 74 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a current MCP spec version (2026-07-28).Pass
- Supports UI / widget rendering.Pass
Checks we ran (14)
- TLS certificate
- Authorization
- HTTPS
- HSTS header
- DNSSEC
- Transport
- Schema quality
- Stability
- Tool coverage
- Injection markers
- Destructive annotations
- Judged manipulation
- MCP protocol
- Capabilities
Does not apply to this hosted endpoint (2)
- Supply Chain SecurityScored on package channels only. This is a hosted endpoint, so there is nothing to install or inspect.
- Provenance & TransparencyScored on package channels only. This is a hosted endpoint, so there is nothing to install or inspect.
The score does not measure everything. What the score does not measure →
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 5 Nov 26 0
- Server version: 0.32.0 → 0.34.0 functional
- 4 Nov 26 0
- New resource “library” functional
- Server version: 0.31.0 → 0.32.0 functional
- Server version: 0.30.0 → 0.31.0 functional
- 3 Nov 26 +1
- New resource “editor” functional
- Server version: 0.29.3 → 0.30.0 functional
- 2 Nov 26 0
- Tool “job.get” rewrote its description, which is the text the model reads security
- Tool “clip.set_mask” rewrote its description, which is the text the model reads security
- Server version: 0.29.2 → 0.29.3 functional
- Server version: 0.29.1 → 0.29.2 functional
- Server version: 0.28.0 → 0.29.1 functional
- “job.get” reworded the description of “wait_seconds” cosmetic
- “clip.set_speed” reworded the description of “interpolate” cosmetic
- “audio.set_gain” reworded the description of “gain_db” cosmetic
- “qa.check” reworded the description of “speech” cosmetic
- 1 Nov 26 +1
- The server rewrote its instructions, which are the text every model session reads security
- Tool “audio.set_gain” rewrote its description, which is the text the model reads security
- Tool “audio.set_master” rewrote its description, which is the text the model reads security
- Tool “caption.add” rewrote its description, which is the text the model reads security
- Tool “clip.replace” rewrote its description, which is the text the model reads security
- Tool “clip.set_animation” rewrote its description, which is the text the model reads security
- Tool “clip.set_mask” rewrote its description, which is the text the model reads security
- Tool “clip.set_transform” rewrote its description, which is the text the model reads security
- Tool “create.cutout” rewrote its description, which is the text the model reads security
- Tool “export.timeline” rewrote its description, which is the text the model reads security
- Tool “export.video” rewrote its description, which is the text the model reads security
- Tool “filter.add” rewrote its description, which is the text the model reads security
- Tool “job.cancel” rewrote its description, which is the text the model reads security
- Tool “job.get” rewrote its description, which is the text the model reads security
- Tool “job.list” rewrote its description, which is the text the model reads security
- Tool “judge.ask” rewrote its description, which is the text the model reads security
- Tool “media.analyze” rewrote its description, which is the text the model reads security
- Tool “media.get” rewrote its description, which is the text the model reads security
- Tool “media.import” rewrote its description, which is the text the model reads security
- Tool “media.listen” rewrote its description, which is the text the model reads security
- Tool “media.seek” rewrote its description, which is the text the model reads security
- Tool “qa.check” rewrote its description, which is the text the model reads security
- Tool “range.extract” rewrote its description, which is the text the model reads security
- Tool “range.lift” rewrote its description, which is the text the model reads security
- Tool “text.set” rewrote its description, which is the text the model reads security
- Tool “timeline.set_sequence” rewrote its description, which is the text the model reads security
- Tool “title.add” rewrote its description, which is the text the model reads security
- Tool “create.motion” rewrote its description, which is the text the model reads security
- Tool “create.still” rewrote its description, which is the text the model reads security
- Tool “job.cancel” is now declared destructive security
- Schema quality: 221 → 248 ▼ functional
- Server version: 0.27.6 → 0.28.0 functional
- Server version: 0.27.5 → 0.27.6 functional
- Server version: 0.27.4 → 0.27.5 functional
- New tool “create.upscaled” functional
- “clip.replace” added an optional parameter “linked” cosmetic
- “clip.set_speed” added an optional parameter “interpolate” cosmetic
- “create.cutout” added an optional parameter “region” cosmetic
- “job.get” added an optional parameter “result” cosmetic
- “job.get” added an optional parameter “wait_seconds” cosmetic
- “job.list” added an optional parameter “media_id” cosmetic
- “job.list” added an optional parameter “result” cosmetic
- “job.list” added an optional parameter “tool” cosmetic
- “media.analyze” added an optional parameter “cluster_threshold” cosmetic
- “media.analyze” added an optional parameter “percentiles” cosmetic
- “media.analyze” added an optional parameter “regions” cosmetic
- “media.analyze” added an optional parameter “speakers” cosmetic
- “text.set” added an optional parameter “spans” cosmetic
- “title.add” added an optional parameter “spans” cosmetic
- “clip.set_animation” reworded the description of “loop” cosmetic
- “filter.add” reworded the description of “name” cosmetic
- “filter.add” reworded the description of “params” cosmetic
- “judge.ask” reworded the description of “state” cosmetic
- “media.analyze” reworded the description of “at_seconds” cosmetic
- “media.analyze” reworded the description of “every_seconds” cosmetic
- “media.analyze” reworded the description of “from_seconds” cosmetic
- “media.analyze” reworded the description of “kind” cosmetic
- “media.analyze” reworded the description of “model” cosmetic
- “media.analyze” reworded the description of “region” cosmetic
- “media.analyze” reworded the description of “threshold” cosmetic
- “media.analyze” reworded the description of “timeline_id” cosmetic
- “media.analyze” reworded the description of “until_seconds” cosmetic
- “media.get” reworded the description of “from_seconds” cosmetic
- “media.get” reworded the description of “include” cosmetic
- 30 Oct 26 +1
- Server version: 0.27.3 → 0.27.4 functional
- Server version: 0.27.1 → 0.27.3 functional
- 28 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.
- 27 Oct 26 0
- Stability: unverified → 0.03 ▲ functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 5 Nov 2026 · Probed https://api.cuttingfloor.ai/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=api.cuttingfloor.ai | CN=YE2,O=Let's Encrypt,C=US | 19 Oct 2026 | 17 Jan 2027 | ECDSA 256 | ECDSA-SHA384 | 50c8d677f68f62714b9d9babf7a5005b14a |
| SANs: api.cuttingfloor.ai | ||||||
| CN=YE2,O=Let's Encrypt,C=US (CA) | CN=Root YE,O=ISRG,C=US | 3 Sept 2025 | 2 Sept 2028 | ECDSA 384 | ECDSA-SHA384 | 4df3b15dd6c0784c507cd37b58e6f115 |
| CN=Root YE,O=ISRG,C=US (CA) | CN=ISRG Root X2,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | ECDSA-SHA384 | 872165fc34b6e5fba8add5b3705fb53a |
| CN=ISRG Root X2,O=Internet Security Research Group,C=US (CA) | CN=ISRG Root X1,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | SHA256-RSA | 6c8f1dc727c7117f7baf853ac980f9cd |
Background: What to check on a remote MCP endpoint →
DNSSEC insecure
Validation of api.cuttingfloor.ai. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | Trust anchor | 20326, 38696 | 8, 8 | Verified |
| ai. | DS published | 1814, 3799 | 8, 8 | Verified |
| cuttingfloor.ai. | No DS | Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation |
Authentication Enforced and verified
The endpoint asked for a token and published valid RFC 9728 metadata describing how to get one.
| Result | Enforced and verified |
|---|---|
| Enforced | On tool calls |
| HTTP status | 200 |
WWW-Authenticate challenge Bearer error="invalid_token", error_description="Missing Authorization header", resource_metadata="https://api.cuttingfloor.ai/.well-known/oauth-protected-resource/mcp"
Bearer error="invalid_token", error_description="Missing Authorization header", resource_metadata="https://api.cuttingfloor.ai/.well-known/oauth-protected-resource/mcp" Protected resource metadata
| Document | https://api.cuttingfloor.ai/.well-known/oauth-protected-resource/mcp |
|---|---|
| Retrieved | Yes |
| Resource | https://api.cuttingfloor.ai/mcp |
| Authorisation server | https://auth.cuttingfloor.ai |
Background: How OAuth 2.1 works in the 2026 MCP spec →
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://api.cuttingfloor.ai/mcp | Verified | 200 | |
| http (plaintext) | http://api.cuttingfloor.ai/mcp | HTTPS enforced | 308 | https://api.cuttingfloor.ai/mcp |
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
filter.remove Remove effect ~71
Take an effect off. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| filter_id | string | yes | – |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
filter.set Set effect ~100
Change an effect's params (merged) or turn it off and on. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| enabled | boolean | – | – |
| filter_id | string | yes | – |
| params | object | – | Merged into its params |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
job.cancel Cancel job ~59
Stop a job, queued or running, within seconds: its engines are killed and its slot goes to the next job. Finished jobs are left as they are.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | yes | Job handle; the same value is the MCP task id |
| Name | Type | Req | Description |
|---|---|---|---|
| created_at | string | yes | – |
| error | object | – | – |
| for | object | – | What it is for, known from the start |
| job_id | string | yes | Job handle; the same value is the MCP task id |
| progress | number | – | – |
| result | – | – | The tool's structured result once completed |
| result_summary | object | – | result "summary": the result in brief; result "full" has all of it |
| seconds_left | number | – | Running: seconds left at its recent rate of progress, once it has moved steadily (later steps may differ) |
| starts_in_seconds | number | – | Queued next in line: seconds until a slot should free, when every job holding one has seconds_left |
| status | string | yes | – |
| status_message | string | – | What it is doing; while it waits, its place in the queue and what it waits for (a job slot, a render slot or the sandbox) |
| tool | string | yes | – |
| updated_at | string | yes | – |
No examples provided.
job.get Get job ~306
Status, progress and, once completed, the result of long-running calls (media.import, media.analyze, create.*, export.video, qa.check). Same record as tasks/get. status_message says what it is doing, or where it stands in the queue; for names the media or timeline. wait_seconds (up to 50) answers when any job asked for finishes, fails or is cancelled, or when the wait runs out (then with its progress), so one call waits instead of many polls. seconds_left and starts_in_seconds come only when progress so far allows. job_ids reads many and answers {jobs, not_found}, each result in brief (result_summary: lists cut to their first items, with what was cut) unless result is full; result summary does the same for one job. A job whose progress stops for minutes ends with a stalled error, freeing its slot.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | – | One job: the answer is that job |
| job_ids | array | – | Many jobs at once (up to 100): the answer is {jobs, not_found} |
| result | string | – | Default full for one job_id; summary for job_ids, so a check on many stays small (full gives them whole) |
| wait_seconds | number | – | Wait up to this long (at most 50 s) for any job asked for to finish (its status changes); progress along the way does not end the wait; 0 answers at once |
Structured output declared, but exposes no named fields.
No examples provided.
job.list List jobs ~144
Your recent jobs, newest first, each with its status, progress and for (the media or timeline it is for), filtered by status, tool or media_id. Results are left out unless you ask (result summary or full); job.get reads one.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | – |
| media_id | string | – | Only jobs for this media (importing, making or reading it) |
| result | string | – | none (default): each job without its result, to find one; summary: lists cut short; full: every result whole |
| status | string | – | – |
| tool | string | – | Only jobs of this tool, e.g. media.import |
| Name | Type | Req | Description |
|---|---|---|---|
| jobs | array | yes | – |
No examples provided.
judge.ask Ask Jev ~793
Ask TypeSafe Jev, a fast judgment model (preview), your questions about a state you give, and get typed answers back in about a second. Jev sees exactly the state: each entry under its name, and your questions refer to entries by name in backticks ("in `passage`"). An entry is text (a string, or a JSON object or array of related facts) or a picture: {media_id} for an imported image, {media_id, at_seconds} for the frame of a video at that time, {timeline_id, at_seconds} for a frame of an edit as it renders (to judge a grade or a layout you made). Mix them freely. Each call stands alone, so put everything a judgment needs into its state. Choose the type by the answer you need. noul: yes or no, answered as noul, the probability of yes from 0 to 1 (0.5 or more reads as yes). choice: one of options you name, answered as choice plus probabilities and confidence. score: a degree on levels you order, answered as score (0 is the first level) plus confidence. box: where one thing is in a picture, answered as box {x, y, w, h} in fractions of that picture plus confidence. Write each question as one thing, worded so yes means it is there. Open with what the state is ("`passage` is a transcript of someone speaking off the cuff"), and give a noul criteria {true, false} with an example of each: Jev answers most accurately from examples. To locate, name the one thing so only it fits ("the yellow headline 'Get started' at the top of `shot`") and ask a noul whether it is there; trust a box with high confidence. One call answers many questions about one state at once, so judge many items together: put the shared text in state once, and give each item its own question carrying its own marked copy. Filler words: state {passage: "So basically the way this works is"}, and per word a noul "`passage` is a transcript of someone speaking off the cuff. With one word marked: "So [[basically]] the way this works is". Is the marked word filler, so the sentence says the same without it?" with crit…
| Name | Type | Req | Description |
|---|---|---|---|
| questions | object | yes | id (yours, not shown to the model) to question; up to 300, all answered about the same state |
| state | object | yes | What Jev sees, entry by entry: name to a string, a JSON object or array, or a picture: {media_id} (an image), {media_id, at_seconds} (a video frame) or {timeline_id, at_seconds} (a frame of an edit a… |
| Name | Type | Req | Description |
|---|---|---|---|
| answers | object | yes | Per question id: noul {noul}; choice {choice, probabilities, confidence}; score {score, probabilities, confidence}; box {box: {x, y, w, h} fractions of the picture, confidence} |
| model | string | yes | – |
No examples provided.
media.analyze Analyze media ~2,506
Find what is in imported media, or in the sound of an edit, as data for edits. transcript: every word with its time (whisper.cpp on the CPU; the model downloads once). For English the word edges are then aligned to the sound (wav2vec2 forced alignment): a word starts and ends within about 0.05 s of where its letters sound, and an end on a soft consonant (s, t, k, f) or a breath can ring on up to about 0.1 s more; timing_confidence says how clearly each word was heard. Other languages keep whisper's own times, up to about a second off. Short ums and uhs are often left out of the words; kind speech still hears them. from_seconds and until_seconds transcribe just that stretch. beats: tempo, beat and downbeat times, and strengths: each beat's onset strength, 0–1 of the strongest (the hardest hits, the drop). faces: face boxes over time, e.g. to reframe to 9:16 with clip.set_transform keyframes. track: follow a region you give from a moment on, a box for each sampled time (found false where the subject was lost); to make a title, sticker, arrow or mask follow the subject, turn the boxes into clip.set_transform (or clip.set_mask) keyframes, no tracking of your own needed. look: your own typed questions about what is on screen, answered on every sampled frame (to find where something happens from plain words, media.seek). silence: stretches quieter than threshold_db for at least min_seconds. scenes: times the picture changes by more than threshold (0–100; hard cuts between ordinary shots score about 10 to 30, see threshold), with each cut's score. speech: the probability that someone is speaking, every 32 ms (Silero VAD), and the spans where it is at least your min_probability; it hears speech, not noise or music, so a cough or a door is not speech. loudness: integrated loudness (LUFS), true peak (dBTP), loudness range (LU), RMS level and noise floor (dBFS) over the whole media or from_seconds to until_seconds. To line up two recordings, media.correlate. sounds: what can…
| Name | Type | Req | Description |
|---|---|---|---|
| at_seconds | array | – | look, colour: sample exactly these times instead |
| cluster_threshold | number | – | speakers: when the count is unknown, how different two voices must be to count as two people; lower finds more speakers (try 0.5 to 0.9) |
| every_seconds | number | – | look, colour: sample a frame this often. Default 1 |
| from_seconds | number | – | Start sampling or measuring here (any kind but beats, and faces and scenes on media). Default 0 |
| idempotency_key | string | – | – |
| kind | string | yes | transcript: words with times (whisper.cpp, then aligned to the sound); beats: tempo, beat times and the onset strength at each; faces: face boxes over time; track: follow a region; look: your own que… |
| labels | array | – | sounds: the sounds to report, named as in video://catalog/sounds (e.g. ["Laughter", "Applause", "Cough"]). Default: every sound that reaches min_probability somewhere |
| language | string | – | transcript: e.g. 'en', or 'auto' |
| media_id | string | – | Analyze imported media (media seconds) |
| min_probability | number | – | speech (required): a 32 ms frame with at least this speech probability is speech; sounds (required): a 40 ms frame has the sound when its probability is at least this |
| min_seconds | number | – | silence (required): only stretches at least this long |
| model | string | – | transcript: whisper size, on the server CPU. Bigger is more accurate and slower: small takes about 3 times as long as base, tiny about half |
| percentiles | array | – | colour: the percentiles to give of each channel |
| questions | object | – | look: id (yours, not shown to the model) to question; up to 32, all asked of every frame |
| region | object | – | track: the box to follow (top-left x, y and size) and when |
| regions | object | – | colour: named boxes to measure besides the whole frame (frame), up to 16 |
| speakers | integer | – | speakers: how many people speak, when you know (or give cluster_threshold) |
| state | object | – | look: text or JSON entries shown beside `frame` on every frame, by name, for questions to refer to (a script line, a definition) |
| threshold | number | – | scenes (required): how much the picture must change to count as a cut, 0–100 (FFmpeg scdet's score): the change from the frame before, in percent of full range, less the change just before it (so ste… |
| threshold_db | number | – | silence (required): quieter than this counts as silence, dBFS |
| timeline_id | string | – | Analyze an edit as it plays (timeline seconds): its mix (speech, loudness, silence, sounds, speakers, voice) or its picture (faces, track, look, scenes, colour, picture) |
| until_seconds | number | – | Stop sampling or measuring here. Default: the end |
| with_previous | boolean | – | look: also show the frame sampled before, as `previous` |
Structured output declared, but exposes no named fields.
No examples provided.
media.correlate Sync offset ~365
Find how far one recording sits from another: the sync offset of b against a, by cross-correlating their sound, with how strongly they match. Use it to line up a camera with a separate microphone, two cameras of the same moment, or a re-recorded line against the original. Each side is imported media or an edit's mix (timeline_id), over its own from_seconds to until_seconds, so you can match a short stretch of b against a longer stretch of a. offset_seconds: b's moment t lines up with a's moment t + offset_seconds, each in its own seconds. To line them up on a timeline, place b's clip so it starts offset_seconds later than a's (earlier when negative), source starts being equal. strength is the correlation of the overlapping sound at that offset, -1 to 1: near 1 the same sound, near 0 unrelated; next_best is the best offset outside the winning peak, and a strength close to it means the match is unclear (repetitive sound, or not the same moment). Offsets are searched up to max_offset_seconds either way, where at least half of the shorter sound overlaps the other. Up to 600 s read from each side. A side with no sound in its range (no audio stream, a range past its end, or digital silence) fails with no_audio, naming which side. Short reads answer at once; longer ones return a task or job.
| Name | Type | Req | Description |
|---|---|---|---|
| a | object | yes | The sound to line up against: the reference |
| b | object | yes | The sound whose offset you want |
| idempotency_key | string | – | – |
| max_offset_seconds | number | yes | Search offsets up to this far either way |
Structured output declared, but exposes no named fields.
No examples provided.
media.delete Delete media ~128
Delete a media for good, so an old or wrong version stops turning up: its file here, proxy, mezzanine and media.analyze results go, and its room comes back to the storage cap. A path import leaves the original in its folder; exports made from it stay. Timelines that use it keep their ids and edits but no longer render (media_offline names it, as for expired media): replace those clips with clip.replace. Importing the same file again makes a new media_id.
| Name | Type | Req | Description |
|---|---|---|---|
| media_id | string | yes | Media handle from media.import or a create.* tool |
| Name | Type | Req | Description |
|---|---|---|---|
| deleted | boolean | yes | – |
| freed_bytes | number | yes | What it held here: the file, its proxy and mezzanine, and its analyses |
| media_id | string | yes | Media handle from media.import or a create.* tool |
| storage | object | yes | – |
No examples provided.
media.get Get media ~447
Status, where it came from (origin: the import source, the export of which timeline_id and preset, or the create.* tool and the media it was made from), a signed download url for the file (media is kept 7 days after last use: save the file from it to keep it, and media.import it again later) and a page_url that plays or shows it in any browser (to give a person), its format (extension and mime_type, read from the file, not its name), everything the probe read (probe: the format and every stream, e.g. pix_fmt, colour space, primaries, transfer), duration, frame rate, conform reasons, integrated loudness and summaries of media.analyze results under analyses (counts only: analyses.transcript.words is how many words). While it is importing or being made, job names the job doing it and its progress. include returns those results at the top level, e.g. include ["transcript"] gives transcript.words, the list of words with times. For long media, read them in pieces: from_seconds and to_seconds keep what overlaps that stretch of the media, offset and limit page through what is left, and pages says, per analysis, the index of the first item returned in the whole list (for transcript words, the index clip.add source {words} takes), how many matched and next_offset for the next page (null at the end).
| Name | Type | Req | Description |
|---|---|---|---|
| from_seconds | number | – | Only what ends after this media time: transcript words, beats, speaker turns, frames |
| include | array | – | Also return these analyses (otherwise only their summaries): in full, or the piece from_seconds, to_seconds, offset and limit choose. One never run on this media is absent, and warnings says so |
| limit | integer | – | At most this many items of each included list; pages says where the next starts |
| media_id | string | yes | Media handle from media.import or a create.* tool |
| offset | integer | – | Skip this many items of each included list (after the time range) |
| to_seconds | number | – | Only what starts before this media time |
| Name | Type | Req | Description |
|---|---|---|---|
| analyses | object | – | Summaries of media.analyze results (counts, not data); include puts each in full at the top level |
| analysis | – | yes | – |
| beats | object | – | – |
| codecs | object | yes | – |
| colour | object | – | – |
| conform | array | yes | Why a mezzanine was made (vfr, rotation, hdr, codec); empty when not needed |
| created_at | string | – | When it was imported or made |
| duration_seconds | number|null | yes | – |
| error | object | – | – |
| expires_at | string | – | When url and page_url stop working |
| extension | string | – | The extension its format takes (.png, .mp4), from the file itself, not its name |
| faces | object | – | – |
| fingerprint | string|null | – | Identity of the file (BLAKE3 of its size and first, middle and last 4 MB), as export.video gives |
| font_family | string | – | Fonts: the family; use font_media_id in a text style to set text in it |
| fps | – | yes | – |
| has_audio | boolean | yes | – |
| has_video | boolean | yes | – |
| height | number|null | yes | – |
| job | object | – | media.get, while importing or being made: the job doing it |
| kind | – | yes | – |
| look | object | – | – |
| media_id | string | yes | Media handle from media.import or a create.* tool |
| mime_type | string | – | Its MIME type, likewise |
| name | string | yes | – |
| origin | object | – | Where it came from, to tell one version from another: the import source, the export of which timeline, or the create.* tool and the media it was made from |
| page_url | string | – | media.get: a page that plays or shows the file in any browser, with no sign-in, to give a person; works until expires_at |
| pages | object | – | media.get with a range, offset or limit: per included analysis, which piece of its list came back |
| picture | object | – | – |
| probe | object | – | media.get: everything FFprobe read from the file, as it gives it: the format and every stream (codec, pix_fmt, color_space, color_primaries, color_transfer, color_range, bit depth, sample rate, chann… |
| sound_seconds | number | – | How long the sound runs; under duration_seconds, the end is soundless |
| sounds | object | – | With include ["sounds"]: the latest media.analyze sounds run, every probability included |
| speakers | object | – | – |
| speech | object | – | With include ["speech"]: the speech probability of every 32 ms frame |
| status | string | yes | importing: not probed yet; processing: usable in edits, proxy still rendering; ready; failed |
| transcript | object | – | With include ["transcript"]: every word as {text, start, end, confidence} |
| upload | object | – | media.import {source: {upload}}: where to send the file. It imports once the last byte arrives (the PUT answers with the job_id); media.get says where it stands |
| url | string | – | media.get: a signed download of the file itself, once it has one |
| voice | object | – | – |
| warnings | array | – | create.motion and create.still: what went wrong in the page as it rendered (uncaught errors, console errors, files blocked or not found, tags never closed). It rendered anyway |
| width | number|null | yes | – |
No examples provided.
media.import Import media ~428
Import the user's own video, audio, image or font to edit with or place as is: probe, fingerprint, conform if needed (VFR, rotation, HDR → BT.709), 540p proxy and integrated loudness. A reference image to recreate needs no import: rebuild it with create.motion or create.still. Long-running: returns a task, or {job_id, media_id} for job.get. The media_id is usable in edits as soon as the job reports processing, so with many files, start every import first and edit while they finish. Sources: an http(s) URL (Dropbox and Google Drive share links work as they are), {upload: {name, bytes}} for a file you send yourself (the answer is the new media with upload.url: PUT the file there, curl --fail -T file '<url>', and it imports once the last byte arrives, with no base64 in a message; a broken PUT resumes as media.upload says), the upload_id of a file sent through media.upload, a small file as a data: URI (up to 8 MB, with its sha256 so a garbled copy is refused), the job_id of a completed export.video (its render becomes media), or still: one frame of a video (media_id) or of an edit (timeline_id) as a PNG still at full size, to place as a picture, cut out or reuse. A URL is fetched once, when the import starts: it only has to work until then (a short-lived signed link from the user's own storage is enough), and the copy stays here 7 days after last use. A file the user attached in the chat comes as file, when the client passes it. Importing the same file again returns the same media_id.
| Name | Type | Req | Description |
|---|---|---|---|
| file | object | – | A file the user attached in the chat, as the client passes it (instead of source) |
| idempotency_key | string | – | – |
| name | string | – | – |
| source | – | – | Where the file is. Give this or file |
Structured output declared, but exposes no named fields.
No examples provided.
media.list List media ~120
What the user has here: imported and created media, and files they sent that aren't imported yet (through media.upload, or dropped on their account page at cuttingfloor.ai/account), with names, sizes and when each expires, plus the account's storage used and its cap. When the user mentions a file they sent or dropped ("the interview I uploaded"), find it here by name and media.import {source: {upload_id}} it; complete says it is all in. Listing does not count as a use, so nothing here lasts longer for being listed.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| media | array | yes | Imported and created media, most recently used first (up to 200) |
| media_count | integer | yes | All media held, including any not listed |
| storage | object | yes | – |
| upload_count | integer | yes | – |
| uploads | array | yes | Files sent to the server and not imported yet, newest first (up to 200): through media.upload, or dropped on the account page by the user |
No examples provided.
media.listen Find sounds by description ~834
Find when something can be heard in a video or audio file, or in an edit as it plays, from what you describe in plain words, so you go straight to it instead of listening to it all: laughter, applause, a cough, a door slamming, a phone ringing, a dog barking, music starting, someone typing, a car passing. Name each sound and say what it is; you get back the stretches of time where each one can be heard, with the moment it is clearest. Use it to: - Find reactions and moments: where the crew or the audience laughs, where people clap or cheer, a gasp, a scream. - Find trouble to cut or cover: a cough, a sneeze, a phone going off, a door, a siren, wind on the microphone, a click or a bump. - Find sound to cut on: a clap, a bang, a hit, the music starting or stopping. HOW IT WORKS. FlexSED, an open-vocabulary sound detection model (local, on the CPU; about 15 to 20 times faster than real time), listens to every 40 ms of the sound and gives each description the probability that it can be heard there. Describe the sound itself, briefly, as you would caption it ("laughter", "a crowd cheering", "glass breaking"), not the scene or a judgment; it is asked as "The sound of <your words>". Short, common sound names work best; put each sound you want in its own entry, up to 16 a call, all listened for at once. LIMITS. Inside music it hears "music", not drums, instruments or single hits (for hits in music, media.analyze beats). A short sound can score higher over a short from_seconds to until_seconds around it. READING THE RESULT. matches gives, per sound, each run of frames at or above min_probability (default 0.5) with its start, end, peak_seconds and frame (for render.frames; media frames for video, timeline frames for an edit). peaks gives the highest probability each sound reaches, and scores every probability (step_seconds apart), for your own cut-off. A sound whose peak stays well under your cut-off is most likely not there; one hovering near it is worth a listen or a lowe…
| Name | Type | Req | Description |
|---|---|---|---|
| from_seconds | number | – | Start listening here (media or timeline seconds). Default 0 |
| idempotency_key | string | – | – |
| media_id | string | – | Listen to imported media (media seconds) |
| min_probability | number | – | A frame matches a sound when its probability is at least this |
| sounds | object | yes | Name (yours) to the sound in plain words: what it is, as you would caption it ("laughter", "a crowd cheering", "a door slams", "a phone ringing", "a dog barking", "keyboard typing"). Up to 16, each l… |
| step_seconds | number | – | scores: one value per this many seconds (the highest in each). Default 0.04, every frame, coarser when that would be too many |
| timeline_id | string | – | Listen to an edit as it plays (timeline seconds): its mix, with cuts, gaps, levels and effects |
| until_seconds | number | – | Stop here. Default: the end |
Structured output declared, but exposes no named fields.
No examples provided.
media.seek Seek by natural language ~1,131
Find the moments in a video, or in an edit as it renders, that match what you describe in plain words, so you go straight to them instead of watching it all. Name each criterion and say what a matching frame shows; you get back the stretches of time where each one holds, with the time and picture of the best frame in each. Use it to: - Check quality: find frames that are too dark or blown out, out of focus, black or blank (a gap in an edit), with a microphone or crew in shot, with text cut off at the edge, or with a caption over a face. With timeline_id it checks the edit as it renders, titles, effects and gaps included. - Watch efficiently: looking through a whole video costs you a picture for every moment. Seek judges the frames for you and says where things are, so you look only there: where the demo starts, where the speaker holds up the product, where the second location begins. - Pick shots: B-roll or a thumbnail moment that fits a description ("a wide shot of a city street at night", "she smiles straight at the camera with her eyes open"). - Find changes: with with_previous, where the slide changes, someone walks in or the camera angle switches ("`frame` shows a different slide from `previous`"). - Find room or trouble for overlays: where the lower third stays empty for a title, or where a logo, a licence plate or a face appears that needs a blur. HOW TO WRITE CRITERIA. TypeSafe Jev, a fast judgment model (preview), judges each sampled frame on its own, as a picture named `frame` (about 512 px on its long edge; with with_previous also `previous`, the frame sampled before), and gives each criterion the probability that it is true. A criterion is a statement or a question about how the picture looks. Keep one judgment to a criterion, and say what shows rather than a verdict: not "bad lighting" but "the faces are too dark to make out their eyes"; not "a good thumbnail" but "a person looks straight at the camera with eyes open and mouth closed". Criteria that n…
| Name | Type | Req | Description |
|---|---|---|---|
| at_seconds | array | – | Sample exactly these times instead |
| criteria | object | yes | Name (yours, not shown to the model) to what a matching frame shows: a statement or question about the picture, or {instructions, criteria: {true, false}} to say what counts as yes and as no. Up to 3… |
| every_seconds | number | – | Sample a frame this often. Default 1 |
| from_seconds | number | – | Start sampling here. Default 0 |
| idempotency_key | string | – | – |
| media_id | string | – | Seek in imported media (media seconds) |
| min_probability | number | – | A frame matches a criterion when its probability is at least this |
| state | object | – | Text or JSON entries shown beside `frame` on every frame, by name, for criteria to refer to (a script line, what a term means) |
| timeline_id | string | – | Seek in an edit as it renders (timeline seconds): gaps, cuts, titles and effects included |
| until_seconds | number | – | Stop here. Default: the end |
| with_previous | boolean | – | Also show the frame sampled before, as `previous`, for criteria about change. The first sample is only shown as `previous` |
Structured output declared, but exposes no named fields.
No examples provided.
media.upload Upload media ~210
A signed URL to send one of the user's files to this server, for files on their machine that the server can't read: PUT the file there (curl --fail -T interview.mp4 '<url>'), then media.import {source: {upload_id}}. The URL needs no other credentials and works for a day. A broken upload resumes: HEAD the url for Upload-Offset (the bytes received), then PUT the rest with Content-Range: bytes <offset>-<last>/<total>. Large files can go in parts the same way. Without a way to send it yourself, give the user the url: opened in a browser it is a page to drop the file on, which sends it and resumes on its own. media.import says whether the file is all in yet.
| Name | Type | Req | Description |
|---|---|---|---|
| bytes | integer | – | The file size, to be refused now rather than part way if it is too big |
| name | string | yes | The file name with its extension, e.g. interview.mp4 |
| Name | Type | Req | Description |
|---|---|---|---|
| expires_at | string | yes | – |
| how | string | yes | – |
| max_bytes | number | yes | – |
| upload_id | string | yes | Upload handle from media.upload |
| url | string | yes | Signed: PUT the file here; no other credentials needed |
No examples provided.
qa.check QA check ~326
Measure the rendered timeline: gaps in the picture, offline media, rendered against expected frame count, integrated loudness (LUFS) and true peak (dBTP), plus black and silent stretches when you give their settings. With speech {min_probability}, every cut is listed with speech facts: the speech probability of the mix just before and after it (Silero VAD), whether it lands while someone is speaking, the seconds without speech each side, and, per clip edge, the transcript word it cuts through and how much of that word is lost (from the media's transcript, when it has one). Facts only, no verdicts. A timeline up to 20 s is checked within the call (which waits its turn for a render when the server is busy); longer ones return a task or job.
| Name | Type | Req | Description |
|---|---|---|---|
| black | object | – | Find black stretches with these settings; left out, none are looked for |
| range | object | – | – |
| silence | object | – | Find silent stretches in the mix with these settings; left out, none are looked for |
| speech | object | – | List every cut with speech facts (Silero VAD on the mix, and the transcript word each clip edge cuts through); left out, cuts are not listed. Only junctions between two clips are listed, so a lone cl… |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
Structured output declared, but exposes no named fields.
No examples provided.
range.extract Extract range ~171
Remove everything in a frame range on the given tracks (default all unlocked, so a picture range takes the sound under it too: lock a track with track.set or name track_ids to keep it) and close the gap. For cutting out silences, filler words or mistakes across all tracks at once. changed lists each clip removed or cut, with its track; a warning says when sound went with the picture. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| range | object | yes | Timeline frames, end exclusive |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_ids | array | – | Tracks to act on. Default: every unlocked track |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
range.lift Lift range ~155
Remove everything in a frame range on the given tracks (default all unlocked, so a picture range takes the sound under it too: lock a track with track.set or name track_ids to keep it), leaving a gap. changed lists each clip removed or cut, with its track; a warning says when sound went with the picture. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| range | object | yes | Timeline frames, end exclusive |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_ids | array | – | Tracks to act on. Default: every unlocked track |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
render.frames Render frames ~280
Look at a timeline (or a media file): up to 16 frames as one contact-sheet image (layout grid) or one image each (single). Media frames are numbered at the media frame rate; a picture that can be see-through (alpha or a palette) is shown over a light checkerboard, so transparency shows as checks and a background baked into the file as itself. size full: up to 4 frames at the sequence (or media) size from the original media, for thumbnails and stills to keep. format webp (default), png or jpeg, with quality for webp and jpeg. Each image comes inline and with a signed download url.
| Name | Type | Req | Description |
|---|---|---|---|
| format | string | – | jpeg: for places that take nothing else; png: lossless |
| frames | array | yes | Timeline frames, or media frames at the media rate |
| layout | string | – | grid: one contact sheet |
| long_edge | integer | – | – |
| media_id | string | – | Media handle from media.import or a create.* tool |
| quality | integer | – | WebP and JPEG, 1–100 (default 82 WebP, 85 JPEG) |
| size | string | – | full: the sequence size from original media (long_edge ignored) |
| timeline_id | string | – | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| images | array | yes | – |
No examples provided.
skills.get Read a skill ~69
The whole skill as Markdown: when to use it, what it needs, numbered Steps naming the tools to call, a decision table and the rules to keep. Follow the Steps in order.
| Name | Type | Req | Description |
|---|---|---|---|
| slug | string | yes | From skills.search, e.g. "cut-pauses-ums-and-restarts" |
| Name | Type | Req | Description |
|---|---|---|---|
| author | string | yes | Who wrote the skill; credit them when you name it |
| author_url | – | yes | A link to the author or the work the skill came from; null when there is none |
| description | string | yes | – |
| markdown | string | yes | The whole skill, frontmatter included: follow its Steps |
| name | string | yes | – |
| related | array | yes | – |
| slug | string | yes | – |
| version | number | yes | – |
No examples provided.
skills.search Find a skill ~180
Find step-by-step instructions for a common job, written for you to follow with these tools: cutting the pauses, ums and restarts out of a talking video, for one. Search in the user's words; every skill comes back best first with its score (roughly 0 to 1). Below 0.1 is unrelated; a real fit usually scores 0.15 to 0.45, lower for a short or general ask, and an unrelated skill that shares words can score as high, so read the description of each one above 0.1 to judge the fit. Read one with skills.get, then follow its Steps.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | Most matches to return, best first |
| query | string | yes | What the user wants done, in their words, e.g. "remove the ums from my video" |
| Name | Type | Req | Description |
|---|---|---|---|
| skills | array | yes | Best first; empty when nothing fits |
No examples provided.
text.set Set text ~224
Change a title's or caption's text, or restyle many at once (clip_ids, or every one on track_id). The style is merged; null turns an outline, shadow, box or highlight off (and max_lines back to all lines). spans (one clip) restyle part of its text. Fonts: video://catalog/fonts, or an unknown font is refused with the close matches. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| clip_ids | array | – | Titles or captions |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| spans | – | – | Only with a single clip: replaces its spans (null removes them). New text drops spans it doesn't give |
| style | – | – | Merged into each clip's style; null fields reset |
| text | string | – | Only with a single clip |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | Instead of clip_ids: every title and caption on a track |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
timeline.create Create timeline ~102
Start an empty timeline. Returns its timeline_id and its tracks (ids for track_id). Timelines are immutable: every edit returns a new timeline_id, and older ids stay valid for 24 hours after last use. For work longer than a day, keep the timeline.get document on your side and restore it with timeline.load.
| Name | Type | Req | Description |
|---|---|---|---|
| name | string | – | – |
| sequence | object | yes | – |
| tracks | object | – | How many empty tracks to start with |
| Name | Type | Req | Description |
|---|---|---|---|
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| tracks | array | yes | The new tracks; edits take the id (track_id), not the name |
No examples provided.
timeline.diff Diff timelines ~51
Which clips, tracks and settings differ between two timelines.
| Name | Type | Req | Description |
|---|---|---|---|
| from | string | yes | Timeline handle; every edit returns a new one |
| to | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
No examples provided.
timeline.get Get timeline ~84
The full timeline: tracks, clips (frames, end exclusive), link groups and the media it uses. The server keeps a timeline 24 hours after last use: for work longer than a day, save this document on your side (it is the edit) and restore it with timeline.load.
| Name | Type | Req | Description |
|---|---|---|---|
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| duration | integer | yes | Timeline length in frames |
| effects | array | – | Effects on the whole picture and mix |
| master | object | – | – |
| media | object | yes | – |
| name | string | yes | – |
| nested | object | – | Timelines nested as compound clips, at any depth: each has this shape without timeline_id, parent or nested |
| parent | string|null | yes | – |
| sequence | object | yes | – |
| timeline_id | string | yes | – |
| tracks | array | yes | Video tracks bottom to top, then audio tracks |
No examples provided.
timeline.history Timeline history ~55
The chain of edits that made this timeline, newest first. To go back, keep using an older timeline_id.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| entries | array | yes | – |
| truncated | boolean | yes | – |
No examples provided.
timeline.load Load timeline ~77
Restore a timeline from the document timeline.get returned, e.g. after it expired (24 h after last use). Its media must still be here: media expires 7 days after last use, so import saved files again with media.import and swap them in with clip.replace.
| Name | Type | Req | Description |
|---|---|---|---|
| timeline | object | yes | A timeline as returned by timeline.get |
| Name | Type | Req | Description |
|---|---|---|---|
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
No examples provided.
timeline.set_sequence Set sequence ~139
Change resolution, frame rate or audio format. Clip times are kept in time, so they survive a rate change. Each picture is refitted to the new shape by its fit (contain by default, which leaves bars where the shapes differ); the result warns how many clips no longer fill the frame, and clip.set_transform {fit: "cover"} fills it. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| sequence | object | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
title.add Add title ~289
Text on a video track, over the tracks below: styled with font, size, colour, outline, shadow and box, placed by x/y, revealed with a typewriter or word animation. A style field you leave out takes the published default (video://catalog/text: bold white Inter at 9% of the frame height, centred, with a soft shadow); timeline.get shows the full style. spans style part of the text (words first_word to last_word, from 0: one word in yellow or bold). It is a clip, so transforms, masks, animations and transitions apply. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| at_frame | integer | – | Default: after the last clip on the track |
| duration_frames | integer | – | Default 3 s |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| spans | array | – | Style part of the text: each span restyles words first_word to last_word (from 0: the timed words, or the text split at spaces), e.g. one word in yellow |
| style | – | – | – |
| text | string | yes | Line breaks with \n |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | yes | A video track; text composites over the tracks below |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
track.add Add track ~87
Add an empty video or audio track. New video tracks go on top. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| kind | string | yes | – |
| name | string | – | – |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
track.remove Remove track ~73
Remove a track and its clips. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
track.set Set track ~109
Rename, mute (audio), hide (video) or lock a track. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| hidden | boolean | – | – |
| locked | boolean | – | – |
| muted | boolean | – | – |
| name | string | – | – |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
transition.add Add transition ~296
Transition at the cut after a clip, into the clip that touches it (video://catalog/transitions). Linked audio gets a crossfade at the same cut. kind crossfade is sound only: on a video clip it crossfades the linked audio and the picture cuts hard (use mode handles for that). mode overlap (default) overlaps the clips and moves later clips earlier; handles uses source past the cut and moves nothing. changed lists each transition added, with its track and kind. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| clip_id | string | yes | The clip before the cut; the clip after it must touch it |
| duration_frames | integer | yes | – |
| kind | string | yes | video://catalog/transitions; crossfade on audio tracks |
| mode | string | – | overlap: the clips overlap by the duration and later clips move earlier; handles: use source past the cut, nothing moves |
| other_tracks | string | – | overlap only: what the other tracks do. stay: they don't move, so what sits after the cut there plays later against the picture (the result warns which clips); extract: the same span comes out of the… |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
transition.remove Remove transition ~77
Remove a transition; the cut becomes a hard cut. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| transition_id | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
transition.set Set transition ~89
Change a transition's kind or duration. Returns the edit result (see the server instructions).
| Name | Type | Req | Description |
|---|---|---|---|
| duration_frames | integer | – | – |
| kind | string | – | – |
| preview | string | – | inline: image in the result; link: URI that renders on read; none |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| transition_id | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| changed | array | yes | – |
| clip_id | string | – | – |
| clip_ids | array | – | – |
| parent | string | yes | Timeline handle; every edit returns a new one |
| preview | – | yes | – |
| timeline_id | string | yes | Timeline handle; every edit returns a new one |
| track_id | string | – | – |
| warnings | array | yes | – |
No examples provided.
How do I install the cuttingfloor MCP server?
cuttingfloor is a hosted endpoint at https://api.cuttingfloor.ai/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
remote · api.cuttingfloor.ai
claude mcp add --transport http mvp2o-ai-cuttingfloor 'https://api.cuttingfloor.ai/mcp'
What is the cuttingfloor MCP server?
cuttingfloor is an MCP server listed in the public MCP registry as io.github.Mvp2o-ai/cuttingfloor. Hosted video editing for AI agents: cut, caption, mix and export real video. Sign in with OAuth. This page covers its hosted endpoint (https://api.cuttingfloor.ai/mcp).
Is the cuttingfloor MCP server safe to use?
cuttingfloor scores 81 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the cuttingfloor MCP server expose?
cuttingfloor exposes 72 tools: timeline.create, timeline.get, timeline.load, timeline.history, timeline.diff, and 67 more. Their descriptions and schemas cost roughly 19,313 tokens of context every time the server is loaded.
Does the cuttingfloor MCP server require authentication?
Yes. cuttingfloor asked us for credentials when we connected, so you will need to authorise it in your MCP client before it can do anything.
Is the cuttingfloor MCP server still maintained?
cuttingfloor is still listed as active in the MCP registry. We last reached this channel on 5 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What did VerifyMCP test on the cuttingfloor MCP server?
VerifyMCP ran 14 checks on cuttingfloor, covering TLS certificate, Authorization, HTTPS, HSTS header and 10 more. Supply Chain Security and Provenance & Transparency do not apply to a hosted endpoint, so they were not tested. The score does not measure everything, and the scoring documentation lists what it leaves out.