@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 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 recommended | Standard mode single image |
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 | `480p` / `720p` / `1080p` | `720p` / `1080p` |
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
- 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`.
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: ["480p", "720p", "1080p"],
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/avi/mkv 视频或 mp3/wav/m4a/aac/ogg 音频。`, "UNSUPPORTED_FILE_TYPE");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infimind/video-mcp-cli",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
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",