About Clipwright
Clipwright is an MCP server that lets coding agents turn scripts or briefs into short vertical videos with a speaking actor or without an on-camera presenter. It provides a free quote before rendering and returns a video URL when the run completes; it does not publish videos.
Key features
- 01Render actor-led vertical videos
- 02Create faceless videos up to 90 seconds
- 03Request a quote before rendering
- 04Poll runs until video is ready
- 05Return warnings for unsupported requests
Use cases
- 01Turn a script into a talking-actor video
- 02Create a narrated video from a short brief
- 03Check a render's credit cost before starting
MCP support
Clipwright provides the same tools through a hosted MCP server over HTTP or a local server over stdio. MCP clients can request quotes, start renders, and check run status.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://mcp.clipwright.io/mcpclaude mcp add --transport http io-clipwright https://mcp.clipwright.io/mcp{"mcpServers":{"io-clipwright":{"type":"http","url":"https://mcp.clipwright.io/mcp"}}}Open in Cursor{"servers":{"io-clipwright":{"type":"http","url":"https://mcp.clipwright.io/mcp"}}}Open in VS Code{"mcpServers":{"io-clipwright":{"type":"http","url":"https://mcp.clipwright.io/mcp"}}}{"mcpServers":{"io-clipwright":{"serverUrl":"https://mcp.clipwright.io/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
14| Parameter | Type | Required | Description |
|---|---|---|---|
| look | enum | No | NOT HONORED YET: look is not honored yet: it does not reach the renderernatural | commercial | raw_iphone |
| name | string | No | NOT HONORED YET: name is not honored yet: it does not reach the renderer |
| image | string | No | Public https url of the actor's photo (PNG, JPEG or WebP, up to 10 MB). A file on disk goes through upload_image (POST /v1/uploads) first — pass the url it returns. A source we cannot use — private or loopback host, http, unreachable, redirecting, over 10 MB, or not one of those image types — is refused (unusable_source) before any charge. We do not detect the face's gender: pass actor_gender or voice, or the default male voice is used with a warning. |
| voice | string | No | Voice name from list_voices. Curated presets: owner_ru_clone | sarah | george | eric | daria_ru_female (owner_ru_clone is the Russian cloned voice). The API refuses a name list_voices does not return, before any charge. Omitted means the default voice for the actor's gender: the gender of actor_id, actor_gender with image, or george for the default actor and for image without actor_gender. gemini-3.8-flash-tts speaks only its own voices (kind model_voice in list_voices, such as kore): naming one selects that model, and a voice is refused with a tts_model that does not speak it, before any charge. Mutually exclusive with voice_id. |
| person | string | No | NOT HONORED YET: person is not honored yet: this request uses the default actor; choose actor_id from list_actors or provide image to select a different face |
| script | string | No | The words the actor says; required unless segments supplies the spoken text. Segments and text-anchored inserts require long-form qualification on the server. Script limits by speech model: eleven_v3: 5000 characters; eleven_flash_v2_5: 10000 characters; eleven_turbo_v2_5: 10000 characters; eleven_v4: 5000 characters; gemini-3.8-flash-tts: 5000 characters. Count includes spaces, audio tags and stress marks; emoji may count as two characters. Break tags are cut from the text sent to eleven_v4 and gemini-3.8-flash-tts and are not counted there. There is no word-count limit. Duration and price are estimates until measured. Russian stress: write the stressed vowel as a capital inside a lowercase word ("потОм", "зАмок") and eleven_v3, eleven_v4 and gemini-3.8-flash-tts receive it as the stress mark U+0301 ("пото́м"); a mark typed directly is kept. A capital at the start of a word stays a capital, and a word with a second capital or a capital consonant inside (all caps, "ВУЗы") is left as it is. A single capital vowel inside a word is always read as stress, so write "Яндекс Еда", not "ЯндексЕда". Tell users writing in Russian that they can mark stress this way. eleven_flash_v2_5 and eleven_turbo_v2_5 cost less but misread stress marks: capitals reach them unchanged. |
| attempt | integer | No | |
| inserts | array | No | Text-anchored image inserts over full narration, each covering cover_words spoken words from its anchor; requires long-form qualification on the server, captions=false, 1080p and explicit broll_policy=anyone. |
| actor_id | string | No | Saved Clipwright actor ID from list_actors. Choose actor_id, image, or person; do not combine them. Without voice or voice_id the voice follows the actor's gender. Do not combine with actor_gender. |
| captions | boolean | No | NOT HONORED YET: captions requested but not rendered in this prototype (stage-B) |
| segments | array | No | Ordered actor and image segments; requires long-form qualification on the server, captions=false and 1080p. Image media requires explicit broll_policy=anyone. |
| voice_id | string | No | Raw vendor voice id (16–32 letters and digits) for a voice outside the catalog. Checked lazily: an unknown id fails the run, not the request. Refused with tts_model=gemini-3.8-flash-tts. Mutually exclusive with voice. |
| tts_model | enum | No | Speech model: eleven_v3 | eleven_flash_v2_5 | eleven_turbo_v2_5 | eleven_v4 | gemini-3.8-flash-tts. Omitted means the model of the chosen voice (list_voices shows it): eleven_v4 for the Russian presets and for catalog voices named ru_*, gemini-3.8-flash-tts for its own voices, eleven_v3 for every other preset and catalog voice. A raw voice_id speaks eleven_v4 when the script is mostly Cyrillic and eleven_v3 otherwise. An explicit tts_model always wins. eleven_v4, eleven_v3 and gemini-3.8-flash-tts read stress marks (a capital vowel inside a Russian word, "потОм", becomes one; see script); eleven_flash_v2_5 and eleven_turbo_v2_5 are cheaper alternatives for languages other than Russian. gemini-3.8-flash-tts speaks only its own voices (list_voices with model=gemini-3.8-flash-tts; without voice, kore for a female actor and charon otherwise) and returns no word timings: a preset or catalog voice, voice_id, captions=true, segments and inserts are refused with it before any charge. Audio tags in square brackets are an eleven_v3 and eleven_v4 convention and are not measured on it. Script limits by speech model: eleven_v3: 5000 characters; eleven_flash_v2_5: 10000 characters; eleven_turbo_v2_5: 10000 characters; eleven_v4: 5000 characters; gemini-3.8-flash-tts: 5000 characters. Count includes spaces, audio tags and stress marks; emoji may count as two characters. Break tags are cut from the text sent to eleven_v4 and gemini-3.8-flash-tts and are not counted there. There is no word-count limit. Duration and price are estimates until measured.eleven_v3 | eleven_flash_v2_5 | eleven_turbo_v2_5 | eleven_v4 | gemini-3.8-flash-tts |
| background | enum | No | white | blur | contain |
| resolution | enum | No | Output resolution: 720p | 1080p | 4k (short side 720 / 1080 / 2160 px). Omitted means 1080p.720p | 1080p | 4k |
| actor_gender | enum | No | Gender of the face in image: female | male. Only with image: picks the default voice of that gender (female: sarah, male: george; on gemini-3.8-flash-tts: kore and charon). Refused with actor_id (its gender is known) and without image. An explicit voice or voice_id wins and the response warns that actor_gender changed nothing.female | male |
| aspect_ratio | enum | No | Output format: 9:16 | 1:1 | 16:9. Omitted means 9:16, and a source of another shape is snapped to 9:16 with a warning — pass it explicitly whenever you pass image. A mismatch above 15% between the request and the source is refused (aspect_conflict) before any charge.9:16 | 1:1 | 16:9 |
| broll_policy | enum | No | STORED ONLY: Saved policy for B-roll: anyone allows people including the actor; no_actor excludes the actor; no_people excludes all people, including hands. Segmented media generation is closed. This setting is stored only and has no effect on actor-only videos. Run override wins over the account actor default; otherwise no_people.anyone | no_actor | no_people |
| caption_style | enum | No | NOT HONORED YET: caption_style is not honored: captions are not rendered in this prototype (stage-B)hormozi | tiktok | minimal |
Details
From the project README@clipwright/mcp-server
Your coding agent writes the script; Clipwright renders
a short vertical video of an actor speaking it, 1080×1920 by default. It also
makes faceless videos of up to 90 seconds from a script or a short brief: narration
over an opening animated clip and image scenes, with no on-camera presenter. The agent
asks for a free quote before it spends anything, and whatever the API cannot
honor comes back in warnings[] instead of being dropped silently.
The agent gets a run_id at once and polls until the video URL is ready.
Clipwright returns the file; it does not publish anywhere. Quotes, voices,
actors and the account balance are free. Renders spend credits: an account opened
by signing up starts with 420 trial credits, enough for one 90-second faceless video. Buy more at
clipwright.io/pricing.
Install
There are two ways to connect: the hosted server at
https://mcp.clipwright.io/mcp, or this package run locally over stdio. Both
serve the same tools.
Hosted server, no key
Add the URL to your client. On first use the client opens a browser, you sign in to Clipwright and approve the app. Claude Code and the device-code flow below were verified live on 2026-10-18; the other commands come from each client's own documentation and have not been tried with Clipwright yet.
- Claude Code (verified live):
claude mcp add --transport http clipwright https://mcp.clipwright.io/mcp, then run/mcpin a session and sign in. - Claude.ai and Claude Desktop: Customize → Connectors → Add custom connector, then paste the URL.
- ChatGPT: add the URL as the remote MCP server of a plugin, as OpenAI's guide "Connect and test your plugin" describes; ChatGPT signs in with OAuth.
- Cursor: in
~/.cursor/mcp.json,{"mcpServers": {"clipwright": {"url": "https://mcp.clipwright.io/mcp"}}}. - VS Code: in
.mcp.jsonat the workspace root,{"mcpServers": {"clipwright": {"type": "http", "url": "https://mcp.clipwright.io/mcp"}}}. - Gemini CLI:
gemini mcp add --transport http clipwright https://mcp.clipwright.io/mcp, then/mcp auth clipwright. - Codex:
codex mcp add clipwright --url https://mcp.clipwright.io/mcp, thencodex mcp login clipwright.
The approval page lists the permissions the app asks for. There are two:
mcp:read— the tools marked read-only: account, runs, shots, voices, actors, workflows and quotes.mcp:write— every other tool. Some start paid runs and spend credits; the rest change the account (actors, uploads, workflow plans).
A connection without mcp:write gets 403 insufficient_scope from those
tools, and nothing is charged. Only an admitted account can approve.
To disconnect an app, open app.clipwright.io/api-keys,
find it under Connected apps and revoke it. Its next request gets 401. A
connection lasts 90 days from approval; after that the app asks you to sign in
again.
On the hosted server upload_image cannot read a file on your machine. Pass a
public https URL as upload_image({url}) and the server downloads it, or call
create_upload and PUT the file to the returned put_url with curl. Either
way you get an upload_id for first_frame, last_frame and references.
An agent with no browser signs in with a device code and the first-party
client clipwright-agent (verified live 2026-10-18):
curl -s https://mcp.clipwright.io/device/auth -d client_id=clipwright-agent \
--data-urlencode "scope=mcp:read mcp:write offline_access" \
--data-urlencode "resource=https://mcp.clipwright.io/mcp"
Open verification_uri (https://mcp.clipwright.io/device) on any device,
enter user_code, sign in and approve; the approval page repeats the code.
Meanwhile poll POST https://mcp.clipwright.io/token with
grant_type=urn:ietf:params:oauth:grant-type:device_code, the device_code
and client_id=clipwright-agent, no more often than every 5 seconds: a faster
poll gets slow_down. Send the access_token as Authorization: Bearer to
/mcp; it lasts an hour. Renew it with grant_type=refresh_token and keep the
newest refresh token each time. The device flow is open to this client only.
Hosted server with a cw_* key
For CI and scripts, send a key instead of signing in. A key holds both permissions.
claude mcp add --transport http clipwright https://mcp.clipwright.io/mcp --header "Authorization: Bearer cw_..."
n8n
n8n's built-in MCP Client node calls one tool per node:
- Server Transport: HTTP Streamable; MCP Endpoint URL:
https://mcp.clipwright.io/mcp. - Authentication: Bearer Auth, with a credential whose token is your
cw_...key. - Tool: pick from the list, for example
quote_ugc; Input Mode: JSON.
Verified live on 2026-10-20 with self-hosted n8n 2.43.8: get_account,
quote_ugc and get_run returned through the node. A workflow to import,
n8n/clipwright-ugc.json in seocombat/clipwright-mcp,
quotes a script, stops when the quote is above max_credits, starts make_ugc
and polls get_run every 15 seconds. The whole workflow ran on n8n Cloud on
2026-10-20 and returned a 1080×1920 MP4 in about three minutes. n8n's OAuth
credential has not been run against Clipwright yet.
Local server over stdio
claude mcp add clipwright -e CLIPWRIGHT_API_KEY=cw_... -- npx -y -p @clipwright/mcp-server clipwright-mcp
Put your own key in place of cw_.... The command is one line and gives the
shell nothing to expand, so bash, zsh and PowerShell run it alike. It needs
Node.js 20 or newer.
Any MCP host works — the local server speaks JSON-RPC over stdio. Issue keys at app.clipwright.io/api-keys.
Environment
These variables configure the local server.
| Variable | Required | Meaning |
|---|---|---|
CLIPWRIGHT_API_KEY | yes | Your cw_* token. The process exits immediately without it. |
CLIPWRIGHT_CLIENT_ID | recommended | Identifies this installation and enters the idempotency key, so two installations never collapse into one run. Left unset, the server keeps one in ~/.clipwright/client-id, which suits a single laptop. Set it yourself where a home directory is shared, cloned or read-only — containers, CI, copied images — as SKILL.md §2 explains. |
CLIPWRIGHT_API_URL | no | Defaults to the hosted API. |
Recipes
Each recipe is a prompt we gave an agent with this server connected, the call the agent sent, and what the video cost. The videos play at clipwright.io/mcp.
1. A creator reel with on-screen graphics
One presenter on camera, with graphics over her that react to what she says: the format of talking-head reels that sell a product. One prompt, no editor.
The prompt, given verbatim, with our actor id, screenshot link and site named alongside it:
Make a 30-second vertical reel for my AI receptionist with my presenter actor and the voice Hope. Script: "Every missed call is a lost deal. Your receptionist sleeps, takes breaks, and sometimes gets annoyed. This one doesn't. It answers calls all day, works around the clock, and never gets annoyed. Here's a real one: a customer calls at two in the morning to book a cleaning, and it handles everything, the time, the price, the confirmation text. Small businesses miss a third of their calls. Not anymore. Set it up in ten minutes and stop losing deals while you sleep."
While she lists what it does, show a checklist with three switches ("answers calls" and "works 24/7" off, "gets annoyed" on) and have a cursor flip each one as she says it. When she gets to the 2 a.m. customer, show an incoming call from +1 (305) 555-0147 on the right and answer it on "handles". Put my dashboard screenshot in the top right corner while she says "Small businesses miss a third of their calls." End on a card with my site and "create your assistant". Caption only the key lines, in the highlighter style, and zoom in a little on "Not anymore." Quote it before you render.
What the agent sent: one make_ugc quote with templates (checklist, phone_call,
end_card), one overlays picture, caption_mode: "highlights",
caption_style: "highlighter" and a punch_in on one emphasis line. Click,
switch and ring sounds come with the templates by default. We checked the prompt
with a fresh agent on this server on 2026-10-24; the video was rendered from the
same call.
Price: 400 credits for 33 seconds. The quote named the price before anything was spent. Watch it.
Tools
list_workflows— read workflow methods and current availability. Free.get_workflow— read one method's required inputs, script rules, evidence and limitations. Free.prepare_workflow_plan— create with a complete document, or save with bothplan_idandbase_version. Free; starts no run.get_workflow_plan— read the current owned plan, revision and readiness. Free.approve_workflow_plan— explicitly approve a chosen revision and content digest. Free; starts no run.quote_workflow_plan— quote the approved revision without spending credits.run_workflow_plan— start a paid workflow using its exact quote fields and an explicit idempotency key. Returns its receipt and run id immediately; pollget_run. Recover an uncertain purchase with the original request and key.list_voices— voices forvoice: the presets, the thirty voices of the speech modelgemini-3.8-flash-ttsand the catalog formake_ugc, with filters by language, gender, age, use case and model, and a short audio sample where the voice has one. The seventeen narration voices ofmake_facelessare listed withskill: "make_faceless". Free, spends no credits.list_template_sounds— the soundsmake_ugctemplates play, by beat: where each beat plays, its default and every nametemplate_soundstakes. Free.list_actors— ready-made faces with stable ids and verified formats. Free.quote_ugc— estimated duration and price, plus a warning whenmake_ugcwith the same input would be refused for money or access. Free.get_account— balance, debt and holds of the account. Free.make_ugc— renders a clip; this is the call that costs money. Returns arun_idimmediately instead of blocking, because a render takes minutes.get_run— status of a run. Free; poll it every few seconds.get_run_shots— the shots of a finished video: the span, kind, description and preview link of each, with the narration words and their timing. Free and read-only. A run with no shots to show answers with the reason instead.quote_revision— price of a revision of a faceless video: what it would change and what it costs. Free.revise_run— starts a revision: a new run that renders the video again with the shots you change; this costs money. Passchanges(a shot and what to generate for it) or the whole editeddocument. A revision that delivers is charged its quoted total; one that fails is charged nothing, even when its paid work was already done. An identical request returns the same run;attemptstarts a second one on purpose. Returns arun_idimmediately; pollget_rununtilsucceeded.quote_candidate— price of a shot candidate of a faceless video: one picture or clip made for one shot, to keep or discard in a revision. Free.create_candidate— makes a shot candidate; this costs money. Pass the shot and the operation to generate for it. A delivered candidate is charged its one unit whether it is kept or discarded; one that fails is charged nothing, even when its paid work was already done. An identical request returns the same run;takemakes another candidate on purpose. Returns arun_idimmediately; pollget_rununtilsucceeded.list_generation_models— the models of single generations with their measured shapes and credits per image or per second. Free.quote_image,quote_video— price of one image or one clip. Free.generate_image,generate_video— make one image or one silent clip; this costs money. One that fails is charged nothing, even when the vendor was already paid. An identical request returns the same run;attemptstarts another on purpose. Pollget_run;generation.urlis the file.upload_image— a PNG/JPEG from a localpathor a public httpsurl→upload_idand a signed https url to pass asimage. Free; 20 per day, at most 30 url downloads per hour, the url lives 24 hours. Setaspect_ratioexplicitly afterwards.create_upload— a presigned PUT for a local PNG/JPEG when the server is remote: PUT the file with curl, then use theupload_id. Free.get_upload— one upload with a fresh url, its size andusable_until; aftercreate_uploadit confirms the PUT. Free.create_asset— save a picture or an MP4 clip as an account asset, kept until you delete it: from arun_id, anupload_id, a publicurl(a clip withkind: "video"),base64, a presignedput, and a localpathon stdio. A clip is checked in the background:get_assetsays when it is ready. Your own material needsrights_confirmed: trueonce. Free; 2000 assets and 20 GB per account, 100 new ones per day.list_assets,get_asset— the account's assets with fresh urls and the usage of the caps. Free.update_asset— rename an asset or change its role. Free.delete_asset— delete an asset; a daily cleanup removes its file once 7 days have passed. Free.quote_actor— price of a personal actor before creating one. Free.create_actor— creates a personal actor from a description; this costs money. Each requested format is a separate charged image. Returns arun_idimmediately; pollget_rununtilsucceeded, then passcreated_actor.actor_idtomake_ugcasactor_id.quote_faceless— price of a faceless video before making one. Free.make_faceless— makes a faceless video from your script or from a short brief; this costs money. You pick 30 to 90 seconds; the video ends with its narration, from 25 seconds to a quarter past your pick (never past 90), and costs at most the quote. Narration plays over an opening animated clip and image scenes, with captions on by default.voicepicks the narration voice by name. Returns arun_idimmediately; pollget_rununtilsucceeded.delete_actor— removes a personal actor of this account. Videos already made with it stay as they are. Free.get_actor_defaults,set_actor_defaults— read and save an actor's B-roll default. It is stored only: today's talking-actor videos do not use it. Free.
Call quote_ugc before make_ugc: the quote is where the contract tells you
what it will and will not honor. Anything the API cannot deliver comes back in
warnings[] rather than being dropped silently, and unsupported fields are
rejected up front instead of being accepted and ignored.
Bumping the attempt field starts a new paid render. It is the way to retry
a failed run on purpose, not a free refresh. For faceless videos attempt
belongs to make_faceless: a quote takes none, and quote_faceless refuses it.
SKILL.md ships inside the package with the full operating instructions, and
CHANGELOG.md next to it says what changed in each release and what was removed.
Privacy
The server sends your script or brief, chosen options and any uploaded image to the Clipwright API to quote and render the video. How that data is handled: clipwright.io/privacy. Documentation: clipwright.io/docs.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.