@infimind/video-mcp-cli 1.0.6 → 1.0.7
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 +6 -7
- package/dist/server.js +47 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,13 +90,12 @@ Parameter applicability:
|
|
|
90
90
|
|
|
91
91
|
| Parameter | S2 极睿Sa专业版 | S5 极睿Sd专业版 | S6 极睿Ha专业版 |
|
|
92
92
|
| --- | --- | --- | --- |
|
|
93
|
-
| `sourceImage` | Optional single image | Not
|
|
93
|
+
| `sourceImage` | Optional single image | Not supported | Standard mode single image |
|
|
94
94
|
| `sourceImages` | Not supported | allinone / firstlast images | reference / edit reference images |
|
|
95
95
|
| `sourceVideos` | Not supported | allinone reference videos | edit input videos |
|
|
96
96
|
| `sourceAudios` | Not supported | allinone reference audio | Not supported |
|
|
97
97
|
| `taskMode` | Not needed | `allinone` / `firstlast` | `standard` / `reference` / `edit` |
|
|
98
|
-
| `resolution` | Not supported | `
|
|
99
|
-
| `inputDurationTier` | Not supported | `short` / `medium` / `long` | edit mode only |
|
|
98
|
+
| `resolution` | Not supported | `720p` / `1080p` | `720p` / `1080p` |
|
|
100
99
|
| `ratio` | Not supported | Not supported | reference or text-to-video standard mode |
|
|
101
100
|
| `generateAudio` | Not supported | Optional | Not supported |
|
|
102
101
|
| `webSearchEnabled` | Not supported | Optional | Not supported |
|
|
@@ -104,7 +103,9 @@ Parameter applicability:
|
|
|
104
103
|
|
|
105
104
|
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.
|
|
106
105
|
|
|
107
|
-
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
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.
|
|
108
109
|
|
|
109
110
|
#### Dry run before creating a task
|
|
110
111
|
|
|
@@ -148,7 +149,6 @@ Use `dryRun: true` to validate parameters, VIP access, estimated credits, and me
|
|
|
148
149
|
"seconds": 10,
|
|
149
150
|
"size": "720x1280",
|
|
150
151
|
"resolution": "720p",
|
|
151
|
-
"inputDurationTier": "medium",
|
|
152
152
|
"generateAudio": true,
|
|
153
153
|
"webSearchEnabled": false
|
|
154
154
|
}
|
|
@@ -222,8 +222,7 @@ Image-to-video:
|
|
|
222
222
|
"taskMode": "edit",
|
|
223
223
|
"sourceVideos": ["/path/to/input.mp4"],
|
|
224
224
|
"sourceImages": ["/path/to/reference.jpg"],
|
|
225
|
-
"resolution": "720p"
|
|
226
|
-
"inputDurationTier": "medium"
|
|
225
|
+
"resolution": "720p"
|
|
227
226
|
}
|
|
228
227
|
```
|
|
229
228
|
|
package/dist/server.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import { readFileSync, existsSync } from "fs";
|
|
9
|
+
import { readFileSync, existsSync, statSync } from "fs";
|
|
10
10
|
import { extname, basename } from "path";
|
|
11
11
|
import { MCP_SERVER_VERSION, getMcpDefaultTaskMode, getMcpEngineName, getMcpVideoStatusHint, isMcpRoute, isValidMcpTaskMode, } from "./videoRules.js";
|
|
12
12
|
class McpToolError extends Error {
|
|
@@ -129,12 +129,7 @@ export function createServer(token, apiBaseUrl) {
|
|
|
129
129
|
resolution: {
|
|
130
130
|
type: "string",
|
|
131
131
|
description: "S5/S6 输出清晰度(默认:720p)",
|
|
132
|
-
enum: ["
|
|
133
|
-
},
|
|
134
|
-
inputDurationTier: {
|
|
135
|
-
type: "string",
|
|
136
|
-
description: "S5 或 S6 edit 模式的输入素材时长档位:short=0-4秒、medium=5-10秒、long=11-15秒(默认:short)",
|
|
137
|
-
enum: ["short", "medium", "long"],
|
|
132
|
+
enum: ["720p", "1080p"],
|
|
138
133
|
},
|
|
139
134
|
ratio: {
|
|
140
135
|
type: "string",
|
|
@@ -155,6 +150,7 @@ export function createServer(token, apiBaseUrl) {
|
|
|
155
150
|
},
|
|
156
151
|
},
|
|
157
152
|
required: ["prompt"],
|
|
153
|
+
additionalProperties: false,
|
|
158
154
|
},
|
|
159
155
|
outputSchema: {
|
|
160
156
|
type: "object",
|
|
@@ -402,21 +398,23 @@ async function processFileInput(fileInput, expectedType) {
|
|
|
402
398
|
".webp": "image/webp",
|
|
403
399
|
".mp4": "video/mp4",
|
|
404
400
|
".mov": "video/quicktime",
|
|
405
|
-
".avi": "video/x-msvideo",
|
|
406
|
-
".mkv": "video/x-matroska",
|
|
407
401
|
".mp3": "audio/mpeg",
|
|
408
402
|
".wav": "audio/wav",
|
|
409
|
-
".m4a": "audio/mp4",
|
|
410
|
-
".aac": "audio/aac",
|
|
411
|
-
".ogg": "audio/ogg",
|
|
412
403
|
};
|
|
413
404
|
if (!(ext in mimeTypes)) {
|
|
414
|
-
throw new McpToolError(`不支持的文件类型:${ext || "无扩展名"}。请使用 jpg/png/webp/gif 图片、mp4/mov
|
|
405
|
+
throw new McpToolError(`不支持的文件类型:${ext || "无扩展名"}。请使用 jpg/png/webp/gif 图片、mp4/mov 视频或 mp3/wav 音频。`, "UNSUPPORTED_FILE_TYPE");
|
|
415
406
|
}
|
|
416
407
|
mimeType = mimeTypes[ext];
|
|
417
408
|
if (expectedType && !mimeType.startsWith(`${expectedType}/`)) {
|
|
418
409
|
throw new McpToolError(`文件 ${basename(filePath)} 类型不匹配:当前是 ${mimeType},但该字段需要 ${expectedType} 文件。`, "INVALID_FILE_TYPE");
|
|
419
410
|
}
|
|
411
|
+
const size = statSync(filePath).size;
|
|
412
|
+
if (expectedType === "video" && size > 50 * 1024 * 1024) {
|
|
413
|
+
throw new McpToolError(`视频文件 ${basename(filePath)} 超过 50MB 限制。`, "MEDIA_FILE_TOO_LARGE");
|
|
414
|
+
}
|
|
415
|
+
if (expectedType === "audio" && size > 15 * 1024 * 1024) {
|
|
416
|
+
throw new McpToolError(`音频文件 ${basename(filePath)} 超过 15MB 限制。`, "MEDIA_FILE_TOO_LARGE");
|
|
417
|
+
}
|
|
420
418
|
// 转换为 base64
|
|
421
419
|
const base64 = `data:${mimeType};base64,${buffer.toString("base64")}`;
|
|
422
420
|
return {
|
|
@@ -438,6 +436,9 @@ async function processFileInputs(fileInputs, expectedType) {
|
|
|
438
436
|
function hasItems(value) {
|
|
439
437
|
return Array.isArray(value) && value.some((item) => typeof item === "string" && item.length > 0);
|
|
440
438
|
}
|
|
439
|
+
function itemCount(value) {
|
|
440
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0).length : 0;
|
|
441
|
+
}
|
|
441
442
|
function normalizeRoute(value) {
|
|
442
443
|
const route = typeof value === "string" ? value : "S2";
|
|
443
444
|
if (!isMcpRoute(route)) {
|
|
@@ -459,13 +460,6 @@ function normalizeTaskMode(route, value) {
|
|
|
459
460
|
}
|
|
460
461
|
return mode;
|
|
461
462
|
}
|
|
462
|
-
function normalizeInputDurationTier(value) {
|
|
463
|
-
const tier = (typeof value === "string" ? value : "short");
|
|
464
|
-
if (!["short", "medium", "long"].includes(tier)) {
|
|
465
|
-
throw new McpToolError("inputDurationTier 只能是 short、medium 或 long。");
|
|
466
|
-
}
|
|
467
|
-
return tier;
|
|
468
|
-
}
|
|
469
463
|
function validateCreateVideoArgs(args) {
|
|
470
464
|
if (!args || typeof args !== "object") {
|
|
471
465
|
throw new McpToolError("请提供视频生成参数。");
|
|
@@ -473,11 +467,19 @@ function validateCreateVideoArgs(args) {
|
|
|
473
467
|
if (!args.prompt || typeof args.prompt !== "string" || !args.prompt.trim()) {
|
|
474
468
|
throw new McpToolError("prompt 不能为空。");
|
|
475
469
|
}
|
|
470
|
+
if (args.count !== undefined && (!Number.isInteger(args.count) || args.count < 1 || args.count > 12)) {
|
|
471
|
+
throw new McpToolError("count 必须是 1-12 的整数。", "INVALID_COUNT");
|
|
472
|
+
}
|
|
476
473
|
const route = normalizeRoute(args.route);
|
|
477
474
|
const taskMode = normalizeTaskMode(route, args.taskMode);
|
|
478
|
-
const inputDurationTier = normalizeInputDurationTier(args.inputDurationTier);
|
|
479
475
|
const resolution = typeof args.resolution === "string" ? args.resolution : "720p";
|
|
480
476
|
const dryRun = args.dryRun === true;
|
|
477
|
+
if (args.inputDurationTier !== undefined) {
|
|
478
|
+
throw new McpToolError("inputDurationTier 不再支持由 MCP 传入;输入视频时长档位由服务端根据素材自动计算。", "UNSUPPORTED_FIELD", route);
|
|
479
|
+
}
|
|
480
|
+
if ((route === "S5" || route === "S6") && resolution !== "720p" && resolution !== "1080p") {
|
|
481
|
+
throw new McpToolError(`${getMcpEngineName(route)} 仅支持 720p 或 1080p。`, "INVALID_RESOLUTION", route);
|
|
482
|
+
}
|
|
481
483
|
if (route === "S2" && args.seconds === 5) {
|
|
482
484
|
throw new McpToolError("极睿Sa专业版不支持 5 秒视频,请使用 10 或 15 秒。", "INVALID_SECONDS", route);
|
|
483
485
|
}
|
|
@@ -487,7 +489,6 @@ function validateCreateVideoArgs(args) {
|
|
|
487
489
|
hasItems(args.sourceVideos) ? "sourceVideos" : undefined,
|
|
488
490
|
hasItems(args.sourceAudios) ? "sourceAudios" : undefined,
|
|
489
491
|
args.resolution !== undefined ? "resolution" : undefined,
|
|
490
|
-
args.inputDurationTier !== undefined ? "inputDurationTier" : undefined,
|
|
491
492
|
args.ratio !== undefined ? "ratio" : undefined,
|
|
492
493
|
args.generateAudio !== undefined ? "generateAudio" : undefined,
|
|
493
494
|
args.webSearchEnabled !== undefined ? "webSearchEnabled" : undefined,
|
|
@@ -509,6 +510,21 @@ function validateCreateVideoArgs(args) {
|
|
|
509
510
|
if (taskMode === "firstlast" && (hasItems(args.sourceVideos) || hasItems(args.sourceAudios))) {
|
|
510
511
|
throw new McpToolError("sourceVideos/sourceAudios 仅适用于极睿Sd专业版全能参考模式;首尾帧模式只使用 sourceImages。", "UNSUPPORTED_FIELD", route);
|
|
511
512
|
}
|
|
513
|
+
if (taskMode === "firstlast" && itemCount(args.sourceImages) > 2) {
|
|
514
|
+
throw new McpToolError("极睿Sd专业版首尾帧模式最多支持 2 张图片。", "MEDIA_COUNT_EXCEEDED", route);
|
|
515
|
+
}
|
|
516
|
+
if (taskMode === "allinone" && itemCount(args.sourceImages) > 9) {
|
|
517
|
+
throw new McpToolError("极睿Sd专业版全能参考模式最多支持 9 张参考图片。", "MEDIA_COUNT_EXCEEDED", route);
|
|
518
|
+
}
|
|
519
|
+
if (taskMode === "allinone" && itemCount(args.sourceVideos) > 3) {
|
|
520
|
+
throw new McpToolError("极睿Sd专业版全能参考模式最多支持 3 个参考视频。", "MEDIA_COUNT_EXCEEDED", route);
|
|
521
|
+
}
|
|
522
|
+
if (taskMode === "allinone" && itemCount(args.sourceAudios) > 3) {
|
|
523
|
+
throw new McpToolError("极睿Sd专业版全能参考模式最多支持 3 个参考音频。", "MEDIA_COUNT_EXCEEDED", route);
|
|
524
|
+
}
|
|
525
|
+
if (taskMode === "allinone" && hasItems(args.sourceAudios) && !hasItems(args.sourceImages) && !hasItems(args.sourceVideos)) {
|
|
526
|
+
throw new McpToolError("极睿Sd专业版全能参考模式中,音频素材必须搭配至少一张参考图片或一个参考视频。", "MISSING_VISUAL_REFERENCE", route);
|
|
527
|
+
}
|
|
512
528
|
}
|
|
513
529
|
if (route === "S6") {
|
|
514
530
|
const unsupported = [
|
|
@@ -531,12 +547,20 @@ function validateCreateVideoArgs(args) {
|
|
|
531
547
|
if (taskMode === "edit" && !hasItems(args.sourceVideos)) {
|
|
532
548
|
throw new McpToolError("极睿Ha专业版视频编辑模式需要在 sourceVideos 中提供至少 1 个参考视频。", "MISSING_SOURCE_VIDEO", route);
|
|
533
549
|
}
|
|
550
|
+
if (taskMode === "reference" && itemCount(args.sourceImages) > 9) {
|
|
551
|
+
throw new McpToolError("极睿Ha专业版图片参考模式最多支持 9 张参考图片。", "MEDIA_COUNT_EXCEEDED", route);
|
|
552
|
+
}
|
|
553
|
+
if (taskMode === "edit" && itemCount(args.sourceVideos) > 1) {
|
|
554
|
+
throw new McpToolError("极睿Ha专业版视频编辑模式最多支持 1 个参考视频。", "MEDIA_COUNT_EXCEEDED", route);
|
|
555
|
+
}
|
|
556
|
+
if (taskMode === "edit" && itemCount(args.sourceImages) > 5) {
|
|
557
|
+
throw new McpToolError("极睿Ha专业版视频编辑模式最多支持 5 张参考图片。", "MEDIA_COUNT_EXCEEDED", route);
|
|
558
|
+
}
|
|
534
559
|
}
|
|
535
560
|
return {
|
|
536
561
|
route,
|
|
537
562
|
taskMode,
|
|
538
563
|
resolution,
|
|
539
|
-
inputDurationTier,
|
|
540
564
|
dryRun,
|
|
541
565
|
statusHint: getMcpVideoStatusHint(dryRun),
|
|
542
566
|
};
|
|
@@ -561,7 +585,6 @@ async function handleCreateVideoTask(apiBaseUrl, token, args) {
|
|
|
561
585
|
taskMode: normalized.taskMode,
|
|
562
586
|
isFirstLastMode: normalized.taskMode === "firstlast",
|
|
563
587
|
resolution: normalized.resolution,
|
|
564
|
-
inputDurationTier: normalized.inputDurationTier,
|
|
565
588
|
ratio: args.ratio || undefined,
|
|
566
589
|
generateAudio: args.generateAudio,
|
|
567
590
|
webSearchEnabled: args.webSearchEnabled,
|
package/package.json
CHANGED