Studio productions over MCP — direct a film from a conversation
The MCP lane onto a studio production (Cloud edition): a Nodaro workflow
whose settings.studio holds an ordered list of scenes — each one a frame, an
optional motion, and the plan, looks, cast bindings and voice that made them.
The same production opens in the editor at
studio.nodaro.ai, so an assistant can draft the film,
hand it over, and pick it up again after the user has moved three scenes around.
Two vocabularies: the user’s words, the document’s keys
The editor and the document name the same things differently, and an assistant talks to a person who only ever sees the editor.
| The user says (the editor) | The document says (keys, arguments, operations) |
|---|---|
| film | the production |
| scene — “Scene 3”, the cards on the timeline | a shots[] entry, addressed by shot_id; add_shot, rename_shot, remove_shot, … |
| a scene’s frame (its results are takes) | still |
| a scene’s motion (its results are takes) | clip |
| the shots inside a motion | beats[], set with set_beats |
The identifiers are the document’s and they stay — every tool, argument and operation name on this page is exact. Prose is the user’s: say “scene 3”, “the frame”, “the motion”, “shot 2 of this scene”, and never call a scene a shot.
- “Shot” means a shot inside a motion — an entry of the
beats[]of the scene the user is looking at — never ashots[]entry. “Change shot 2” isset_beatson that scene, and “delete shot 3” removes thatbeats[]entry, never a scene. If no scene is in view, if its motion has no shots, or if that motion has fewer shots than the number they named, ask which they mean rather than guessing. - “Scene N” is always
shots[N-1]: “rename scene 3” isrename_shoton it and “delete scene 3” isremove_shoton it. - A scene’s frame is its
stilland nothing else. A planned frame is a different thing — a frame plan the user reviews and accepts, generated withgenerate_studio_keyframe— and a scene’s start frame and end frame are the endpoints its motion runs between. - The operating guide’s operation list and the
receiptsan edit returns are the document’s own text: their operation names are exact, but where they say “shot” they mean a scene. Do not repeat that wording to the user.
Prefer this lane for “make me a film / a scene / a sequence, and let me keep editing it”. Recast authoring is the lane for a movie authored as one JSON document; the Video Director and the workflow tools remain the right choice for a canvas build.
The loop
get_studio_production_skill— read the guide first. Four parts:authoring(how a plan is written),catalog(every picker, model and enum, in full),schema(the strict JSON Schema a plan is validated against) andoperating(the tool map, the loops, the operation vocabulary, and what these tools will not do). All four are rendered server-side from the version that is live, so they describe the deployment you are actually talking to rather than what was true when this page was written. Free.validate_studio_plan— free, persists nothing, and resolves cast names against the caller’s own library. Loop onerrors[].pathuntilvalid: true. Do this before spending anything.create_studio_production(a new film, optionally landing the plan in the same call) orimport_studio_production(add a plan’s scenes to a production that already exists). Both are free — no media is generated. For a brief rather than a plan,describe_studio_productionruns the Director: it starts a job and records it on the production, and the drafted scenes arrive when that run is landed, like any other finished job.get_studio_production— the production as it now stands: its scenes, what has landed on them, and apendingblock naming what is still in flight. Withworkflows:write, reading also lands what has finished — the view you get back is the one taken after that landing, so a re-read is how a finished generation reaches its scene. With read-only scope it is a pure read: nothing lands, and a job that has finished stays inpendinguntil someone who may write brings the production up to date.detail: "full"adds every result with the context that regenerates it;shot_idreads one scene, the cheap re-read after a generation.edit_studio_production— every change to a production is an operation, and this is the one tool that applies them. See below.- Generate:
generate_studio_stillgenerates a scene’s frame,generate_studio_clipits motion,new_studio_shot_from_framegrabs a frame out of a motion,voice_studio_shotspeaks a line over a scene,revoice_studio_cliprecasts the voices of a motion,score_studio_productionwrites the soundtrack. plan_studio_export— the ordered steps that assemble the film, each with its price. Run them with the ordinary generation verbs and record the finished file back on the production.share_studio_production/clone_studio_production— open the share-by-link read, or take a copy.
An abandoned conversation strands nothing: the production is a real row the user can open in the editor, and a generation that was still running is landed onto it the next time the production is brought up to date — by you, by the editor, or by the next assistant the user asks.
Editing: operations, not patches
edit_studio_production takes a batch of operations and applies it atomically
against the newest document:
- An operation addresses by stable key — a scene by its id, a cast row by its role slug, a result by its job id or url — never by position, because the user may be editing the same production in the browser while you work.
- A batch composed against a slightly older version is still applied to the
newest one and the reply says it was rebased;
strictrefuses instead. - One bad operation refuses the whole batch and writes nothing. The error names the offending operation by its zero-based index, so fix that one and send the same batch again.
receiptsis one past-tense line per operation — the thing to show a user who asks what you just did.- Many deletes are recoverable: a removed scene, take or planned frame goes to the production’s bin and can be restored. Others are not — clearing a cast member, a voice or the soundtrack, deleting a cut, removing a sequence, purging one bin entry or emptying the bin. Do not tell a user a delete can be undone unless the preview below marked that operation restorable.
- Sharing is not an operation. It has its own tool, so a batch that was editing something else can never change who can see the work.
The operation vocabulary itself is served, not printed here: read it from
get_studio_production_skill { part: "operating" }, which is generated from
what the deployment accepts.
Previewing a batch
edit_studio_production { dry_run: true } answers what the batch WOULD do and
writes nothing: the version it was taken at, one receipt per operation carrying
that operation’s class (S changes the document, D deletes, P changes who
can reach the work, $ spends) and, on a delete that put something in the bin,
restorable: true. Show that to a person before you apply the batch; then send
the same batch again without the flag.
The preview is proved, not assumed, and the proof is the client’s duty.
POST /v1/studio/productions/:id/ops ignores keys it does not know, so a
deployment whose studio service predates the flag would simply APPLY a batch
sent with it — the write the preview existed to prevent. So a client asks for
the capability first, with the one batch that has never written anything: the
EMPTY one. { ops: [], dryRun: true } comes back as { dryRun: true, version,
receipts: [], warnings: [] } where previews are served, and as the ordinary
empty-batch reply where they are not; only the first earns the real batch. The
MCP tool does this for you and refuses with studio_preview_unavailable when
the answer lacks the literal, having sent nothing.
Preview ids are preview-only. The apply mints its own, which is why an undo reads the bin ids off the APPLY’s receipts and never the preview’s.
Tools and scopes
| Tool | Scope | What it does |
|---|---|---|
get_studio_production_skill |
none | The four-part guide. Free. |
validate_studio_plan |
workflows:read |
Check a plan against the caller’s library. Free, and it persists nothing — but it resolves every cast name against the caller’s own entities, so it is gated exactly where its route is. |
list_studio_productions |
workflows:read |
The caller’s productions, newest first. |
get_studio_production |
workflows:read |
One production — what has landed and what is pending. With workflows:write it also lands what has finished before it reads; read-only, it lands nothing. Pass reconcile: false to read without landing — for an in-app editor that lands its own. |
plan_studio_export |
workflows:read |
The ordered export steps and their prices. |
create_studio_production |
workflows:write |
A new production, optionally from a plan. |
import_studio_production |
workflows:write |
Append a plan’s scenes to one that exists. |
edit_studio_production |
workflows:write |
Apply a batch of operations. |
share_studio_production |
workflows:write |
Open or close the share-by-link read. |
clone_studio_production |
workflows:write |
Copy one into the caller’s own Studio project. |
describe_studio_production |
workflows:write + workflows:execute |
Turn a brief into scenes (a Director run). |
generate_studio_still |
workflows:write + workflows:execute |
Generate a scene’s frame — count candidates. |
generate_studio_clip |
workflows:write + workflows:execute |
Generate a scene’s motion. |
new_studio_shot_from_frame |
workflows:write + workflows:execute |
A frame out of a scene’s motion, placed where you say. |
voice_studio_shot |
workflows:write + workflows:execute |
Speak a scene’s line. |
revoice_studio_clip |
workflows:write + workflows:execute |
Recast the voices of a scene’s motion. |
score_studio_production |
workflows:write + workflows:execute |
Write the film’s soundtrack. |
The seven that spend need both grants, not either one: their routes
authorize on workflows:write, and starting the run is workflows:execute. A
tool whose scopes have not all been granted is omitted from tools/list
entirely — it is not there to call and fail — so a session holding only one
half of the pair sees none of them and gets no explanation. Grant both for the
generating lane.
Deleting a production is deliberately not exposed. The soft hide (archive) is an
operation on edit_studio_production, and it is reversible.
Confirmation classes
A tool that spends credits, or that changes who can see the work, says so on its
own definition — in _meta.nodaro.confirm. There are two values. Read the mark
rather than hard-coding a list of tool names: the mark is what a client should
put a confirmation prompt in front of, and it stays right as the family grows.
_meta.nodaro.confirm |
Ask first because… | Tools |
|---|---|---|
"$" |
It costs credits. | describe_studio_production, generate_studio_still, generate_studio_keyframe, generate_studio_clip, new_studio_shot_from_frame, voice_studio_shot, revoice_studio_clip, score_studio_production |
"P" |
It changes who can see the work. | share_studio_production |
A tool with neither mark reads, or changes only the caller’s own document.
edit_studio_production is in that group and carries no mark on purpose: what a
batch does is decided by the operations inside it, and the tool layer does not
classify them — a second copy of the vocabulary there would drift from the one
the route validates against. A client that wants to confirm before something is
removed asks the tool for a preview instead, and shows what comes back:
every operation is classed there, and each delete says whether it can be taken
back.
Spending discipline
Two levers, and both matter more here than in a one-shot generation, because an agent driving a film makes dozens of calls:
dry_run: trueongenerate_studio_stillandgenerate_studio_clipreturns the quote — the model and what the run would cost — and writes nothing.credits: nullmeans the model is unpriced, which is “unknown”, not “free”. Present the price and let the user accept before you spend. The remaining spending tools —describe_studio_productionincluded — have no dry run: quote them from the model’s own price, or ask first.client_request_idmakes a retry safe: the same token answers with the jobs the first call started, having submitted nothing and charged nothing. Mint it yourself (8–128 characters ofA-Za-z0-9_.:-) and never derive it from the request body. Never retry a spend without it.
Generation is run-then-poll, and a finished job becomes a take on the scene only when it is landed. Nothing here blocks a request for minutes:
- a frame or motion run returns its job ids immediately;
- wait on those jobs —
get_job, or the production’s ownpendingblock; - re-read with
get_studio_production. Holdingworkflows:write, that read lands everything that has finished before it answers, so the view already carries the new results —shot_idis the cheap one. Holding onlyworkflows:readit lands nothing and what finished stays inpending, for a writer to pick up; - show the user what arrived.
Three things make that landing step fall through to the plain read instead of
failing it: a deployment that does not serve the landing sweep, a caller who
may not write, and a production busy under another writer at that instant
(the sweep gives way; what finished lands on the next read). Any other
failure of that step is reported rather than hidden. Landing one named job is also part of the
operation vocabulary the operating guide serves, applied with
edit_studio_production, for when you want that instead of a sweep. Over REST
the two halves are separate — POST /v1/studio/productions/:id/reconcile and
then the GET, which never writes on its own — and get_studio_production is
that pair in one call.
Availability
Studio productions are a Nodaro Cloud feature. On a deployment that does not
serve them every tool in the family answers not_available — a plain refusal,
not an error to retry. Feature-detect with list_studio_productions if you need
to know before offering the lane.
Where the format is documented
The plan format (nodaro-studio-production) has one published home: the studio
app serves the same rendered authoring guide, catalog and JSON Schema at
studio.nodaro.ai/skills/studio-production/.
get_studio_production_skill renders it from the platform side, so an assistant
that reads the tool and a person who reads the page are reading one document.
REST and SDK equivalents
The same lane over raw REST is /v1/studio/productions;
over the SDK it is client.studio.productions.* — see
SDK Reference.
Retake a linked take
On a server advertising operations.retakeLinkedClips, call
generate_studio_clip with production_id, shot_id, retake_result_key, and
dry_run: true. Review the returned price and inputHash. Submit the same take
with expected_input_hash and a fresh client_request_id; omit dry_run,
mode, and overrides. The server uses the take’s original settings and retained
endpoint images, independently of later plan changes. A retained copied take can
use its copy result key after the source production or job is deleted. Missing or unverifiable
original inputs refuse the request. Existing takes remain in history. This
requires an explicit generation request and spends credits only on submission.