coze-video-concatenator-mcp
Version:
MCP Server for Coze video concatenation workflow
196 lines (172 loc) • 6.13 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { spawn } from 'child_process';
// 创建MCP服务器
const server = new Server({
name: 'coze-video-concatenator',
version: '1.0.0',
});
// 列出可用工具
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error('[DEBUG] ListToolsRequestSchema called');
return {
tools: [
{
name: 'run_video_concatenation_workflow',
description: '使用Coze工作流进行视频拼接,支持多个视频URL输入进行拼接',
inputSchema: {
type: 'object',
properties: {
input: {
type: 'array',
items: { type: 'string' },
description: '输入视频的URL数组,多个视频将按顺序拼接'
},
workflow_id: {
type: 'string',
description: 'Coze视频拼接工作流ID',
default: '7543475034791936027'
}
},
required: ['input']
}
}
]
};
});
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
console.error('[DEBUG] CallToolRequestSchema called:', JSON.stringify(request.params, null, 2));
const { name, arguments: args } = request.params;
try {
let result;
switch (name) {
case 'run_video_concatenation_workflow':
console.error('[DEBUG] Running video concatenation workflow');
result = await runVideoConcatenationWorkflow(args);
console.error('[DEBUG] Workflow completed successfully');
// 格式化输出结果,提取关键信息
if (result && result.data) {
try {
const data = typeof result.data === 'string' ? JSON.parse(result.data) : result.data;
if (data.output) {
result.formatted_output = {
success: true,
output_video_url: data.output,
debug_url: result.debug_url,
usage: result.usage,
message: '视频拼接完成'
};
}
} catch (e) {
console.error('[DEBUG] Failed to parse data field:', e.message);
}
}
break;
default:
result = `未知工具: ${name}`;
}
return { content: [{ type: 'text', text: typeof result === 'string' ? result : JSON.stringify(result) }] };
} catch (error) {
console.error('[ERROR] Tool execution failed:', error.message);
return {
content: [
{
type: 'text',
text: `执行错误: ${error.message}`
}
]
};
}
});
// Coze视频拼接工作流工具:根据多个视频URL进行视频拼接
async function runVideoConcatenationWorkflow({
input,
workflow_id = '7543475034791936027'
}) {
console.error('[DEBUG] runVideoConcatenationWorkflow called with:', { input, workflow_id });
const apiKey = process.env.COZE_API_KEY;
if (!apiKey) {
throw new Error('请设置 COZE_API_KEY 环境变量');
}
// 验证 input 为字符串数组
if (!Array.isArray(input)) {
throw new Error('input 必须为字符串数组');
}
const urlList = input
.map((s) => String(s).trim())
.filter((s) => s.length > 0);
if (urlList.length === 0) {
throw new Error('input 不能为空');
}
// 基础URL格式校验(简单判断)
const invalid = urlList.find((u) => !/^https?:\/\//i.test(u));
if (invalid) {
throw new Error(`检测到无效URL: ${invalid}`);
}
// 构建输入参数 - 多个视频URL保持为数组格式
console.error('[DEBUG] Final input array:', urlList);
const payload = {
parameters: {
input: urlList
},
workflow_id: workflow_id
};
console.error('[DEBUG] Sending payload to Coze API:', JSON.stringify(payload, null, 2));
return new Promise((resolve, reject) => {
const args = [
'-sS',
'-X', 'POST', 'https://api.coze.cn/v1/workflow/run',
'-H', 'Content-Type: application/json',
'-H', `Authorization: Bearer ${apiKey}`,
'-d', JSON.stringify(payload)
];
console.error('[DEBUG] Executing curl with args:', args);
const proc = spawn('curl', args, { stdio: ['ignore', 'pipe', 'pipe'] });
let stdout = '';
let stderr = '';
proc.stdout.on('data', (d) => { stdout += d.toString(); });
proc.stderr.on('data', (d) => { stderr += d.toString(); });
proc.on('close', (code) => {
console.error('[DEBUG] curl process closed with code:', code);
console.error('[DEBUG] stdout:', stdout);
console.error('[DEBUG] stderr:', stderr);
if (code !== 0) {
return reject(new Error(`curl 执行失败,退出码 ${code}, 错误: ${stderr || '无'}`));
}
try {
const json = JSON.parse(stdout);
console.error('[DEBUG] Parsed response:', JSON.stringify(json, null, 2));
// 检查API响应状态
if (json.code !== 0) {
return reject(new Error(`Coze API 返回错误: ${json.msg || '未知错误'}`));
}
resolve(json);
} catch (e) {
reject(new Error(`解析响应失败: ${e.message}; 原始输出: ${stdout}`));
}
});
proc.on('error', (err) => {
console.error('[ERROR] curl process error:', err.message);
reject(new Error(`启动curl失败: ${err.message}`));
});
});
}
// 启动服务器
async function main() {
try {
console.error('[DEBUG] Starting MCP server...');
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Coze Video Concatenator MCP Server started');
} catch (error) {
console.error('[ERROR] Failed to start server:', error.message);
process.exit(1);
}
}
main().catch((error) => {
console.error('[ERROR] Main function failed:', error.message);
process.exit(1);
});