@infimind/video-mcp-cli 1.0.10 → 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 +3 -3
- package/dist/server.js +36 -28
- package/dist/videoRules.d.ts +32 -0
- package/dist/videoRules.js +59 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,7 +135,7 @@ Use `dryRun: true` to validate parameters, VIP access, media rules, and estimate
|
|
|
135
135
|
|
|
136
136
|
```json
|
|
137
137
|
{
|
|
138
|
-
"prompt": "
|
|
138
|
+
"prompt": "@image1 作为角色参考,生成自然的走秀镜头",
|
|
139
139
|
"route": "S6",
|
|
140
140
|
"taskMode": "reference",
|
|
141
141
|
"sourceImages": ["/path/to/character.jpg"],
|
|
@@ -235,7 +235,7 @@ Image-to-video:
|
|
|
235
235
|
|
|
236
236
|
```json
|
|
237
237
|
{
|
|
238
|
-
"prompt": "
|
|
238
|
+
"prompt": "@image1 和 @image2 作为角色参考,生成自然的走秀镜头",
|
|
239
239
|
"route": "S6",
|
|
240
240
|
"taskMode": "reference",
|
|
241
241
|
"sourceImages": ["/path/to/character-1.jpg", "/path/to/character-2.jpg"],
|
|
@@ -335,7 +335,7 @@ Set `SORA_API_URL=http://localhost:3000/api` when testing against a local app se
|
|
|
335
335
|
- If a client reports schema mismatch, run `npm run build` and `npm run test:smoke` in `mcp-server`.
|
|
336
336
|
- If `get_video_status` cannot connect, verify `SORA_API_URL` includes `/api`, for example `http://localhost:3000/api`.
|
|
337
337
|
- If S5/S6 returns VIP errors, confirm the MCP token belongs to a VIP user.
|
|
338
|
-
- 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.
|
|
339
339
|
- If local file inputs fail, use absolute paths and supported media types: jpg/png/webp/gif, mp4/mov, mp3/wav.
|
|
340
340
|
|
|
341
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;
|
|
@@ -125,10 +125,16 @@ export function createServer(token, apiBaseUrl) {
|
|
|
125
125
|
minimum: 1,
|
|
126
126
|
maximum: 12,
|
|
127
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
|
+
},
|
|
128
133
|
route: {
|
|
129
134
|
type: "string",
|
|
130
|
-
description: "
|
|
135
|
+
description: "内部线路代码(已废弃,请改用 model)。兼容老客户端:S2=极睿Sa专业版、S5=极睿Sd专业版、S6=极睿Ha专业版",
|
|
131
136
|
enum: ["S2", "S5", "S6"],
|
|
137
|
+
deprecated: true,
|
|
132
138
|
},
|
|
133
139
|
taskMode: {
|
|
134
140
|
type: "string",
|
|
@@ -166,8 +172,10 @@ export function createServer(token, apiBaseUrl) {
|
|
|
166
172
|
properties: {
|
|
167
173
|
success: { type: "boolean" },
|
|
168
174
|
taskId: { type: "number" },
|
|
175
|
+
model: { type: "string" },
|
|
169
176
|
route: { type: "string" },
|
|
170
177
|
engineName: { type: "string" },
|
|
178
|
+
usedRouteAlias: { type: "boolean" },
|
|
171
179
|
taskMode: { type: "string" },
|
|
172
180
|
videoCount: { type: "number" },
|
|
173
181
|
dryRun: { type: "boolean" },
|
|
@@ -209,10 +217,7 @@ export function createServer(token, apiBaseUrl) {
|
|
|
209
217
|
totalVideos: { type: "number" },
|
|
210
218
|
completedVideos: { type: "number" },
|
|
211
219
|
estimatedCredits: { type: "number" },
|
|
212
|
-
chargedVideos: { type: "number" },
|
|
213
|
-
actualRoute: { type: "string" },
|
|
214
220
|
taskMode: { type: ["string", "null"] },
|
|
215
|
-
retryCount: { type: "number" },
|
|
216
221
|
statusHint: { type: "string" },
|
|
217
222
|
createdAt: { type: ["string", "null"] },
|
|
218
223
|
updatedAt: { type: ["string", "null"] },
|
|
@@ -238,9 +243,6 @@ export function createServer(token, apiBaseUrl) {
|
|
|
238
243
|
errorCode: { type: ["string", "null"] },
|
|
239
244
|
errorCategory: { type: ["string", "null"] },
|
|
240
245
|
retryable: { type: ["boolean", "null"] },
|
|
241
|
-
actualRoute: { type: ["string", "null"] },
|
|
242
|
-
retryCount: { type: "number" },
|
|
243
|
-
creditsCharged: { type: "boolean" },
|
|
244
246
|
createdAt: { type: ["string", "null"] },
|
|
245
247
|
completedAt: { type: ["string", "null"] },
|
|
246
248
|
},
|
|
@@ -470,12 +472,27 @@ function hasItems(value) {
|
|
|
470
472
|
function itemCount(value) {
|
|
471
473
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0).length : 0;
|
|
472
474
|
}
|
|
473
|
-
function normalizeRoute(
|
|
474
|
-
|
|
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";
|
|
475
487
|
if (!isMcpRoute(route)) {
|
|
476
|
-
throw new McpToolError("
|
|
488
|
+
throw new McpToolError("model 必须是 SA、SD 或 HA;route 只接受 S2、S5、S6(已废弃),请改用 model。", "INVALID_ENGINE");
|
|
477
489
|
}
|
|
478
|
-
|
|
490
|
+
const m = route === "S2" ? "SA" : route === "S5" ? "SD" : "HA";
|
|
491
|
+
return {
|
|
492
|
+
route,
|
|
493
|
+
model: m,
|
|
494
|
+
usedRouteAlias: true,
|
|
495
|
+
};
|
|
479
496
|
}
|
|
480
497
|
function normalizeTaskMode(route, value) {
|
|
481
498
|
const mode = typeof value === "string" ? value : getMcpDefaultTaskMode(route);
|
|
@@ -501,7 +518,8 @@ function validateCreateVideoArgs(args) {
|
|
|
501
518
|
if (args.count !== undefined && (!Number.isInteger(args.count) || args.count < 1 || args.count > 12)) {
|
|
502
519
|
throw new McpToolError("count 必须是 1-12 的整数。", "INVALID_COUNT");
|
|
503
520
|
}
|
|
504
|
-
const
|
|
521
|
+
const norm = normalizeRoute(args);
|
|
522
|
+
const route = norm.route;
|
|
505
523
|
const taskMode = normalizeTaskMode(route, args.taskMode);
|
|
506
524
|
const resolution = typeof args.resolution === "string" ? args.resolution : "720p";
|
|
507
525
|
const dryRun = args.dryRun === true;
|
|
@@ -587,6 +605,8 @@ function validateCreateVideoArgs(args) {
|
|
|
587
605
|
}
|
|
588
606
|
return {
|
|
589
607
|
route,
|
|
608
|
+
model: norm.model,
|
|
609
|
+
usedRouteAlias: norm.usedRouteAlias,
|
|
590
610
|
taskMode,
|
|
591
611
|
resolution,
|
|
592
612
|
dryRun,
|
|
@@ -609,6 +629,7 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
|
|
|
609
629
|
seconds: args.seconds || 10,
|
|
610
630
|
language: args.language || "zh-CN",
|
|
611
631
|
count: args.count || 1,
|
|
632
|
+
model: normalized.model,
|
|
612
633
|
route: normalized.route,
|
|
613
634
|
dryRun: normalized.dryRun,
|
|
614
635
|
};
|
|
@@ -663,8 +684,10 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
|
|
|
663
684
|
structuredContent: {
|
|
664
685
|
success: response.success,
|
|
665
686
|
taskId: response.taskId,
|
|
687
|
+
model: response.model ?? normalized.model,
|
|
666
688
|
route,
|
|
667
689
|
engineName,
|
|
690
|
+
usedRouteAlias: normalized.usedRouteAlias,
|
|
668
691
|
taskMode,
|
|
669
692
|
videoCount: response.videoCount,
|
|
670
693
|
dryRun,
|
|
@@ -693,10 +716,7 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
|
|
|
693
716
|
`- **进度**: ${response.progress}% (${response.completedVideos}/${response.totalVideos} 个视频)`,
|
|
694
717
|
response.engineName ? `- **生成引擎**: ${response.engineName}` : undefined,
|
|
695
718
|
response.taskMode ? `- **模式**: ${response.taskMode}` : undefined,
|
|
696
|
-
response.actualRoute ? `- **实际线路**: ${response.actualRoute}` : undefined,
|
|
697
719
|
typeof response.estimatedCredits === "number" ? `- **预估积分**: ${response.estimatedCredits}` : undefined,
|
|
698
|
-
typeof response.chargedVideos === "number" ? `- **已扣费视频**: ${response.chargedVideos}/${response.totalVideos}` : undefined,
|
|
699
|
-
typeof response.retryCount === "number" ? `- **重试次数**: ${response.retryCount}` : undefined,
|
|
700
720
|
``,
|
|
701
721
|
].filter((line) => line !== undefined);
|
|
702
722
|
if (response.errorMessage) {
|
|
@@ -726,15 +746,6 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
|
|
|
726
746
|
if (video.errorCode) {
|
|
727
747
|
result.push(` 错误代码: ${video.errorCode}`);
|
|
728
748
|
}
|
|
729
|
-
if (video.actualRoute) {
|
|
730
|
-
result.push(` 实际线路: ${video.actualRoute}`);
|
|
731
|
-
}
|
|
732
|
-
if (typeof video.retryCount === "number" && video.retryCount > 0) {
|
|
733
|
-
result.push(` 重试次数: ${video.retryCount}`);
|
|
734
|
-
}
|
|
735
|
-
if (typeof video.creditsCharged === "boolean") {
|
|
736
|
-
result.push(` 已扣费: ${video.creditsCharged ? "是" : "否"}`);
|
|
737
|
-
}
|
|
738
749
|
});
|
|
739
750
|
}
|
|
740
751
|
if (response.statusHint) {
|
|
@@ -757,9 +768,6 @@ async function handleGetVideoStatus(apiBaseUrl, token, args) {
|
|
|
757
768
|
route: response.route,
|
|
758
769
|
engineName: response.engineName,
|
|
759
770
|
taskMode: response.taskMode,
|
|
760
|
-
actualRoute: response.actualRoute,
|
|
761
|
-
retryCount: response.retryCount,
|
|
762
|
-
chargedVideos: response.chargedVideos,
|
|
763
771
|
estimatedCredits: response.estimatedCredits,
|
|
764
772
|
statusHint: response.statusHint,
|
|
765
773
|
createdAt: response.createdAt,
|
package/dist/videoRules.d.ts
CHANGED
|
@@ -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
|
@@ -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