Developer Guide
Use OpenStory from an agent
Connect an MCP client or script to OpenStory — inspect a production, edit a scene, plan and approve generation, poll it, and export
OpenStory has two agent surfaces:
- MCP at
/mcp— inspect a whole production, edit scenes, plan and run generation, and export. This is the one to use from Claude, Cursor or any MCP client. - REST at
/api/v1— create a sequence from a script in one call, then poll it. See Public API. Use it where MCP is not available, and for creating sequences (MCP cannot create one yet).
Connect
Every tool name has the openstory. prefix (openstory.get_scene); this guide drops it for brevity. Only whoami has none.
| What | Value |
|---|---|
| Server URL | https://<your-openstory-host>/mcp (Streamable HTTP, POST only, no sessions) |
| Protocol revision | 2026-07-28, and the 2025 handshake for clients that still open with initialize. There is no session either way: each request is served on its own, so a request may reach any server instance. With the official SDK client, set versionNegotiation: { mode: 'auto' } (or pin 2026-07-28). |
| OAuth (preferred) | Discovery at /.well-known/oauth-protected-resource/mcp; dynamic client registration; authorization code + PKCE; resource = https://<host>/mcp. An unauthenticated request gets a 401 with a WWW-Authenticate challenge pointing at that document. |
| Static header | Authorization: Bearer osk_… with an API key from Settings → Developer. API keys are not scoped. |
| Getting a key | Without a browser redirect, use the REST device-code login: POST /api/v1/device/code, show the user the code, poll the returned link until it returns the key. |
Scopes (OAuth only)
| Scope | Grants |
|---|---|
sequences:read | Every read tool and resource. |
sequences:write | update_scene, update_sequence, archive_sequence, unarchive_sequence, scene and shot create / reorder / delete / restore, update_shot, shot prompts, spec and dialogue edits, picking a still, clip, dialogue version or reading, character / location / element edits, voice and sheet picks, the music prompt and track, upload_media and the set_*_from_upload tools, select_sequence_model, Studio favourites and deletes, cancelling a video render, dialogue recording, voice design or pending prompt/still, plan_export, start_export. |
generate | create_sequence, Studio generation, edits, finals and prompt drafts, regenerate_storyboard, rebuild_shot_prompts, plan_generation, execute_generation, retry_failed_work, one shot's still, variants and clip, rendering drafts at quality, add_model_to_sequence, add_element / replace_element, sheets, recasts, voice design, music and dialogue — anything that can spend credits. |
A missing scope is a tool error with code INSUFFICIENT_SCOPE and details.scope naming what to re-authorize with. Tool discovery and whoami need no scope.
Identifiers
sequenceIdis required on every production tool; everything below it is checked against it.- A scene id (
sceneId) and a shot id (shotId) are different things.get_scenetakes a scene id and refuses a shot id;get_shottakes a shot id and refuses a scene id. A shot carries its parentsceneId, so you can always navigate back up. - What a scene or shot shows is its selected version (script, prompt, still, clip). Reads return the selected version and its id;
update_sceneasks for the selected script version you read, so an edit never overwrites one you did not see. - Ids from another team, another sequence or a deleted row are reported as not found.
Reads, pages and limits
- Start with
list_sequences, thenget_sequence(summary and counts) orget_sequence_status(failures, cheaper). list_scenesandlist_shotspage withlimitand the opaquenextCursor. Prompts and media are opt-in (includePrompts,includeAssets).get_production_biblereturns style, cast, locations, elements and every scene's narrative in one call. When it cannot include everything, a*Truncatedfield names the list tool (and cursor) that continues it.- A successful result has structured content and the same JSON as text; a coded error carries
structuredContent.error(code,message,details). A read over 256 KiB is refused with a message telling you to page; nothing is silently cut. A write whose result cannot be returned answersRESULT_NOT_RETURNEDwith the ids it did write: the work is done, so do not call it again; read the entity back.
Resources
For hosts that attach context rather than call tools:
openstory://sequences/{sequenceId}/summary— same asget_sequence.openstory://sequences/{sequenceId}/bible— same asget_production_bible.openstory://sequences/{sequenceId}/scenes/{sceneId}— same asget_scene.
resources/templates/list returns the templates, resources/list the summary and bible of your 50 latest sequences. Every resource has a tool, so nothing depends on a host supporting resources.
Inline views (MCP Apps)
get_sequence links an MCP Apps view (ui://openstory/sequence-card.html): hosts that render MCP Apps show the poster, status, counts and music inline, with a link that opens the sequence in the app. While the run is processing the card updates itself and, when it ends, posts a message into the chat so the agent picks up without polling. Hosts without MCP Apps get the plain structured result; the view changes nothing in it.
The production workflow
This is the loop an agent should follow. It is also the procedure to put in an agent skill.
- Create (optional).
create_sequencewith a script starts a new sequence (same input asPOST /api/v1/sequences); pollget_sequence_statusuntil the storyboard is done.regenerate_storyboardreplaces a sequence's whole script, style or aspect ratio and rebuilds every scene, so ask first. - Inspect.
get_sequence_statusfor what is ready and what failed.get_production_biblefor the story and cast.get_scenefor the scene you will touch — notescript.id. - Edit.
update_scenewithsequenceId,sceneId,expectedScriptVersionId(thescript.idyou read) and only the fields you change (scriptExtract,title,location,timeOfDay,storyBeat,continuity). ACONFLICTmeans the scene changed since you read it — possibly your own edit whose reply was lost (details.selectedScriptVersionIdis the current version): read it again and redo the edit only if it is still needed.changed: falsemeans nothing differed. Structure:create_scene(optionally with its script),reorder_scenes,delete_scene/restore_scene,create_shot,reorder_shots,delete_shot/restore_shot,update_shot(length, start frame on/off),update_sequence(title, target length, music on/off, default video model). Deletes are soft and undoable:list_deletedandlist_archived_sequencesshow what can be restored. None of these starts generation. Shot content:get_shot_spec/update_shot_spec(rebuilds the prompts from the spec, free),update_shot_prompt(a hand-written prompt is kept by later rebuilds),restore_shot_prompt_version,rebuild_shot_prompts(free unless the spec is stale, then an AI rewrite that spends credits),list_shot_dialogue/update_shot_dialogue/select_shot_dialogue_version,select_shot_dialogue_reading/discard_shot_dialogue_reading,select_shot_image_version,select_shot_video_version(ids fromlist_versions). Cast and music:create_character/update_character/delete_character/restore_character,set_character_voice_enabled,list_character_voices/select_character_voice_version,select_character_sheet_versionanddiscard_/undiscard_character_sheet_version; the same for locations (create_location…select_location_sheet_version);set_element_description,rename_element_token,delete_element/restore_element;update_music_prompt,restore_music_prompt_version,select_music_track,discard_music_track/undiscard_music_track.list_deleted_castshows what can be restored. None of these starts generation. One shot, one sheet, one track (each spends credits, so confirm with the user first — these do not go through a plan):generate_shot_image,generate_shot_image_variants→get_shot_variant_grid→select_shot_image_variant,generate_shot_video(cancel_video_renderstops waiting on one),render_shot_at_quality/render_sequence_drafts_at_qualityfor Ark drafts,add_model_to_sequencethenselect_sequence_model,regenerate_character_sheet/regenerate_location_sheet,recast_character/recast_location,generate_character_voice(the first take becomes the voice;cancel_character_voice),generate_music,rewrite_music_prompt,regenerate_shot_dialogue(list_shot_dialogue_claims/cancel_shot_dialogue),cancel_pending_shot_artifact. Model ids come fromlist_models. Each returns when the run starts; read the shot, cast or music again to see the result. Files: when the user attaches an image, clip or audio file,upload_mediawith itsurlor base64data(8 MB) anduse, then attach the returneduploadwithset_shot_image_from_upload,set_shot_video_from_upload,set_music_from_upload,set_character_sheet_from_upload,set_location_sheet_from_upload,add_elementorreplace_element. An image of a real person is refused (ATTESTATION_REQUIRED) until the user confirms they hold the rights: ask them, then upload again withportraitAttestation. - See the effect. The edit returns the scene id, its new script version id, its shot ids and its first shots' staleness;
get_scenereads the scene back.list_shot_stalenesspages the rest. Editing starts no generation. - Plan.
plan_generationwithmode: "stale"and adepth(prompts,images,dialogue,videoormusic: how far to update what the edit made stale; whole sequence,sceneIdsorshotIds) ormode: "missing"withstopAt(continue an unfinished sequence, whole sequence only), orretry_failed_workafter failures. A plan starts nothing. It returns per-stage shot ids, skipped shots, models, an estimate in USD (nullwhen a component has no price) andblockers. - Ask for approval. Show the user the concrete work and the cost from the plan. Do not execute without a yes.
- Execute.
execute_generationwithplanTokenandconfirm: true. If the work or price moved since planning it is refused (CONFLICTwithdetails.code: "PLAN_CHANGED": plan again). A live run, a blocker or too few credits also refuse it, and the plan stays executable. Safe to retry after a timeout: a repeat returns the sameworkflowRunIdsand never charges twice;PLAN_CHANGEDorGENERATION_IN_PROGRESSon a repeat means the earlier call started the work — checkget_sequence_status. - Poll.
get_operation_statuswith theworkflowRunIds, everypollAfterSeconds. It reports those runs only: per-shot failures, skips, and aterminalflag.partially_failedlists what failed; plan a retry for it.LAUNCH_INCOMPLETEfromexecute_generationnames the runs that did start: poll them and do not plan the same work again. - Inspect and export. Read the shots again.
plan_exportpreviews what an export would do;start_exportrenders an MP4 (no plan or confirm: it spends no credits, and a ready MP4 of the same cut is reused). Pollget_export_status. The MP4 renderer runs in production only: on previews, local dev and self-hosted Deploy-button installs an export does not render.
The Studio
The Studio makes standalone images and videos, outside any sequence. Each generation spends credits, so confirm with the user first.
- Choose a model.
list_models: an entry withstudioset is offered in the Studio, andstudiosays what it takes (video modes, durations, reference limits, end frame, audio). - Attach files (optional).
upload_mediawithuse: "studio"and nosequenceId. Pass the returneduploadas a reference, start or end frame. Earlier uploads are inlist_studio_uploads, earlier results inlist_generated_assets(source: "studio"); their URLs are accepted too. A real person needsportraitAttestation, as above. - Write the prompt (optional).
draft_studio_promptwrites one from the references (uses credits). - Generate.
create_studio_assetswithactivity(imageorvideo), the model,aspectRatioand the prompt; a video also takesdurationandmode(text,referenceorframes). Up to 4 at once. - Poll.
get_generated_assetwith each returned id untilstatusiscompletedorfailed. - Refine.
edit_studio_assetrewrites a finished Seedance clip from a prompt;get_studio_edit_historyshows the chain.render_studio_asset_at_qualityturns a finished draft into its 1080p final.set_studio_asset_favoritemarks keepers;delete_studio_assetremoves one for good (confirm first).
Not available through MCP yet
- Choosing another designed voice take or assigning a library voice (both write ElevenLabs' account-wide voice slots).
- Publishing to social platforms.
- Cancelling a running operation from
execute_generation(single renders and claims can be cancelled; see above).
Client compatibility
| Client | Status |
|---|---|
Official MCP SDK client 2.1.0 (versionNegotiation: auto) | Tested in CI against the server handler (auth stubbed; the HTTP auth layer has its own tests): discovery, reads, sequence-wide and scene-filtered shot paging, scene ↔ shot navigation, resources, missing scope. |
| Clients that only speak the 2025 handshake | Served with no session; each request independent, tools and resources alike (tested with the official SDK client in legacy mode). |
| Claude Code | Untested against a deployment. |
| Claude.ai custom connector | Untested. |
| Cursor | Untested. |
| Codex, ChatGPT | Untested. |
"Untested" means no one has connected that client to a deployment and run the workflow above. Results are recorded here as they are verified.