FilmLab
REMOTE · MCP-LAB.THEFILMRADAR.COM · SCANNED OCT 5
AI film lab for filmmakers: generate and review images/clips with input provenance, cost preflight
Available components
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 Security89
- 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 offers only Dynamic Client Registration (RFC 7591), which MCP 2026-07-28 deprecated in favour of Client ID Metadata Documents. View diagnostics → Partial
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability76
- 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 32442 tokens (~345/item across 94 items; 93 tools + 1 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 Management3
- Stability check failed: schema churn in the 1 day we've observed: 0 tool removals, 1 breaking changes, 0 auth/transport breaks, 4 additions. See how to fix → Fail
Tool Coverage100
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 100% of tool parameters carry a description.Pass
Tool Safety75
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- Manipulation not yet verified: none of the 95 captured unit(s) of tool text has been judged yet, so we will not certify text no model has read as clean.Unverified
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
- Supports UI / widget rendering.Pass
How do I install the FilmLab MCP server?
FilmLab is a hosted endpoint at https://mcp-lab.thefilmradar.com/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 · mcp-lab.thefilmradar.com
claude mcp add --transport http com-thefilmradar-filmlab 'https://mcp-lab.thefilmradar.com/mcp'
{
"mcpServers": {
"com-thefilmradar-filmlab": {
"url": "https://mcp-lab.thefilmradar.com/mcp"
}
}
} {
"servers": {
"com-thefilmradar-filmlab": {
"type": "http",
"url": "https://mcp-lab.thefilmradar.com/mcp"
}
}
} [mcp_servers.com-thefilmradar-filmlab] url = "https://mcp-lab.thefilmradar.com/mcp"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"com-thefilmradar-filmlab": {
"type": "remote",
"url": "https://mcp-lab.thefilmradar.com/mcp",
"enabled": true
}
}
} openclaw mcp add com-thefilmradar-filmlab --url 'https://mcp-lab.thefilmradar.com/mcp' --transport streamable-http
mcp_servers:
com-thefilmradar-filmlab:
url: "https://mcp-lab.thefilmradar.com/mcp" {
"McpServers": {
"com-thefilmradar-filmlab": {
"Transport": "http",
"Url": "https://mcp-lab.thefilmradar.com/mcp"
}
}
} assistant mcp add com-thefilmradar-filmlab -t streamable-http -u 'https://mcp-lab.thefilmradar.com/mcp'
{
"mcpServers": {
"com-thefilmradar-filmlab": {
"type": "http",
"url": "https://mcp-lab.thefilmradar.com/mcp"
}
}
} The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.
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 Oct 26 0
- Stability: unverified → fail ▼ security
- The server rewrote its instructions, which are the text every model session reads security
- Tool “lab_whoami” rewrote its description, which is the text the model reads security
- 4 Oct 26 +40
- Transport: unverified → pass ▲ security
- Injection markers: unverified → pass ▲ security
- First check of Judged manipulation: unverified security
- Authorization: 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. security
- MCP protocol: unverified → pass ▲ functional
- Schema quality: unverified → 100 ▲ functional
- Tool coverage: unverified → 100 ▲ functional
- First check of Schema quality: fail functional
- First check of Schema quality: excellent functional
- First check of Schema quality: fail functional
- First check of Capabilities: pass functional
- First check of Destructive annotations: 100 functional
- First check of Tool coverage: 100 functional
- 28 Sept 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 25 Sept 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 26 Aug 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 17 Aug 26 0
First indexed and scored.
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 7 Oct 2026 · Probed https://mcp-lab.thefilmradar.com/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=thefilmradar.com | CN=WE1,O=Google Trust Services,C=US | 6 Sept 2026 | 5 Dec 2026 | ECDSA 256 | ECDSA-SHA256 | e40c48743b6b9189133ceb0cd0ece3c0 |
| SANs: thefilmradar.com, mcp-lab.thefilmradar.com, *.mcp-lab.thefilmradar.com | ||||||
| CN=WE1,O=Google Trust Services,C=US (CA) | CN=GTS Root R4,O=Google Trust Services LLC,C=US | 13 Dec 2023 | 20 Feb 2029 | ECDSA 256 | ECDSA-SHA384 | 7ff31977972c224a76155d13b6d685e3 |
| CN=GTS Root R4,O=Google Trust Services LLC,C=US (CA) | CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE | 15 Nov 2023 | 28 Jan 2028 | ECDSA 384 | SHA256-RSA | 7fe530bf331343bedd821610493d8a1b |
Background: What to check on a remote MCP endpoint →
DNSSEC insecure
Validation of mcp-lab.thefilmradar.com. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | trust_anchor | 20326, 38696 | 8, 8 | Verified |
| com. | present | 19718 | 13 | Verified |
| thefilmradar.com. | absent | 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 realm="OAuth", resource_metadata="https://mcp-lab.thefilmradar.com/.well-known/oauth-protected-resource/mcp", scope="filmlab:read filmlab:write"
Bearer realm="OAuth", resource_metadata="https://mcp-lab.thefilmradar.com/.well-known/oauth-protected-resource/mcp", scope="filmlab:read filmlab:write" Protected resource metadata
| Document | https://mcp-lab.thefilmradar.com/.well-known/oauth-protected-resource/mcp |
|---|---|
| Retrieved | Yes |
| Resource | https://mcp-lab.thefilmradar.com/mcp |
| Authorisation server | https://mcp-lab.thefilmradar.com |
Background: How OAuth 2.1 works in the 2026 MCP spec →
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://mcp-lab.thefilmradar.com/mcp | Verified | 200 | |
| http (plaintext) | http://mcp-lab.thefilmradar.com/mcp | HTTPS enforced | 301 | https://mcp-lab.thefilmradar.com/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 →
lab_abgleich Prompt Check ~219
Prompt check of ONE generated image or clip: does the result show what its prompt asked for? The backend first DESCRIBES the result without seeing the prompt, then splits the prompt into checkable claims (counts as digits, quoted text, identity, place, framing, style; for clips the motion) and scores each. verdict: pass | mismatch | unverified, plus `checks`, `issues` and the neutral `beschreibung`. Runs automatically after every generation (lab_job_status shows it as `abgleich`); call this to read it, or with neu=true to check again. On mismatch: offer at most ONE retry per original, lead the new prompt with the failed claim; never re-run a video on your own. Free for the account (FilmLab carries the cost).
| Name | Type | Req | Description |
|---|---|---|---|
| id | string | yes | Asset id (positive integer as string) |
| neu | boolean | – | Discard an existing result and check again (ignored within 60 s of the last check) |
| reference_type | string | yes | What kind of asset |
No output schema declared.
No examples provided.
lab_add_clip Add Clip to Sequence ~529
Append a shot to a sequence's timeline. Call it once per shot, in cutting order — `start_time` is computed from the clips already there, so appending in order is enough. The continuity pass reads exactly this timeline: a shot missing here is a shot nobody checks. Returns the new clip's index and the sequence's total duration.
| Name | Type | Req | Description |
|---|---|---|---|
| duration_seconds | number | – | Clip duration in seconds. Backend default is 5.0 — pass the clip's REAL length, otherwise the sequence duration and the render drift apart |
| metadata | object | – | JSON stored alongside the clip. The continuity pass reads video_id (a generated video's id — it checks the start image), image_url (a frame URL), location_id and scene_num from here; for a 'lower_thi… |
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
| source_id | string | – | For type 'image': the id of a generated image (from lab_generate). For type 'video': source_id is IGNORED by the continuity pass (a video id used to be misread as an unrelated image, see vault/follow… |
| start_time | number | – | Absolute start on the timeline. Omit to append after the last clip (the normal case); set it only to place a clip deliberately |
| text | string | – | Text content for type 'title', 'credits' or 'lower_third' |
| type | string | yes | What this clip is. 'image' references a generated image via source_id; 'title', 'credits' and 'lower_third' carry `text` instead. 'lower_third' is an OVERLAY: appended without start_time it is laid o… |
No output schema declared.
No examples provided.
lab_add_media Add Media ~486
Bring your own video, audio or image into a project — not generated, yours. Two ways: `media_url` (a public http(s) address; the server fetches it, max 100 MB) or `file_name` + `content_type` (you get a signed `upload_url` and the `header` that are part of its signature: PUT the file's raw bytes there within the hour with EXACTLY those headers — the answer's `anleitung` has the ready curl line; a missing header is a 403). Either way the answer is the `asset_key` — use it in lab_timeline_apply as clips[].asset_key (video) or bett.asset_key (audio). Exception: in projects that check images, an uploaded image is not in the project after the PUT — the answer then carries `abschluss_noetig` and an `abschluss_key`; after the PUT call lab_add_media again with the same project_id, art, herkunft and `abschluss_key` only. That call checks the image and returns the `asset_key`, or says why it was refused. Free. Internal addresses are refused.
| Name | Type | Req | Description |
|---|---|---|---|
| abschluss_key | string | – | Only after an upload whose answer said `abschluss_noetig`: the `abschluss_key` from that answer, given AFTER the PUT returned 200. Give it instead of media_url and file_name |
| art | string | yes | What the file is |
| content_type | string | – | Media type of the upload, e.g. video/mp4, audio/mpeg — required with file_name |
| file_name | string | – | Name of a local file to upload, with extension. Give this OR media_url |
| herkunft | string | yes | Required, no default: 'eigen' = shot, recorded or made by people (no AI generation); 'ki' = generated or altered with AI. Decides the EU AI Act Art. 50 disclosure of every export that uses this file.… |
| media_url | string | – | Public http(s) URL of the file. Give this OR file_name |
| project_id | string | yes | Project id (from lab_list_projects) — positive integer as string |
No output schema declared.
No examples provided.
lab_analyze_location_image Analyze Location Image ~124
Read a photo and extract location fields from it (Gemini Vision, costs quota). Returns the fields — it does NOT create a board; feed the result into lab_create_location yourself. Note the payload: the image travels as base64 inside the call, so keep it small (a downscaled JPEG, not a full-resolution capture).
| Name | Type | Req | Description |
|---|---|---|---|
| image_base64 | string | yes | The image as base64. A data-URL prefix ('data:image/jpeg;base64,…') is stripped by the backend |
| mime_type | string | – | Defaults to 'image/jpeg' |
No output schema declared.
No examples provided.
lab_animate Animate Image to Clip ~2,629
Animate a completed image into a clip with a video model. Pick the model from lab_list_models; check the price with get_cost or let the user confirm a card via lab_propose. Costs credits when called without get_cost.
| Name | Type | Req | Description |
|---|---|---|---|
| abdruck_startframe | boolean | – | Generate frame one WITH the character's trained Figurenabdruck (LoRA) instead of taking the flat studio portrait from the character sheet — the clip then starts IN the scene rather than in a photo st… |
| apply_lens | boolean | – | Defaults to TRUE on the backend: the project's lens_state UrPrompt is appended to the prompt. Pass false only to deliberately opt out for this one call. |
| audio_url | string | – | URL of an existing audio track to lay under the clip. Independent of generate_audio, which asks the PROVIDER to synthesise its own. |
| camera_motion | string | – | Structured camera move sent to the model itself — only models whose lab_list_models entry lists camera_motions. Prefer this over prose when a keyframe chain must MOVE rather than cross-fade. |
| character_refs | array | – | Character ids (positive integer strings) whose reference image locks the cast's identity for this clip. On most providers only the FIRST character's image is used, as the start frame — with two chara… |
| character_states | object | – | Which STATE each character is in for this clip, as { characterId: stateKey } — e.g. { "12": "wet" }. A state is its own asset with its own full descriptor (clean / soaked / bloodied are three assets,… |
| character_voices | object | – | Bind a voice per character: character id -> fal voice id. Voice ids come from fal-ai/kling-video/create-voice — FilmLab keeps no voice registry of its own, the id is passed through unverified. Every… |
| duration_seconds | number | – | Clip length in seconds. Must lie inside the model's duration grid from lab_list_models; inside the grid the provider may snap to its nearest step. IGNORED when `shots` is given — then the rendered le… |
| end_image_id | string | – | id of a completed image to land the clip on (last frame). REQUIRES a start frame (source_image_id or character_refs) — an end frame alone is rejected before quota is spent. Only kling_3_pro. |
| familie | string | – | Pick the MODEL, not the access route: 'seedance-2.0' or 'seedance-2.5'. The server takes the cheapest route that this account may use and that can do the job (OpenRouter only with the user's consent;… |
| generate_audio | boolean | – | Ask for the model's native audio track. Only meaningful where lab_list_models says audio: schaltbar — models with audio: immer always have it, audio: nie never. |
| get_cost | boolean | – | Preflight: return the price in credits and your balance WITHOUT generating, reading the same catalog as lab_list_models. Do this before a batch or when the user wants to see the cost. For a card the… |
| in_sec | number | – | Cut the reference clip server-side before it reaches the provider: start of the excerpt, in seconds from the clip's beginning. Only together with out_sec, and only with source_video_id. |
| intent | string | – | Say WHAT the shot is instead of picking a model — the router chooses by priority and a cost cap. 'draft' and 'ambient' route to the cheapest provider, 'dialog' to the one with the steadiest faces, 'h… |
| kamerapfad | object | – | The camera move as INPUT instead of prose: an object following shared/kamerapfad/schema.json — poses over time (position, look-at or quaternion, metres, Y up) from the Kadrage terrain flight or drawn… |
| kamerapfad_url | string | – | Address of a kamerapfad JSON instead of the object — the url of a file in THIS project (lab_list_assets), e.g. the kamerapfad JSON that Kadrage writes next to its flight video. Fetched by the backend… |
| location_id | string | – | Location board id (from lab_list_locations). The board's `prompt_prefix_en` is appended to your prompt as 'Location: …', so a clip stays in the same place as the images shot there. A board WITHOUT a… |
| metadata | object | – | Free-form JSON stored alongside the video. Not interpreted by the pipeline. |
| ohne_keyframe | boolean | – | Skip the keyframe checkpoint on purpose. By default a paid clip only starts from a start image the user has approved (lab_review_asset verdict: accepted) — look at the frame first, then spend on the… |
| out_sec | number | – | End of the excerpt in the reference clip, in seconds. Must lie behind in_sec; both are set together or neither. Only with source_video_id. |
| project_id | string | yes | Project id (from lab_list_projects) |
| prompt | string | yes | Motion/camera instruction for the animation |
| referenzen | array | – | Typed references for the clip path (source_video_id). The position IS the address: the first image is @Image1, the first extra clip is @Video2 (the reference clip itself is @Video1), the first sound… |
| resolution | string | – | Resolution tier, exactly as listed for the model in lab_list_models. Omit for the model's default. Some models have no resolution field at all — then leave it out. |
| scene_ref | string | – | Scene label (e.g. 'SZ 04'). Stored on the row, so clips can be matched back to a shot list and reviewed per scene with lab_review_queue. |
| seed | integer | – | Seed for a repeatable run. Omit it and the project's stored seed applies (POST /api/lab/projects/{id}/einstellungen), and without that the provider picks one. The seed that actually rendered comes ba… |
| shot_type | string | – | How to cut a multi-shot clip. 'customize' (default) renders exactly the shots you gave. 'intelligent' lets the model decide the cutting — your shot prompts become a suggestion, not a spec. Belongs to… |
| shots | array | – | Multi-shot: a sequence of shots rendered as ONE output, holding character, place and voice across the cuts. This is NOT the same as several clips stitched together — those you pay for individually an… |
| source_image_id | string | – | id of a completed image (from lab_generate/lab_job_status) to animate — positive integer as string. Required unless get_cost is true |
| source_video_id | string | – | Animate FROM A CLIP instead of a still: the id of a generated video (from lab_animate/lab_job_status) or the asset key of an uploaded one (from lab_list_assets, asset_type 'video'). The run then goes… |
| tool | string | – | Video model id — copy it verbatim from lab_list_models (kind: video). Omit to let the intent router choose. Every model has its own resolutions, duration grid, audio behaviour and price; the catalog… |
No output schema declared.
No examples provided.
lab_animate_batch Animate Batch ~276
Animate MANY images into videos in ONE call (max 25 jobs). Prefer this over looping lab_animate: it validates every job BEFORE spending any quota, so an invalid job 12 of 15 does not leave 11 paid-for generations running. Quota is spent once, collectively (one ledger row); refunds stay per-clip, so a partial failure refunds only that clip. Returns a batch_id — poll lab_batch_status with it instead of polling each id separately. `get_cost: true` preflights the whole batch without generating anything.
| Name | Type | Req | Description |
|---|---|---|---|
| get_cost | boolean | – | Preflight: return the summed price in credits for the WHOLE batch and your balance WITHOUT generating, reading the same catalog as lab_list_models. For a card the user confirms, use lab_propose inste… |
| jobs | array | yes | One entry per clip, max 25. All jobs are validated before any quota is spent |
| ohne_keyframe | boolean | – | Skip the keyframe checkpoint on purpose. By default a paid clip only starts from a start image the user has approved (lab_review_asset verdict: accepted) — look at the frame first, then spend on the… |
| project_id | string | yes | Project id (from lab_list_projects) |
No output schema declared.
No examples provided.
lab_apply_rhythm Apply Rhythm to Timeline ~518
Compute the sequence's clip durations from the project's cutting preset and — when the tempo allows it — pull the cut points onto the measured beat grid. WRITES the timeline; costs nothing. The old duration of every changed clip is kept in `clip.metadata.dauer_vorher`, so one undo step exists (a second differing run overwrites it — there is no history). Clip count is never changed: no clip is added or removed. SNAPPING IS NOT GUARANTEED, AND A REFUSAL TO SNAP IS A RESULT, NOT A FAILURE — read `gerastet` and `raster.grund` before you repeat anything: 'tempo_unverlaesslich' = measured below the confidence threshold (deliberate; pass trotz_unsicherem_tempo to override), 'nicht_gemessen' = run lab_beatgrid first, 'keine_musikspur'/'musikspur_nicht_aufgeloest' = run lab_bind_audio first, 'rhythmus_ohne_raster' = the preset's rhythm is 'frei' or 'atmend', which means not snapping IS the promise ('auf den Beat', 'auf die Songstruktur' and 'gegen den Takt' are the three that snap), 'toleranz_null' = you passed toleranz_s 0. In every one of those cases the durations are still recomputed from the preset and written — that alone is the difference between 40 and 6 cuts over 30 seconds. Also read `raster.getroffen` of `raster.von`: `gerastet: true` only says snapping ran; this pair says whether it landed. And `geklemmt` lists clips the renderer's own duration clamp moved back OFF the grid.
| Name | Type | Req | Description |
|---|---|---|---|
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
| toleranz_s | number | – | How far a cut point may travel to land on a beat, in seconds (default 0.12 — three frames at 25 fps). 0 disables snapping entirely and only recomputes the durations |
| trotz_unsicherem_tempo | boolean | – | Snap onto a grid whose own measurement reports `verlaesslich: false`. Default false, and that default is the honest one: below the threshold the detector has not confirmed it found the pulse. Set it… |
No output schema declared.
No examples provided.
lab_auto_lauf Auto Run (Spending Cap) ~312
Auto run with a hard spending cap: the person approves ONE run with an upper limit in credits; inside it you may generate without asking per item, but never above the cap. aktion: anlegen (project_id + deckel_credits → status 'angelegt', nothing can be spent yet) · freigeben (ONLY after the person explicitly approved the cap) · stand (cap, reserved, free, items) · beenden. While a run is active, EVERY paid call in that project reserves its price against the cap — whichever tool makes it; reservations are never refunded (a failed item keeps its share, a retry reserves again), upscales and exports count too. The cap counts credits: runs on the person's OWN provider key (BYOK, 0 credits) are not counted. Other projects are not affected. When the cap would be crossed the backend answers budget_exceeded: stop, report what finished, what is open and the minimum budget still needed. Under budget pressure lower the resolution first, never cut runtime, never drop character or location variants.
| Name | Type | Req | Description |
|---|---|---|---|
| aktion | string | yes | What to do |
| deckel_credits | integer | – | anlegen only: the cap in credits. Name it to the person in credits AND in the price list's terms before freigeben |
| lauf_id | string | – | freigeben / stand / beenden: the id from anlegen |
| project_id | string | yes | Project id (from lab_list_projects) |
No output schema declared.
No examples provided.
lab_auto_storyboard Auto-Storyboard ~521
Auto-generate a storyboard from a script: an LLM splits the script into scenes, then one storyboard frame is generated per scene. COSTS CREDITS — every scene is one generated image, charged up front for the number of scenes the script is split into (at most max_scenes). Ask the human before running it. CHARACTERS ARE NOT GUESSED: every character who appears in more than one frame first gets a reference image (an existing project character with a reference is reused for free; a new one is created and its reference sheet is charged separately as character_sheet_generate), and every frame showing such a character is generated WITH those references on seedream_5_pro instead of `model`. Frames without a recurring character use `model`. The answer lists `figuren` (name, status, scenes), `figuren_kosten_geschaetzt` and `kosten_je_bild`; prices per model come from lab_list_models. If a reference cannot be made, the frames that need it fail and are refunded — they are never generated without it. The scene split is SYNCHRONOUS and can take up to ~55s and then time out (504) for long scripts; run it in the app if so. The answer lists the new image ids; the frames themselves render in the background as pending images — poll them with lab_job_status (type 'image').
| Name | Type | Req | Description |
|---|---|---|---|
| creativity | string | – | Krea 2 creativity setting; ignored by flux_pro. Default medium. |
| figuren_raten | boolean | – | Opt-out, off by default: true skips the character references and lets the image model invent every character from the text — a different face in every frame. Only set it when the human explicitly ask… |
| max_scenes | integer | – | Upper limit for the number of scenes, and therefore images charged. Backend default when omitted. |
| model | string | – | Image model for the frames. Default krea_2_medium. Prices per model: lab_list_models. |
| project_id | string | yes | Project id |
| script | string | yes | Screenplay or scene description to storyboard. Required — the backend splits it into scenes; each scene becomes one image. |
| style_reference_url | string | – | Shared moodboard image applied to ALL frames for one consistent look. Used by the Krea 2 models only. Must be an asset of this project or an absolute URL — anything else is refused before credits are… |
No output schema declared.
No examples provided.
lab_batch_status Batch Status ~121
Aggregated progress of a video batch started by lab_animate_batch. Returns {total, completed, failed, pending, done, items[]} — poll this ONCE per batch instead of calling lab_job_status per clip. `done: true` means nothing is pending any more (some items may still have failed). Each item carries retry_count (an automatic second attempt happened) and fallback_from (the provider was swapped after a content-policy rejection).
| Name | Type | Req | Description |
|---|---|---|---|
| batch_id | string | yes | The batch_id returned by lab_animate_batch (format: vb_<hex>) |
No output schema declared.
No examples provided.
lab_beatgrid Measure Beat Grid ~588
Measure the TEMPO of the sequence's bound music track and store the beat grid — opens the real file with ffmpeg and analyses it, no guessing and no provider, so it costs nothing but a few seconds. Writes `bpm` and `metadata.beat_grid` on the audio row; it does NOT touch the timeline (`timeline_geaendert: false`). Deterministic and cached: a second call returns the same numbers without downloading again (pass `neu_messen` to force). READ `confidence` AND `verlaesslich` BEFORE YOU CUT. Below the threshold (0.35) lab_apply_rhythm computes durations but deliberately does NOT snap them to the grid — that is a decision, not a failure. Soft material measures badly: a real swing track came out at 0.164, an ordered hard pulse (techno, 128 bpm) at 0.621. Half and double tempo are the classic misreads, so `bpm_alternativen` is part of the answer. `frame_konflikt` reports the collision between beat grid and frame grid, for BOTH renderers: `export` (24 fps, the delivered MP4) and `murnau` (25 fps, the editor timeline). Read `frames_pro_schlag` as a DEVIATION, not a count: it is how far one beat sits from a whole frame, between 0 and 0.5. 0 means beat and frame coincide (100, 125 and 150 bpm at 25 fps do); anything above means beat-accurate and frame-accurate cannot both hold, and the rounding error accumulates across clips — measured on a finished MP4 at 128 bpm: up to 64.7 ms off the audible beat although every cut point had snapped. Shown here, not fixed: that decision belongs to a human. Refusals that name their own remedy: 409 without a bound music track (run lab_bind_audio), 409 when the track is pending (poll lab_list_audio), 502/504 when the audio host is unreachable or slow, 422 when the file carries no usable audio, 503 when ffmpeg is missing.
| Name | Type | Req | Description |
|---|---|---|---|
| neu_messen | boolean | – | Measure again even though a result for this exact file already exists. Default false. The measurement is deterministic — the same file gives the same number, so this only helps after the track behind… |
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
| takt | integer | – | Beats per bar (default 4). INPUT, not detection — time-signature detection is explicitly out of scope. It only affects where the downbeats fall, which is what the rhythm 'auf die Songstruktur' snaps… |
No output schema declared.
No examples provided.
lab_bilder Frames at Seconds ~299
See a clip at EXACT seconds: up to 12 frames, in the order you list them, tiled into one image and shown. Use it to check what is on screen where you plan a cut, whether a motion has ended, or whether a face drifted between two moments — lab_contact_sheet spreads frames evenly and cannot answer 'what is at 3.2 s'. A second past the clip's end is rejected by name, not clamped. Works on generated clips (video_id) AND on your own uploaded footage (project_id + asset_key from lab_add_media) — look at it before you cut it. Free (no credits).
| Name | Type | Req | Description |
|---|---|---|---|
| asset_key | string | – | Storage key of uploaded footage (from lab_add_media / lab_list_assets). Needs project_id |
| kachel_breite | integer | – | Width of one frame in px (default 480) |
| preview | boolean | – | Show the image inline (default true) |
| project_id | string | – | Project the asset_key belongs to — required with asset_key |
| sekunden | array | yes | Seconds into the clip, e.g. [0.5, 3.2, 3.24]. Order is kept: left to right, then the next row (4 per row) |
| video_id | string | – | Generated video id (from lab_animate/lab_job_status) — positive integer as string. Give this OR asset_key |
No output schema declared.
No examples provided.
lab_bind_audio Bind Audio to Sequence ~278
Bind the project's generated audio to a sequence's tracks — ONE call that does both halves of the job, because doing only the first half silently produces a silent film. Half one (`POST …/soundmix`) creates the four empty tracks (dialog, music, sfx, atmo); half two (`POST …/soundmix/resolve-stems`) is what actually attaches the audio rows to them. On 2026-08-28 that split cost a complete silent export: the mix looked configured, and every track was empty. From here it is one tool, and it is idempotent — an existing mix is NOT re-initialised (that would throw away volumes, effects and the other tracks' bindings), only re-resolved. Per track it takes the audio row marked `selected`, otherwise the newest one with a url. Rows still pending have no url and stay unbound. That choice is re-made on every call: generate a second music track and run this again, and the music track follows the NEWER row — unless one row is pinned with `selected`, which is set in the web UI, not from here. Changes state, costs nothing. Next: lab_beatgrid.
| Name | Type | Req | Description |
|---|---|---|---|
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
No output schema declared.
No examples provided.
lab_capture Frames of the Cut ~308
See the CUT at exact seconds of the finished film: up to 12 frames, tiled into one image and shown. Each second is mapped like the export maps it — which clip is on screen, where in its source (src_in_s counts), the export's framing (scaled and letterboxed), title cards drawn. A video whose source is shorter than its clip duration ends early in the export, and everything after it moves forward; this tool follows that, so you see the film that will be rendered. The answer names per frame the clip (position, clip_id), clip start and offset; a clip that would render black says so ('schwarz'). NOT in the image: lens/grade, lower thirds, the AI badge — that needs lab_start_export. Use it after lab_timeline_apply to check cut points before exporting. Free (no credits).
| Name | Type | Req | Description |
|---|---|---|---|
| kachel_breite | integer | – | Width of one frame in px (default 480) |
| preview | boolean | – | Show the image inline (default true) |
| profile | string | – | Export profile key (default 'web'); a 9:16 profile gives portrait frames — see lab_export_profiles |
| sekunden | array | yes | Seconds into the finished film, e.g. [0.5, 4.9, 5.1]. Order is kept: left to right, then the next row (4 per row) |
| seq_id | string | yes | Sequence id — positive integer as string |
No output schema declared.
No examples provided.
lab_cast_autopopulate Autopopulate Cast ~144
Create character sheets for the people found in a project's material — the writing counterpart to lab_cast_gaps. Creates at most 6 per call and binds each to the project. Costs an LLM run per call. Run lab_cast_gaps first if you want to see what it would create. The sheets it creates have NO reference image: each one still needs lab_generate_character_reference before lab_generate accepts it as `character_refs`. Fail-open: no script or no AI key returns an EMPTY `created` list with a `note`, not an error.
| Name | Type | Req | Description |
|---|---|---|---|
| project_id | string | yes | Project id (from lab_list_projects) — positive integer as string |
No output schema declared.
No examples provided.
lab_cast_gaps Find Cast Gaps ~147
Find people who appear in a project's material (script / cast module) but have NO character sheet yet. Returns up to 6 `missing` entries, each with a name and a short hint from the text. It creates nothing — lab_cast_autopopulate does that. Costs an LLM run, so it is credit-gated. Fail-open by design: no script, no AI key, or an unparseable answer all come back as an EMPTY list (with a `note` saying which) — an empty list is the correct answer, not an error.
| Name | Type | Req | Description |
|---|---|---|---|
| project_id | string | yes | Project id (from lab_list_projects) — positive integer as string |
No output schema declared.
No examples provided.
lab_contact_sheet Contact Sheet ~282
Build a contact sheet for a generated clip — N frames spread evenly over its runtime, tiled into ONE image — and show it. This is how a clip gets judged at all: MCP has no video content type, so without a sheet a clip is just a URL and a promise. It is also the fastest way to SEE camera drift: measured 2026-08-04, the wander in one clip was a PSNR number until frames sat side by side. The sheet is cached on the video row — a second call returns the same URL without re-rendering (pass force to redo it). Rendering downloads the clip and runs ffmpeg, so the first call takes a few seconds.
| Name | Type | Req | Description |
|---|---|---|---|
| cols | integer | – | Grid columns (default 3) |
| force | boolean | – | Re-render even if a sheet already exists |
| preview | boolean | – | Attach the sheet itself, downscaled to 512px, instead of only its URL. Default true — showing it is the whole point; pass false to get just the URL. |
| rows | integer | – | Grid rows (default 3). cols x rows = number of frames |
| video_id | string | yes | Generated video id (from lab_animate/lab_job_status) — positive integer as string |
| width | integer | – | Total sheet width in px (default 1280) |
No output schema declared.
No examples provided.
lab_continuity_check Continuity Check ~281
Start the continuity pass over a sequence — every shot is compared against its PREDECESSOR and, with check_references, against the character and location reference boards. This is the pass that catches drift (the flat that changed between two shots, the cardigan that turned into a coat), which is the actual enemy in AI film, not the cut. Runs in the BACKGROUND: returns a `job_id` with HTTP 202 — poll it with lab_get_job until status is done|failed, the findings sit in the job result. Two refusals worth knowing before you spend time: an empty timeline is rejected with 400 (add shots with lab_add_clip first), and the backend answers 503 if GEMINI_API_KEY is not configured.
| Name | Type | Req | Description |
|---|---|---|---|
| check_references | boolean | – | Also check every shot against the character and location reference boards, not just against its predecessor. Backend default is true; pass false for a cheaper neighbour-only pass |
| model | string | – | Vision model for the comparison. Omit it — the backend resolves its default through the model catalogue, so it follows a model retirement on its own. A literal passed here does not, and that is how a… |
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
No output schema declared.
No examples provided.
lab_create_character Create Character ~378
Create a character sheet in the cast register. The backend derives an English `prompt_prefix_en` from the fields you give — that prefix is what later describes this person in every image, so the richer the description, the more stable the identity. This is step 1 of 2: a fresh sheet has NO reference image and is therefore not yet usable as `character_refs`. Run lab_generate_character_reference on the returned id to finish it. Free — it writes a row, it burns no credits.
| Name | Type | Req | Description |
|---|---|---|---|
| age | integer | – | Age in years, as a number |
| build | string | – | Body build and posture |
| clothing | string | – | Default costume the character is seen in |
| demeanor | string | – | How the person carries themselves, in prose |
| distinctive_features | string | – | Scars, tattoos, glasses, anything that must appear in every image |
| eyes | string | – | Eye colour and expression |
| facial_features | string | – | Face shape, nose, mouth, jaw — what makes this face recognisable |
| gender | string | – | e.g. 'female', 'male', 'non-binary' |
| hair | string | – | Hair colour, length, cut |
| name | string | yes | Character name, e.g. 'Maren' — the only required field |
| project_id | string | – | Bind the sheet to this project (from lab_list_projects) — positive integer as string. PASS THIS whenever the character belongs to a project. Without it the sheet stays an orphan: lab_cast_gaps will n… |
| project_name | string | – | Free-text project label shown in listings (does NOT bind the sheet — that is project_id) |
| skin | string | – | Skin tone and texture |
No output schema declared.
No examples provided.
lab_create_location Create Location ~263
Create a location board. The backend derives an English `prompt_prefix_en` from the fields you give — that prefix is what later anchors every image and clip shot there, so the richer the description, the more consistent the place. Pass project_id to bind the board to a project (recommended: unbound boards are invisible to lab_list_project_locations).
| Name | Type | Req | Description |
|---|---|---|---|
| architecture | string | – | Building style, era, materials |
| atmosphere | string | – | Mood, weather, sound of the place |
| colors | string | – | Dominant palette |
| description | string | – | What the place is, in prose |
| lighting_notes | string | – | Light direction, quality, sources |
| location_type | string | – | e.g. 'interior', 'exterior', 'apartment', 'street' |
| name | string | yes | Board name, e.g. 'Wohnung Hanna — Balkon' |
| project_id | string | – | Bind the board to this project (from lab_list_projects) — positive integer as string |
| project_name | string | – | Free-text project label shown in listings |
| props | string | – | Objects that must be present |
| time_of_day | string | – | e.g. 'morning', 'blue hour', 'night' |
No output schema declared.
No examples provided.
lab_create_project Create Project ~39
Create a new FilmLab project. Returns the new project id. Optionally set a title.
| Name | Type | Req | Description |
|---|---|---|---|
| title | string | – | Project title (optional) |
No output schema declared.
No examples provided.
lab_create_sequence Create Sequence ~115
Create a sequence (Stage 9 timeline) inside a project — START HERE to turn generated shots into something the continuity pass can read. The new sequence is EMPTY: add shots with lab_add_clip before anything else works. lab_continuity_check rejects an empty timeline with 400. Returns the sequence record including its `id`.
| Name | Type | Req | Description |
|---|---|---|---|
| project_id | string | yes | Project id (from lab_list_projects) — positive integer as string |
| title | string | – | Sequence title. Defaults to 'Main Sequence' on the backend |
No output schema declared.
No examples provided.
lab_delete_location Delete Location ~118
DELETE a location board permanently. There is no undo and no soft-archive — the row is removed. Images and clips that were generated with this `location_id` keep their already-rendered prompt, but the board they point at is gone: nothing can be re-anchored to it, and lab_get_location will 404. Confirm with the user before calling this. Use lab_restyle_location if you only want the place to look different.
| Name | Type | Req | Description |
|---|---|---|---|
| loc_id | string | yes | Location board id to delete permanently (from lab_list_locations) |
No output schema declared.
No examples provided.
lab_depth_map Depth Map ~304
Create or read a depth map for a project video in FilmLab: use it for fog, depth of field, relighting or occlusion. Near is white, like Resolve's Depth Map. Free on our own CPU, no credits; processing takes minutes (about 0.5 seconds per frame). Reuses the existing map or running job by default; neu: true starts a new job. Returns status and job_id; when done, tiefe_url is FFV1 gray16 and vorschau_url is a playable H.264 grayscale preview, both signed for download without a browser session. wait_s waits at most 15 seconds (default 0). If still running, call this tool again with the same project_id and video_id, neu: false. Once done, use resolve_ergebnis_an_timeline with vorschau_url to put the preview as a matte above the original clip in Resolve.
| Name | Type | Req | Description |
|---|---|---|---|
| neu | boolean | – | Start a new depth job instead of reusing the existing map or running job (default false) |
| nur_lesen | boolean | – | Only read: never create a job. Without an existing job the answer is status 'keine' (default false) |
| project_id | string | yes | Project id (from lab_list_projects) |
| video_id | integer | yes | Completed video id belonging to this project |
| wait_s | number | – | Wait up to this many seconds for completion (default 0, maximum 15) |
No output schema declared.
No examples provided.
lab_diagnose Diagnose ~140
Is everything wired? Backend reachability and latency, identity, credit check, catalog age, last error of this account, host widget support. Traffic light plus one sentence per point. Read-only, free.
| Name | Type | Req | Description |
|---|---|---|---|
| problem_report | boolean | – | Return the same findings as one block of text to paste into a bug report. This covers the MCP side only — server version, backend health, last failed job of this account. The Resolve bridge log lives… |
No output schema declared.
No examples provided.
lab_dossier Build Dossier ~278
Build a dossier of a project's takes: one ZIP with an offline index.html (no external resources) plus the media files, named Project_SceneNN_YYYYMMDD-Tnn.ext. modus 'kunde' is for a client and NEVER shows seed or costs, whatever the switches say; 'intern' shows everything that helps to rebuild a take. Both modes carry the AI disclosure (EU AI Act Art. 50) on the page and in the image files. Free (no credits). Runs as a job — poll lab_get_dossier.
| Name | Type | Req | Description |
|---|---|---|---|
| auswahl | string | – | gewaehlt (default): the selected take per scene, plus every take without a scene (e.g. a series) that is not rejected; alle: every take that is not rejected. Stars/favourites do not count as a select… |
| bild_ids | array | – | Exactly these image ids (overrides auswahl) |
| modus | string | – | kunde (default) or intern |
| project_id | string | yes | Project id |
| schalter | object | – | Switches: prompt, referenzen, aufloesung_dauer, datum, modell, seed, kosten, audio, mini_proxies (480p video proxies). Unknown keys are ignored. |
| video_ids | array | – | Exactly these video ids (overrides auswahl) |
No output schema declared.
No examples provided.
lab_dub Dub Sequence ~853
Dub a sequence into another language: either a ready audio track (en_audio_url — e.g. the one lab_dub_tts just produced) OR a cloned voice reading a line of text (voice_id + dub_text), then a controlled visual lipsync runs over the sequence's own footage (ROI-crop + composite for a long static talking-head shot, or the full-frame veed_v2 path for multi-shot sequences and shots where the mouth is partly hidden). ASYNCHRONOUS: returns a job_id immediately: poll lab_job_status until it completes. Needs a cloned voice first — lab_voice_clone makes one, lab_list_voices shows what a project already has.
| Name | Type | Req | Description |
|---|---|---|---|
| backend | string | – | Lipsync engine. Omit to use the backend default (replicate — ROI-crop, best for one long static talking-head shot). veed_v2 renders the full frame instead and is the pick for multi-shot sequences or… |
| dub_text | string | – | The line(s) this cloned voice should read, in the target `language`. Required together with voice_id. |
| einwilligung | boolean | – | Consent of the person whose mouth this dub re-syncs, when there is no Likeness Register id for them yet. Required together with einwilligung_notiz if likeness_ref is not given — a dub always alters a… |
| einwilligung_notiz | string | – | Free text: who consented, when, and for what — required together with einwilligung. Goes on record with the generated clip. |
| en_audio_url | string | – | URL of a READY dub track to lay under the sequence — typically the audio_url lab_dub_tts just returned. Mutually exclusive with voice_id+dub_text: a dub has exactly one audio source, and picking one… |
| enable_lipsync | boolean | – | Whether to run the visual lipsync pass at all. Omit to use the backend default (on). Off only swaps the audio track and skips the mouth-sync render entirely. |
| get_cost | boolean | – | `true` reports the price and your balance WITHOUT starting a dub and without spending quota. The number is computed by the same function that later charges you, from THIS sequence: a sound rate per v… |
| language | string | yes | Target language for the dub, e.g. 'de', 'fr', 'es' — drives the cloned voice's TTS. Required: there is no silent default here. |
| likeness_ref | string | – | A Likeness Register consent id (likeness.thefilmradar.com) for the person whose mouth this dub re-syncs. Checked live against the register before anything is spent; a revoked or unknown id refuses th… |
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
| voice_id | string | – | A cloned voice's id (lab_cloned_voices.id, from lab_voice_clone or lab_list_voices) — positive integer as string. Required together with dub_text, and mutually exclusive with en_audio_url. |
No output schema declared.
No examples provided.
lab_dub_tts Generate Dub TTS Track ~309
Generate the EN-dub audio track for a sequence via ElevenLabs TTS — SYNCHRONOUS, returns a ready audio_url immediately, no job_id and nothing to poll. Optionally pads the track with silence up to the sequence's own length so a shorter reading doesn't shorten the final cut. Feed the resulting audio_url into lab_dub's en_audio_url to lipsync the sequence to this track.
| Name | Type | Req | Description |
|---|---|---|---|
| get_cost | boolean | – | `true` reports the price and your balance WITHOUT generating anything and without spending quota. This one is billed flat, one sound rate per run — the length of your text does not change it. Not dra… |
| pad_to_video | boolean | – | Pad the generated track with silence up to the sequence's own duration if the reading comes out shorter. Omit to use the backend default (on). |
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
| text | string | yes | The line(s) to speak. Kept short — very long text is rejected by the backend before anything is spent. |
| voice_id | string | – | An ElevenLabs voice id, from lab_dub_voices. Omit to use the backend's configured default voice — NOT a lab_cloned_voices id. |
No output schema declared.
No examples provided.
lab_dub_voices List Dub TTS Voices ~92
List the ElevenLabs TTS voices available for lab_dub_tts's `voice_id` — this is the EN-dub-track text-to-speech voice list, NOT a project's cloned voices (those are lab_list_voices). Reads only — no state changes, no credits. If ELEVENLABS_API_KEY isn't configured on the backend the list comes back empty and `configured` is false.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
lab_enrich_location_prefix Enrich Location Prefix ~148
Append context to a board's `prompt_prefix_en` (plain string concatenation server-side — no AI call, no quota). Idempotent: context already present in the prefix is not duplicated. This does NOT rewrite or regenerate the prefix — for a board whose prefix is empty, too thin or a raw field dump, use lab_restyle_location (it rewrites prompt_prefix_en with an AI call, costs quota) instead of recreating the board.
| Name | Type | Req | Description |
|---|---|---|---|
| context | string | yes | What to add or sharpen, e.g. 'late 1970s West Berlin, nicotine-stained walls' |
| loc_id | string | yes | Location board id (from lab_list_locations) |
No output schema declared.
No examples provided.
lab_export_profiles Export Profiles ~121
List the available export delivery profiles (web, social_vertical, social_youtube, streaming_hdr, cinema, master, alpha_prores4444, alpha_png_sequence) and quality presets (draft, standard, high). Call this before lab_start_export to pick a valid profile/quality. The two alpha profiles keep transparency (ProRes 4444, or one RGBA PNG per frame delivered as a zip) and are REFUSED unless the sequence actually carries an alpha source — a cut-out layer element, or a Switch X transform whose alpha track was kept.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
lab_fassungen Versions of a Cut ~436
The history and the versions of ONE cut (sequence). Every timeline write (lab_timeline_apply, the editor, the Resolve bridge) first saves the state it replaces; this tool reads those saved states and works with them. Costs nothing. aktion 'liste' (default): the saved states, newest first, with `marke` (a name a person gave a state), plus where this sequence was branched from (`ursprung`, with what changed since the branch point) and which versions were branched from it (`abgezweigt`). 'markieren': give a state a name (`marke`); a named state is never cleaned up — states without a name are capped per sequence. Empty `marke` removes the name. 'vergleichen': what changed since a state — against the current cut, or against another state (`gegen`), clip by clip. 'zurueckholen': write a state back as the current cut; the cut it replaces is saved first, so this is undoable. 'abzweigen': make a NEW sequence (a version — e.g. a broadcast cut next to the festival cut) from a state, or from the current cut without `checkpoint_id`. The new sequence keeps the clip ids and the shot layer, knows its origin, and the branch point gets a name so it stays. There is no merging back: a version is its own cut from then on. Edit it with lab_timeline_apply on the returned sequence id.
| Name | Type | Req | Description |
|---|---|---|---|
| aktion | string | – | What to do. Default 'liste' |
| checkpoint_id | integer | – | The state (id from 'liste'). Needed for markieren, vergleichen, zurueckholen; optional for abzweigen (without: the current cut) |
| gegen | integer | – | vergleichen: a second state instead of the current cut |
| marke | string | – | markieren: the name. Empty removes it |
| seq_id | string | yes | Sequence id (from lab_list_sequences) — positive integer as string |
| titel | string | – | abzweigen: the name of the new version |
No output schema declared.
No examples provided.
lab_feedback Report a Bug or Wish ~278
Tell the FilmLab team about a bug or a missing feature — it reaches the people who can fix it, the chat does not. Use art 'fehler' when a tool returned something wrong, failed without a clear reason, or contradicted its own description; 'wunsch' when a step you needed does not exist. Write what you called, what you expected and what came back; put ids, arguments and the error text into `kontext` (no secrets, no personal data of third parties). One report per problem. Do not report a refused request that was correct (e.g. 'Project not found' for someone else's project). Needs an account. Free.
| Name | Type | Req | Description |
|---|---|---|---|
| art | string | yes | fehler = something is broken; wunsch = something is missing |
| kontext | object | – | Machine-readable details: ids, arguments, error text. Max ~8000 characters as JSON |
| project_id | string | – | Your project it happened in (only your own projects are accepted) |
| text | string | yes | What you called, what you expected, what happened — in a few sentences |
| titel | string | yes | One line, e.g. 'lab_capture: black frame for an image clip' |
| werkzeug | string | – | The tool it is about, e.g. 'lab_timeline_apply' |
No output schema declared.
No examples provided.
lab_flow_angebot Quote Flow (Pipeline) ~336
Price a whole Flow (Pipeline) before running it: one quote over every node, checked against the credit balance and the project's cost cap. `workflow` is a FilmOS workflow in schema v1 (vault/workflows/_schema.md): nodes of type `lab.tool` call FilmLab tools by name, `{{node_id.field}}` passes one node's result into the next, `parallel` and `condition` are supported, `foreach` and `workflow.invoke` not yet. Every node whose tool costs money is priced with that tool's own get_cost preflight; a paid tool without a preflight, or a price that cannot be determined, rejects the whole quote by name — nothing is estimated. Nothing is generated and nothing is charged. Returns `angebot_id` (valid 24 h, for exactly this workflow, these inputs and this project), the total, one line per node, the balance and what the cap still allows. A node whose price only exists once its input exists (lab_upscale fed by a clip from another node) is listed with `preis_folgt: true` and is NOT in the total: the run stops before it, names the exact price and continues only after lab_flow_fortsetzen — tell the person so when you name the total.
| Name | Type | Req | Description |
|---|---|---|---|
| inputs | object | – | Values for the workflow's `inputs`, referenced as {{name}} |
| project_id | string | yes | Project id (from lab_list_projects) — every node runs in this project |
| workflow | object | yes | The Flow (Pipeline) as a FilmOS workflow object, schema_version "1" |
No output schema declared.
No examples provided.
lab_flow_fortsetzen Resume Flow Run ~199
Resume a Flow (Pipeline) run that stopped and waits (status 'wartet' with a reason like 'Wartet auf Freigabe'). A run stops when a clip node needs a start image the person has not approved yet — approve the image first (lab_review_asset, verdict accepted), then resume. A run also stops before a node whose price only exists once its input exists (lab_upscale after a clip — the quote listed it as `preis_folgt`): the reason starts with 'Wartet auf Preisbestätigung' and names the exact credits. Resuming IS the confirmation of that price — ask the person first. The confirmed amount travels as max_credits; if the price rises before the run, nothing is charged and the run stops again with the new price. The waiting nodes are tried again; nothing that already ran is repeated.
| Name | Type | Req | Description |
|---|---|---|---|
| lauf_id | string | yes | From lab_flow_starten |
No output schema declared.
No examples provided.
lab_flow_starten Run Flow (Pipeline) ~235
Run a Flow (Pipeline) that was quoted with lab_flow_angebot. Pass the SAME workflow and inputs plus the angebot_id — a changed workflow, an expired quote (24 h) or a quote that was already used is refused; get a new quote then. Only after the person confirmed the price. Returns right away with `lauf_id`; the run continues on the server without anyone watching. Follow it with lab_flow_status. Each node calls its FilmLab tool under your account; every tool's own gates still apply (approved keyframe before a paid clip, cost cap of the project). If the cost cap is hit, the whole run stops and nothing after it starts. The quoted price is also the run's own hard cap (auto_lauf_id): every node reserves against it, failed nodes keep their share, and a run whose retries would cost more than the quote stops with budget_exceeded instead of spending more.
| Name | Type | Req | Description |
|---|---|---|---|
| angebot_id | string | yes | From lab_flow_angebot |
| inputs | object | – | Exactly the inputs that were quoted |
| workflow | object | yes | Exactly the workflow that was quoted |
No output schema declared.
No examples provided.
lab_flow_status Flow Run Status ~93
State of a Flow (Pipeline) run started with lab_flow_starten: the run's status (wartet, laeuft, fertig, fehlgeschlagen, abgebrochen) and, per node, its status, attempts, result and error. A node's result is exactly what its tool returned — ids from there go into the next tools.
| Name | Type | Req | Description |
|---|---|---|---|
| lauf_id | string | yes | From lab_flow_starten |
No output schema declared.
No examples provided.
lab_generate Generate Images ~1,921
Generate an image on the production pipeline (fal.ai, quota-tracked, persisted to the project's gallery). Waits up to `wait_s` seconds (default 45) for the image and then answers with the finished rows — image_url set, inline previews attached, so the picture is in the answer like lab_job_status with preview. Pass `wait_s: 0` to return right away with rows in status 'pending' (poll lab_job_status with id + project_id then). If the budget runs out, the rows come back as they are and `fertig: false` says so. Default tool is 'flux_pro'; use 'nano_banana_pro' (Gemini 3 Pro Image) for the Papercut-style look. Pass seed_image_url to run img-to-img guided by an existing image URL. `get_cost: true` preflights credits — it returns the price without generating anything. FOR A PERSON WHO MUST LOOK THE SAME across several images, do not re-describe them per prompt: get an id from lab_list_characters (or build one with lab_create_character + lab_generate_character_reference) and pass it as `character_refs`.
| Name | Type | Req | Description |
|---|---|---|---|
| anker_id | string | – | Look anchor id (positive integer string) whose CHOSEN image guides this generation as img-to-img. An anchor is a look imported from Midjourney: a handful of images plus the grammar of the old `--prof… |
| apply_lens | boolean | – | Defaults to TRUE on the backend: the project's lens_state UrPrompt is appended to the prompt. Pass false only to deliberately opt out for this one call. |
| aspect | array | – | Frame as [width, height], e.g. [9, 16]. Overrides the project's pose_state — pass it when the Pose-Director's blockout is set but not yet saved. Measured 2026-07-31: without it a 9:16 blockout came b… |
| aspect_ratio | string | – | Aspect ratio as W:H (e.g. 16:9, 9:16). Must be one of the model's aspect_ratios where the model has an enum; models without an enum accept any W:H. Omit to use the project's camera aspect. |
| character_refs | array | – | Character ids (positive integer strings) whose reference images lock the cast's identity. Routes the call to img-to-img and sends EVERY listed character's reference image, in the order given, up to t… |
| character_states | object | – | Which STATE each character is in for this image, as { characterId: stateKey } — e.g. { "12": "wet" }. A state is its own asset with its own full descriptor (clean / soaked / bloodied are three assets… |
| costume_refs | array | – | The costume plate of these characters goes along as an extra reference image — the outfit on an invisible mannequin, NO person, so it carries the costume, never the face. Every id must also be in cha… |
| get_cost | boolean | – | Preflight: return the price in credits and your balance WITHOUT generating, reading the same catalog as lab_list_models. Do this before a batch or when the user wants to see the cost. For a card the… |
| location_id | string | – | Location board id (from lab_list_locations). Anchors the shot: the board's `prompt_prefix_en` is appended to your prompt as 'Location: …' before generation, so every image of the same place stays in… |
| metadata | object | – | Free-form JSON stored alongside the image (e.g. { parent_id, note }). Not interpreted by the pipeline. |
| project_id | string | yes | Project id (from lab_list_projects) — generation is always project-scoped |
| prompt | string | yes | What to generate (required) |
| resolution | string | – | Resolution tier, exactly as listed for the model in lab_list_models. Omit for the model's default; the aspect ratio still comes from the project's pose_state — this only sets the area. Some models ha… |
| scene_ref | string | – | Scene label (e.g. 'SZ 04'). Stored on the row, so results can be matched back to a shot list and reviewed per scene with lab_review_queue. Omit it and the image is an orphan. |
| seed | integer | – | Seed for a repeatable run. Omit it and the project's stored seed applies (POST /api/lab/projects/{id}/einstellungen), and without that the provider picks one. The seed that actually rendered comes ba… |
| seed_image_url | string | – | Optional: run img-to-img guided by this existing image URL (e.g. a prior generation's url). For a PERSON who must look the same across several images, use character_refs instead — this URL binds one… |
| tool | string | – | Image model id — copy it verbatim from lab_list_models (kind: image). Omit to use the default. Every model has its own resolutions, aspect handling and price; the catalog is the only place those live… |
| variants | integer | – | How many variants to generate (default 1, max 5) |
| wait_s | number | – | Seconds to wait for the finished image before answering (default 45, max 80). 0 = answer immediately with pending rows. |
No output schema declared.
No examples provided.
lab_generate_character_reference Generate Character Reference ~640
Generate the reference sheet for a character — six core views rendered from the sheet's `prompt_prefix_en`: detail (three-quarter close-up, the identity anchor), frontal (frontal close-up), profile, fullbody (front), fullbody_back (back view: hair and the back of the costume) and costume (the outfit on an invisible mannequin, NO person — use it where the costume is meant, never as a face reference). Back view and costume are derived from fullbody, everything else from detail. The poses pose_sitting, pose_walking, pose_action are optional extras for blocking — not part of the default run and not used as references for image or video models. A seventh view, 'detail_smile', is NOT in the default set and must be asked for: the same close-up with the mouth open and the teeth visible. Render it for every character who SPEAKS — otherwise the model invents the teeth and the jaw the first time they laugh, and the smile arrives as somebody else's mouth. It is an extra view for the mouth, never an identity anchor: a smiling reference carries its smile into every shot, funerals included. MEASURED 2026-09-15 (n=10, criterion fixed beforehand): the teeth land 10/10, but the PERSON only holds in about 4 of 10 even with the anchor active — two runs were plainly a different man. Generate it, then LOOK before you rely on it; the failure is obvious to the eye (a stranger's mouth on your character), not silent. THIS IS WHAT MAKES A SHEET USABLE: until it has run, lab_generate rejects the id as `character_refs`. Step 2 of 2 after lab_create_character. COSTS CREDITS — one provider call per view, so the default run is six. Pass `views` to render fewer. Takes about 25 s PER VIEW (the tool requests them one by one, anchor first), so a full run is 2–3 minutes. If a single request times out, the answer says `noch_in_arbeit` instead of failing: that view is still rendering and already paid for — check it with lab_get_character_sheet, do NOT re-run it. The sheet needs a `prompt_prefix_en`; the…
| Name | Type | Req | Description |
|---|---|---|---|
| character_id | string | yes | Character sheet id (from lab_list_characters or lab_create_character) — positive integer as string |
| views | array | – | Which views to render. Omit for the six core views. Allowed: detail, frontal, profile, fullbody, fullbody_back, costume, pose_sitting, pose_walking, pose_action. Fewer views = fewer credits; `detail`… |
No output schema declared.
No examples provided.
lab_generate_location_variants Generate Location Variants ~91
Generate reference images for a location board (costs quota). Without `variants` the backend picks its default set. The images are what makes a board usable as a visual reference.
| Name | Type | Req | Description |
|---|---|---|---|
| loc_id | string | yes | Location board id (from lab_list_locations) |
| variants | array | – | Which views to render, e.g. ['wide', 'detail']. Omit for the backend default set |
No output schema declared.
No examples provided.
lab_generate_music Generate Music ~455
Generate a MUSIC track for a project (Stable Audio via fal.ai) — the first step of the rhythm chain, and the only one that costs credits. ASYNCHRONOUS: the call returns immediately with a row whose `audio_url` is null and `metadata.status` is 'pending'. Nothing is playable and nothing can be bound yet. Poll lab_list_audio (audio_type 'music') until that row's status is 'completed' and audio_url is set — usually under a minute, and the backend gives up after five (status turns 'failed', metadata.error carries the reason, and the credits are refunded). PUT THE TEMPO IN THE PROMPT if you intend to cut on the beat. This is measured, not assumed: a soft swing track measured at confidence 0.164 (unusable), an ordered hard pulse ('techno, 128 bpm, four-on-the-floor') at 0.621. The tempo detector is autocorrelation over an onset envelope — it finds a hard pulse and shrugs at drone, ambient and rubato. Then: lab_bind_audio, lab_beatgrid, lab_apply_rhythm.
| Name | Type | Req | Description |
|---|---|---|---|
| duration_seconds | number | – | Track length in seconds (default 30, Stable Audio's ceiling is 190). Make it at least as long as the sequence you want to cut — lab_apply_rhythm computes durations for the clips that exist and neithe… |
| get_cost | boolean | – | `true` preflights the credit price and your balance WITHOUT generating anything and without spending quota. |
| project_id | string | yes | Project id (from lab_list_projects) — positive integer as string |
| prompt | string | yes | What the music should be. Name genre, instrumentation, mood AND the tempo in bpm if the cut is supposed to sit on the beat — e.g. 'driving techno, four-on-the-floor kick, 128 bpm, no vocals'. A promp… |
| scene_ref | string | – | Optional scene label, stored on the row. Useful to tell several tracks of one project apart |
No output schema declared.
No examples provided.
lab_get_character Get Character ~208
Get ONE character sheet in full — every descriptive field plus the three that decide what you can do with it. `prompt_prefix_en` is the text block describing this person; it is what the backend prepends wherever the sheet is used. `reference_images` decides whether the sheet is usable at all: lab_generate REJECTS a character_refs entry without one, before any quota is spent — if it is empty, run lab_generate_character_reference first. `states` carries the state sheets against character drift (costume, light, physical condition); lab_get_character_sheet renders them agent-readable.
| Name | Type | Req | Description |
|---|---|---|---|
| character_id | string | yes | Character sheet id (from lab_list_characters) — positive integer as string |
| preview | boolean | – | Attach the reference images themselves, downscaled to 512px. `reference_images` decides whether the sheet is usable at all — seeing them is the check, counting them is not: a sheet can carry a URL to… |
No output schema declared.
No examples provided.
lab_get_character_sheet Get Character Sheet for Prompting ~272
The character sheet PREPARED FOR PROMPTING: base prefix, the state sheets by name, which reference views exist, and the ready-made sentence to paste into a lab_generate prompt. Same source as lab_get_character, different shape — use this one when you are about to generate, and lab_get_character when you want the raw record. State sheets exist against character drift: one sheet per costume, light or physical-condition change, so a person stays the same person when the situation changes. THIS IS THE ANSWER to 'same person, different light': character_refs alone routes to i2i and drags the reference's lighting along, so a drastically different light fights the reference. A state sheet puts the change into the TEXT, where the prompt can win. Free.
| Name | Type | Req | Description |
|---|---|---|---|
| character_id | string | yes | Character sheet id (from lab_list_characters) — positive integer as string |
| preview | boolean | – | Attach the reference views themselves, downscaled to 512px. `usable_as_character_ref` only says a URL exists — the picture says whether it is the right person, in the right view. That check is the wh… |
| state | string | – | Render the snippet for THIS state key (from `states[].key`) instead of the base state |
No output schema declared.
No examples provided.
lab_get_context Preview Module Context ~81
Read the accumulated context/output for one pipeline module of a project (e.g. the IDEE, STORYBOARD or SHOTS stage). Needs project_id and module name.
| Name | Type | Req | Description |
|---|---|---|---|
| module | string | yes | Pipeline module/stage id, e.g. 'idee', 'storyboard', 'shots' |
| project_id | string | yes | The FilmLab project id |
No output schema declared.
No examples provided.
lab_get_dossier Get Dossier ~96
Poll a dossier job by job_id (from lab_dossier). status is queued|processing|done|failed. When done, result.archiv_url is a signed https link to the ZIP that loads WITHOUT a login and expires after 24 h — call again for a fresh one. result.fehlend lists takes that could not be loaded.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | yes | The dossier job id returned by lab_dossier |
No output schema declared.
No examples provided.
lab_get_export Get Export ~281
Poll an export render job by job_id (from lab_start_export). status is queued|processing|done|failed; when done, the result (output url) is included. Call repeatedly until done|failed. NB: this is the EXPORT poll — different from lab_get_generation. Where the address sits depends on the profile: a rendered film is in video_url, but profile alpha_png_sequence delivers a ZIP of RGBA PNGs and therefore leaves video_url null and puts the address in archiv_url — a .zip under a key called video_url would read as something playable to every client. download_url carries the address in both cases and is the safe one to hand to a user. All result addresses — video_url, download_url, archiv_url and the provenance record herkunft.pdf_url / herkunft.json_url — are signed https links that load WITHOUT a login and expire after 24 h; call lab_get_export again for fresh ones. view_url is the short session-bound form for the web app only. A finished result also carries zielmarkt_checkliste: per target market of the project (see lab_zielgruppe) what the customer must observe when delivering there — empty without markets. Pass it on to the person with the download; it is not legal advice.
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | yes | The export job id returned by lab_start_export |
No output schema declared.
No examples provided.
lab_get_job Get Job ~88
Get the status of a background job by job_id (status is queued|processing|done|failed). Poll this after starting a long-running generate/export. Returns the job record; its payload and result are objects (for a clip chain: result.zusammenfassung, result.ketten, result.clips).
| Name | Type | Req | Description |
|---|---|---|---|
| job_id | string | yes | The job id returned by a generate/export tool |
No output schema declared.
No examples provided.
lab_get_location Get Location ~105
Get ONE location board in full — all descriptive fields plus its reference images and `prompt_prefix_en`. Use it to check what a board actually anchors before generating with it.
| Name | Type | Req | Description |
|---|---|---|---|
| loc_id | string | yes | Location board id (from lab_list_locations) — positive integer as string |
| preview | boolean | – | Attach the board's reference images themselves, downscaled to 512px — the fastest way to check whether a board anchors the place you mean before spending quota on it. |
No output schema declared.
No examples provided.
lab_get_project Get Project ~44
Get a single FilmLab project with its full stage state. Needs the project_id (from lab_list_projects).
| Name | Type | Req | Description |
|---|---|---|---|
| project_id | string | yes | The FilmLab project id |
No output schema declared.
No examples provided.
lab_get_sequence Get Sequence ~82
Get ONE sequence in full — timeline (the shots in order), audio_tracks, transitions, duration_seconds and render status. Check `timeline` here before starting a continuity pass: an empty one is the single most common reason the pass refuses.
| Name | Type | Req | Description |
|---|---|---|---|
| seq_id | string | yes | Sequence id (from lab_create_sequence/lab_list_sequences) — positive integer as string |
No output schema declared.
No examples provided.
What is the FilmLab MCP server?
FilmLab is an MCP server listed in the public MCP registry as com.thefilmradar/filmlab. AI film lab for filmmakers: generate and review images/clips with input provenance, cost preflight. This page covers its hosted endpoint (https://mcp-lab.thefilmradar.com/mcp).
Is the FilmLab MCP server safe to use?
FilmLab scores 76 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 FilmLab MCP server expose?
FilmLab exposes 111 tools: lab_list_projects, lab_wissen, lab_widerruf, lab_playbook, lab_list_models, and 106 more. Their descriptions and schemas cost roughly 39,090 tokens of context every time the server is loaded.
Does the FilmLab MCP server require authentication?
Yes. FilmLab 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 FilmLab MCP server still maintained?
FilmLab is still listed as active in the MCP registry. We last reached this channel on 5 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.