@infimind/video-mcp-cli 1.0.9 → 1.0.11

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/README.md CHANGED
@@ -100,6 +100,8 @@ Parameter applicability:
100
100
  | `sourceImages` | Not supported | allinone / firstlast images | reference / edit reference images |
101
101
  | `sourceVideos` | Not supported | allinone reference videos | edit input videos |
102
102
  | `sourceAudios` | Not supported | allinone reference audio | Not supported |
103
+ | `sourceVideoDurationSeconds` | Not supported | dryRun estimate for videos | dryRun estimate for edit video |
104
+ | `sourceAudioDurationSeconds` | Not supported | dryRun estimate for audio | Not supported |
103
105
  | `taskMode` | Not needed | `allinone` / `firstlast` | `standard` / `reference` / `edit` |
104
106
  | `resolution` | Not supported | `720p` / `1080p` | `720p` / `1080p` |
105
107
  | `ratio` | Not supported | Not supported | reference or text-to-video standard mode |
@@ -107,7 +109,7 @@ Parameter applicability:
107
109
  | `webSearchEnabled` | Not supported | Optional | Not supported |
108
110
  | `dryRun` | Optional preflight | Optional preflight | Optional preflight |
109
111
 
110
- Local file paths and public URLs are both supported. Local files are converted to base64 by the MCP server before being sent to the API.
112
+ Local file paths and public URLs are both supported.
111
113
 
112
114
  Video inputs support MP4/MOV. Audio inputs support MP3/WAV. The server validates media duration, size, count, resolution, and URL safety before estimating credits or creating tasks.
113
115
 
@@ -119,22 +121,21 @@ Media limits:
119
121
  - S5 firstlast: up to 2 images; the first frame is required.
120
122
  - S6 reference: up to 9 images.
121
123
  - S6 edit: up to 1 video and 5 images.
122
- - `inputDurationTier` is not accepted from clients; the server calculates it from the media.
123
124
 
124
125
  URL safety rules:
125
126
 
126
127
  - Only `http` and `https` URLs are accepted.
127
128
  - `localhost`, `127.0.0.1`, private network addresses, reserved IP ranges, and `file:` URLs are rejected.
128
129
  - Redirect targets are revalidated.
129
- - Remote files that are too large, not media, or cannot be safely probed are rejected.
130
+ - Remote files that are too large, not media, or cannot be read safely are rejected.
130
131
 
131
- Use `dryRun: true` to validate parameters, VIP access, estimated credits, and media rules without creating a task or deducting credits. The result includes `success`, `dryRun`, `route`, `engineName`, `taskMode`, `estimatedCredits`, `remainingCredits`, and `validationWarnings`. Specific credit tables are server-side only; use dry run to get the current estimated consumption.
132
+ Use `dryRun: true` to validate parameters, VIP access, media rules, and estimated credits without creating a task or deducting credits. If a dry run includes video or audio, add `sourceVideoDurationSeconds` / `sourceAudioDurationSeconds` to get an estimated credit result. If these duration fields are omitted, dry run can still pass, but the result may not include `estimatedCredits`.
132
133
 
133
134
  #### Dry run before creating a task
134
135
 
135
136
  ```json
136
137
  {
137
- "prompt": "@图片1 作为角色参考,生成自然的走秀镜头",
138
+ "prompt": "@image1 作为角色参考,生成自然的走秀镜头",
138
139
  "route": "S6",
139
140
  "taskMode": "reference",
140
141
  "sourceImages": ["/path/to/character.jpg"],
@@ -172,6 +173,8 @@ S2 uses `sourceImage`, `seconds`, and `size`; do not pass `taskMode` or `resolut
172
173
  "sourceImages": ["/path/to/reference.jpg"],
173
174
  "sourceVideos": ["/path/to/reference.mp4"],
174
175
  "sourceAudios": ["/path/to/music.mp3"],
176
+ "sourceVideoDurationSeconds": [5],
177
+ "sourceAudioDurationSeconds": [5],
175
178
  "seconds": 10,
176
179
  "size": "720x1280",
177
180
  "resolution": "720p",
@@ -232,7 +235,7 @@ Image-to-video:
232
235
 
233
236
  ```json
234
237
  {
235
- "prompt": "@图片1 和 @图片2 作为角色参考,生成自然的走秀镜头",
238
+ "prompt": "@image1 和 @image2 作为角色参考,生成自然的走秀镜头",
236
239
  "route": "S6",
237
240
  "taskMode": "reference",
238
241
  "sourceImages": ["/path/to/character-1.jpg", "/path/to/character-2.jpg"],
@@ -253,12 +256,13 @@ Image-to-video:
253
256
  "taskMode": "edit",
254
257
  "sourceVideos": ["/path/to/input.mp4"],
255
258
  "sourceImages": ["/path/to/reference.jpg"],
259
+ "sourceVideoDurationSeconds": [5],
256
260
  "resolution": "720p",
257
261
  "dryRun": true
258
262
  }
259
263
  ```
260
264
 
261
- After dry run succeeds and `estimatedCredits` looks correct, remove `dryRun` or set it to `false` to create the real task. After creating a task, use `get_video_status` with the returned `taskId` to query progress and video links.
265
+ After dry run succeeds and the estimated credits look correct, remove `dryRun` or set it to `false` to create the real task. After creating a task, use `get_video_status` with the returned `taskId` to query progress and video links.
262
266
 
263
267
  ## Development
264
268
 
@@ -331,7 +335,7 @@ Set `SORA_API_URL=http://localhost:3000/api` when testing against a local app se
331
335
  - If a client reports schema mismatch, run `npm run build` and `npm run test:smoke` in `mcp-server`.
332
336
  - If `get_video_status` cannot connect, verify `SORA_API_URL` includes `/api`, for example `http://localhost:3000/api`.
333
337
  - If S5/S6 returns VIP errors, confirm the MCP token belongs to a VIP user.
334
- - If S6 reference/edit prompts contain `@图片N`, keep the original markers; those modes skip prompt translation to preserve image references.
338
+ - If S6 reference/edit prompts contain canonical `@imageN` markers, keep the original markers; those modes skip prompt translation to preserve image references. Legacy `@图片N` markers are still accepted and normalized by the server.
335
339
  - If local file inputs fail, use absolute paths and supported media types: jpg/png/webp/gif, mp4/mov, mp3/wav.
336
340
 
337
341
  ## Publishing
package/dist/server.js CHANGED
@@ -8,7 +8,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
9
9
  import { readFileSync, existsSync, statSync } from "fs";
10
10
  import { extname, basename } from "path";
11
- import { MCP_SERVER_VERSION, getMcpDefaultTaskMode, getMcpEngineName, getMcpVideoStatusHint, isMcpRoute, isValidMcpTaskMode, } from "./videoRules.js";
11
+ import { MCP_SERVER_VERSION, getMcpDefaultTaskMode, getMcpEngineName, getMcpVideoStatusHint, isMcpRoute, isValidMcpTaskMode, isMcpModel, MCP_VIDEO_MODELS, } from "./videoRules.js";
12
12
  class McpToolError extends Error {
13
13
  code;
14
14
  route;
@@ -67,8 +67,7 @@ export function createServer(token, apiBaseUrl) {
67
67
  name: "create_video_task",
68
68
  title: "创建视频生成任务",
69
69
  description: "创建视频生成任务。支持本地文件路径或公网 URL 作为参考图片。" +
70
- "支持极睿Sd专业版和极睿Ha专业版的多模态素材输入。" +
71
- "提示词会根据目标语言自动翻译和优化。",
70
+ "支持极睿Sd专业版和极睿Ha专业版的多模态素材输入。",
72
71
  inputSchema: {
73
72
  type: "object",
74
73
  properties: {
@@ -78,23 +77,33 @@ export function createServer(token, apiBaseUrl) {
78
77
  },
79
78
  sourceImage: {
80
79
  type: "string",
81
- description: "可选的单张参考图片。S2 使用此字段;S6 标准模式可用作单图图生视频。可以是本地文件路径或公网 URL,本地文件会被自动转换为 base64",
80
+ description: "可选的单张参考图片。S2 使用此字段;S6 标准模式可用作单图图生视频。可以是本地文件路径或公网 URL",
82
81
  },
83
82
  sourceImages: {
84
83
  type: "array",
85
- description: "S5/S6 多张参考图片。本地文件路径或公网 URL 数组;本地文件会被自动转换为 base64。S5 首尾帧模式下第1张为首帧、第2张为尾帧;S6 reference/edit 模式作为参考图",
84
+ description: "S5/S6 多张参考图片。本地文件路径或公网 URL 数组。S5 首尾帧模式下第1张为首帧、第2张为尾帧;S6 reference/edit 模式作为参考图",
86
85
  items: { type: "string" },
87
86
  },
88
87
  sourceVideos: {
89
88
  type: "array",
90
- description: "S5 全能参考或 S6 视频编辑模式的视频素材。本地文件路径或公网 URL 数组;本地文件会被自动转换为 base64",
89
+ description: "S5 全能参考或 S6 视频编辑模式的视频素材。本地文件路径或公网 URL 数组",
91
90
  items: { type: "string" },
92
91
  },
93
92
  sourceAudios: {
94
93
  type: "array",
95
- description: "S5 全能参考模式的音频素材。本地文件路径或公网 URL 数组;本地文件会被自动转换为 base64",
94
+ description: "S5 全能参考模式的音频素材。本地文件路径或公网 URL 数组",
96
95
  items: { type: "string" },
97
96
  },
97
+ sourceVideoDurationSeconds: {
98
+ type: "array",
99
+ description: "可选。dryRun 使用的视频素材时长秒数数组,顺序与 sourceVideos 一致;用于获得预估积分",
100
+ items: { type: "number" },
101
+ },
102
+ sourceAudioDurationSeconds: {
103
+ type: "array",
104
+ description: "可选。dryRun 使用的音频素材时长秒数数组,顺序与 sourceAudios 一致;用于获得预估积分",
105
+ items: { type: "number" },
106
+ },
98
107
  size: {
99
108
  type: "string",
100
109
  description: "视频方向/比例。S2 支持横竖屏;S5/S6 还支持正方形、4:3、3:4(默认:1280x720)",
@@ -116,10 +125,16 @@ export function createServer(token, apiBaseUrl) {
116
125
  minimum: 1,
117
126
  maximum: 12,
118
127
  },
128
+ model: {
129
+ type: "string",
130
+ description: "生成引擎短码(推荐):SA=极睿Sa专业版(默认,Sora 2,失败自动回退 S4)、SD=极睿Sd专业版(Seedance 2.0)、HA=极睿Ha专业版(HappyHorse 1.1)。注意:SD、HA 仅限 VIP 用户使用",
131
+ enum: ["SA", "SD", "HA"],
132
+ },
119
133
  route: {
120
134
  type: "string",
121
- description: "使用的生成引擎:S2=极睿Sa专业版(默认)、S5=极睿Sd专业版、S6=极睿Ha专业版。注意:S5、S6 仅限 VIP 用户使用",
135
+ description: "内部线路代码(已废弃,请改用 model)。兼容老客户端:S2=极睿Sa专业版、S5=极睿Sd专业版、S6=极睿Ha专业版",
122
136
  enum: ["S2", "S5", "S6"],
137
+ deprecated: true,
123
138
  },
124
139
  taskMode: {
125
140
  type: "string",
@@ -157,12 +172,15 @@ export function createServer(token, apiBaseUrl) {
157
172
  properties: {
158
173
  success: { type: "boolean" },
159
174
  taskId: { type: "number" },
175
+ model: { type: "string" },
160
176
  route: { type: "string" },
161
177
  engineName: { type: "string" },
178
+ usedRouteAlias: { type: "boolean" },
162
179
  taskMode: { type: "string" },
163
180
  videoCount: { type: "number" },
164
181
  dryRun: { type: "boolean" },
165
182
  estimatedCredits: { type: "number" },
183
+ estimateSource: { type: "string" },
166
184
  remainingCredits: { type: "number" },
167
185
  statusHint: { type: "string" },
168
186
  validationWarnings: {
@@ -199,10 +217,7 @@ export function createServer(token, apiBaseUrl) {
199
217
  totalVideos: { type: "number" },
200
218
  completedVideos: { type: "number" },
201
219
  estimatedCredits: { type: "number" },
202
- chargedVideos: { type: "number" },
203
- actualRoute: { type: "string" },
204
220
  taskMode: { type: ["string", "null"] },
205
- retryCount: { type: "number" },
206
221
  statusHint: { type: "string" },
207
222
  createdAt: { type: ["string", "null"] },
208
223
  updatedAt: { type: ["string", "null"] },
@@ -228,9 +243,6 @@ export function createServer(token, apiBaseUrl) {
228
243
  errorCode: { type: ["string", "null"] },
229
244
  errorCategory: { type: ["string", "null"] },
230
245
  retryable: { type: ["boolean", "null"] },
231
- actualRoute: { type: ["string", "null"] },
232
- retryCount: { type: "number" },
233
- creditsCharged: { type: "boolean" },
234
246
  createdAt: { type: ["string", "null"] },
235
247
  completedAt: { type: ["string", "null"] },
236
248
  },
@@ -245,13 +257,13 @@ export function createServer(token, apiBaseUrl) {
245
257
  name: "create_video_analysis_task",
246
258
  title: "创建视频拆解任务",
247
259
  description: "创建视频拆解任务,将视频拆解为脚本、风格、卖点话术、爆款公式,并生成用于视频创作的提示词。" +
248
- "支持本地文件路径或公网 URL 作为视频文件。本地文件会被自动转换为 base64。",
260
+ "支持本地文件路径或公网 URL。",
249
261
  inputSchema: {
250
262
  type: "object",
251
263
  properties: {
252
264
  videoFile: {
253
265
  type: "string",
254
- description: "要分析的视频文件 - 可以是本地文件路径(如 /path/to/video.mp4)或公网 URL。本地文件会被自动转换为 base64",
266
+ description: "要分析的视频文件,可以是本地文件路径或公网 URL",
255
267
  },
256
268
  },
257
269
  required: ["videoFile"],
@@ -361,8 +373,8 @@ async function handleGetUserInfo(apiBaseUrl, token) {
361
373
  `# 用户信息`,
362
374
  ``,
363
375
  `- **用户名**: ${response.username}`,
364
- `- **姓名**: ${response.name || "N/A"}`,
365
- `- **邮箱**: ${response.email || "N/A"}`,
376
+ `- **姓名**: ${response.name || "未提供"}`,
377
+ `- **邮箱**: ${response.email || "未提供"}`,
366
378
  `- **角色**: ${response.role === "admin" ? "管理员" : "用户"}`,
367
379
  `- **积分**: ${response.credits}`,
368
380
  `- **VIP 状态**: ${response.isVip ? "是" : "否"}`,
@@ -460,12 +472,27 @@ function hasItems(value) {
460
472
  function itemCount(value) {
461
473
  return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0).length : 0;
462
474
  }
463
- function normalizeRoute(value) {
464
- const route = typeof value === "string" ? value : "S2";
475
+ function normalizeRoute(args) {
476
+ // 优先 model;若 model 未传或不是 SA/SD/HA,再看 route(route 现在是 deprecated alias)
477
+ if (args && typeof args === "object" && isMcpModel(args.model)) {
478
+ const m = args.model;
479
+ return {
480
+ route: MCP_VIDEO_MODELS[m].route,
481
+ model: m,
482
+ usedRouteAlias: false,
483
+ };
484
+ }
485
+ const raw = args && typeof args === "object" ? args.route : undefined;
486
+ const route = typeof raw === "string" ? raw : "S2";
465
487
  if (!isMcpRoute(route)) {
466
- throw new McpToolError("route 只能是 S2、S5 或 S6。S2=极睿Sa专业版,S5=极睿Sd专业版,S6=极睿Ha专业版。", "INVALID_ROUTE");
488
+ throw new McpToolError("model 必须是 SA、SD 或 HA;route 只接受 S2、S5、S6(已废弃),请改用 model。", "INVALID_ENGINE");
467
489
  }
468
- return route;
490
+ const m = route === "S2" ? "SA" : route === "S5" ? "SD" : "HA";
491
+ return {
492
+ route,
493
+ model: m,
494
+ usedRouteAlias: true,
495
+ };
469
496
  }
470
497
  function normalizeTaskMode(route, value) {
471
498
  const mode = typeof value === "string" ? value : getMcpDefaultTaskMode(route);
@@ -491,13 +518,11 @@ function validateCreateVideoArgs(args) {
491
518
  if (args.count !== undefined && (!Number.isInteger(args.count) || args.count < 1 || args.count > 12)) {
492
519
  throw new McpToolError("count 必须是 1-12 的整数。", "INVALID_COUNT");
493
520
  }
494
- const route = normalizeRoute(args.route);
521
+ const norm = normalizeRoute(args);
522
+ const route = norm.route;
495
523
  const taskMode = normalizeTaskMode(route, args.taskMode);
496
524
  const resolution = typeof args.resolution === "string" ? args.resolution : "720p";
497
525
  const dryRun = args.dryRun === true;
498
- if (args.inputDurationTier !== undefined) {
499
- throw new McpToolError("inputDurationTier 不再支持由 MCP 传入;输入视频时长档位由服务端根据素材自动计算。", "UNSUPPORTED_FIELD", route);
500
- }
501
526
  if ((route === "S5" || route === "S6") && resolution !== "720p" && resolution !== "1080p") {
502
527
  throw new McpToolError(`${getMcpEngineName(route)} 仅支持 720p 或 1080p。`, "INVALID_RESOLUTION", route);
503
528
  }
@@ -580,6 +605,8 @@ function validateCreateVideoArgs(args) {
580
605
  }
581
606
  return {
582
607
  route,
608
+ model: norm.model,
609
+ usedRouteAlias: norm.usedRouteAlias,
583
610
  taskMode,
584
611
  resolution,
585
612
  dryRun,
@@ -602,6 +629,7 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
602
629
  seconds: args.seconds || 10,
603
630
  language: args.language || "zh-CN",
604
631
  count: args.count || 1,
632
+ model: normalized.model,
605
633
  route: normalized.route,
606
634
  dryRun: normalized.dryRun,
607
635
  };
@@ -615,6 +643,10 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
615
643
  requestBody.ratio = args.ratio || undefined;
616
644
  requestBody.generateAudio = args.generateAudio;
617
645
  requestBody.webSearchEnabled = args.webSearchEnabled;
646
+ if (normalized.dryRun) {
647
+ requestBody.sourceVideoDurationSeconds = args.sourceVideoDurationSeconds;
648
+ requestBody.sourceAudioDurationSeconds = args.sourceAudioDurationSeconds;
649
+ }
618
650
  }
619
651
  const response = await callRestApi(apiBaseUrl, "/mcp/video/create", requestBody, "POST");
620
652
  const route = response.route || normalized.route;
@@ -630,9 +662,11 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
630
662
  `- **模式**: ${taskMode}`,
631
663
  `- **消息**: ${response.message}`,
632
664
  `- **生成视频数量**: ${response.videoCount}`,
633
- `- **预估积分**: ${estimatedCredits ?? "N/A"}(视频完成后按成功扣费)`,
634
665
  `- **当前积分**: ${response.remainingCredits}`,
635
666
  ];
667
+ if (typeof estimatedCredits === "number") {
668
+ result.splice(6, 0, `- **预估积分**: ${estimatedCredits}(视频完成后按成功扣费)`);
669
+ }
636
670
  if (!dryRun && response.taskId !== undefined) {
637
671
  result.splice(4, 0, `- **任务 ID**: ${response.taskId}`);
638
672
  }
@@ -650,12 +684,15 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
650
684
  structuredContent: {
651
685
  success: response.success,
652
686
  taskId: response.taskId,
687
+ model: response.model ?? normalized.model,
653
688
  route,
654
689
  engineName,
690
+ usedRouteAlias: normalized.usedRouteAlias,
655
691
  taskMode,
656
692
  videoCount: response.videoCount,
657
693
  dryRun,
658
694
  estimatedCredits,
695
+ estimateSource: response.estimateSource,
659
696
  remainingCredits: response.remainingCredits,
660
697
  statusHint,
661
698
  validationWarnings: response.validationWarnings || [],
@@ -679,10 +716,7 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
679
716
  `- **进度**: ${response.progress}% (${response.completedVideos}/${response.totalVideos} 个视频)`,
680
717
  response.engineName ? `- **生成引擎**: ${response.engineName}` : undefined,
681
718
  response.taskMode ? `- **模式**: ${response.taskMode}` : undefined,
682
- response.actualRoute ? `- **实际线路**: ${response.actualRoute}` : undefined,
683
719
  typeof response.estimatedCredits === "number" ? `- **预估积分**: ${response.estimatedCredits}` : undefined,
684
- typeof response.chargedVideos === "number" ? `- **已扣费视频**: ${response.chargedVideos}/${response.totalVideos}` : undefined,
685
- typeof response.retryCount === "number" ? `- **重试次数**: ${response.retryCount}` : undefined,
686
720
  ``,
687
721
  ].filter((line) => line !== undefined);
688
722
  if (response.errorMessage) {
@@ -712,15 +746,6 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
712
746
  if (video.errorCode) {
713
747
  result.push(` 错误代码: ${video.errorCode}`);
714
748
  }
715
- if (video.actualRoute) {
716
- result.push(` 实际线路: ${video.actualRoute}`);
717
- }
718
- if (typeof video.retryCount === "number" && video.retryCount > 0) {
719
- result.push(` 重试次数: ${video.retryCount}`);
720
- }
721
- if (typeof video.creditsCharged === "boolean") {
722
- result.push(` 已扣费: ${video.creditsCharged ? "是" : "否"}`);
723
- }
724
749
  });
725
750
  }
726
751
  if (response.statusHint) {
@@ -743,9 +768,6 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
743
768
  route: response.route,
744
769
  engineName: response.engineName,
745
770
  taskMode: response.taskMode,
746
- actualRoute: response.actualRoute,
747
- retryCount: response.retryCount,
748
- chargedVideos: response.chargedVideos,
749
771
  estimatedCredits: response.estimatedCredits,
750
772
  statusHint: response.statusHint,
751
773
  createdAt: response.createdAt,
@@ -1,6 +1,6 @@
1
1
  export type McpRoute = "S2" | "S5" | "S6";
2
2
  export type McpTaskMode = "allinone" | "firstlast" | "standard" | "reference" | "edit";
3
- export declare const MCP_SERVER_VERSION = "1.0.9";
3
+ export declare const MCP_SERVER_VERSION = "1.0.10";
4
4
  export declare const MCP_VIDEO_STATUS_HINT = "\u4EFB\u52A1\u5DF2\u521B\u5EFA\u540E\uFF0C\u4F7F\u7528 get_video_status \u5E76\u4F20\u5165 taskId \u67E5\u8BE2\u751F\u6210\u8FDB\u5EA6\u548C\u89C6\u9891\u94FE\u63A5\u3002";
5
5
  export declare const MCP_VIDEO_DRY_RUN_HINT = "dryRun \u4EC5\u5B8C\u6210\u53C2\u6570\u3001\u6743\u9650\u3001\u79EF\u5206\u548C\u7D20\u6750\u89C4\u5219\u9884\u68C0\uFF0C\u4E0D\u4F1A\u521B\u5EFA\u4EFB\u52A1\u6216\u6263\u8D39\u3002";
6
6
  export declare const MCP_VIDEO_ENGINES: Record<McpRoute, {
@@ -14,4 +14,36 @@ export declare function getMcpEngineName(route: string): string;
14
14
  export declare function getMcpTaskModel(route: McpRoute): string;
15
15
  export declare function getMcpDefaultTaskMode(route: McpRoute, isFirstLastMode?: boolean): McpTaskMode;
16
16
  export declare function isValidMcpTaskMode(route: McpRoute, taskMode: string): taskMode is McpTaskMode;
17
+ /**
18
+ * 面向 MCP 客户端的引擎短码:
19
+ * SA = S2(极睿Sa专业版,Sora 2 模型,自动 S2→S4 兜底)
20
+ * SD = S5(极睿Sd专业版,Seedance 2.0)
21
+ * HA = S6(极睿Ha专业版,HappyHorse 1.1)
22
+ */
23
+ export type McpModel = "SA" | "SD" | "HA";
24
+ export declare const MCP_VIDEO_MODELS: Record<McpModel, {
25
+ route: McpRoute;
26
+ engineName: string;
27
+ providerModel: string;
28
+ }>;
29
+ export declare function isMcpModel(value: unknown): value is McpModel;
30
+ export declare function getMcpModelForRoute(route: McpRoute): McpModel;
31
+ /**
32
+ * 把客户端传入的 model / route 值解析成内部 route。
33
+ * - 如果 model 有效,直接用对应的内部 route。
34
+ * - 否则如果 route 是有效 McpRoute,返回该 route。
35
+ * - 否则返回 "SA"(与现有默认行为保持一致)。
36
+ *
37
+ * 第二返回值 true 表示 route 走的是 deprecated alias(仅使用 route 而非 model)。
38
+ */
39
+ export declare function resolveMcpEngine(args: {
40
+ model?: unknown;
41
+ route?: unknown;
42
+ }): {
43
+ route: McpRoute;
44
+ model: McpModel;
45
+ engineName: string;
46
+ providerModel: string;
47
+ usedRouteAlias: boolean;
48
+ };
17
49
  export declare function getMcpVideoStatusHint(dryRun?: boolean): string;
@@ -1,4 +1,4 @@
1
- export const MCP_SERVER_VERSION = "1.0.9";
1
+ export const MCP_SERVER_VERSION = "1.0.10";
2
2
  export const MCP_VIDEO_STATUS_HINT = "任务已创建后,使用 get_video_status 并传入 taskId 查询生成进度和视频链接。";
3
3
  export const MCP_VIDEO_DRY_RUN_HINT = "dryRun 仅完成参数、权限、积分和素材规则预检,不会创建任务或扣费。";
4
4
  export const MCP_VIDEO_ENGINES = {
@@ -16,7 +16,7 @@ export const MCP_VIDEO_ENGINES = {
16
16
  },
17
17
  S6: {
18
18
  engineName: "极睿Ha专业版",
19
- model: "happyhorse-1.0",
19
+ model: "happyhorse-1.1",
20
20
  defaultTaskMode: "standard",
21
21
  taskModes: ["standard", "reference", "edit"],
22
22
  },
@@ -39,6 +39,64 @@ export function getMcpDefaultTaskMode(route, isFirstLastMode) {
39
39
  export function isValidMcpTaskMode(route, taskMode) {
40
40
  return MCP_VIDEO_ENGINES[route].taskModes.includes(taskMode);
41
41
  }
42
+ export const MCP_VIDEO_MODELS = {
43
+ SA: { route: "S2", engineName: "极睿Sa专业版", providerModel: "sora-2" },
44
+ SD: { route: "S5", engineName: "极睿Sd专业版", providerModel: "doubao-seedance-2-0-260128" },
45
+ HA: { route: "S6", engineName: "极睿Ha专业版", providerModel: "happyhorse-1.1" },
46
+ };
47
+ export function isMcpModel(value) {
48
+ return value === "SA" || value === "SD" || value === "HA";
49
+ }
50
+ export function getMcpModelForRoute(route) {
51
+ if (route === "S2")
52
+ return "SA";
53
+ if (route === "S5")
54
+ return "SD";
55
+ if (route === "S6")
56
+ return "HA";
57
+ return "SA"; // 默认回退
58
+ }
59
+ /**
60
+ * 把客户端传入的 model / route 值解析成内部 route。
61
+ * - 如果 model 有效,直接用对应的内部 route。
62
+ * - 否则如果 route 是有效 McpRoute,返回该 route。
63
+ * - 否则返回 "SA"(与现有默认行为保持一致)。
64
+ *
65
+ * 第二返回值 true 表示 route 走的是 deprecated alias(仅使用 route 而非 model)。
66
+ */
67
+ export function resolveMcpEngine(args) {
68
+ if (isMcpModel(args.model)) {
69
+ const entry = MCP_VIDEO_MODELS[args.model];
70
+ return {
71
+ route: entry.route,
72
+ model: args.model,
73
+ engineName: entry.engineName,
74
+ providerModel: entry.providerModel,
75
+ usedRouteAlias: false,
76
+ };
77
+ }
78
+ if (isMcpRoute(typeof args.route === "string" ? args.route : "")) {
79
+ const r = args.route;
80
+ const m = getMcpModelForRoute(r);
81
+ const entry = MCP_VIDEO_MODELS[m];
82
+ return {
83
+ route: r,
84
+ model: m,
85
+ engineName: entry.engineName,
86
+ providerModel: entry.providerModel,
87
+ usedRouteAlias: true,
88
+ };
89
+ }
90
+ // 默认 SA
91
+ const entry = MCP_VIDEO_MODELS.SA;
92
+ return {
93
+ route: entry.route,
94
+ model: "SA",
95
+ engineName: entry.engineName,
96
+ providerModel: entry.providerModel,
97
+ usedRouteAlias: false,
98
+ };
99
+ }
42
100
  export function getMcpVideoStatusHint(dryRun) {
43
101
  return dryRun ? MCP_VIDEO_DRY_RUN_HINT : MCP_VIDEO_STATUS_HINT;
44
102
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infimind/video-mcp-cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "MCP Server for Sora AI Video Generator - Access video generation features through Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",