Framesail
Create long-form (faceless YouTube) videos end to end from any MCP client: script, locked character references, storyboard, voiceover, and final video editing — with characters and style held consistent across every shot. Making long-form AI video today means 8+ tabs stitched by hand — an LLM for the script, a voice model, an image model, a video model — with characters drifting between tools and style resetting at every export. Framesail replaces the patchwork: the whole pipeline runs in one place and manages your video's context end to end. Six stages: Style (paste images, videos, or YouTube links and Framesail reverse-engineers the look, voice, and direction), Script (write it yourself or generate it in your narrative style), Reference images (auto-generated for every character, place, and prop), Voiceover (one narrator or many characters, with word-level timing), Storyboard (planned scene by scene), and Editor (captions, music, SFX, then export). No black box: you control every prompt, asset, model, and setting.
How to connect
https://framesail--framesail.run.tools
tools/list returns the expected tools before relying on them in production.Tools
(50)-
list_channelsList your channels. Every project lives in a channel, which owns the reusable styles (art/narrative/director) that drive generation.
-
create_channelCreate a new channel — the container for projects and their reusable styles. Use when the user wants a fresh creative identity rather than adding to an existing channel.
-
list_projectsList projects in a channel.
-
create_projectCreate a project. The description (the video concept/topic) seeds script generation, so write a meaningful one. The response's web_url is the project's page in the web app — share it so the user can follow along.
-
get_projectFetch a project row — settings, voice config, default style, export URL.
-
update_projectPatch project fields. Updatable: title, description, asset_mix, sfx_level, video_concept, voice_mix, voice_tts_provider, script_target_minutes, narrator_speed. (The narrator's TTS voice is NOT here — use set_narrator_voice.)
-
delete_projectPermanently delete a project and everything in it (script versions, assets, voiceover, segments, renders). Irreversible — confirm with your user first.
-
set_project_styleSet the project's default style — the style whose art/narrative/director fields drive its generations. Use after create_style to put a new visual identity into effect, or to switch a project between channel styles.
-
update_caption_configMerge a patch into the project's burned-in caption config (keys like enabled, plus styling). Read the current value from get_project (caption_config). Applies at the next export — no rebuild needed.
-
get_pipeline_progressTHE resume/orientation tool: one call returns every pipeline step's state (script -> scan -> reference_images -> voices -> voiceover -> style_templates -> storyboard -> segment_assets -> scenes -> export), any running jobs, and a next_action telling you exactly what to do next. Call this when picking up an existing project, after any await_jobs, or whenever you're unsure where a video stands — never guess pipeline position. Every step carries a web_url — the page in the
-
get_workflow_statusPoll this between steps: returns active + recently-finished AI jobs (scope by project_id, or style_id for style analysis), plus per-segment- asset render statuses for projects. A step is done when its jobs reach status=complete (or error, with a user-readable message). NOTE: finished jobs drop out of `jobs` after ~30s — a short list does NOT mean work was lost; judge render batches by `segment_assets` statuses (or get_pipeline_progress), never by counting jobs. Prefer awa
-
await_jobsBlock (server-side) until the scope has no pending/running jobs, or the timeout passes — use this instead of polling get_workflow_status yourself. Returns {done, jobs}. If done=false the work is still running: just call await_jobs again (a 3-5 minute storyboard takes a few consecutive calls). Keep timeout_seconds <= 50 so the client doesn't time out the tool call.
-
get_section_templateInspect the prompt sections a generation job exposes for per-call override via editable_sections (jobs: script, script_scan, storyboard, segment_image, segment_video, voice_block, ...). Sections marked locked cannot be overridden.
-
list_modelsList the models allowed for a generation job, with display names, credit estimates, and each model's settings_schema — the valid keys for that tool's `settings` param (e.g. image quality/orientation, video duration). The first entry is the default every tool uses when model is omitted.
-
generate_scriptGenerate the project's script from its description/concept and the channel's narrative style. Async — returns {job_id}; poll get_workflow_status.
-
get_scriptRead the active script's full text + the version list. Use this to show the script to your user for review/feedback before scan_script — the review-edit-resave loop (get_script -> discuss -> save_script) is the expected workflow when the user wants input.
-
save_scriptSave script text (your own draft, or an edited version of the generated one — saving creates a new version, old versions are kept). Run scan_script afterwards so assets and voice blocks reflect the new text.
-
revise_scriptAI-rewrite a passage of the active script in the project's narrative voice (the same in-editor revise the UI offers). selected_text must appear verbatim in the script; omit it to revise the whole script. Synchronous — returns {revised_text}, which is NOT saved: splice it over selected_text (or replace the full script) and call save_script to keep it.
-
activate_script_versionSwitch the project's active script to another saved version (ids come from get_script's version list — every save_script creates one). Re-run scan_script / rescan_voice_blocks afterwards if the text differs, since downstream artifacts follow the active version.
-
scan_scriptAnalyze the active script: extracts character/environment/object assets and splits narration into voice blocks. DESTRUCTIVE on re-run (assets are recreated, not merged — curated descriptions, reference images, and voices are lost; prefer rescan_voice_blocks after script edits). Async — returns {job_id}.
-
list_assetsList the project's assets extracted by scan_script — characters, environments, objects. Each has a description (the spec every shot uses to render it — surfaced top-level here; the raw row nests it at ai_output.description), an optional reference image (file_path is a public URL — view_image it), and for characters a voice_id. Review these after scan_script: fix descriptions, then generate_asset_reference for each one (all of them need a reference image before voiceover).
-
create_assetManually add a character/environment/object the scan missed. asset_type: "character" | "environment" | "object". The description is the generation-facing spec of its look — be specific.
-
update_assetRename an asset and/or rewrite its description. If the look changed, regenerate its reference image afterwards so renders match.
-
delete_assetDelete a project asset (e.g. one the scan over-extracted).
-
generate_asset_referenceRender an asset's reference image in the channel's art style — the visual anchor that keeps a character/environment looking identical across every shot. EVERY character, environment, and object asset needs one before generate_voiceover (the server enforces this; fire the jobs for all assets, then one await_jobs). Async — the job writes the image onto the asset row: await_jobs(project_id), then list_assets and view_image the file_path to check likeness.
-
set_character_voiceBind a TTS voice to a character asset — required before generate_voiceover for every character with dialogue (the narrator's voice is separate: set_narrator_voice). Browse ids with list_voices.
-
list_voicesList available TTS voices (id, label, preview audio URL) for a provider: "minimax" (default engine) or "elevenlabs". Match the project's voice_tts_provider (see get_project) so picked ids work with its engine.
-
set_narrator_voiceSet the project's narrator TTS voice — required before generate_voiceover whenever the script has narration. Browse ids with list_voices. (Character dialogue voices are separate: set_character_voice.)
-
list_voice_blocksList the project's voice blocks (per-speaker narration chunks) with their audio status and assigned voices.
-
update_voice_blockOverride one voice block's voice or playback volume (block ids from list_voice_blocks). Re-run generate_voiceover for the block afterwards if you changed its voice — existing audio is not regenerated automatically.
-
rescan_voice_blocksRe-extract voice blocks from the active script WITHOUT touching assets or their reference images — the non-destructive alternative to scan_script after a script edit. Existing audio is superseded by the new block split, so re-run generate_voiceover afterwards. Async — returns {job_id}.
-
generate_voiceoverGenerate TTS audio for the project's voice blocks. Without voice_block_ids it fills gaps: only blocks with no audio yet run, so re-calling it is always safe (already-generated and currently-generating blocks are skipped, never re-billed). Pass voice_block_ids to explicitly REgenerate those blocks (e.g. after changing a block's voice). Speakers must have voices bound first — set_narrator_voice / set_character_voice. Optional editable_sections/settings apply to every select
-
generate_storyboardPlan the full visual storyboard: segments, shot pacing, image/video prompts, overlays, continuation chains — driven by the channel's director and art styles. Requires voiceover to exist (timing comes from it). BEFORE calling: confirm the project's asset_mix with your user (image vs video vs generated_graphic percentages, update_project) — the storyboard plans against it and video-heavy mixes multiply the later render cost. Async — returns {job_id}; this is the longest LLM
-
get_segmentsList the storyboard's segments (narration span, type, duration, creative direction). The 1-based segment_number is the handle every segment tool takes (update/split/combine/continuation/regenerate) — you never need a UUID. Each segment also reports `continues_from_segment`. When set, this shot is a CONTINUATION of that earlier segment: its rendered frame carries over (same composition, characters, and setting) and only the delta changes — an added overlay, a shifted expr
-
get_segment_assetsList one segment's assets (images/video/overlays) including their status, config (prompts, model), and public URLs of rendered files — pass an image's public_url to view_image to actually look at it.
-
regenerate_segment_assetRegenerate a segment's primary image or video with optional overrides — the API equivalent of the editor's expert drawer. asset_type: "image" | "video" (for a video segment, "image" targets its start frame). Use a different model, override prompt sections (see get_section_template("segment_image")), or tweak settings (e.g. image quality), then re-run just this asset. Async — returns {job_id}.
-
change_segment_typeChange a segment's visual type: "image" | "video" | "overlay_scene". carry_frame=true reuses the already-rendered image as the video start frame (or vice versa) instead of recreating it.
-
update_segment_contentRewrite one segment's creative direction from feedback ("make this shot a close-up", "show the machine from above") — an LLM rewrites the shot's prompts; continuation links, SFX, and overlays are preserved. The visual assets reset to not_started: re-render them afterwards (generate_segments or regenerate_segment_asset). For a precise prompt tweak with no rewrite, use regenerate_segment_asset with editable_sections instead.
-
split_segmentSplit a segment at the given time offsets (ms, 1-3 cuts → 2-4 parts). inherit_index picks which resulting part keeps the original creative data. Later segments renumber — re-check get_segments before further edits.
-
combine_segmentsMerge a segment with an adjacent one (segment numbers must be neighbors). keep: "this" | "other" — whose creative data survives. Later segments renumber — re-check get_segments before further edits.
-
set_segment_continuationMake a segment's image render as a continuation of an EARLIER segment's frame (same composition evolving — the storyboard's continues_from_segment, settable after the fact). continues_from is that earlier segment's number; pass 0 to clear the link. Regenerate the segment's image afterwards — the reference is applied at generation time.
-
add_segment_sfxAttach a sound effect from the audio library to a segment (find track ids via browse_audio_library with category="sfx"). Re-run build_scenes to get it onto the timeline.
-
remove_segment_sfxRemove a sound effect from a segment. With one SFX attached, no name needed; with several, pass sfx_name (the asset name shown by get_segment_assets).
-
generate_segmentsRender every actionable segment asset (images, video clips, overlays) across the project, in dependency order. THE most expensive call in the pipeline: ALWAYS dry_run=true first, show your user the estimate next to get_credit_balance, and wait for a fresh yes before the real run — prior blanket permission ("do the whole thing") does not cover this spend. Pass segment_numbers to render only a subset — e.g. segments 1-18 for the opening minute before committing to the full
-
build_scenesCompile segments + assets + voiceover into the editor/render timeline (scenes). Run after segment assets are complete, before export.
-
list_scenesList the project's scenes (composition layers, durations, layout).
-
director_noteEdit ONE scene with a natural-language note (the same director chat the editor UI uses): move/restyle/add/remove layers and overlays, retime, etc. Synchronous — returns the applied mutations + updated scene. Use list_scenes to find scene ids; for notes spanning the whole video use project_director_note instead.
-
project_director_noteApply a project-WIDE director note ("make the intro punchier", "all captions bigger", "tighten pacing in the back half"). A routing pass picks only the scenes the note applies to and edits each one. Synchronous — a few seconds per affected scene. Returns the per-scene results; re-run export_video afterwards to see changes in the final render.
-
export_videoRender the final MP4 (Remotion). Fetches the current timeline and queues the render. Async — poll get_workflow_status for the video_export job, then call get_video_url.
-
get_video_urlDownload URL for the most recent completed export.