@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 +12 -8
- package/dist/server.js +64 -42
- package/dist/videoRules.d.ts +33 -1
- package/dist/videoRules.js +60 -2
- package/package.json +1 -1
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.
|
|
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
|
|
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,
|
|
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": "
|
|
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": "
|
|
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
|
|
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
|
|
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
|
|
80
|
+
description: "可选的单张参考图片。S2 使用此字段;S6 标准模式可用作单图图生视频。可以是本地文件路径或公网 URL",
|
|
82
81
|
},
|
|
83
82
|
sourceImages: {
|
|
84
83
|
type: "array",
|
|
85
|
-
description: "S5/S6 多张参考图片。本地文件路径或公网 URL
|
|
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
|
|
89
|
+
description: "S5 全能参考或 S6 视频编辑模式的视频素材。本地文件路径或公网 URL 数组",
|
|
91
90
|
items: { type: "string" },
|
|
92
91
|
},
|
|
93
92
|
sourceAudios: {
|
|
94
93
|
type: "array",
|
|
95
|
-
description: "S5 全能参考模式的音频素材。本地文件路径或公网 URL
|
|
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: "
|
|
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
|
|
260
|
+
"支持本地文件路径或公网 URL。",
|
|
249
261
|
inputSchema: {
|
|
250
262
|
type: "object",
|
|
251
263
|
properties: {
|
|
252
264
|
videoFile: {
|
|
253
265
|
type: "string",
|
|
254
|
-
description: "
|
|
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 || "
|
|
365
|
-
`- **邮箱**: ${response.email || "
|
|
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(
|
|
464
|
-
|
|
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("
|
|
488
|
+
throw new McpToolError("model 必须是 SA、SD 或 HA;route 只接受 S2、S5、S6(已废弃),请改用 model。", "INVALID_ENGINE");
|
|
467
489
|
}
|
|
468
|
-
|
|
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
|
|
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,
|
package/dist/videoRules.d.ts
CHANGED
|
@@ -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.
|
|
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;
|
package/dist/videoRules.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const MCP_SERVER_VERSION = "1.0.
|
|
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.
|
|
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