{"server":{"name":"shortdrama.studio","title":"shortdrama.studio","version":"0.3.0","websiteUrl":"https://shortdrama.studio/mcp"},"endpoint":"https://mcp.shortdrama.studio/mcp","toolsets":{"writer":"/mcp/writer","producer":"/mcp/producer"},"tools":[{"name":"attach_skills","title":"Use skills in a drama","description":"Pins skills to a drama (at most 12) at their latest version, so every agent and the studio follow the same house style for it, whoever works on it next. Listing a skill the drama already uses updates its pin to the latest version. get_drama_context shows the pinned skills.","scopes":["drama:write","skills:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"skills":{"minItems":1,"maxItems":12,"type":"array","items":{"type":"string","minLength":1,"maxLength":300}}},"required":["drama_id","skills"]}},{"name":"cancel_job","title":"Cancel a job","description":"Cancels a queued or running generation and releases its credits. A provider may still finish work already sent; the result is then discarded.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"job_id":{"type":"string","pattern":"^job_[0-9a-f]{32}$"}},"required":["job_id"]}},{"name":"create_drama","title":"Create a drama","description":"Creates a new drama (a series of vertical episodes, or a single short) with an empty story bible, and returns its id. Nothing is required; set what you know now, then update_drama, update_bible, upsert_characters and upsert_episodes. Leave the title empty until one is chosen (a premise is not a title), and set it with update_drama. Call get_drama_spec and get_catalog first to know the fields and allowed values. Example: {\"title\": \"The Contract Bride\", \"format\": \"series\", \"genre\": \"billionaire_ceo\", \"language\": \"id\", \"episode_target_count\": 30, \"episode_target_seconds\": 90, \"content_rating\": \"16_plus\"}.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"default":"","description":"Empty when the drama has no title yet","type":"string","maxLength":120},"logline":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"format":{"default":"series","type":"string","enum":["series","short_film","drama_ads","film_trailer","music_video"]},"genre":{"description":"A sub-genre key from get_catalog(\"genres\") (or a genre key), or the creator's own genre name","anyOf":[{"type":"string","maxLength":80},{"type":"null"}]},"language":{"default":"en","description":"Language of the dialogue","type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"aspect_ratio":{"default":"9:16","type":"string","enum":["9:16","16:9","1:1","4:5"]},"episode_target_count":{"anyOf":[{"type":"integer","minimum":1,"maximum":200},{"type":"null"}]},"episode_target_seconds":{"anyOf":[{"type":"integer","minimum":15,"maximum":1800},{"type":"null"}]},"content_rating":{"anyOf":[{"type":"string","enum":["all_ages","7_plus","13_plus","16_plus"],"description":"Who the drama is for, by age, from get_catalog(\"content_ratings\"): the ceiling the story is written to and every image and video is generated within. Pick it from the genre and the audience: 16_plus (TV-14) is the vertical-drama standard for romance, revenge and thriller; 13_plus for teen and sweet stories; 7_plus or all_ages for children and family"},{"type":"null"}]}}}},{"name":"create_upload_url","title":"Create an upload URL","description":"A one-time URL to upload a file straight to storage (PUT the raw bytes; valid 30 minutes): a voice sample (kind audio, purpose voice_sample) for set_character_voice, or a reference image (kind image, purpose reference) for the reference_asset_ids of generate_character_sheet and friends. Returns the asset id the file will have.","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]},"kind":{"type":"string","enum":["image","audio","video"]},"purpose":{"type":"string","enum":["voice_sample","reference"]},"label":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"bytes":{"description":"File size, to fail early when too large","anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]}},"required":["kind","purpose"]}},{"name":"delete_custom_preset","title":"Delete a custom preset","description":"Removes a workspace custom preset by catalog and key. Dramas that already use it keep their stored value.","scopes":["skills:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"catalog":{"type":"string","enum":["genres","tropes","visual_styles","moods","lighting","camera_moves","music_moods","story_structures","voices"]},"key":{"type":"string","minLength":1,"maxLength":60}},"required":["catalog","key"]}},{"name":"delete_items","title":"Delete items","description":"Soft-deletes characters, looks, locations, props, episodes or scenes of one drama. Deleted items disappear from the drama and can be restored for 30 days with revert_revisions. Warns when a deleted item is still used by scenes. Call it only when the user has just asked to delete these exact items; never to tidy up, make room, or carry out an earlier request again. Each item names what you mean in `expect`, checked against the id: if they disagree nothing is deleted. Report what was deleted from the result's `was`, not from the request.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"items":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["character","look","location","prop","episode","scene"]},"id":{"type":"string","maxLength":40},"expect":{"type":"string","minLength":1,"maxLength":200,"description":"What this id is, as the user named it: an episode's number (\"8\"), a scene's episode/position (\"8/3\"), otherwise the exact name (\"Arman\")"}},"required":["type","id","expect"]}}},"required":["drama_id","items"]}},{"name":"delete_render","title":"Delete a render","description":"Deletes an episode render that is not in use, with its MP4 and subtitle files. The render the episode uses (in_use in list_renders) is refused (RENDER_IN_USE): select_render another first; so is one still rendering. Other renders keep their version numbers. Cannot be undone.","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"render_id":{"type":"string","pattern":"^rnd_[0-9a-f]{32}$"}},"required":["render_id"]}},{"name":"delete_skill","title":"Delete a skill","description":"Deletes one of the workspace's own skills and detaches it from every drama that used it. Official skills cannot be deleted.","scopes":["skills:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"skill":{"type":"string","minLength":1,"maxLength":300}},"required":["skill"]}},{"name":"delete_take","title":"Delete a take","description":"Deletes a take that is not in use (a music track, video, keyframe, portrait… that is not the selected one), with its file. The selected take is refused (TAKE_IN_USE): select another first. Cannot be undone.","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"take_id":{"type":"string","pattern":"^tk_[0-9a-f]{32}$"}},"required":["take_id"]}},{"name":"detach_skill","title":"Stop using a skill in a drama","description":"Removes a skill from a drama's pinned skills. The skill itself stays in the library.","scopes":["drama:write"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"skill":{"type":"string","minLength":1,"maxLength":300}},"required":["drama_id","skill"]}},{"name":"estimate_cost","title":"Estimate credits","description":"Credits a production plan would cost, before spending anything, with the workspace balance. Give items with a target id for an exact figure (the clip length and references of that scene), or a type with seconds/count for a rough one, e.g. [{type:\"scene_video\", seconds: 8, count: 12}].","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"items":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["character_sheet","location_plate","prop_image","cover","scene_frame","scene_video","episode_music","render_episode"]},"id":{"description":"The target id (look, location, prop, scene, episode; drama for cover). With it the estimate is exact.","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"resolution":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"seconds":{"description":"Video/music length when no id is given","anyOf":[{"type":"number","minimum":1,"maximum":600},{"type":"null"}]},"count":{"default":1,"type":"integer","minimum":1,"maximum":500}},"required":["type"]}}},"required":["drama_id","items"]}},{"name":"export_drama_package","title":"Export the drama package","description":"Everything needed to publish or submit the drama: a ZIP (link valid 24 h) with the script (JSON and Fountain), the story bible and cast as Markdown, the cover, and subtitles (SRT/VTT) for every rendered episode, plus direct links to each episode MP4 (valid 7 days). Free.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"}},"required":["drama_id"]}},{"name":"export_script","title":"Export the script","description":"Exports a drama (or a range of episodes) as one document: \"json\" (shortdrama.script.v1: settings, bible, cast, locations, props, episodes with scenes, references by name) or \"fountain\" (a standard screenplay; machine data rides in notes so it imports back losslessly). Edit it anywhere, then import_script.","scopes":["drama:read"],"toolsets":["writer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"format":{"default":"json","type":"string","enum":["json","fountain"]},"episode_from":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"episode_to":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]}},"required":["drama_id"]}},{"name":"generate_batch","title":"Generate many at once","description":"Starts many generations in one call, e.g. every scene video of episode 1, or all portraits and plates. Checks first that the total fits the balance (and the connection's spend cap); if not, nothing starts and the answer says how much is missing. With idempotency_key, each item uses \"<key>:<index>\". Jobs queue and run a few at a time. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"items":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["character_sheet","location_plate","prop_image","cover","scene_frame","scene_video","episode_music"]},"id":{"type":"string","minLength":1,"maxLength":60,"description":"Character (or look), location, prop, drama, scene or episode id"},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"resolution":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["type","id"]}},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["drama_id","items"]}},{"name":"generate_character_sheet","title":"Generate a character portrait","description":"A reference portrait for one look of a character: one person, head to knees, on plain white, from the character's appearance and the look's description. It becomes the identity reference for every frame and video the character appears in. Without look, the default look (created as \"Default\" if the character has none). Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"character_id":{"type":"string","pattern":"^chr_[0-9a-f]{32}$"},"look":{"description":"Look id or name","anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"reference_asset_ids":{"description":"Uploaded reference images (create_upload_url), e.g. a face to match","anyOf":[{"maxItems":4,"type":"array","items":{"type":"string","pattern":"^ast_[0-9a-f]{32}$"}},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["character_id"]}},{"name":"generate_cover","title":"Generate the cover","description":"Poster key art (9:16) for the drama, as the vertical-drama apps' hits have it: the title lettered on it in the drama's language (the only text), one or two of the central cast in a composition that is not a line-up, their faces from their selected portraits (generate those first, or the cover invents faces that will not match), the genre's mood and lettering. Each take picks a fresh composition unless `composition` is given. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"composition":{"description":"hero: the lead's face large; couple: the two leads in one charged gesture; rivals: the lead before a looming rival; moment: the defining moment at its peak; symbol: the lead with the object the story turns on","anyOf":[{"type":"string","enum":["hero","couple","rivals","moment","symbol"]},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["drama_id"]}},{"name":"generate_location_plate","title":"Generate a location plate","description":"An establishing plate of a location (one wide view, empty of people) at a time of day; the set reference for every scene there. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"location_id":{"type":"string","pattern":"^loc_[0-9a-f]{32}$"},"time_of_day":{"description":"e.g. day, night; default the location's first time variant","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"reference_asset_ids":{"description":"Uploaded reference images (create_upload_url), e.g. a face to match","anyOf":[{"maxItems":4,"type":"array","items":{"type":"string","pattern":"^ast_[0-9a-f]{32}$"}},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["location_id"]}},{"name":"generate_music","title":"Generate episode music","description":"An instrumental score for one episode (ElevenLabs Music v2.5), composed scene by scene: each scene is a section as long as the scene, so the cold open hits on the hook, the music sits low under dialogue, peaks on the spike and ends on a cliffhanger sting. prompt: the direction for the score, any language: genre and sound, instruments, mood, tempo (BPM), key, era (e.g. \"dark hybrid orchestral revenge score, pulsing synth bass, taiko, 90 BPM, C minor\"); it is saved as the episode's music_prompt. Without one, the episode's saved music_prompt, else a brief from the drama's genre and visual style. get_episode_music shows the cue sheet first (free). Render mixes it under the clips' own sound, ducked when someone speaks. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"prompt":{"description":"The score's direction; saved as the episode's music_prompt","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"mood":{"description":"Older: a get_catalog(\"music_moods\") key, added to the direction","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"seconds":{"anyOf":[{"type":"number","minimum":10,"maximum":300},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["episode_id"]}},{"name":"generate_prop_image","title":"Generate a prop reference","description":"A reference image of a story prop (the object alone on a pure white background), used whenever a scene includes it. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prop_id":{"type":"string","pattern":"^prp_[0-9a-f]{32}$"},"reference_asset_ids":{"description":"Uploaded reference images (create_upload_url), e.g. a face to match","anyOf":[{"maxItems":4,"type":"array","items":{"type":"string","pattern":"^ast_[0-9a-f]{32}$"}},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["prop_id"]}},{"name":"generate_scene_frame","title":"Generate a scene keyframe","description":"A scene's key visual, from its action, camera, cast (their selected portraits), location plate and props. Cheap; approve it before the video. What it is follows the scene's key_visual: direct (the default: none, refused; the video goes straight from the references), frame (the opening frame; generate_scene_video starts from it, or uses it as a reference when voice samples are sent), storyboard / storyboard_3x3 (a comic page of 4–6 or 7–9 panels, each a camera setup; the video plays them in order as cuts). Every source picture must exist first (each cast portrait, the location plate, each prop image): otherwise MISSING_ASSETS lists them. Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scene_id":{"type":"string","pattern":"^scn_[0-9a-f]{32}$"},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["scene_id"]}},{"name":"generate_scene_video","title":"Generate a scene video","description":"The clip of a scene, with its dialogue spoken by the video model (lip-synced, in the drama language) and on-set sound; music is added at render. References are picked for you: the selected keyframe, the cast's portraits, the location plate, props, and, on models that take them, each speaker's voice sample. Every source picture must exist first (each cast portrait, the location plate, each prop image, and the key visual unless key_visual is direct): otherwise MISSING_ASSETS lists them. The clip length is the scene duration fitted to the model. start_from: \"auto\" (keyframe as first frame unless voice samples are sent), \"keyframe\", or \"references\". Check the prompt first with get_scene. The most expensive step: Costs credits (see estimate_cost); the result arrives as a take: poll with get_jobs or wait_for_jobs.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scene_id":{"type":"string","pattern":"^scn_[0-9a-f]{32}$"},"resolution":{"description":"One of the model's resolutions (list_models); default the drama's or the model's","anyOf":[{"type":"string","maxLength":10},{"type":"null"}]},"duration_seconds":{"description":"Override the scene duration for this clip (6–15 s)","anyOf":[{"type":"number","minimum":6,"maximum":15},{"type":"null"}]},"start_from":{"default":"auto","type":"string","enum":["auto","keyframe","references"]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"notes":{"description":"Extra direction for this generation only (not saved to the story)","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"idempotency_key":{"description":"Any unique string you choose; resending the same key returns the first job instead of paying twice","anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["scene_id"]}},{"name":"get_catalog","title":"Get catalogs of options","description":"The allowed and recommended values for production fields, with what each means and when to use it: formats, story_structures, genres, tropes, visual_styles, aspect_ratios, content_ratings, episode_lengths, shot_sizes, camera_angles, camera_moves, lenses, lighting, moods, transitions, languages, voices, music_moods. Each item has a stable key (store the key), a label, a description and often use_when. Genres have sub-genres (a key each, with a setting); styles have a medium, settings and a palette. Prompt text is not listed: the brief for the genre and style of a drama comes with get_drama_context. Workspace custom presets appear in the same lists with source \"custom\". Ask for only the catalogs you need.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"names":{"minItems":1,"maxItems":18,"type":"array","items":{"type":"string","enum":["formats","story_structures","genres","tropes","visual_styles","aspect_ratios","content_ratings","episode_lengths","shot_sizes","camera_angles","camera_moves","lenses","lighting","moods","transitions","languages","voices","music_moods"]}},"locale":{"default":"en","description":"Language of the labels and descriptions","type":"string","enum":["en","id","ms","fil","th","vi","hi","zh","ja","ko","es","pt","fr","de","ar"]}},"required":["names"]}},{"name":"get_credit_balance","title":"Get credit balance","description":"Returns the workspace credit balance: total, held by running jobs, and available to spend. Writing stories costs nothing; credits pay for generation (images, voice, video) and rendering.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{}}},{"name":"get_drama_context","title":"Get the whole drama","description":"The most important read: everything about a drama in one call. Returns settings, the effective production spec, the story bible, characters (with looks), locations, props, every episode outline with status and runtime, open cliffhangers, a lint summary, and, with detail \"full\", the scripts (scenes with ids, versions, cast, dialogue) of the requested episodes. Use \"overview\" to orient, \"outline\" before planning, \"full\" before editing scripts. episode_from/episode_to pick the episodes whose outline (and, with \"full\", script) you get; the others are listed briefly. max_chars is the size budget (scripts: 30,000 by default; outline: all the outlines unless you give it). If truncated, call again with episode_from = continue_from_episode. In a long drama read only the episodes you work on, or one episode with get_episode. open_threads lists cliffhangers the next episode has not yet paid off (no next episode, no connect_back, or no script); the text is in the episode when its outline is included.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"detail":{"default":"outline","type":"string","enum":["overview","outline","full"]},"episode_from":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"episode_to":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"max_chars":{"description":"Size budget for the response in characters: pages through the scripts (full, 30,000 by default) or the outlines (outline)","type":"integer","minimum":5000,"maximum":100000}},"required":["drama_id"]}},{"name":"get_drama_spec","title":"Get the production spec","description":"What you can and must fill, before you write: every field per level (drama, bible, character, location, prop, episode, scene) with type, whether the story or production needs it, its catalog, and ranges. With a drama_id, also the values in effect for that drama (format, ratio and resolution, episode length, scenes per episode, scene duration limits, paywall window, the brief of the genre, and the medium, scene register, people and camera of the visual style) and readiness (what still blocks the story or production). Pair it with get_catalog for allowed values.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]}}}},{"name":"get_episode","title":"Get an episode script","description":"One episode with its outline fields and full script: scenes in order with ids, versions, beat, action, camera, location, cast, props and dialogue.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"}},"required":["episode_id"]}},{"name":"get_episode_music","title":"Read the episode score plan","description":"What generate_music would compose, without composing: the brief (the episode's music_prompt, or the suggested one from the drama's genre and visual style when it has none), the cue sheet (each scene: its section, length, role, level 0 silent … 4 full and whether it is automatic, and styles), the length, the model and its credits, and the music level the render uses. Set a section's level with upsert_episodes music_cues {scene_id: 0-4}. Free.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"prompt":{"description":"A direction to try instead of the saved one","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]}},"required":["episode_id"]}},{"name":"get_jobs","title":"Get generation jobs","description":"Status of generation jobs: by ids, or the latest of a drama (optionally by status). A finished job names its take; failed ones say why (credits were refunded).","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"job_ids":{"anyOf":[{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^job_[0-9a-f]{32}$"}},{"type":"null"}]},"drama_id":{"anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]},"status":{"anyOf":[{"type":"array","items":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]}},{"type":"null"}]},"limit":{"default":50,"type":"integer","minimum":1,"maximum":200}}}},{"name":"get_production_status","title":"Get production status","description":"The production board of a drama: every character look, location, prop, the cover, and each scene's keyframe and video, with the selected take, whether it is stale, a preview URL, each character's voice (description and whether a sample is set), and next_steps. Use it to plan what to generate.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"episode_from":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"episode_to":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]}},"required":["drama_id"]}},{"name":"get_scene","title":"Get a scene for production","description":"One scene as production sees it: its selected keyframe and video (stale or not) and the exact prompt, references (images and voice samples) and clip length the next generate_scene_frame / generate_scene_video would send, with warnings. Read it before spending on video; fix the scene (update_scene) or set prompt_override if the prompt is wrong.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scene_id":{"type":"string","pattern":"^scn_[0-9a-f]{32}$"},"model":{"description":"A model id from list_models; omitted = the drama's default for this kind","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]}},"required":["scene_id"]}},{"name":"get_skill","title":"Load a skill","description":"Loads a skill by name or skill:// URI: its full SKILL.md (follow it) and the list of supporting files. Read a supporting file with read_skill_file only when SKILL.md tells you to. When working on a drama that uses the skill, pass drama_id to get the version the drama pins.","scopes":["skills:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"skill":{"type":"string","minLength":1,"maxLength":300,"description":"A name like \"storycraft-vertical-drama\" or a skill:// URI"},"drama_id":{"description":"Read the version this drama pins (get_drama_context lists its skills)","anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]},"version":{"description":"A version from list_skill_versions; omitted = the latest (or the one the drama pins)","anyOf":[{"type":"string","maxLength":80},{"type":"null"}]}},"required":["skill"]}},{"name":"get_takes","title":"Get takes","description":"Every result generated for a target (newest first) with a signed URL (valid 1 h), which one is selected, and whether it is stale (the story changed since it was made). target_type: character_look (look id), location, prop, cover (drama id), scene_frame / scene_video (scene id), episode_music (episode id).","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"target_type":{"type":"string","enum":["character_look","location","prop","cover","scene_frame","scene_video","episode_music"]},"target_id":{"type":"string","minLength":1,"maxLength":60}},"required":["target_type","target_id"]}},{"name":"import_script","title":"Import a script","description":"Writes a whole document into a drama in one transaction: \"json\" (shortdrama.script.v1, as export_script produces) or \"fountain\" (any screenplay; sections \"# Episode N: Title\" become episodes, scene headings become scenes and locations, character cues become characters). Characters, locations and props are matched by name (created or updated), episodes by number, and every episode that includes scenes has its script replaced. Ideal for writing a season locally and sending it at once. Run lint_drama afterwards.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"format":{"type":"string","enum":["json","fountain"]},"content":{"anyOf":[{"type":"string","maxLength":2000000},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"Fountain text, or the JSON document (object or string)"}},"required":["drama_id","format","content"]}},{"name":"import_skill","title":"Import a skill from GitHub","description":"Imports the skill (or every skill) in a public GitHub folder into the workspace, e.g. https://github.com/owner/repo/tree/main/skills/my-skill or a link to its SKILL.md. Each becomes a private workspace skill marked \"imported\". Imported skills are other people's instructions: read the warnings, and tell the user what the skill does before using it.","scopes":["skills:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","maxLength":500,"format":"uri"}},"required":["url"]}},{"name":"lint_drama","title":"Check the drama","description":"Deterministic quality check (no AI): missing logline or bible essentials, characters without appearance or inner life, episodes without hook, cliffhanger or connect-back, scripts that do not open on a hook or end on a button, runtime against target, scene durations outside the limits, dialogue lines that are too long, deleted references, missing paywall gate, outlined episodes not yet written. Each finding has a severity, the stage it concerns and a fix. Iterate until clean. Runtime is flagged when an episode is more than 25% off its target. With an episode range, findings and readiness cover that range only (summary.scope says so); summary.unwritten_episodes always counts the whole drama. The story is finished when story_ready is true for the whole drama and unwritten_episodes is 0.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"episode_from":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"episode_to":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"min_severity":{"default":"info","type":"string","enum":["info","warning","error"]},"include_takes":{"default":false,"description":"Also report stale selected takes and scenes without a video (production)","type":"boolean"}},"required":["drama_id"]}},{"name":"list_assets","title":"List assets across dramas","description":"Lists the workspace's characters, locations, props, scenes or episodes across every drama (or one drama_id), most recently changed first, each with the drama it belongs to and its selected picture (and video for scenes and episodes) as signed URLs (1 h). Use it to find something made for one drama to reuse in another. Pages of up to 50; pass next_cursor back as cursor.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["characters","locations","props","scenes","episodes"]},"drama_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"q":{"description":"Words to find in the name, description or drama title","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"sort":{"default":"recent","description":"recent = last changed first, oldest = first created first, name = A-Z","type":"string","enum":["recent","oldest","name"]},"limit":{"default":24,"type":"integer","minimum":1,"maximum":50},"cursor":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}},"required":["kind"]}},{"name":"list_dramas","title":"List dramas","description":"Lists the dramas in the user's workspace, most recently updated first (or oldest first, or by title). Use it to find a drama's id before reading or editing it. Filter by status, format or words in the title, logline or genre. Pages of up to 50; pass next_cursor back as cursor for the next page.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"limit":{"default":20,"type":"integer","minimum":1,"maximum":50},"cursor":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}]},"status":{"anyOf":[{"type":"string","enum":["draft","in_production","complete"]},{"type":"null"}]},"format":{"anyOf":[{"type":"string","enum":["series","short_film","drama_ads","film_trailer","music_video"]},{"type":"null"}]},"q":{"description":"Words to find in the title, logline or genre","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"sort":{"default":"recent","description":"recent = last changed first, oldest = first created first, name = title A-Z","type":"string","enum":["recent","oldest","name"]}}}},{"name":"list_models","title":"List generation models","description":"The image, video and music models you can generate with: what each is good at, clip lengths, resolutions, reference limits, whether it takes voice samples, and credits per image, per second of video or per minute of music. Every video model speaks the dialogue itself (there is no text-to-speech step); models with voice_reference also take each speaking character's voice sample.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"anyOf":[{"type":"string","enum":["image","video","music"]},{"type":"null"}]}}}},{"name":"list_renders","title":"List episode renders","description":"Renders of an episode or a whole drama with status and download links (MP4, SRT, VTT; valid 24 h). Each render has its version (v1, v2… kept after another is deleted) and in_use (the one the episode uses: selected with select_render, else the newest finished). latest_only keeps the render in use per episode.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"anyOf":[{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},{"type":"null"}]},"drama_id":{"anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]},"latest_only":{"default":true,"type":"boolean"}}}},{"name":"list_revisions","title":"List changes","description":"The change history of a drama, newest first: what changed (fields, before → after), who changed it (user, studio agent, or which MCP client) and when. Changes made together share a batch_id. Use since_seq to see what changed after a given point.","scopes":["drama:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"since_seq":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"entity_id":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"batch_id":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"limit":{"default":30,"type":"integer","minimum":1,"maximum":100}},"required":["drama_id"]}},{"name":"list_skill_versions","title":"List skill versions","description":"The saved versions of a skill, newest first, with their changelog. Versions never change once saved; dramas pin one.","scopes":["skills:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"skill":{"type":"string","minLength":1,"maxLength":300}},"required":["skill"]}},{"name":"list_skills","title":"List skills","description":"Skills are reusable instructions in the Agent Skills format (SKILL.md): how to write a genre, storycraft, visual prompting, the workflow… Lists official shortdrama.studio skills and the workspace's own, with name, description, category and skill:// URI. Load one with get_skill when its description matches your task. Start with shortdrama-workflow.","scopes":["skills:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"category":{"anyOf":[{"type":"string","enum":["workflow","story","genre","visual","production","dialogue","qa"]},{"type":"null"}]},"query":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"source":{"description":"Only official skills, or only the workspace's own","anyOf":[{"type":"string","enum":["official","mine"]},{"type":"null"}]}}}},{"name":"preview_render","title":"Preview an episode render","description":"What render_episode would put together, without rendering: the clips in order with their lengths, the music, the subtitle cues and timings, what is missing or stale. Free.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"subtitles":{"default":"burn","description":"burn: drawn into the video (and also as SRT/VTT); file: SRT/VTT only; none","type":"string","enum":["burn","file","none"]},"music":{"default":true,"description":"Mix the episode's selected music under the clips (ducked under dialogue)","type":"boolean"},"music_volume":{"description":"The music's level under the clips, 0–1; omitted: the episode's music_volume, else 0.5","anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]},"resolution":{"default":"1080p","type":"string","enum":["1080p","720p"]},"allow_missing":{"default":true,"type":"boolean"},"allow_stale":{"default":true,"type":"boolean"}},"required":["episode_id"]}},{"name":"rate_take","title":"Rate a take","description":"Rates a take 1–5 and/or notes what is wrong with it (helps the next attempt and the creator).","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"take_id":{"type":"string","pattern":"^tk_[0-9a-f]{32}$"},"rating":{"anyOf":[{"type":"integer","minimum":1,"maximum":5},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["take_id"]}},{"name":"read_skill_file","title":"Read a skill file","description":"Reads one supporting file of a skill by its skill:// URI (from get_skill's files list). Images come back base64-encoded.","scopes":["skills:read"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uri":{"type":"string","minLength":1,"maxLength":300,"format":"starts_with","pattern":"^skill:\\/\\/.*"},"drama_id":{"description":"Read the version this drama pins (get_drama_context lists its skills)","anyOf":[{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},{"type":"null"}]},"version":{"description":"A version from list_skill_versions; omitted = the latest (or the one the drama pins)","anyOf":[{"type":"string","maxLength":80},{"type":"null"}]}},"required":["uri"]}},{"name":"render_episode","title":"Render an episode","description":"Joins the selected video of every scene, in order, into the episode MP4 (1080×1920 for 9:16, H.264/AAC): the clips' own dialogue and sound, the episode music underneath (ducked whenever someone speaks), subtitles burned in large for phones and also delivered as SRT/VTT, and a short fade out. Refuses when a scene has no selected video or a stale one, unless allowed. Costs 1 credit per episode. Follow it with wait_for_jobs, then list_renders for the links.","scopes":["generate"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"subtitles":{"default":"burn","description":"burn: drawn into the video (and also as SRT/VTT); file: SRT/VTT only; none","type":"string","enum":["burn","file","none"]},"music":{"default":true,"description":"Mix the episode's selected music under the clips (ducked under dialogue)","type":"boolean"},"music_volume":{"description":"The music's level under the clips, 0–1; omitted: the episode's music_volume, else 0.5","anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]},"resolution":{"default":"1080p","type":"string","enum":["1080p","720p"]},"allow_missing":{"default":false,"description":"Leave out scenes without a selected video instead of refusing","type":"boolean"},"allow_stale":{"default":false,"description":"Use videos made before their scene changed instead of refusing","type":"boolean"},"idempotency_key":{"anyOf":[{"type":"string","minLength":8,"maxLength":100},{"type":"null"}]}},"required":["episode_id"]}},{"name":"reorder_scenes","title":"Reorder scenes","description":"Sets the order of all scenes of an episode: list every scene id exactly once, first to last.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"scene_ids":{"minItems":1,"maxItems":60,"type":"array","items":{"type":"string","pattern":"^scn_[0-9a-f]{32}$"}}},"required":["episode_id","scene_ids"]}},{"name":"revert_revisions","title":"Undo changes","description":"Undoes a batch of changes (batch_id) or chosen revisions, newest first: restores old values, un-deletes, or removes what was created. If something was edited again since, nothing is reverted and the conflicts are listed; resend with force: true to overwrite. The undo is itself undoable. Call it only when the user has just asked to undo these changes.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"batch_id":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"revision_ids":{"anyOf":[{"maxItems":80,"type":"array","items":{"type":"string","maxLength":40}},{"type":"null"}]},"force":{"default":false,"type":"boolean"}},"required":["drama_id"]}},{"name":"save_custom_preset","title":"Save a custom preset","description":"Adds or replaces a workspace option in one of genres, tropes, visual_styles, moods, lighting, camera_moves, music_moods, story_structures, voices: e.g. a visual style \"Neo-noir Jakarta\" with its prompt fragment, or a house genre. Its key is custom:<slug> and can be used anywhere the official keys are (update_drama visual_style, genre, scene mood…).","scopes":["skills:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"catalog":{"type":"string","enum":["genres","tropes","visual_styles","moods","lighting","camera_moves","music_moods","story_structures","voices"]},"key":{"description":"Reuse a key to replace that preset; omitted = derived from the label","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"label":{"type":"string","minLength":1,"maxLength":80},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"prompt_fragment":{"description":"Visual styles: lighting, lens, palette, texture added to every prompt","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"negative_fragment":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"data":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]}},"required":["catalog","label"]}},{"name":"save_skill","title":"Save a skill","description":"Creates or updates one of the workspace's own skills in the Agent Skills format. Send every file of the skill: SKILL.md (YAML frontmatter with name — lowercase-hyphenated, not an official skill's name — and description: what it does and when to use it; optional license, metadata.version, metadata.shortdrama-category) plus any supporting files. Each save that changes something is a new version (metadata.version if newer, else a patch bump); saving identical files changes nothing. Pass expected_version (the version you loaded) to avoid overwriting a newer save. Use it to keep the user's house style, e.g. \"Save this drama's dialogue rules as a skill\". Show the user the SKILL.md before saving.","scopes":["skills:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"files":{"minItems":1,"maxItems":64,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":300,"description":"Relative path: SKILL.md, references/beats.md, assets/presets.json…"},"content":{"type":"string","maxLength":4000000},"encoding":{"description":"base64 for images (PNG, JPEG, WebP, GIF); default utf8","anyOf":[{"type":"string","enum":["utf8","base64"]},{"type":"null"}]}},"required":["path","content"]}},"changelog":{"description":"What changed in this version","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"expected_version":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}]}},"required":["files"]}},{"name":"select_render","title":"Use a render for its episode","description":"Makes a finished render (list_renders) the one its episode uses: the episode's card, export package and links show it, and it cannot be deleted. A new render takes over when it finishes. Free.","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"render_id":{"type":"string","pattern":"^rnd_[0-9a-f]{32}$"}},"required":["render_id"]}},{"name":"select_take","title":"Select a take","description":"Makes a take the one used for its target: a portrait or plate becomes the reference for later frames and videos, a scene video goes into the render, a cover becomes the drama's cover. A new portrait, plate, prop image or cover is selected as it lands (the earlier takes stay); a new keyframe, video or music track is selected only when it is the first.","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"take_id":{"type":"string","pattern":"^tk_[0-9a-f]{32}$"}},"required":["take_id"]}},{"name":"set_character_voice","title":"Set a character's voice","description":"Locks how a character sounds across every scene. There is no text-to-speech: the video model speaks the dialogue, and this sets what it is told. voice_key: a key from get_catalog(\"voices\") or a short description (\"low, tired, husky\"). sample: {asset_id, consent: true} for an uploaded clip of the voice (create_upload_url; only voices the user has the right to use), {take_id, start_seconds, end_seconds} to cut 2–10 s from a scene video where this character speaks alone and sounds right, or null to remove the sample. The sample is sent as an audio reference to models that take one (Wan 3.0 through DashScope).","scopes":["drama:write"],"toolsets":["producer"],"annotations":{"readOnlyHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"character_id":{"type":"string","pattern":"^chr_[0-9a-f]{32}$"},"voice_key":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"sample":{"anyOf":[{"anyOf":[{"type":"object","properties":{"asset_id":{"type":"string","pattern":"^ast_[0-9a-f]{32}$"},"consent":{"type":"boolean"}},"required":["asset_id","consent"]},{"type":"object","properties":{"take_id":{"type":"string","pattern":"^tk_[0-9a-f]{32}$"},"start_seconds":{"type":"number","minimum":0,"maximum":60},"end_seconds":{"type":"number","minimum":0,"maximum":60}},"required":["take_id","start_seconds","end_seconds"]}]},{"type":"null"}]}},"required":["character_id"]}},{"name":"update_bible","title":"Update the story bible","description":"Writes the story bible: premise, structural conflict, theme, tone references, world, central question, engine of satisfaction, season arc (phases with episode ranges and beats), dramatic-irony ledger, production notes, character relationships. Send only the sections to change. Map the whole season here before writing scenes. expected_version is the bible version from get_drama_context.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The version you read; the write fails with VERSION_CONFLICT if it changed since"},"patch":{"type":"object","properties":{"premise":{"anyOf":[{"type":"string","maxLength":3000},{"type":"null"}]},"structural_conflict":{"description":"Why the premise produces conflict in every scene by itself","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"theme":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"tone_refs":{"description":"The feel, with 2–3 reference titles","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"world":{"anyOf":[{"type":"string","maxLength":3000},{"type":"null"}]},"central_question":{"description":"The dramatic question of the whole season","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"engine_of_satisfaction":{"description":"Which injustice is planted and which payoff is promised","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"season_arc":{"anyOf":[{"maxItems":12,"type":"array","items":{"type":"object","properties":{"phase":{"type":"string","minLength":1,"maxLength":80},"episodes":{"type":"string","maxLength":20,"description":"e.g. \"1-10\""},"beats":{"type":"string","maxLength":2000}},"required":["phase","episodes","beats"]}},{"type":"null"}]},"irony_ledger":{"description":"What the audience knows that characters do not, and the episodes where each side learns it","anyOf":[{"maxItems":30,"type":"array","items":{"type":"object","properties":{"secret":{"type":"string","minLength":1,"maxLength":500},"audience_learns":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"characters_learn":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]}},"required":["secret"]}},{"type":"null"}]},"production_notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"relationships":{"description":"Who each character is to the others, and where loyalty is fragile","anyOf":[{"type":"string","maxLength":3000},{"type":"null"}]}}}},"required":["drama_id","expected_version","patch"]}},{"name":"update_drama","title":"Update drama settings","description":"Changes drama settings: title, logline, format, genre, subgenres, tropes, language, target audience, aspect ratio, target episode count and length, visual style, status. Send only the fields to change. genre is best a sub-genre key from get_catalog(\"genres\"); for a genre of the creator's own send its name as genre and what it is as genre_description. visual_style takes a key from get_catalog(\"visual_styles\") or {name, prompt_fragment, negative_fragment}. When the genre, style or language changed, the result carries the new brief: write to it.","scopes":["drama:write"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The version you read; the write fails with VERSION_CONFLICT if it changed since"},"patch":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120},"logline":{"description":"One sentence: protagonist + situation + structural conflict + stakes","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"format":{"type":"string","enum":["series","short_film","drama_ads","film_trailer","music_video"]},"genre":{"description":"The kind of story: a sub-genre key from get_catalog(\"genres\") (best: it carries the brief the story is written to), a genre key (leaves the kind open), a custom: preset, or the creator's own genre name with genre_description","anyOf":[{"type":"string","maxLength":80},{"type":"null"}]},"genre_description":{"description":"For a genre of the creator's own: what it is (the brief the story is written to). Ignored for catalog genres","anyOf":[{"type":"string","maxLength":5000},{"type":"null"}]},"subgenres":{"maxItems":5,"type":"array","items":{"type":"string","maxLength":80},"description":"Extra genre tags; genre alone decides the brief"},"tropes":{"maxItems":6,"type":"array","items":{"type":"string","maxLength":80},"description":"Keys from get_catalog(\"tropes\") or free text"},"language":{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$","description":"Dialogue language"},"target_audience":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"content_rating":{"anyOf":[{"type":"string","enum":["all_ages","7_plus","13_plus","16_plus"],"description":"Who the drama is for, by age, from get_catalog(\"content_ratings\"): the ceiling the story is written to and every image and video is generated within. Pick it from the genre and the audience: 16_plus (TV-14) is the vertical-drama standard for romance, revenge and thriller; 13_plus for teen and sweet stories; 7_plus or all_ages for children and family"},{"type":"null"}]},"aspect_ratio":{"type":"string","enum":["9:16","16:9","1:1","4:5"]},"episode_target_count":{"anyOf":[{"type":"integer","minimum":1,"maximum":200},{"type":"null"}]},"episode_target_seconds":{"anyOf":[{"type":"integer","minimum":15,"maximum":1800},{"type":"null"}]},"visual_style":{"anyOf":[{"anyOf":[{"type":"string","minLength":1,"maxLength":80,"description":"A key from get_catalog(\"visual_styles\"), official or custom:"},{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"prompt_fragment":{"type":"string","minLength":1,"maxLength":1000,"description":"Added to every image/video prompt: lighting, lens, palette, texture"},"negative_fragment":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}},"required":["name","prompt_fragment"],"description":"A one-off style; save it with save_custom_preset to reuse it"}]},{"type":"null"}]},"default_models":{"type":"object","properties":{"image":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"video":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"music":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"video_resolution":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}]}},"description":"Models generation uses for this drama (ids from list_models) and the video resolution"},"status":{"type":"string","enum":["draft","in_production","complete"]}}}},"required":["drama_id","expected_version","patch"]}},{"name":"update_scene","title":"Edit one scene","description":"Changes one scene: send only the fields to change. Sending dialogue, characters or props replaces that whole list. Use it for targeted fixes (a sharper hook, a shorter line, a different look) instead of rewriting the episode.","scopes":["drama:write"],"toolsets":["writer","producer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"scene_id":{"type":"string","pattern":"^scn_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The version you read; the write fails with VERSION_CONFLICT if it changed since"},"patch":{"type":"object","properties":{"beat":{"type":"string","enum":["hook","friction","spike","button","other"],"description":"hook (0–15s, grab within 3s) · friction · spike · button (last 5–10s, cut on the question)"},"heading":{"description":"INT./EXT. LOCATION — TIME","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"action":{"type":"string","maxLength":2500,"description":"What we see: action, micro-expressions, reactions. Written for close-ups."},"camera":{"anyOf":[{"type":"object","properties":{"shot_size":{"description":"get_catalog(\"shot_sizes\"), e.g. \"mcu\"","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"angle":{"description":"get_catalog(\"camera_angles\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"move":{"description":"get_catalog(\"camera_moves\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"lens":{"description":"get_catalog(\"lenses\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"shots":{"description":"Cuts inside the scene, for models that render multi-shot clips","anyOf":[{"maxItems":6,"type":"array","items":{"type":"object","properties":{"shot_size":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"angle":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"move":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"description":{"type":"string","minLength":1,"maxLength":400},"seconds":{"anyOf":[{"type":"number","minimum":0.5,"maximum":30},{"type":"null"}]}},"required":["description"]}},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}]}}},{"type":"null"}]},"mood":{"description":"get_catalog(\"moods\") or free text","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"duration_seconds":{"description":"Seconds, 6–15: one scene is one clip. A moment that needs longer becomes two scenes.","anyOf":[{"type":"number","minimum":6,"maximum":15},{"type":"null"}]},"location":{"anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}]},"time_of_day":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"characters":{"maxItems":8,"type":"array","items":{"type":"object","properties":{"character":{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},"look":{"description":"A look id or name; omitted = the character's default look","anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}]},"role_in_scene":{"anyOf":[{"type":"string","maxLength":150},{"type":"null"}]},"position_hint":{"description":"Where they are in frame","anyOf":[{"type":"string","maxLength":150},{"type":"null"}]}},"required":["character"]}},"props":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"}},"dialogue":{"maxItems":40,"type":"array","items":{"type":"object","properties":{"character":{"anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}],"description":"Who speaks; null for narration / voice-over"},"text":{"type":"string","minLength":1,"maxLength":600},"delivery":{"description":"How it is said: \"whispering, voice breaking\"","anyOf":[{"type":"string","maxLength":150},{"type":"null"}]},"lang":{"anyOf":[{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},{"type":"null"}]}},"required":["character","text"]}},"continuity":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Continuity with the scene before when both play in the same place. match_previous (default true): the video takes a still of the previous clip as a reference for set, light and wardrobe, and cuts to a new camera setup. Set { match_previous: false } when time passes in the same place."},"director_notes":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"overrides":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"prompt_override":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Per-model prompt text, for experts; replaces the compiled prompt"},"key_visual":{"description":"How the key visual is made: direct (the default: no key visual, the video gets the portraits, plate and props and a free camera), frame (one opening keyframe), storyboard (a comic page of 4–6 panels the video plays as cuts), storyboard_3x3 (7–9 panels). Leave it out: every scene starts as direct, and a script written by an agent is always saved as direct. Set frame or a storyboard only with update_scene when the user asks for it on that scene.","anyOf":[{"type":"string","enum":["frame","storyboard","storyboard_3x3","direct"]},{"type":"null"}]}}}},"required":["scene_id","expected_version","patch"]}},{"name":"upsert_characters","title":"Create or update characters","description":"Creates characters (no id, name required) or updates them (id + expected_version, only the fields sent change), with their looks (costumes). Create each character complete, all in one call: name, role, age, gender, logline, appearance (canonical: reused for every image), voice_style, and for central characters (≤3) their story (one text: want, need, wound, flaw, stakes, contradiction, empathy lever, arc, a labelled line each); give a look a name and a description. Cast for contrast: central characters must be easy to tell apart at thumbnail size (different hair length and colour, silhouette, wardrobe colours). Each result lists what the character is still `missing`: fill it in your next call (send the id, expected_version and those fields); `notes` say what to fix about an appearance just written. Only the fields listed here exist; any other is refused. A name already in use fails with DUPLICATE_NAME and the existing id.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"characters":{"minItems":1,"maxItems":30,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"age":{"description":"e.g. \"28\", \"early 30s\"","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"gender":{"description":"e.g. \"female\", \"male\"","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"role":{"type":"string","enum":["protagonist","antagonist","love_interest","supporting","extra"]},"logline":{"description":"Who they are + the core conflict they carry","anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"story":{"description":"What drives them, one short labelled line each: Want (outer goal), Need (inner truth they must learn), Wound (past event), Flaw (the lie they believe), Stakes (what they lose), Contradiction, Empathy lever (how the audience is made to care at first sight), Arc (start → end)","anyOf":[{"type":"string","maxLength":3000},{"type":"null"}]},"voice_style":{"description":"How they speak","anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"appearance":{"description":"Canonical physical description reused for every image, 40–90 words, in this order: age and ethnicity or region; face (shape, eyes, brows, nose, lips, skin tone); hair (length, colour, texture, usual style); build and height; one or two distinctive marks that survive any shot (a mole, a scar, a grey streak, glasses). The body only: clothing goes in looks, mood and story elsewhere. Set it once: changing it later outdates every portrait and shot of the character","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"voice_key":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}]},"id":{"type":"string","pattern":"^chr_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991},"looks":{"description":"Looks to add or update (by id); omitted looks are kept","maxItems":10,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^look_[0-9a-f]{32}$"},"name":{"description":"e.g. \"office\", \"wedding dress\"; required for a new look","type":"string","minLength":1,"maxLength":80},"description":{"description":"What they wear and how they are styled in this look: outfit, colours, hair, makeup, accessories. Clothing lives here, never in appearance; scenes pick a look by name, and the same look keeps the same portrait","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"is_default":{"type":"boolean"}},"additionalProperties":false}}},"additionalProperties":false,"description":"With id + expected_version: update. Without id: create (name required)."}}},"required":["drama_id","characters"]}},{"name":"upsert_episodes","title":"Create or update episode outlines","description":"Creates episodes (no id, number required) or updates them (id + expected_version): title, summary, goal, hook, cliffhanger, connect_back, beat skeleton, target length, paywall gate, overrides. Outline many episodes per call (up to 50). Scripts are written with write_episode_script.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"episodes":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"number":{"type":"integer","minimum":1,"maximum":500},"title":{"anyOf":[{"type":"string","maxLength":150},{"type":"null"}]},"summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"goal":{"description":"What conflict moves forward and which emotion is targeted","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"hook":{"description":"What grabs attention in the first 3 seconds","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"cliffhanger":{"description":"The question the episode ends on","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"connect_back":{"description":"How it pays off or escalates the previous episode's cliffhanger","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"beat_skeleton":{"maxItems":12,"type":"array","items":{"type":"object","properties":{"beat":{"type":"string","enum":["hook","friction","spike","button","other"]},"at_seconds":{"type":"number","minimum":0,"maximum":600}},"required":["beat","at_seconds"]}},"target_seconds":{"anyOf":[{"type":"integer","minimum":10,"maximum":1800},{"type":"null"}]},"overrides":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Episode-level overrides: visual_style, mood, models"},"is_paywall_gate":{"type":"boolean","description":"The last free episode; it carries the strongest cliffhanger"},"music_prompt":{"description":"Direction for the episode score, any language: genre and sound, instruments, mood, tempo/BPM, key, era. generate_music scores it scene by scene (cold open, tension, climax, cliffhanger sting); empty: a brief from the genre and visual style","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"music_volume":{"description":"The music's level in the render, 0–1 (null: the default, 0.5)","anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]},"music_cues":{"description":"The score's level per section, by scene id: 0 silent, 1 whisper, 2 soft, 3 build, 4 full; a scene left out gets its automatic level (get_episode_music shows them)","anyOf":[{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"integer","minimum":0,"maximum":4}},{"type":"null"}]},"id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991}},"additionalProperties":false}}},"required":["drama_id","episodes"]}},{"name":"upsert_locations","title":"Create or update locations","description":"Creates locations (no id, name required) or updates them (id + expected_version). The studio shows each one's name, time_variants (its times of day) and description: always fill all three. The description is what the camera sees (architecture, dressing, light) and says whether it is indoors or outdoors; set interior to match. Size the count to the season (see the shortdrama-workflow skill).","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"locations":{"minItems":1,"maxItems":40,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"description":"What it looks like: architecture, dressing, light","anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"interior":{"type":"boolean"},"time_variants":{"maxItems":6,"type":"array","items":{"type":"string","maxLength":40},"description":"e.g. [\"day\", \"night\"]"},"id":{"type":"string","pattern":"^loc_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991}},"additionalProperties":false}}},"required":["drama_id","locations"]}},{"name":"upsert_props","title":"Create or update props","description":"Creates props (no id, name required) or updates them (id + expected_version): objects that matter to the plot and must look the same every time. The studio shows a prop's name and description: always describe what it looks like (its image is drawn from it). significance is optional.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"drama_id":{"type":"string","pattern":"^drm_[0-9a-f]{32}$"},"props":{"minItems":1,"maxItems":40,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"significance":{"description":"Why it matters to the story","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"id":{"type":"string","pattern":"^prp_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991}},"additionalProperties":false}}},"required":["drama_id","props"]}},{"name":"wait_for_jobs","title":"Wait for jobs","description":"Waits (up to timeout_seconds, at most 50) until the given jobs finish, then returns them — cheaper than polling get_jobs. Images take ~10–40 s, videos 1–5 min: call again if all_finished is false.","scopes":["drama:read"],"toolsets":["producer"],"annotations":{"readOnlyHint":true,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"job_ids":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^job_[0-9a-f]{32}$"}},"timeout_seconds":{"default":45,"type":"integer","minimum":1,"maximum":50}},"required":["job_ids"]}},{"name":"write_episode_script","title":"Write an episode's script","description":"Writes the scenes of one episode. mode \"replace\" (default) swaps the whole script (the old scenes stay restorable); \"append\" adds after the last scene. Each scene: beat (hook → friction → spike → button), action written for close-ups, dialogue lines by speaker name, cast with looks, location, props, camera, mood and duration_seconds. Refer to characters, looks, locations and props by exact name or id; create them first. Every reference is checked before anything is written. Run lint_drama afterwards.","scopes":["drama:write"],"toolsets":["writer"],"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"input_schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"episode_id":{"type":"string","pattern":"^ep_[0-9a-f]{32}$"},"expected_version":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The version you read; the write fails with VERSION_CONFLICT if it changed since"},"mode":{"default":"replace","type":"string","enum":["replace","append"]},"scenes":{"minItems":1,"maxItems":40,"type":"array","items":{"type":"object","properties":{"beat":{"type":"string","enum":["hook","friction","spike","button","other"],"description":"hook (0–15s, grab within 3s) · friction · spike · button (last 5–10s, cut on the question)"},"heading":{"description":"INT./EXT. LOCATION — TIME","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"action":{"type":"string","minLength":1,"maxLength":2500,"description":"What we see: action, micro-expressions, reactions. Written for close-ups."},"camera":{"anyOf":[{"type":"object","properties":{"shot_size":{"description":"get_catalog(\"shot_sizes\"), e.g. \"mcu\"","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"angle":{"description":"get_catalog(\"camera_angles\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"move":{"description":"get_catalog(\"camera_moves\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"lens":{"description":"get_catalog(\"lenses\")","anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"shots":{"description":"Cuts inside the scene, for models that render multi-shot clips","anyOf":[{"maxItems":6,"type":"array","items":{"type":"object","properties":{"shot_size":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"angle":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"move":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"description":{"type":"string","minLength":1,"maxLength":400},"seconds":{"anyOf":[{"type":"number","minimum":0.5,"maximum":30},{"type":"null"}]}},"required":["description"]}},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}]}}},{"type":"null"}]},"mood":{"description":"get_catalog(\"moods\") or free text","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"duration_seconds":{"description":"Seconds, 6–15: one scene is one clip. A moment that needs longer becomes two scenes.","anyOf":[{"type":"number","minimum":6,"maximum":15},{"type":"null"}]},"location":{"anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}]},"time_of_day":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"characters":{"maxItems":8,"type":"array","items":{"type":"object","properties":{"character":{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},"look":{"description":"A look id or name; omitted = the character's default look","anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}]},"role_in_scene":{"anyOf":[{"type":"string","maxLength":150},{"type":"null"}]},"position_hint":{"description":"Where they are in frame","anyOf":[{"type":"string","maxLength":150},{"type":"null"}]}},"required":["character"]}},"props":{"maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"}},"dialogue":{"maxItems":40,"type":"array","items":{"type":"object","properties":{"character":{"anyOf":[{"type":"string","minLength":1,"maxLength":120,"description":"An id, or the exact name"},{"type":"null"}],"description":"Who speaks; null for narration / voice-over"},"text":{"type":"string","minLength":1,"maxLength":600},"delivery":{"description":"How it is said: \"whispering, voice breaking\"","anyOf":[{"type":"string","maxLength":150},{"type":"null"}]},"lang":{"anyOf":[{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},{"type":"null"}]}},"required":["character","text"]}},"continuity":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Continuity with the scene before when both play in the same place. match_previous (default true): the video takes a still of the previous clip as a reference for set, light and wardrobe, and cuts to a new camera setup. Set { match_previous: false } when time passes in the same place."},"director_notes":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}]},"overrides":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"prompt_override":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Per-model prompt text, for experts; replaces the compiled prompt"},"key_visual":{"description":"How the key visual is made: direct (the default: no key visual, the video gets the portraits, plate and props and a free camera), frame (one opening keyframe), storyboard (a comic page of 4–6 panels the video plays as cuts), storyboard_3x3 (7–9 panels). Leave it out: every scene starts as direct, and a script written by an agent is always saved as direct. Set frame or a storyboard only with update_scene when the user asks for it on that scene.","anyOf":[{"type":"string","enum":["frame","storyboard","storyboard_3x3","direct"]},{"type":"null"}]}},"required":["action"]}}},"required":["episode_id","expected_version","scenes"]}}]}