@infimind/video-mcp-cli 1.0.0
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 +119 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +45 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +399 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @infimind/video-mcp-cli
|
|
2
|
+
|
|
3
|
+
MCP Server for Sora AI Video Generator - Access video generation features through Claude Desktop.
|
|
4
|
+
|
|
5
|
+
## What is MCP?
|
|
6
|
+
|
|
7
|
+
MCP (Model Context Protocol) is an open protocol that enables Claude Desktop to securely connect to external data sources and tools. This MCP server allows you to interact with the Sora AI Video Generator API directly from Claude Desktop.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### Via npm (Recommended)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @infimind/video-mcp-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Via npx (No installation required)
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"sora-video-generator": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@infimind/video-mcp-cli", "--token=YOUR_TOKEN"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
### 1. Get Your API Token
|
|
33
|
+
|
|
34
|
+
1. Log in to your Sora AI Video Generator account
|
|
35
|
+
2. Navigate to "API Tokens" in the sidebar
|
|
36
|
+
3. Click "New Token" and give it a name
|
|
37
|
+
4. Copy the generated token (it will only be shown once!)
|
|
38
|
+
|
|
39
|
+
### 2. Configure Claude Desktop
|
|
40
|
+
|
|
41
|
+
Add the following to your Claude Desktop configuration file:
|
|
42
|
+
|
|
43
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
44
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"sora-video-generator": {
|
|
50
|
+
"command": "sora-mcp",
|
|
51
|
+
"args": ["--token=YOUR_TOKEN_HERE"],
|
|
52
|
+
"env": {
|
|
53
|
+
"SORA_API_URL": "https://your-api-domain.com/api/trpc"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Environment Variables
|
|
61
|
+
|
|
62
|
+
- `SORA_API_URL` (optional): Your API base URL. Defaults to `http://localhost:3000/api/trpc`
|
|
63
|
+
|
|
64
|
+
### 3. Restart Claude Desktop
|
|
65
|
+
|
|
66
|
+
After saving the configuration, restart Claude Desktop for the changes to take effect.
|
|
67
|
+
|
|
68
|
+
## Available Tools
|
|
69
|
+
|
|
70
|
+
### `get_user_info`
|
|
71
|
+
|
|
72
|
+
Get information about the authenticated user.
|
|
73
|
+
|
|
74
|
+
**Example usage in Claude:**
|
|
75
|
+
```
|
|
76
|
+
Get my user information for the video generator
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Clone the repository
|
|
83
|
+
git clone <repository-url>
|
|
84
|
+
|
|
85
|
+
# Install dependencies
|
|
86
|
+
cd mcp-server
|
|
87
|
+
npm install
|
|
88
|
+
|
|
89
|
+
# Build
|
|
90
|
+
npm run build
|
|
91
|
+
|
|
92
|
+
# Run in development mode
|
|
93
|
+
node dist/index.js --token=YOUR_TOKEN
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Publishing
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Build the package
|
|
100
|
+
npm run build
|
|
101
|
+
|
|
102
|
+
# Publish to npm
|
|
103
|
+
npm publish --access public
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Security
|
|
107
|
+
|
|
108
|
+
- Never share your API token with others
|
|
109
|
+
- Store tokens securely and rotate them periodically
|
|
110
|
+
- Use environment variables for sensitive configuration
|
|
111
|
+
- The token is transmitted over HTTPS in production
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
|
116
|
+
|
|
117
|
+
## Support
|
|
118
|
+
|
|
119
|
+
For issues and support, please visit: https://github.com/infimind/sora-video-generator
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
/**
|
|
5
|
+
* CLI Entry Point for Sora MCP Server
|
|
6
|
+
*
|
|
7
|
+
* Usage: sora-mcp --token=YOUR_TOKEN
|
|
8
|
+
*
|
|
9
|
+
* Environment Variables:
|
|
10
|
+
* SORA_API_URL - API base URL (default: http://localhost:3000/api)
|
|
11
|
+
*/
|
|
12
|
+
async function main() {
|
|
13
|
+
// Parse command line arguments
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const tokenArg = args.find(arg => arg.startsWith("--token="));
|
|
16
|
+
if (!tokenArg) {
|
|
17
|
+
console.error("Error: --token argument is required");
|
|
18
|
+
console.error("Usage: sora-mcp --token=YOUR_TOKEN");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const token = tokenArg.split("=")[1];
|
|
22
|
+
if (!token) {
|
|
23
|
+
console.error("Error: Token value is empty");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
// Get API URL from environment variable or use default
|
|
27
|
+
// Note: Default should be just the base path without /trpc
|
|
28
|
+
const apiUrl = process.env.SORA_API_URL || "http://localhost:3000/api";
|
|
29
|
+
// Create and start the MCP server
|
|
30
|
+
const server = createServer(token, apiUrl);
|
|
31
|
+
// Set up stdio transport
|
|
32
|
+
const transport = new StdioServerTransport();
|
|
33
|
+
// Connect the server to the transport
|
|
34
|
+
await server.connect(transport);
|
|
35
|
+
// Server is now running and listening for messages via stdio
|
|
36
|
+
// Graceful shutdown
|
|
37
|
+
process.on("SIGINT", async () => {
|
|
38
|
+
await server.close();
|
|
39
|
+
process.exit(0);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
main().catch((error) => {
|
|
43
|
+
console.error("Fatal error:", error);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Implementation for Sora AI Video Generator
|
|
3
|
+
*
|
|
4
|
+
* This server implements the Model Context Protocol to allow Claude Desktop
|
|
5
|
+
* to interact with the Sora AI Video Generator API.
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Create and configure the MCP Server
|
|
10
|
+
*/
|
|
11
|
+
export declare function createServer(token: string, apiBaseUrl: string): Server;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Implementation for Sora AI Video Generator
|
|
3
|
+
*
|
|
4
|
+
* This server implements the Model Context Protocol to allow Claude Desktop
|
|
5
|
+
* to interact with the Sora AI Video Generator API.
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create and configure the MCP Server
|
|
11
|
+
*/
|
|
12
|
+
export function createServer(token, apiBaseUrl) {
|
|
13
|
+
const server = new Server({
|
|
14
|
+
name: "sora-video-generator-mcp",
|
|
15
|
+
version: "1.0.0",
|
|
16
|
+
}, {
|
|
17
|
+
capabilities: {
|
|
18
|
+
tools: {},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* List available tools
|
|
23
|
+
*/
|
|
24
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
25
|
+
const tools = [
|
|
26
|
+
{
|
|
27
|
+
name: "get_user_info",
|
|
28
|
+
description: "Get information about the authenticated user, including username, name, email, role, credits, and VIP status",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "create_video_task",
|
|
36
|
+
description: "Create a video generation task. Supports local file paths or public URLs for reference images. " +
|
|
37
|
+
"The prompt will be automatically translated and optimized based on the target language.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
prompt: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "The video generation prompt describing what you want to create",
|
|
44
|
+
},
|
|
45
|
+
sourceImage: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Optional reference image - can be a local file path (e.g., /path/to/image.jpg) or a public URL",
|
|
48
|
+
},
|
|
49
|
+
model: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "Video model to use: sora_video2-15s (default) or sora-pro",
|
|
52
|
+
enum: ["sora_video2-15s", "sora-pro"],
|
|
53
|
+
},
|
|
54
|
+
size: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Video resolution (default: 1280x704)",
|
|
57
|
+
enum: ["1280x704", "1920x1080", "1080x1920", "704x1280"],
|
|
58
|
+
},
|
|
59
|
+
seconds: {
|
|
60
|
+
type: "number",
|
|
61
|
+
description: "Video duration in seconds: 10 (default) or 15",
|
|
62
|
+
enum: [10, 15],
|
|
63
|
+
},
|
|
64
|
+
language: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Target language for the video (default: zh-CN)",
|
|
67
|
+
enum: ["zh-CN", "en-US", "ja-JP", "ko-KR", "es-ES", "fr-FR"],
|
|
68
|
+
},
|
|
69
|
+
count: {
|
|
70
|
+
type: "number",
|
|
71
|
+
description: "Number of videos to generate (1-10, default: 1)",
|
|
72
|
+
minimum: 1,
|
|
73
|
+
maximum: 10,
|
|
74
|
+
},
|
|
75
|
+
route: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "API route to use: S1 (default), S2, or S3",
|
|
78
|
+
enum: ["S1", "S2", "S3"],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["prompt"],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "get_video_status",
|
|
86
|
+
description: "Get the status of a video generation task, including progress and video URLs",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {
|
|
90
|
+
taskId: {
|
|
91
|
+
type: "number",
|
|
92
|
+
description: "The video generation task ID",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ["taskId"],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "create_video_analysis_task",
|
|
100
|
+
description: "Create a video analysis task that breaks down a video into script, style, selling points, viral formula, and generates a prompt for video creation. " +
|
|
101
|
+
"Supports local file paths or public URLs for the video file.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
videoFile: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description: "Video file to analyze - can be a local file path (e.g., /path/to/video.mp4) or a public URL",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["videoFile"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "get_video_analysis_status",
|
|
115
|
+
description: "Get the status and results of a video analysis task",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
analysisId: {
|
|
120
|
+
type: "number",
|
|
121
|
+
description: "The video analysis task ID",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ["analysisId"],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
return { tools };
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* Handle tool calls
|
|
132
|
+
*/
|
|
133
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
134
|
+
const { name, arguments: args } = request.params;
|
|
135
|
+
try {
|
|
136
|
+
switch (name) {
|
|
137
|
+
case "get_user_info":
|
|
138
|
+
return await handleGetUserInfo(apiBaseUrl, token);
|
|
139
|
+
case "create_video_task":
|
|
140
|
+
return await handleCreateVideoTask(apiBaseUrl, token, args);
|
|
141
|
+
case "get_video_status":
|
|
142
|
+
return await handleGetVideoStatus(apiBaseUrl, token, args);
|
|
143
|
+
case "create_video_analysis_task":
|
|
144
|
+
return await handleCreateVideoAnalysisTask(apiBaseUrl, token, args);
|
|
145
|
+
case "get_video_analysis_status":
|
|
146
|
+
return await handleGetVideoAnalysisStatus(apiBaseUrl, token, args);
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: `Error: ${errorMessage}`,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
isError: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return server;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Handle get_user_info tool call
|
|
168
|
+
*/
|
|
169
|
+
async function handleGetUserInfo(apiBaseUrl, token) {
|
|
170
|
+
const response = await callRestApi(apiBaseUrl, "/mcp/user-info", { token }, "GET");
|
|
171
|
+
const userInfo = [
|
|
172
|
+
`# User Information`,
|
|
173
|
+
``,
|
|
174
|
+
`- **Username**: ${response.username}`,
|
|
175
|
+
`- **Name**: ${response.name || "N/A"}`,
|
|
176
|
+
`- **Email**: ${response.email || "N/A"}`,
|
|
177
|
+
`- **Role**: ${response.role === "admin" ? "Administrator" : "User"}`,
|
|
178
|
+
`- **Credits**: ${response.credits}`,
|
|
179
|
+
`- **VIP Status**: ${response.isVip ? "Yes" : "No"}`,
|
|
180
|
+
].join("\n");
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: userInfo,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Handle create_video_task tool call
|
|
192
|
+
*/
|
|
193
|
+
async function handleCreateVideoTask(apiBaseUrl, token, args) {
|
|
194
|
+
const response = await callRestApi(apiBaseUrl, "/mcp/video/create", {
|
|
195
|
+
token,
|
|
196
|
+
prompt: args.prompt,
|
|
197
|
+
sourceImage: args.sourceImage,
|
|
198
|
+
model: args.model || "sora_video2-15s",
|
|
199
|
+
size: args.size || "1280x704",
|
|
200
|
+
seconds: args.seconds || 10,
|
|
201
|
+
language: args.language || "zh-CN",
|
|
202
|
+
count: args.count || 1,
|
|
203
|
+
route: args.route || "S1",
|
|
204
|
+
}, "POST");
|
|
205
|
+
const result = [
|
|
206
|
+
`# Video Generation Task Created`,
|
|
207
|
+
``,
|
|
208
|
+
`- **Task ID**: ${response.taskId}`,
|
|
209
|
+
`- **Message**: ${response.message}`,
|
|
210
|
+
`- **Videos to Generate**: ${response.videoCount}`,
|
|
211
|
+
`- **Credits Used**: ${response.creditsUsed}`,
|
|
212
|
+
`- **Remaining Credits**: ${response.remainingCredits}`,
|
|
213
|
+
``,
|
|
214
|
+
`Use \`get_video_status\` with task ID ${response.taskId} to check the progress.`,
|
|
215
|
+
].join("\n");
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: result,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Handle get_video_status tool call
|
|
227
|
+
*/
|
|
228
|
+
async function handleGetVideoStatus(apiBaseUrl, token, args) {
|
|
229
|
+
const response = await callRestApi(apiBaseUrl, `/mcp/video/status/${args.taskId}`, { token }, "GET");
|
|
230
|
+
const statusText = response.status === "completed" ? "✅ Completed" :
|
|
231
|
+
response.status === "processing" ? "🔄 Processing" :
|
|
232
|
+
response.status === "failed" ? "❌ Failed" : `⏳ ${response.status}`;
|
|
233
|
+
let result = [
|
|
234
|
+
`# Video Generation Task Status`,
|
|
235
|
+
``,
|
|
236
|
+
`- **Task ID**: ${response.taskId}`,
|
|
237
|
+
`- **Status**: ${statusText}`,
|
|
238
|
+
`- **Progress**: ${response.progress}% (${response.completedVideos}/${response.totalVideos} videos)`,
|
|
239
|
+
``,
|
|
240
|
+
];
|
|
241
|
+
if (response.videos && response.videos.length > 0) {
|
|
242
|
+
result.push(`## Videos:`);
|
|
243
|
+
response.videos.forEach((video, index) => {
|
|
244
|
+
const videoStatus = video.status === "completed" ? "✅" :
|
|
245
|
+
video.status === "processing" ? "🔄" :
|
|
246
|
+
video.status === "failed" ? "❌" : "⏳";
|
|
247
|
+
result.push(``);
|
|
248
|
+
result.push(`${index + 1}. ${videoStatus} Video ID: ${video.id}`);
|
|
249
|
+
if (video.videoUrl) {
|
|
250
|
+
result.push(` 📹 [Video](${video.videoUrl})`);
|
|
251
|
+
}
|
|
252
|
+
if (video.thumbnailUrl) {
|
|
253
|
+
result.push(` 🖼️ [Thumbnail](${video.thumbnailUrl})`);
|
|
254
|
+
}
|
|
255
|
+
if (video.errorMessage) {
|
|
256
|
+
result.push(` ❌ Error: ${video.errorMessage}`);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
content: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: result.join("\n"),
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Handle create_video_analysis_task tool call
|
|
271
|
+
*/
|
|
272
|
+
async function handleCreateVideoAnalysisTask(apiBaseUrl, token, args) {
|
|
273
|
+
const response = await callRestApi(apiBaseUrl, "/mcp/video-analysis/create", {
|
|
274
|
+
token,
|
|
275
|
+
videoFile: args.videoFile,
|
|
276
|
+
}, "POST");
|
|
277
|
+
const result = [
|
|
278
|
+
`# Video Analysis Task Created`,
|
|
279
|
+
``,
|
|
280
|
+
`- **Analysis ID**: ${response.analysisId}`,
|
|
281
|
+
`- **Message**: ${response.message}`,
|
|
282
|
+
`- **Credits Used**: ${response.creditsUsed}`,
|
|
283
|
+
`- **Remaining Credits**: ${response.remainingCredits}`,
|
|
284
|
+
``,
|
|
285
|
+
`Use \`get_video_analysis_status\` with analysis ID ${response.analysisId} to check the progress.`,
|
|
286
|
+
].join("\n");
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
text: result,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Handle get_video_analysis_status tool call
|
|
298
|
+
*/
|
|
299
|
+
async function handleGetVideoAnalysisStatus(apiBaseUrl, token, args) {
|
|
300
|
+
const response = await callRestApi(apiBaseUrl, `/mcp/video-analysis/status/${args.analysisId}`, { token }, "GET");
|
|
301
|
+
const statusText = response.status === "completed" ? "✅ Completed" :
|
|
302
|
+
response.status === "processing" ? "🔄 Processing" :
|
|
303
|
+
response.status === "pending" ? "⏳ Pending" :
|
|
304
|
+
response.status === "failed" ? "❌ Failed" : `⏳ ${response.status}`;
|
|
305
|
+
let result = [
|
|
306
|
+
`# Video Analysis Task Status`,
|
|
307
|
+
``,
|
|
308
|
+
`- **Analysis ID**: ${response.analysisId}`,
|
|
309
|
+
`- **Status**: ${statusText}`,
|
|
310
|
+
];
|
|
311
|
+
if (response.conversionProgress !== null) {
|
|
312
|
+
result.push(`- **Conversion Progress**: ${response.conversionProgress}%`);
|
|
313
|
+
}
|
|
314
|
+
if (response.status === "completed") {
|
|
315
|
+
result.push(``);
|
|
316
|
+
result.push(`## Analysis Results:`);
|
|
317
|
+
result.push(``);
|
|
318
|
+
if (response.script) {
|
|
319
|
+
result.push(`### 📝 Video Script`);
|
|
320
|
+
result.push(response.script);
|
|
321
|
+
result.push(``);
|
|
322
|
+
}
|
|
323
|
+
if (response.style) {
|
|
324
|
+
result.push(`### 🎨 Video Style`);
|
|
325
|
+
result.push(response.style);
|
|
326
|
+
result.push(``);
|
|
327
|
+
}
|
|
328
|
+
if (response.sellingPoints) {
|
|
329
|
+
result.push(`### 🎯 Selling Points`);
|
|
330
|
+
result.push(response.sellingPoints);
|
|
331
|
+
result.push(``);
|
|
332
|
+
}
|
|
333
|
+
if (response.viralFormula) {
|
|
334
|
+
result.push(`### 💡 Viral Formula`);
|
|
335
|
+
result.push(response.viralFormula);
|
|
336
|
+
result.push(``);
|
|
337
|
+
}
|
|
338
|
+
if (response.generatedPrompt) {
|
|
339
|
+
result.push(`### 🎬 Generated Prompt (for Sora)`);
|
|
340
|
+
result.push(`\`\`\``);
|
|
341
|
+
result.push(response.generatedPrompt);
|
|
342
|
+
result.push(`\`\`\``);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
else if (response.status === "failed" && response.errorMessage) {
|
|
346
|
+
result.push(``);
|
|
347
|
+
result.push(`### ❌ Error`);
|
|
348
|
+
result.push(response.errorMessage);
|
|
349
|
+
}
|
|
350
|
+
if (response.videoFileName) {
|
|
351
|
+
result.push(``);
|
|
352
|
+
result.push(`**Source File**: ${response.videoFileName}`);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
content: [
|
|
356
|
+
{
|
|
357
|
+
type: "text",
|
|
358
|
+
text: result.join("\n"),
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Call REST API
|
|
365
|
+
*
|
|
366
|
+
* @param apiBaseUrl - Base URL of the API (e.g., http://localhost:3001/api)
|
|
367
|
+
* @param path - API path (e.g., /mcp/user-info)
|
|
368
|
+
* @param params - Query parameters or request body
|
|
369
|
+
* @param method - HTTP method (GET or POST)
|
|
370
|
+
* @returns Parsed response data
|
|
371
|
+
*/
|
|
372
|
+
async function callRestApi(apiBaseUrl, path, params, method = "GET") {
|
|
373
|
+
// Build URL
|
|
374
|
+
const baseUrl = apiBaseUrl.endsWith("/") ? apiBaseUrl.slice(0, -1) : apiBaseUrl;
|
|
375
|
+
const fullPath = path.startsWith("/") ? path : "/" + path;
|
|
376
|
+
let fullUrl = `${baseUrl}${fullPath}`;
|
|
377
|
+
// For GET requests, add params as query string
|
|
378
|
+
if (method === "GET" && Object.keys(params).length > 0) {
|
|
379
|
+
const queryParams = new URLSearchParams(params).toString();
|
|
380
|
+
fullUrl += `?${queryParams}`;
|
|
381
|
+
}
|
|
382
|
+
const response = await fetch(fullUrl, {
|
|
383
|
+
method,
|
|
384
|
+
headers: {
|
|
385
|
+
"Content-Type": "application/json",
|
|
386
|
+
},
|
|
387
|
+
body: method === "POST" ? JSON.stringify(params) : undefined,
|
|
388
|
+
});
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
const errorText = await response.text();
|
|
391
|
+
throw new Error(`API request failed with status ${response.status}: ${errorText}`);
|
|
392
|
+
}
|
|
393
|
+
const data = await response.json();
|
|
394
|
+
// Check for error in response
|
|
395
|
+
if (data.error) {
|
|
396
|
+
throw new Error(data.error.message || "API returned an error");
|
|
397
|
+
}
|
|
398
|
+
return data;
|
|
399
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@infimind/video-mcp-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Server for Sora AI Video Generator - Access video generation features through Claude Desktop",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sora-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"claude",
|
|
23
|
+
"video-generation",
|
|
24
|
+
"sora"
|
|
25
|
+
],
|
|
26
|
+
"author": "INFIMIND",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
30
|
+
"node-fetch": "^3.3.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.17.10",
|
|
34
|
+
"typescript": "^5.7.2"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|