@starreel/mcp 0.1.1 → 0.1.3

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.
@@ -16,20 +16,89 @@ function jsonResult(data) {
16
16
  }
17
17
  const CONFIRM_HINT = '⚠️ 批量报价确认流程:先调对应的 quote_* 工具,把返回的 estimated_points 原样告诉用户,' +
18
18
  '用户明确同意后,才用返回的 quote_id 调本工具。不要擅自确认。';
19
+ // 完整工作流顺序(照 get_pipeline_status 的 10 步真相走,不要跳步):
20
+ // create_drama → set_script(原始) → rewrite_script(AI改写) → [get_script/edit_rewritten_script 审改]
21
+ // → extract_assets(角色/场景/道具) → generate_character_portraits(定妆图·一致性关键)
22
+ // → quote/generate_storyboards → quote/generate_frames → quote/generate_videos
23
+ // → compose_episode → get_final_cut / get_export
24
+ // 项目设定随时可 update_project_settings;剧目级资产(色彩脚本/动作模板/世界观图/美术圣经)可选增强。
25
+ const WORKFLOW_HINT = '照完整工作流按序推进,别跳步:set_script(原始)→ rewrite_script(AI改写)→ extract_assets →' +
26
+ ' generate_character_portraits → storyboards → frames → videos → compose。用 get_pipeline_status 查进度。';
27
+ const ETHNICITY_CODES = [
28
+ 'east_asian', 'southeast_asian', 'south_asian', 'central_asian',
29
+ 'caucasian', 'middle_eastern', 'african', 'latin_american',
30
+ 'native_american', 'aboriginal_australian', 'mixed', 'custom',
31
+ ];
32
+ /** 把友好 ethnicity 枚举 + note 组装成后端认的结构化族裔锁 { code, note? }。 */
33
+ function buildEthnicityLock(ethnicity, note) {
34
+ if (!ethnicity)
35
+ return undefined;
36
+ return ethnicity === 'custom' ? { code: 'custom', note: note ?? '' } : { code: ethnicity };
37
+ }
19
38
  export function registerProduceTools(server, client) {
20
39
  // ---------- 建剧 ----------
40
+ // 参数=「项目设定 / 新建项目」页对外能设的全部通用字段(不含内部产线开关)。
41
+ // 尤其 setting_brief(世界观 Brief)与 ethnicity:它们是全链 ERA LOCK / 族裔单一真相源,
42
+ // 建剧时设好,下游剧本改写·分镜·所有出图都据它约束——不设则按剧本语言自动推断(旧行为)。
21
43
  server.tool('create_drama', '新建一部短剧(剧壳)。按 total_episodes 自动建对应数量的空集,返回 drama_id 与各集 episode_id。免费。', {
22
44
  title: z.string().describe('剧名'),
23
45
  total_episodes: z.number().int().min(1).max(200).optional().describe('集数(默认 1),自动建 N 个空集'),
24
46
  genre: z.string().optional().describe('题材,如 都市/悬疑/古装'),
25
47
  style: z.string().optional().describe('风格描述'),
26
48
  description: z.string().optional(),
27
- }, async (args) => jsonResult(await client.producePost('/dramas', args)));
28
- // ---------- 灌本 ----------
29
- server.tool('set_script', '给某一集设置可拍剧本文本(拆镜的输入)。免费。', {
49
+ aspect_ratio: z
50
+ .enum(['9:16', '16:9', '1:1', '4:5', '4:3', '21:9'])
51
+ .optional()
52
+ .describe('画幅比例(默认 9:16 竖屏短剧)'),
53
+ video_resolution: z
54
+ .enum(['480p', '720p', '1080p', '4k'])
55
+ .optional()
56
+ .describe('视频分辨率(默认 720p;成本随分辨率上升)'),
57
+ setting_brief: z
58
+ .string()
59
+ .optional()
60
+ .describe('世界观 Brief:一段话锁定语言/文化/时代/地点,作为 AI 强制约束(ERA LOCK),' +
61
+ '影响剧本改写·分镜·所有出图。不设则按剧本语言与文化自动推断。'),
62
+ ethnicity: z.enum(ETHNICITY_CODES).optional()
63
+ .describe('全剧角色族裔锁(单一真相源)。不设=按剧本自动推断;custom 时用 ethnicity_note 写自由描述'),
64
+ ethnicity_note: z.string().optional().describe("ethnicity='custom' 时的自由文本(如 北欧/波斯);其余取值忽略"),
65
+ project_type: z.enum(['drama', 'ad', 'mv', 'brand_film']).optional()
66
+ .describe('项目类型(默认 drama)。ad=广告(改写走 ad_script_rewriter);mv=音乐(走歌词→故事→剧本子流程);brand_film=品牌微电影(默认16:9)'),
67
+ rewrite_mode: z.enum(['standard', 'director']).optional().describe('AI改写深度:standard 或 director(导演级)'),
68
+ director_style: z.string().optional().describe('导演风格包 key'),
69
+ }, async (args) => {
70
+ const { ethnicity, ethnicity_note, ...rest } = args;
71
+ const body = { ...rest };
72
+ const lock = buildEthnicityLock(ethnicity, ethnicity_note);
73
+ if (lock)
74
+ body.ethnicity_lock = lock;
75
+ return jsonResult(await client.producePost('/dramas', body));
76
+ });
77
+ // ---------- 灌本(原始内容) ----------
78
+ server.tool('set_script', '给某一集设置**原始剧本**(content)。这是 AI 改写的输入,不是最终可拍稿。免费。' +
79
+ '设完调 rewrite_script 做 AI 改写。' + WORKFLOW_HINT, {
80
+ episode_id: z.number().int().positive(),
81
+ script: z.string().min(1).describe('该集的原始剧本文本(原稿)'),
82
+ }, async ({ episode_id, script }) => jsonResult(await client.producePut(`/episodes/${episode_id}/script`, { content: script })));
83
+ // ---------- AI 改写 / 审阅 / 改稿 ----------
84
+ server.tool('rewrite_script', 'AI 改写:把原始剧本改写成可拍稿(读 content → 写 script_content)。按项目类型自动选改写 agent' +
85
+ '(广告走 ad 改写;MV 不走标准改写会被拦)。后台异步(分钟级),文本步按 token 后付、不欠费,无需报价。' +
86
+ '完成后用 get_script 审阅、edit_rewritten_script 改稿。' + WORKFLOW_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/rewrite`)));
87
+ server.tool('get_script', '读某一集的原始内容 + AI 改写后的可拍稿 + 改写状态(供审阅、决定是否 edit_rewritten_script)。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/script`)));
88
+ server.tool('edit_rewritten_script', '客户改 AI 改写后的可拍稿(写 script_content)。用于人工润色/纠正 AI 改写结果。免费。', {
30
89
  episode_id: z.number().int().positive(),
31
- script: z.string().min(1).describe('该集的完整剧本文本'),
32
- }, async ({ episode_id, script }) => jsonResult(await client.producePut(`/episodes/${episode_id}/script`, { script_content: script })));
90
+ script: z.string().min(1).describe('改好的可拍剧本(覆盖 AI 改写稿)'),
91
+ }, async ({ episode_id, script }) => jsonResult(await client.producePut(`/episodes/${episode_id}/rewritten-script`, { script_content: script })));
92
+ // ---------- 提取(角色/场景/道具) ----------
93
+ server.tool('extract_assets', '从可拍稿提取角色/场景/道具(一次写三表,是下游一致性的地基)。后台异步,文本步后付不欠费。' +
94
+ '前置:已 rewrite_script(或至少 set_script)。' + WORKFLOW_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/extract`)));
95
+ // ---------- 完整工作流进度 ----------
96
+ server.tool('get_pipeline_status', '查某一集完整工作流 10 步的进度(script_rewrite/提取/分镜/出图/出视频/合成…各步 done/ready/pending)。' +
97
+ '照它按序推进、不跳步。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/pipeline-status`)));
98
+ // ---------- 角色定妆图(一致性锚) ----------
99
+ server.tool('quote_character_portraits', '报价:给缺定妆图的角色批量出定妆图要多少点。返回 portraits_to_generate、estimated_points、quote_id。零扣费。' +
100
+ '定妆图是身份一致性的锚(缺它角色会漂移),强烈建议出视频前先出。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits/quote`)));
101
+ server.tool('generate_character_portraits', '确认后批量出角色定妆图:后台异步。用 get_pipeline_status/get_storyboards 查进度。' + CONFIRM_HINT, { episode_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_character_portraits') }, async ({ episode_id, quote_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits/generate`, { quote_id })));
33
102
  // ---------- 拆镜(storyboards) ----------
34
103
  server.tool('quote_storyboards', '报价:把某一集的剧本拆成分镜(storyboards)要多少点。返回 estimated_points 与 quote_id。零扣费。' +
35
104
  '拿到后把点数告诉用户征求同意,再用 quote_id 调 generate_storyboards。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/storyboards/quote`)));
@@ -48,4 +117,62 @@ export function registerProduceTools(server, client) {
48
117
  server.tool('compose_episode', '把某一集所有镜头视频拼成一条成片(终拼)。**免费**(纯拼接,无需报价确认),后台异步。' +
49
118
  '要求本集至少有一个镜有视频;部分就绪会拼出部分成片并在返回里标 partial。发起后用 get_final_cut 轮询。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/compose`)));
50
119
  server.tool('get_final_cut', '查某一集成片状态与下载链接。status=completed 时返回 download_url(我方 COS 直链,可直接下载)。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/final-cut`)));
120
+ server.tool('get_export', '查某一集导出/母版状态(成片终拼后的可下载母版)。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/export`)));
121
+ // ========== 项目设定(建后可改)==========
122
+ server.tool('update_project_settings', '建剧后修改项目设定(画幅/分辨率/世界观Brief/族裔/题材/改写深度/导演风格/字幕/长镜,及各类型专属字段)。免费。' +
123
+ '⚠️ 世界观 Brief/族裔/画幅等改后,已生成的内容不会自动更新,需重生对应内容。', {
124
+ drama_id: z.number().int().positive(),
125
+ genre: z.string().optional(),
126
+ style: z.string().optional(),
127
+ description: z.string().optional(),
128
+ tags: z.array(z.string()).optional(),
129
+ aspect_ratio: z.enum(['9:16', '16:9', '1:1', '4:5', '4:3', '21:9']).optional(),
130
+ video_resolution: z.enum(['480p', '720p', '1080p', '4k']).optional(),
131
+ setting_brief: z.string().optional().describe('世界观 Brief(ERA LOCK)'),
132
+ ethnicity: z.enum(ETHNICITY_CODES).optional().describe('角色族裔锁'),
133
+ ethnicity_note: z.string().optional(),
134
+ rewrite_mode: z.enum(['standard', 'director']).optional(),
135
+ director_style: z.string().optional(),
136
+ theme_statement: z.string().optional().describe('一句话主题'),
137
+ subtitle_preset: z.string().optional(),
138
+ scene_group_mode: z.boolean().optional().describe('长镜模式(连续动作/电影级长镜)'),
139
+ // 广告专属
140
+ cta_text: z.string().optional(),
141
+ target_duration_s: z.number().optional(),
142
+ brand_voice: z.string().optional(),
143
+ // 品牌微电影专属
144
+ narrative_tone: z.string().optional(),
145
+ story_structure: z.string().optional(),
146
+ end_card_variant: z.string().optional(),
147
+ // MV 专属
148
+ mv_lyrics: z.string().optional(),
149
+ mv_audio_url: z.string().optional(),
150
+ }, async (args) => {
151
+ const { drama_id, ethnicity, ethnicity_note, ...rest } = args;
152
+ const body = { ...rest };
153
+ const lock = buildEthnicityLock(ethnicity, ethnicity_note);
154
+ if (lock)
155
+ body.ethnicity_lock = lock;
156
+ return jsonResult(await client.producePut(`/dramas/${drama_id}`, body));
157
+ });
158
+ // ========== 项目设定页子资源(可选增强,提升一致性/风格锁)==========
159
+ server.tool('generate_world_concept', '生成世界观概念图(固定4格:全景/时代/主场景/色彩)。需 setting_brief ≥30字。图片步,按固定成本后付、不欠费。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/world-concept`)));
160
+ server.tool('generate_art_bible', '生成美术圣经(色调/材质/气质,读角色+元数据)。文本步后付不欠费。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/art-bible`)));
161
+ server.tool('extract_visual_lock', '从世界观 Brief + 角色抽取「视觉锁定」规则(写 visual_lock,统一出图风格/族裔)。文本步后付不欠费。', {
162
+ drama_id: z.number().int().positive(),
163
+ setting_brief: z.string().optional().describe('可选:临时覆盖世界观原文'),
164
+ }, async ({ drama_id, setting_brief }) => jsonResult(await client.producePost(`/dramas/${drama_id}/visual-lock`, setting_brief ? { setting_brief } : {})));
165
+ server.tool('extract_setting_brief', '从一段世界观原文提炼简洁的 setting_brief(并存回项目)。文本步后付不欠费。', {
166
+ drama_id: z.number().int().positive(),
167
+ raw_text: z.string().min(1).describe('世界观原文'),
168
+ }, async ({ drama_id, raw_text }) => jsonResult(await client.producePost(`/dramas/${drama_id}/extract-setting-brief`, { raw_text })));
169
+ server.tool('generate_video_style', '生成视频风格锁 prompt(读 brief/artBible/visualLock,统一视频生成方向)。文本步后付不欠费。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/video-style`)));
170
+ // ========== 剧目级共享资产(可选增强)==========
171
+ server.tool('generate_color_script', '生成剧目色彩脚本(统一全片配色情绪)。需该剧/集已有剧本文本。文本步后付不欠费。', {
172
+ drama_id: z.number().int().positive(),
173
+ episode_id: z.number().int().positive().optional().describe('可选:按某一集生成'),
174
+ }, async ({ drama_id, episode_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/color-script`, episode_id ? { episode_id } : {})));
175
+ server.tool('generate_motion_templates', '从分镜自动抽取动作模板(统一全片运动语言)。**需先有分镜**(先 generate_storyboards)。文本步后付不欠费。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/motion-templates`)));
176
+ server.tool('quote_scene_images', '报价:给缺图场景批量出场景图要多少点。返回 images_to_generate、estimated_points、quote_id。零扣费。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/scene-images/quote`)));
177
+ server.tool('generate_scene_images', '确认后批量出场景图:后台异步。' + CONFIRM_HINT, { drama_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_scene_images') }, async ({ drama_id, quote_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/scene-images/generate`, { quote_id })));
51
178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starreel/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
3
3
  "name": "ai.starreel/starreel",
4
- "description": "AI short-drama production pipeline: turn a script into a finished, downloadable episode — create drama, break into storyboards, generate frames and videos, and compose the final cut, all with batch quote/confirm billing.",
5
- "version": "0.1.1",
4
+ "description": "Turn a script into a finished, downloadable short-drama episode — via MCP or REST.",
5
+ "version": "0.1.3",
6
6
  "websiteUrl": "https://starreel.ai",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@starreel/mcp",
11
- "version": "0.1.1",
11
+ "version": "0.1.3",
12
12
  "transport": { "type": "stdio" },
13
13
  "environmentVariables": [
14
14
  {