@starreel/mcp 0.1.128 → 0.1.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -85,13 +85,13 @@ announces a condensed version as MCP `instructions` at connect time.
85
85
  | Their own portraits / scene / prop / shot images | `upload_image` · `set_character_portrait` · `upload_scene_image` · `upload_prop_sheet` · `upload_shot_frame` — **check the image before uploading** (see *Customer-supplied images* below); after a portrait swap, `generate_character_sheet` for that character (the old sheet is invalidated and **not** regenerated automatically) | rendering a "fix" elsewhere and uploading it — use `generate_shot_frame`; uploading a group photo, a captioned/watermarked image, or a multi-view sheet as a portrait; uploading a scene photo with people in it |
86
86
  | Their own **footage** for a shot (screen recording, product b-roll, an existing clip) | `upload_shot_footage` → that shot is no longer AI-generated (frames/video skipped, final cut uses the clip as-is, duration written back from the clip); **it overwrites whatever AI video that shot already had**, and afterwards every AI video call on it (including `edit_video_shot`) is refused with **409** until you `clear_shot_footage` or pass `replace_user_footage: true` | pasting an externally AI-generated clip to "fix" a shot — it carries none of this film's identity / style anchors; use `regenerate_shot_video` instead |
87
87
  | A clip that should **drive the motion** of an AI shot (a blocking/previz pass, a dance or action reference) | `edit_video_shot` with `reference_video_urls` — the shot keeps its AI video and borrows the clip's movement. To make the clip itself the source and restyle it in place, `upload_shot_footage` it first, then `edit_video_shot` with `replace_user_footage: true` | `upload_shot_footage` alone — that registers the clip as the finished shot, so nothing gets restyled and every later AI call on it returns 409 |
88
- | A scene plate that came out wrong (backdrop, era, light, layout) | `get_scene_prompt` → `update_scene` (`image_prompt`) → `regenerate_scene_image`; already-rendered shot frames still anchor on the old plate, so regenerate those shots too | re-running `generate_scene_images` (it only fills scenes that have **no** plate — it will not touch this one) |
88
+ | A scene plate that came out wrong (backdrop, era, light, layout) | `get_scene_prompt` → `update_scene` (`image_prompt`) → `quote_regenerate_scene_image` (tell the customer the points — it includes the lighting reference the platform auto-derives after a plate change when no wide-shot frame exists) → `regenerate_scene_image` with that `quote_id` (after `upload_scene_image` the platform does **not** auto-create the lighting reference — if the reply says `lighting_ref.needed`, quote it with `quote_scene_lighting_ref` and, once the customer agrees, `generate_scene_lighting_ref`); already-rendered shot frames still anchor on the old plate, so regenerate those shots too | re-running `generate_scene_images` (it only fills scenes that have **no** plate — it will not touch this one) |
89
89
  | A voice sample / a required voice | `clone_voice` → `speak_with_voice` → `set_character_voice` / `assign_voices` | cloning without the rights-holder's consent |
90
90
  | No sample, but a character already speaks in a rendered shot | `set_voice_anchor_from_shot` (free; native-audio dramas: locks the voice for later videos) → regenerate `stale_shots` | picking the voice after the whole episode is rendered |
91
91
  | **No sample** — just an idea of the voice (a narrator, a character that deserves its own voice) | `design_voice` (one-sentence description, free) → `get_voice_design` until `done` (candidates arrive as local wav files — let the customer listen) → `save_designed_voice` (pick one; billed like a clone) → `set_character_voice` | generating every line straight from the description — each generation is a different person; naming a real person to imitate their voice |
92
92
  | A song + lyrics | `create_drama` (`project_type: "mv"`) → `set_mv_lyrics` → `generate_mv_story` → `generate_mv_script` | `rewrite_script` (blocked for MV) |
93
93
  | A product / brand | `create_drama` (`project_type: "ad"` or `"brand_film"`) → `add_product` → `generate_product_sheet` | writing brand copy as dialogue (it gets spoken) |
94
- | Generated shots / a cut that needs changes | `scan_dialogue_coverage` / `scan_intra_shot_cuts` first, then `get_shot_prompts` · `update_shot` · `replace_shot_dialogue` · `repair_episode_dialogue` · `split_shot` · `trim_shot` · `regenerate_shot_video` · `edit_video_shot` → `rerender_episode`. To put **specific shots on a different video engine** in a whole-episode run, pass `shot_engines` (`{storyboard_id: engine}`) to `quote_videos` (a scene group that contains a picked shot is **split into per-shot generation** — listed in `shot_engines_ungrouped`; its other shots stay on the drama engine but lose the group's one-take continuity, so don't break a good group for one shot; a shot that already has a video is not re-run by the episode run, so picking an engine for it is rejected). Re-quoting after a partial run prices only the shots that will actually be dispatched (`shots_total`; already-finished ones are counted in `shots_already_done`, except that a scene group with any unfinished shot is re-run whole). To make a shot **always** use a different engine (single, batch and regen alike), set `video_engine` with `update_shot` (stored on the shot, shown in the web workbench; such a shot is never put in a scene group; explicit per-call parameters still win). To retry **one shot on a different video engine**, pass `model` to `quote_regenerate_shot_video` (that shot, that regeneration only — the drama's `video_engine` is untouched; the quote pins the engine, so changing it means re-quoting) | re-composing to fix what a clip *says*; switching the whole drama's `video_engine` just to fix one shot |
94
+ | Generated shots / a cut that needs changes | `scan_dialogue_coverage` / `scan_intra_shot_cuts` first, then `get_shot_prompts` · `update_shot` · `replace_shot_dialogue` · `repair_episode_dialogue` · `split_shot` · `trim_shot` · `regenerate_shot_video` · `edit_video_shot` → `rerender_episode`. To put **specific shots on a different video engine** in a whole-episode run, pass `shot_engines` (`{storyboard_id: engine}`) to `quote_videos` (a scene group that contains a picked shot is **split into per-shot generation** — listed in `shot_engines_ungrouped`; its other shots stay on the drama engine but lose the group's one-take continuity, so don't break a good group for one shot; a shot that already has a video is not re-run by the episode run, so picking an engine for it is rejected). Re-quoting after a partial run prices only the shots that will actually be dispatched (`shots_total`; already-finished ones are counted in `shots_already_done`, except that a scene group with any unfinished shot is re-run whole). To make a shot **always** use a different engine (single, batch and regen alike), set `video_engine` with `update_shot` (stored on the shot, shown in the web workbench; such a shot is never put in a scene group; explicit per-call parameters still win). To re-run **one scene group** (one request, keeps in-group continuity) — optionally on a different engine for that run — use `quote_regenerate_scene_group` (`shot_numbers` = exactly one group from `get_scene_group_plan`, optional `video_engine`; the whole group must fit that engine's single-clip limit) → `regenerate_scene_group`. To retry **one shot on a different video engine**, pass `model` to `quote_regenerate_shot_video` (that shot, that regeneration only — the drama's `video_engine` is untouched; the quote pins the engine, so changing it means re-quoting) | re-composing to fix what a clip *says*; switching the whole drama's `video_engine` just to fix one shot |
95
95
  | Wants old portraits / sheets / a shot's old tail frame "deleted" | `set_character_portrait` (old assets listed in `old_assets`) · `clear_shot_frame` (old tail frame) → `regenerate_shot_video`; `list_unlinked_assets` lists the drama's unlinked old assets (a history list, not proof of ownership). Both only **unlink** — files are not deleted (`physically_deleted` is always empty; `in_history: false` means the platform kept no record, so save that URL if it matters). Tell the customer exactly that | telling the customer the old image was deleted; writing `last_frame_image: null` through the internal API |
96
96
  | Wants an old image **permanently** deleted from storage (not just unlinked) | `preview_delete_asset` → show the customer exactly what it says (blocking references, history that will break, copies) → only after an explicit "confirm delete" → `delete_asset` with the `confirm_token`. Irreversible; only the drama owner; only files with trusted provenance (generated for this drama, or uploaded through the platform with a grant record) | deleting without showing the preview; retrying automatically; treating `list_unlinked_assets` as proof the file is unused |
97
97
  | Cross-era drama (scenes in different periods) — checking props fit their period | `get_props` → read each prop's `era_audit` (`form_consistent`, `conflicting_scopes`, `violations`); `audit_prop_era` to re-check after a new sheet. On a conflict: `update_prop` with a precise `era_lock` (dynasty + physical form) → `generate_prop_sheet`, or bind a different prop to those shots. `run_precheck` also lists shots whose bound prop conflicts (`prop-era-conflict`) | trusting the prop's name or description alone; regenerating shot frames before the prop sheet is fixed |
@@ -206,7 +206,7 @@ export const PIPELINE = [
206
206
  'generate_world_concept(默认必做,仍走报价)', 'generate_color_script',
207
207
  'generate_art_bible', 'extract_visual_lock', 'extract_setting_brief', 'generate_video_style',
208
208
  'quote_scene_images', 'generate_scene_images', 'generate_prop_sheet',
209
- 'get_scene_prompt(读某场空景图的提示词正文,免费)', 'update_scene(image_prompt 改正文)', 'regenerate_scene_image(单场重出)',
209
+ 'get_scene_prompt(读某场空景图的提示词正文,免费)', 'update_scene(image_prompt 改正文)', 'quote_regenerate_scene_image(单场重出报价,含可能派生的打光参考图)', 'regenerate_scene_image(单场重出,带 quote_id)',
210
210
  ],
211
211
  billing: '报价确认后扣点',
212
212
  note: '分镜后建只给出场角色出图更省。' +
@@ -277,7 +277,7 @@ export const QA_TOOLS = [
277
277
  { symptom: '画面多出一个人 / 多出一件道具', run: 'get_storyboards(先看该镜实际用的首帧)', then: ['generate_shot_frame(首帧本身就有→重生首帧再重生视频)', 'split_shot(帧干净、片中长出来→拆成 3~5 秒短镜)'] },
278
278
  { symptom: '出图 / 出视频前想知道哪些镜会被厂商拒', run: 'run_precheck', then: ['update_shot', 'generate_shot_frame'] },
279
279
  { symptom: '要跑全集 / 想知道整部剧有多少问题、该先修哪几集', run: 'run_drama_precheck', then: ['run_precheck(对 attention 里那几集拿逐条明细)', 'plan_precheck_fix', 'update_shot'] },
280
- { symptom: '场景图(空景基板)不对 / 重出还是同一类图', run: 'get_scene_prompt', then: ['update_scene(改 image_prompt 正文——只改地点/时段是让平台重拼,拧不过来)', 'regenerate_scene_image(单场重出,覆盖旧图)', 'upload_scene_image(客户自有实拍/外部精修图)', 'generate_shot_frame(下游镜头帧不会自动跟着重出)'] },
280
+ { symptom: '场景图(空景基板)不对 / 重出还是同一类图', run: 'get_scene_prompt', then: ['update_scene(改 image_prompt 正文——只改地点/时段是让平台重拼,拧不过来)', 'quote_regenerate_scene_image(先报价)', 'regenerate_scene_image(单场重出,覆盖旧图)', 'upload_scene_image(客户自有实拍/外部精修图)', 'quote_scene_lighting_ref → generate_scene_lighting_ref(换图后补打光参考图,先报价)', 'generate_shot_frame(下游镜头帧不会自动跟着重出)'] },
281
281
  { symptom: '整集健康度 / 缺镜 / 进度', run: 'get_pipeline_status', then: ['get_health_report', 'review_all', 'get_storyboards', 'get_jobs', 'get_run_status', 'get_autofill_status(导入/一键填空的后台补全进度)'] },
282
282
  { symptom: '拆完分镜想核对台词有没有丢 / 谁说的 / 哪几镜是关键镜 / 情绪曲线', run: 'get_storyboards(每镜 dialogue_lines/is_key_moment/emotion_intensity,整集一次拿)', then: ['review_storyboards', 'update_shot(补漏句/改台词,改完回 get_storyboards 核对)', 'get_shot_prompts(看某镜画面与首尾帧提示词)'] },
283
283
  { symptom: '预算 / 余额', run: 'get_budget_status', then: ['get_cost_estimate'] },
@@ -1203,7 +1203,10 @@ export function registerProduceTools(server, client) {
1203
1203
  '单一完整画面(不是拼图/多格);无叠加文字/水印/logo(画面里本来就有的招牌门牌可以)。' +
1204
1204
  '客户的实拍图里有人:请客户换无人版本,或改用 regenerate_scene_image 让平台出空景。' +
1205
1205
  '被拒时把 message 里的问题原样告诉客户;只有客户看过问题后明确坚持,才带 allow_issues:true 重传(自动检查可能误判)。' +
1206
- '登记免费;内容检查是一次小额视觉审核,按用量后付。', {
1206
+ '登记免费;内容检查是一次小额视觉审核,按用量后付。' +
1207
+ '★换了场景图后旧的打光参考图作废,平台**不会**自动补(那要另扣一张图的点数):' +
1208
+ '回执 lighting_ref.needed=true 时把 estimated_points 告诉客户,客户同意再调 quote_scene_lighting_ref → generate_scene_lighting_ref。' +
1209
+ '上传时该场景还没有分镜(needed=false 且分镜还没建)的,等分镜建好后再调 quote_scene_lighting_ref 看要不要补——平台不会自动补上传图的打光参考图。', {
1207
1210
  scene_id: z.number().int().positive(),
1208
1211
  file_path: z.string().describe('本地场景图路径'),
1209
1212
  allow_issues: z.boolean().optional().describe('内容检查报了问题、客户看过后仍明确坚持用这张图时才传 true(默认拦截)'),
@@ -1663,7 +1666,16 @@ export function registerProduceTools(server, client) {
1663
1666
  '★逐场按需:改哪场读哪场(整集列表 get_scenes 是纯资产视图,不含提示词)。' +
1664
1667
  '★这是场景表里的正文层;出图时平台还会在其上拼画风块、空景约束与时代锁(不在此处,也无需你写)。' +
1665
1668
  '回执的 has_image 说明这一场是否已有图。', { scene_id: z.number().int().positive() }, async ({ scene_id }) => jsonResult(await client.produceGet(`/scenes/${scene_id}/prompt`)));
1669
+ server.tool('quote_scene_lighting_ref', '给某个场景补一张打光参考图的报价(免费,不出图)。打光参考图=只复现该场景光线的中性人脸特写,' +
1670
+ '近景/特写镜看不到场景图时靠它统一光线。场景缺它时(例如上传了新场景图后)才需要补;' +
1671
+ '有宽景镜但还没出首帧的场景不必补(宽景镜出帧时平台自动派生)。不够格时返回 400 并说明原因。' + CONFIRM_HINT, { scene_id: z.number().int().positive() }, async ({ scene_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/lighting-ref/quote`)));
1672
+ server.tool('generate_scene_lighting_ref', '按报价补这一场的打光参考图(后台出图,按用量扣点)。完成后 get_scenes 里该场景的打光参考图字段会有值。', { scene_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_scene_lighting_ref') }, async ({ scene_id, quote_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/lighting-ref/generate`, { quote_id })));
1673
+ server.tool('quote_regenerate_scene_image', '单场景图重出的报价(免费,不出图)。返回 estimated_points(上界)/typical_points,含场景图一张,' +
1674
+ '以及换图后平台会不会自动派生一张打光参考图(lighting_ref.will_generate,给近景镜统一光线,同样扣点)。' +
1675
+ '拿到 quote_id 后传给 regenerate_scene_image。' + CONFIRM_HINT, { scene_id: z.number().int().positive() }, async ({ scene_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/image/regenerate/quote`)));
1666
1676
  server.tool('regenerate_scene_image', '按当前提示词重出**这一场**的场景图(改完 image_prompt 让画面跟上)。图片步,按用量后付不欠费。' +
1677
+ '★先调 quote_regenerate_scene_image 拿报价、把点数告诉客户,再带 quote_id 调本工具:' +
1678
+ '换了场景图后,没有可用宽景镜首帧的场景平台会自动再出一张打光参考图(另扣一张图的点数),报价里已含。' +
1667
1679
  '★覆盖式:出好后本场旧图被换掉(要留档先 get_scene_prompt/资产列表拿旧图 URL)。' +
1668
1680
  '★已生成的镜头帧不会自动跟着重出——它们仍拿旧场景图当背景锚,要跟上得逐镜重出。' +
1669
1681
  '★与 generate_scene_images 的区别:那个是整剧批量、只补**缺图**的场景,已有图的一律跳过;' +
@@ -1672,9 +1684,10 @@ export function registerProduceTools(server, client) {
1672
1684
  '只有回执报「本场景被多集分镜共用」时才需要点名:那说明同一地点跨集复用,' +
1673
1685
  '平台不猜该按哪一集的时代/世界观出图——猜错就是画面年代静默错掉。', {
1674
1686
  scene_id: z.number().int().positive(),
1687
+ quote_id: z.string().optional().describe('来自 quote_regenerate_scene_image(建议必带)'),
1675
1688
  episode_id: z.number().int().positive().optional()
1676
1689
  .describe('按哪一集的时代/世界观 brief 出图。通常不必传;跨集共用的场景被要求时才传'),
1677
- }, async ({ scene_id, episode_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/image/regenerate`, episode_id ? { episode_id } : {})));
1690
+ }, async ({ scene_id, quote_id, episode_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/image/regenerate`, { ...(quote_id ? { quote_id } : {}), ...(episode_id ? { episode_id } : {}) })));
1678
1691
  server.tool('delete_scene', '删除一个场景。免费。', { scene_id: z.number().int().positive() }, async ({ scene_id }) => jsonResult(await client.produceDelete(`/scenes/${scene_id}`)));
1679
1692
  server.tool('generate_character_sheet', '给**单个**角色出三视图设定图(镜头一致性根锚,所有镜头帧都会引用;比定妆图更完整)。整集批量用 generate_character_sheets。' +
1680
1693
  '前置:该角色已有定妆图。图片步,按用量后付不欠费。', { character_id: z.number().int().positive(), episode_id: z.number().int().positive().optional() }, async ({ character_id, episode_id }) => jsonResult(await client.producePost(`/characters/${character_id}/sheet`, episode_id ? { episode_id } : {})));
@@ -1797,6 +1810,22 @@ export function registerProduceTools(server, client) {
1797
1810
  quote_id: z.string().describe('来自 quote_align_prev_shot'),
1798
1811
  }, async ({ storyboard_id, quote_id }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/align-prev`, { quote_id })));
1799
1812
  server.tool('get_scene_group_plan', '查某一集的场景组规划(镜头如何归组做连续长镜)。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/scene-group-plan`)));
1813
+ server.tool('quote_regenerate_scene_group', '报价:重出**一个场景组**(整组一次视频请求,组内连贯性保留)要多少点。返回 quote_id。零扣费。' +
1814
+ 'shot_numbers 必须正好等于 get_scene_group_plan 里某一组的镜号。' +
1815
+ '\n★可选 video_engine:只为这一次整组重出换引擎(不改剧级 video_engine);报价按它计、quote_id 钉死,regenerate_scene_group 派发的就是它。' +
1816
+ '整组仍一次成片,所以整组总时长必须在该引擎单段上限内(hailuo-3 15s;seedance-2.5/wan3.0 30s)且支持本剧分辨率,否则 400——' +
1817
+ '那种情况改用 update_shot 给个别镜设 video_engine(设了的镜会单独出)。' +
1818
+ '报价按逐镜计(整组失败会逐镜兜底,逐镜是上界),正常路径实扣通常更低。', {
1819
+ episode_id: z.number().int().positive(),
1820
+ shot_numbers: z.array(z.number().int().positive()).min(1).describe('这一组的镜号(storyboard_number,见 get_scene_group_plan)'),
1821
+ video_engine: z.enum(VIDEO_ENGINES).optional().describe('本次整组重出的引擎(缺省=跟随剧 video_engine)'),
1822
+ }, async ({ episode_id, shot_numbers, video_engine }) => jsonResult(await client.producePost(`/episodes/${episode_id}/scene-groups/regenerate/quote`, { shot_numbers, ...(video_engine ? { video_engine } : {}) })));
1823
+ server.tool('regenerate_scene_group', '确认后重出一个场景组(整组一次视频请求,后台异步;用 get_storyboards 轮询各镜 video_url)。' +
1824
+ '分组、引擎都由 quote_id 钉死,这里不能改;报价后组内任一镜被改、或分组变了,quote 作废(409),重新报价即可(免费)。' +
1825
+ CONFIRM_HINT, {
1826
+ episode_id: z.number().int().positive(),
1827
+ quote_id: z.string().describe('来自 quote_regenerate_scene_group'),
1828
+ }, async ({ episode_id, quote_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/scene-groups/regenerate`, { quote_id })));
1800
1829
  server.tool('generate_scene_groups', '生成场景组(把连续镜头归组,连续长镜/批量出图的地基)。后台异步。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/scene-groups`)));
1801
1830
  server.tool('render_multi_aspect', '把成片按多个画幅(竖/横/方)重渲一版,便于多平台分发。免费(纯 ffmpeg),后台异步。', {
1802
1831
  episode_id: z.number().int().positive(),
package/openapi.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "StarReel Production API",
5
- "version": "0.1.128",
6
- "description": "Turn a script into a finished, downloadable short-drama episode over REST.\n\nPipeline: script → AI rewrite → cast/scenes/props extraction → storyboards → portraits & sheets + scene plates → keyframes → video shots → TTS → final cut (.mp4).\n\n**Billing is prepaid and agent-safe**: big-ticket stages are quote-then-generate (`quote_*` returns a `quote_id`). For video, quote == actual charge. For images the quote is a *range* — models billed per reference image are priced before those are assembled, so `estimated_points` is the upper bound (size the balance by it and a run never dies half-way) and `typical_points` is the usual cost; fixed-price models return the two equal. Insufficient balance returns 402 — nothing half-runs and the account never goes negative.\n\nAuth: exchange your API key at `POST /v1/agent/token` for a 15-minute bearer token.\n\nGenerated from the @starreel/mcp v0.1.128 tool surface (operationIds match MCP tool names 1:1)."
5
+ "version": "0.1.130",
6
+ "description": "Turn a script into a finished, downloadable short-drama episode over REST.\n\nPipeline: script → AI rewrite → cast/scenes/props extraction → storyboards → portraits & sheets + scene plates → keyframes → video shots → TTS → final cut (.mp4).\n\n**Billing is prepaid and agent-safe**: big-ticket stages are quote-then-generate (`quote_*` returns a `quote_id`). For video, quote == actual charge. For images the quote is a *range* — models billed per reference image are priced before those are assembled, so `estimated_points` is the upper bound (size the balance by it and a run never dies half-way) and `typical_points` is the usual cost; fixed-price models return the two equal. Insufficient balance returns 402 — nothing half-runs and the account never goes negative.\n\nAuth: exchange your API key at `POST /v1/agent/token` for a 15-minute bearer token.\n\nGenerated from the @starreel/mcp v0.1.130 tool surface (operationIds match MCP tool names 1:1)."
7
7
  },
8
8
  "servers": [
9
9
  {
@@ -6785,6 +6785,152 @@
6785
6785
  }
6786
6786
  }
6787
6787
  },
6788
+ "/episodes/{episode_id}/scene-groups/regenerate": {
6789
+ "post": {
6790
+ "operationId": "regenerate_scene_group",
6791
+ "summary": "确认后重出一个场景组(整组一次视频请求,后台异步;用 get_storyboards 轮询各镜 video_url)",
6792
+ "description": "确认后重出一个场景组(整组一次视频请求,后台异步;用 get_storyboards 轮询各镜 video_url)。分组、引擎都由 quote_id 钉死,这里不能改;报价后组内任一镜被改、或分组变了,quote 作废(409),重新报价即可(免费)。⚠️ 批量报价确认流程:先调对应的 quote_* 工具,把返回的 estimated_points 原样告诉用户,用户明确同意后,才用返回的 quote_id 调本工具。不要擅自确认。★**出图类报价(定妆图/场景图/出帧)给的是两个数**:estimated_points 是**上界**(按该模型参考图张数上限估,保证你不会按低了的预算开跑、出到一半 402 中断);同一响应里的 typical_points 是**通常花费**。两个都要告诉客户——只说上界会让人以为贵得多(满参考图的上界可达通常值的 2 倍以上),只说通常值又会让余额准备不足。固定价模型下两者相等,这时说一个数就行。出视频的报价与扣费同函数,不存在这个区间。",
6793
+ "tags": [
6794
+ "episodes"
6795
+ ],
6796
+ "parameters": [
6797
+ {
6798
+ "name": "episode_id",
6799
+ "in": "path",
6800
+ "required": true,
6801
+ "schema": {
6802
+ "type": "integer",
6803
+ "exclusiveMinimum": 0
6804
+ }
6805
+ }
6806
+ ],
6807
+ "requestBody": {
6808
+ "required": true,
6809
+ "content": {
6810
+ "application/json": {
6811
+ "schema": {
6812
+ "type": "object",
6813
+ "properties": {
6814
+ "quote_id": {
6815
+ "type": "string",
6816
+ "description": "来自 quote_regenerate_scene_group"
6817
+ }
6818
+ },
6819
+ "required": [
6820
+ "quote_id"
6821
+ ]
6822
+ }
6823
+ }
6824
+ }
6825
+ },
6826
+ "responses": {
6827
+ "200": {
6828
+ "description": "StarReel envelope",
6829
+ "content": {
6830
+ "application/json": {
6831
+ "schema": {
6832
+ "type": "object",
6833
+ "properties": {
6834
+ "code": {
6835
+ "type": "integer"
6836
+ },
6837
+ "message": {
6838
+ "type": "string"
6839
+ },
6840
+ "data": {
6841
+ "description": "Operation result payload"
6842
+ }
6843
+ }
6844
+ }
6845
+ }
6846
+ }
6847
+ },
6848
+ "402": {
6849
+ "description": "Insufficient prepaid balance (never overdrafts); response carries `needed` points"
6850
+ }
6851
+ }
6852
+ }
6853
+ },
6854
+ "/episodes/{episode_id}/scene-groups/regenerate/quote": {
6855
+ "post": {
6856
+ "operationId": "quote_regenerate_scene_group",
6857
+ "summary": "报价:重出**一个场景组**(整组一次视频请求,组内连贯性保留)要多少点",
6858
+ "description": "报价:重出**一个场景组**(整组一次视频请求,组内连贯性保留)要多少点。返回 quote_id。零扣费。shot_numbers 必须正好等于 get_scene_group_plan 里某一组的镜号。\n★可选 video_engine:只为这一次整组重出换引擎(不改剧级 video_engine);报价按它计、quote_id 钉死,regenerate_scene_group 派发的就是它。整组仍一次成片,所以整组总时长必须在该引擎单段上限内(hailuo-3 15s;seedance-2.5/wan3.0 30s)且支持本剧分辨率,否则 400——那种情况改用 update_shot 给个别镜设 video_engine(设了的镜会单独出)。报价按逐镜计(整组失败会逐镜兜底,逐镜是上界),正常路径实扣通常更低。",
6859
+ "tags": [
6860
+ "episodes"
6861
+ ],
6862
+ "parameters": [
6863
+ {
6864
+ "name": "episode_id",
6865
+ "in": "path",
6866
+ "required": true,
6867
+ "schema": {
6868
+ "type": "integer",
6869
+ "exclusiveMinimum": 0
6870
+ }
6871
+ }
6872
+ ],
6873
+ "requestBody": {
6874
+ "required": true,
6875
+ "content": {
6876
+ "application/json": {
6877
+ "schema": {
6878
+ "type": "object",
6879
+ "properties": {
6880
+ "shot_numbers": {
6881
+ "type": "array",
6882
+ "items": {
6883
+ "type": "integer",
6884
+ "exclusiveMinimum": 0
6885
+ },
6886
+ "minItems": 1,
6887
+ "description": "这一组的镜号(storyboard_number,见 get_scene_group_plan)"
6888
+ },
6889
+ "video_engine": {
6890
+ "type": "string",
6891
+ "enum": [
6892
+ "seedance-2.5",
6893
+ "hailuo-3",
6894
+ "wan3.0",
6895
+ "wan3.0-prime"
6896
+ ]
6897
+ }
6898
+ },
6899
+ "required": [
6900
+ "shot_numbers"
6901
+ ]
6902
+ }
6903
+ }
6904
+ }
6905
+ },
6906
+ "responses": {
6907
+ "200": {
6908
+ "description": "StarReel envelope",
6909
+ "content": {
6910
+ "application/json": {
6911
+ "schema": {
6912
+ "type": "object",
6913
+ "properties": {
6914
+ "code": {
6915
+ "type": "integer"
6916
+ },
6917
+ "message": {
6918
+ "type": "string"
6919
+ },
6920
+ "data": {
6921
+ "description": "Operation result payload"
6922
+ }
6923
+ }
6924
+ }
6925
+ }
6926
+ }
6927
+ },
6928
+ "402": {
6929
+ "description": "Insufficient prepaid balance (never overdrafts); response carries `needed` points"
6930
+ }
6931
+ }
6932
+ }
6933
+ },
6788
6934
  "/episodes/{episode_id}/scenes": {
6789
6935
  "get": {
6790
6936
  "operationId": "get_scenes",
@@ -9388,7 +9534,7 @@
9388
9534
  "post": {
9389
9535
  "operationId": "regenerate_scene_image",
9390
9536
  "summary": "按当前提示词重出**这一场**的场景图(改完 image_prompt 让画面跟上)",
9391
- "description": "按当前提示词重出**这一场**的场景图(改完 image_prompt 让画面跟上)。图片步,按用量后付不欠费。★覆盖式:出好后本场旧图被换掉(要留档先 get_scene_prompt/资产列表拿旧图 URL)。★已生成的镜头帧不会自动跟着重出——它们仍拿旧场景图当背景锚,要跟上得逐镜重出。★与 generate_scene_images 的区别:那个是整剧批量、只补**缺图**的场景,已有图的一律跳过;这个是单场景强制重出。客户自己上传过的场景图也会被覆盖,先确认是不是要保留。★episode_id 通常不必传(平台按场景归属、再按引用它的分镜自动推导)。只有回执报「本场景被多集分镜共用」时才需要点名:那说明同一地点跨集复用,平台不猜该按哪一集的时代/世界观出图——猜错就是画面年代静默错掉。",
9537
+ "description": "按当前提示词重出**这一场**的场景图(改完 image_prompt 让画面跟上)。图片步,按用量后付不欠费。★先调 quote_regenerate_scene_image 拿报价、把点数告诉客户,再带 quote_id 调本工具:换了场景图后,没有可用宽景镜首帧的场景平台会自动再出一张打光参考图(另扣一张图的点数),报价里已含。★覆盖式:出好后本场旧图被换掉(要留档先 get_scene_prompt/资产列表拿旧图 URL)。★已生成的镜头帧不会自动跟着重出——它们仍拿旧场景图当背景锚,要跟上得逐镜重出。★与 generate_scene_images 的区别:那个是整剧批量、只补**缺图**的场景,已有图的一律跳过;这个是单场景强制重出。客户自己上传过的场景图也会被覆盖,先确认是不是要保留。★episode_id 通常不必传(平台按场景归属、再按引用它的分镜自动推导)。只有回执报「本场景被多集分镜共用」时才需要点名:那说明同一地点跨集复用,平台不猜该按哪一集的时代/世界观出图——猜错就是画面年代静默错掉。",
9392
9538
  "tags": [
9393
9539
  "scenes"
9394
9540
  ],
@@ -9410,6 +9556,9 @@
9410
9556
  "schema": {
9411
9557
  "type": "object",
9412
9558
  "properties": {
9559
+ "quote_id": {
9560
+ "type": "string"
9561
+ },
9413
9562
  "episode_id": {
9414
9563
  "type": "integer",
9415
9564
  "exclusiveMinimum": 0
@@ -9447,6 +9596,166 @@
9447
9596
  }
9448
9597
  }
9449
9598
  },
9599
+ "/scenes/{scene_id}/image/regenerate/quote": {
9600
+ "post": {
9601
+ "operationId": "quote_regenerate_scene_image",
9602
+ "summary": "单场景图重出的报价(免费,不出图)",
9603
+ "description": "单场景图重出的报价(免费,不出图)。返回 estimated_points(上界)/typical_points,含场景图一张,以及换图后平台会不会自动派生一张打光参考图(lighting_ref.will_generate,给近景镜统一光线,同样扣点)。拿到 quote_id 后传给 regenerate_scene_image。⚠️ 批量报价确认流程:先调对应的 quote_* 工具,把返回的 estimated_points 原样告诉用户,用户明确同意后,才用返回的 quote_id 调本工具。不要擅自确认。★**出图类报价(定妆图/场景图/出帧)给的是两个数**:estimated_points 是**上界**(按该模型参考图张数上限估,保证你不会按低了的预算开跑、出到一半 402 中断);同一响应里的 typical_points 是**通常花费**。两个都要告诉客户——只说上界会让人以为贵得多(满参考图的上界可达通常值的 2 倍以上),只说通常值又会让余额准备不足。固定价模型下两者相等,这时说一个数就行。出视频的报价与扣费同函数,不存在这个区间。",
9604
+ "tags": [
9605
+ "scenes"
9606
+ ],
9607
+ "parameters": [
9608
+ {
9609
+ "name": "scene_id",
9610
+ "in": "path",
9611
+ "required": true,
9612
+ "schema": {
9613
+ "type": "integer",
9614
+ "exclusiveMinimum": 0
9615
+ }
9616
+ }
9617
+ ],
9618
+ "responses": {
9619
+ "200": {
9620
+ "description": "StarReel envelope",
9621
+ "content": {
9622
+ "application/json": {
9623
+ "schema": {
9624
+ "type": "object",
9625
+ "properties": {
9626
+ "code": {
9627
+ "type": "integer"
9628
+ },
9629
+ "message": {
9630
+ "type": "string"
9631
+ },
9632
+ "data": {
9633
+ "description": "Operation result payload"
9634
+ }
9635
+ }
9636
+ }
9637
+ }
9638
+ }
9639
+ },
9640
+ "402": {
9641
+ "description": "Insufficient prepaid balance (never overdrafts); response carries `needed` points"
9642
+ }
9643
+ }
9644
+ }
9645
+ },
9646
+ "/scenes/{scene_id}/lighting-ref/generate": {
9647
+ "post": {
9648
+ "operationId": "generate_scene_lighting_ref",
9649
+ "summary": "按报价补这一场的打光参考图(后台出图,按用量扣点)",
9650
+ "description": "按报价补这一场的打光参考图(后台出图,按用量扣点)。完成后 get_scenes 里该场景的打光参考图字段会有值。",
9651
+ "tags": [
9652
+ "scenes"
9653
+ ],
9654
+ "parameters": [
9655
+ {
9656
+ "name": "scene_id",
9657
+ "in": "path",
9658
+ "required": true,
9659
+ "schema": {
9660
+ "type": "integer",
9661
+ "exclusiveMinimum": 0
9662
+ }
9663
+ }
9664
+ ],
9665
+ "requestBody": {
9666
+ "required": true,
9667
+ "content": {
9668
+ "application/json": {
9669
+ "schema": {
9670
+ "type": "object",
9671
+ "properties": {
9672
+ "quote_id": {
9673
+ "type": "string",
9674
+ "description": "来自 quote_scene_lighting_ref"
9675
+ }
9676
+ },
9677
+ "required": [
9678
+ "quote_id"
9679
+ ]
9680
+ }
9681
+ }
9682
+ }
9683
+ },
9684
+ "responses": {
9685
+ "200": {
9686
+ "description": "StarReel envelope",
9687
+ "content": {
9688
+ "application/json": {
9689
+ "schema": {
9690
+ "type": "object",
9691
+ "properties": {
9692
+ "code": {
9693
+ "type": "integer"
9694
+ },
9695
+ "message": {
9696
+ "type": "string"
9697
+ },
9698
+ "data": {
9699
+ "description": "Operation result payload"
9700
+ }
9701
+ }
9702
+ }
9703
+ }
9704
+ }
9705
+ },
9706
+ "402": {
9707
+ "description": "Insufficient prepaid balance (never overdrafts); response carries `needed` points"
9708
+ }
9709
+ }
9710
+ }
9711
+ },
9712
+ "/scenes/{scene_id}/lighting-ref/quote": {
9713
+ "post": {
9714
+ "operationId": "quote_scene_lighting_ref",
9715
+ "summary": "给某个场景补一张打光参考图的报价(免费,不出图)",
9716
+ "description": "给某个场景补一张打光参考图的报价(免费,不出图)。打光参考图=只复现该场景光线的中性人脸特写,近景/特写镜看不到场景图时靠它统一光线。场景缺它时(例如上传了新场景图后)才需要补;有宽景镜但还没出首帧的场景不必补(宽景镜出帧时平台自动派生)。不够格时返回 400 并说明原因。⚠️ 批量报价确认流程:先调对应的 quote_* 工具,把返回的 estimated_points 原样告诉用户,用户明确同意后,才用返回的 quote_id 调本工具。不要擅自确认。★**出图类报价(定妆图/场景图/出帧)给的是两个数**:estimated_points 是**上界**(按该模型参考图张数上限估,保证你不会按低了的预算开跑、出到一半 402 中断);同一响应里的 typical_points 是**通常花费**。两个都要告诉客户——只说上界会让人以为贵得多(满参考图的上界可达通常值的 2 倍以上),只说通常值又会让余额准备不足。固定价模型下两者相等,这时说一个数就行。出视频的报价与扣费同函数,不存在这个区间。",
9717
+ "tags": [
9718
+ "scenes"
9719
+ ],
9720
+ "parameters": [
9721
+ {
9722
+ "name": "scene_id",
9723
+ "in": "path",
9724
+ "required": true,
9725
+ "schema": {
9726
+ "type": "integer",
9727
+ "exclusiveMinimum": 0
9728
+ }
9729
+ }
9730
+ ],
9731
+ "responses": {
9732
+ "200": {
9733
+ "description": "StarReel envelope",
9734
+ "content": {
9735
+ "application/json": {
9736
+ "schema": {
9737
+ "type": "object",
9738
+ "properties": {
9739
+ "code": {
9740
+ "type": "integer"
9741
+ },
9742
+ "message": {
9743
+ "type": "string"
9744
+ },
9745
+ "data": {
9746
+ "description": "Operation result payload"
9747
+ }
9748
+ }
9749
+ }
9750
+ }
9751
+ }
9752
+ },
9753
+ "402": {
9754
+ "description": "Insufficient prepaid balance (never overdrafts); response carries `needed` points"
9755
+ }
9756
+ }
9757
+ }
9758
+ },
9450
9759
  "/scenes/{scene_id}/prompt": {
9451
9760
  "get": {
9452
9761
  "operationId": "get_scene_prompt",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starreel/mcp",
3
- "version": "0.1.128",
3
+ "version": "0.1.130",
4
4
  "mcpName": "ai.starreel/starreel",
5
5
  "description": "StarReel MCP server — drive the AI short-drama production pipeline (script → storyboards → frames → video → final cut) from Claude, Cursor, or any MCP client",
6
6
  "license": "MIT",
package/server.json CHANGED
@@ -2,13 +2,13 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
3
3
  "name": "ai.starreel/starreel",
4
4
  "description": "Turn a script into a finished, downloadable short-drama episode — via MCP or REST.",
5
- "version": "0.1.128",
5
+ "version": "0.1.130",
6
6
  "websiteUrl": "https://starreel.ai",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@starreel/mcp",
11
- "version": "0.1.128",
11
+ "version": "0.1.130",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  },