mirror of
https://github.com/sun-guannan/CapCutAPI.git
synced 2025-11-25 03:15:00 +08:00
479 lines
23 KiB
Python
479 lines
23 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
CapCut API MCP Server (Complete Version)
|
|||
|
|
|
|||
|
|
完整版本的MCP服务器,集成所有CapCut API接口
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import sys
|
|||
|
|
import os
|
|||
|
|
import json
|
|||
|
|
import traceback
|
|||
|
|
import io
|
|||
|
|
import contextlib
|
|||
|
|
from typing import Any, Dict, List, Optional
|
|||
|
|
|
|||
|
|
# 添加项目根目录到Python路径
|
|||
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|||
|
|
|
|||
|
|
# 导入CapCut API功能
|
|||
|
|
try:
|
|||
|
|
from create_draft import get_or_create_draft
|
|||
|
|
from add_text_impl import add_text_impl
|
|||
|
|
from add_video_track import add_video_track
|
|||
|
|
from add_audio_track import add_audio_track
|
|||
|
|
from add_image_impl import add_image_impl
|
|||
|
|
from add_subtitle_impl import add_subtitle_impl
|
|||
|
|
from add_effect_impl import add_effect_impl
|
|||
|
|
from add_sticker_impl import add_sticker_impl
|
|||
|
|
from add_video_keyframe_impl import add_video_keyframe_impl
|
|||
|
|
from get_duration_impl import get_video_duration
|
|||
|
|
from save_draft_impl import save_draft_impl
|
|||
|
|
from pyJianYingDraft.text_segment import TextStyleRange
|
|||
|
|
CAPCUT_AVAILABLE = True
|
|||
|
|
except ImportError as e:
|
|||
|
|
print(f"Warning: Could not import CapCut modules: {e}", file=sys.stderr)
|
|||
|
|
CAPCUT_AVAILABLE = False
|
|||
|
|
|
|||
|
|
# 完整的工具定义
|
|||
|
|
TOOLS = [
|
|||
|
|
{
|
|||
|
|
"name": "create_draft",
|
|||
|
|
"description": "创建新的CapCut草稿",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_video",
|
|||
|
|
"description": "添加视频到草稿,支持转场、蒙版、背景模糊等效果",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"video_url": {"type": "string", "description": "视频URL"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"start": {"type": "number", "default": 0, "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "description": "结束时间(秒)"},
|
|||
|
|
"target_start": {"type": "number", "default": 0, "description": "目标开始时间(秒)"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"},
|
|||
|
|
"transform_x": {"type": "number", "default": 0, "description": "X轴位置"},
|
|||
|
|
"transform_y": {"type": "number", "default": 0, "description": "Y轴位置"},
|
|||
|
|
"scale_x": {"type": "number", "default": 1, "description": "X轴缩放"},
|
|||
|
|
"scale_y": {"type": "number", "default": 1, "description": "Y轴缩放"},
|
|||
|
|
"speed": {"type": "number", "default": 1.0, "description": "播放速度"},
|
|||
|
|
"track_name": {"type": "string", "default": "main", "description": "轨道名称"},
|
|||
|
|
"volume": {"type": "number", "default": 1.0, "description": "音量"},
|
|||
|
|
"transition": {"type": "string", "description": "转场类型"},
|
|||
|
|
"transition_duration": {"type": "number", "default": 0.5, "description": "转场时长"},
|
|||
|
|
"mask_type": {"type": "string", "description": "蒙版类型"},
|
|||
|
|
"background_blur": {"type": "integer", "description": "背景模糊级别(1-4)"}
|
|||
|
|
},
|
|||
|
|
"required": ["video_url"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_audio",
|
|||
|
|
"description": "添加音频到草稿,支持音效处理",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"audio_url": {"type": "string", "description": "音频URL"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"start": {"type": "number", "default": 0, "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "description": "结束时间(秒)"},
|
|||
|
|
"target_start": {"type": "number", "default": 0, "description": "目标开始时间(秒)"},
|
|||
|
|
"volume": {"type": "number", "default": 1.0, "description": "音量"},
|
|||
|
|
"speed": {"type": "number", "default": 1.0, "description": "播放速度"},
|
|||
|
|
"track_name": {"type": "string", "default": "audio_main", "description": "轨道名称"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"}
|
|||
|
|
},
|
|||
|
|
"required": ["audio_url"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_image",
|
|||
|
|
"description": "添加图片到草稿,支持动画、转场、蒙版等效果",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"image_url": {"type": "string", "description": "图片URL"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"start": {"type": "number", "default": 0, "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "default": 3.0, "description": "结束时间(秒)"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"},
|
|||
|
|
"transform_x": {"type": "number", "default": 0, "description": "X轴位置"},
|
|||
|
|
"transform_y": {"type": "number", "default": 0, "description": "Y轴位置"},
|
|||
|
|
"scale_x": {"type": "number", "default": 1, "description": "X轴缩放"},
|
|||
|
|
"scale_y": {"type": "number", "default": 1, "description": "Y轴缩放"},
|
|||
|
|
"track_name": {"type": "string", "default": "main", "description": "轨道名称"},
|
|||
|
|
"intro_animation": {"type": "string", "description": "入场动画"},
|
|||
|
|
"outro_animation": {"type": "string", "description": "出场动画"},
|
|||
|
|
"transition": {"type": "string", "description": "转场类型"},
|
|||
|
|
"mask_type": {"type": "string", "description": "蒙版类型"}
|
|||
|
|
},
|
|||
|
|
"required": ["image_url"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_text",
|
|||
|
|
"description": "添加文本到草稿,支持文本多样式、文字阴影和文字背景",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"text": {"type": "string", "description": "文本内容"},
|
|||
|
|
"start": {"type": "number", "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "description": "结束时间(秒)"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"font_color": {"type": "string", "default": "#ffffff", "description": "字体颜色"},
|
|||
|
|
"font_size": {"type": "integer", "default": 24, "description": "字体大小"},
|
|||
|
|
"shadow_enabled": {"type": "boolean", "default": False, "description": "是否启用文字阴影"},
|
|||
|
|
"shadow_color": {"type": "string", "default": "#000000", "description": "阴影颜色"},
|
|||
|
|
"shadow_alpha": {"type": "number", "default": 0.8, "description": "阴影透明度"},
|
|||
|
|
"shadow_angle": {"type": "number", "default": 315.0, "description": "阴影角度"},
|
|||
|
|
"shadow_distance": {"type": "number", "default": 5.0, "description": "阴影距离"},
|
|||
|
|
"shadow_smoothing": {"type": "number", "default": 0.0, "description": "阴影平滑度"},
|
|||
|
|
"background_color": {"type": "string", "description": "背景颜色"},
|
|||
|
|
"background_alpha": {"type": "number", "default": 1.0, "description": "背景透明度"},
|
|||
|
|
"background_style": {"type": "integer", "default": 0, "description": "背景样式"},
|
|||
|
|
"background_round_radius": {"type": "number", "default": 0.0, "description": "背景圆角半径"},
|
|||
|
|
"text_styles": {"type": "array", "description": "文本多样式配置列表"}
|
|||
|
|
},
|
|||
|
|
"required": ["text", "start", "end"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_subtitle",
|
|||
|
|
"description": "添加字幕到草稿,支持SRT文件和样式设置",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"srt_path": {"type": "string", "description": "SRT字幕文件路径或URL"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"track_name": {"type": "string", "default": "subtitle", "description": "轨道名称"},
|
|||
|
|
"time_offset": {"type": "number", "default": 0, "description": "时间偏移(秒)"},
|
|||
|
|
"font": {"type": "string", "description": "字体"},
|
|||
|
|
"font_size": {"type": "number", "default": 8.0, "description": "字体大小"},
|
|||
|
|
"font_color": {"type": "string", "default": "#FFFFFF", "description": "字体颜色"},
|
|||
|
|
"bold": {"type": "boolean", "default": False, "description": "是否粗体"},
|
|||
|
|
"italic": {"type": "boolean", "default": False, "description": "是否斜体"},
|
|||
|
|
"underline": {"type": "boolean", "default": False, "description": "是否下划线"},
|
|||
|
|
"border_width": {"type": "number", "default": 0.0, "description": "边框宽度"},
|
|||
|
|
"border_color": {"type": "string", "default": "#000000", "description": "边框颜色"},
|
|||
|
|
"background_color": {"type": "string", "default": "#000000", "description": "背景颜色"},
|
|||
|
|
"background_alpha": {"type": "number", "default": 0.0, "description": "背景透明度"},
|
|||
|
|
"transform_x": {"type": "number", "default": 0.0, "description": "X轴位置"},
|
|||
|
|
"transform_y": {"type": "number", "default": -0.8, "description": "Y轴位置"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"}
|
|||
|
|
},
|
|||
|
|
"required": ["srt_path"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_effect",
|
|||
|
|
"description": "添加特效到草稿",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"effect_type": {"type": "string", "description": "特效类型名称"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"start": {"type": "number", "default": 0, "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "default": 3.0, "description": "结束时间(秒)"},
|
|||
|
|
"track_name": {"type": "string", "default": "effect_01", "description": "轨道名称"},
|
|||
|
|
"params": {"type": "array", "description": "特效参数列表"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"}
|
|||
|
|
},
|
|||
|
|
"required": ["effect_type"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_sticker",
|
|||
|
|
"description": "添加贴纸到草稿",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"resource_id": {"type": "string", "description": "贴纸资源ID"},
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"start": {"type": "number", "description": "开始时间(秒)"},
|
|||
|
|
"end": {"type": "number", "description": "结束时间(秒)"},
|
|||
|
|
"transform_x": {"type": "number", "default": 0, "description": "X轴位置"},
|
|||
|
|
"transform_y": {"type": "number", "default": 0, "description": "Y轴位置"},
|
|||
|
|
"scale_x": {"type": "number", "default": 1.0, "description": "X轴缩放"},
|
|||
|
|
"scale_y": {"type": "number", "default": 1.0, "description": "Y轴缩放"},
|
|||
|
|
"alpha": {"type": "number", "default": 1.0, "description": "透明度"},
|
|||
|
|
"rotation": {"type": "number", "default": 0.0, "description": "旋转角度"},
|
|||
|
|
"track_name": {"type": "string", "default": "sticker_main", "description": "轨道名称"},
|
|||
|
|
"width": {"type": "integer", "default": 1080, "description": "视频宽度"},
|
|||
|
|
"height": {"type": "integer", "default": 1920, "description": "视频高度"}
|
|||
|
|
},
|
|||
|
|
"required": ["resource_id", "start", "end"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "add_video_keyframe",
|
|||
|
|
"description": "添加视频关键帧,支持位置、缩放、旋转、透明度等属性动画",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"},
|
|||
|
|
"track_name": {"type": "string", "default": "main", "description": "轨道名称"},
|
|||
|
|
"property_type": {"type": "string", "description": "关键帧属性类型(position_x, position_y, rotation, scale_x, scale_y, uniform_scale, alpha, saturation, contrast, brightness, volume)"},
|
|||
|
|
"time": {"type": "number", "default": 0.0, "description": "关键帧时间点(秒)"},
|
|||
|
|
"value": {"type": "string", "description": "关键帧值"},
|
|||
|
|
"property_types": {"type": "array", "description": "批量模式:关键帧属性类型列表"},
|
|||
|
|
"times": {"type": "array", "description": "批量模式:关键帧时间点列表"},
|
|||
|
|
"values": {"type": "array", "description": "批量模式:关键帧值列表"}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "get_video_duration",
|
|||
|
|
"description": "获取视频时长",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"video_url": {"type": "string", "description": "视频URL"}
|
|||
|
|
},
|
|||
|
|
"required": ["video_url"]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "save_draft",
|
|||
|
|
"description": "保存草稿",
|
|||
|
|
"inputSchema": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"draft_id": {"type": "string", "description": "草稿ID"}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
@contextlib.contextmanager
|
|||
|
|
def capture_stdout():
|
|||
|
|
"""捕获标准输出,防止CapCut API的调试信息干扰JSON响应"""
|
|||
|
|
old_stdout = sys.stdout
|
|||
|
|
sys.stdout = io.StringIO()
|
|||
|
|
try:
|
|||
|
|
yield sys.stdout
|
|||
|
|
finally:
|
|||
|
|
sys.stdout = old_stdout
|
|||
|
|
|
|||
|
|
def convert_text_styles(text_styles_data):
|
|||
|
|
"""将字典格式的text_styles转换为TextStyleRange对象列表"""
|
|||
|
|
if not text_styles_data:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
text_style_ranges = []
|
|||
|
|
for style_dict in text_styles_data:
|
|||
|
|
style_range = TextStyleRange(
|
|||
|
|
start=style_dict.get("start", 0),
|
|||
|
|
end=style_dict.get("end", 0),
|
|||
|
|
font_size=style_dict.get("font_size"),
|
|||
|
|
font_color=style_dict.get("font_color"),
|
|||
|
|
bold=style_dict.get("bold", False),
|
|||
|
|
italic=style_dict.get("italic", False),
|
|||
|
|
underline=style_dict.get("underline", False)
|
|||
|
|
)
|
|||
|
|
text_style_ranges.append(style_range)
|
|||
|
|
return text_style_ranges
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Error converting text_styles: {e}", file=sys.stderr)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def execute_tool(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|||
|
|
"""执行具体的工具"""
|
|||
|
|
try:
|
|||
|
|
print(f"[DEBUG] Executing tool: {tool_name} with args: {arguments}", file=sys.stderr)
|
|||
|
|
|
|||
|
|
if not CAPCUT_AVAILABLE:
|
|||
|
|
return {"success": False, "error": "CapCut modules not available"}
|
|||
|
|
|
|||
|
|
# 捕获标准输出,防止调试信息干扰
|
|||
|
|
with capture_stdout() as captured:
|
|||
|
|
if tool_name == "create_draft":
|
|||
|
|
draft_id, script = get_or_create_draft(
|
|||
|
|
width=arguments.get("width", 1080),
|
|||
|
|
height=arguments.get("height", 1920)
|
|||
|
|
)
|
|||
|
|
result = {
|
|||
|
|
"draft_id": str(draft_id),
|
|||
|
|
"draft_url": f"https://www.install-ai-guider.top/draft/downloader?draft_id={draft_id}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
elif tool_name == "add_video":
|
|||
|
|
result = add_video_track(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_audio":
|
|||
|
|
result = add_audio_track(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_image":
|
|||
|
|
result = add_image_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_text":
|
|||
|
|
# 处理text_styles参数
|
|||
|
|
text_styles_converted = None
|
|||
|
|
if "text_styles" in arguments and arguments["text_styles"]:
|
|||
|
|
text_styles_converted = convert_text_styles(arguments["text_styles"])
|
|||
|
|
arguments["text_styles"] = text_styles_converted
|
|||
|
|
|
|||
|
|
result = add_text_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_subtitle":
|
|||
|
|
result = add_subtitle_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_effect":
|
|||
|
|
result = add_effect_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_sticker":
|
|||
|
|
result = add_sticker_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "add_video_keyframe":
|
|||
|
|
result = add_video_keyframe_impl(**arguments)
|
|||
|
|
|
|||
|
|
elif tool_name == "get_video_duration":
|
|||
|
|
duration = get_video_duration(arguments["video_url"])
|
|||
|
|
result = {"duration": duration}
|
|||
|
|
|
|||
|
|
elif tool_name == "save_draft":
|
|||
|
|
save_result = save_draft_impl(**arguments)
|
|||
|
|
if isinstance(save_result, dict) and "draft_url" in save_result:
|
|||
|
|
result = {"draft_url": save_result["draft_url"]}
|
|||
|
|
else:
|
|||
|
|
result = {"draft_url": f"https://www.install-ai-guider.top/draft/downloader?draft_id=unknown"}
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
return {"success": False, "error": f"Unknown tool: {tool_name}"}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"result": result,
|
|||
|
|
"features_used": {
|
|||
|
|
"shadow": arguments.get("shadow_enabled", False) if tool_name == "add_text" else False,
|
|||
|
|
"background": bool(arguments.get("background_color")) if tool_name == "add_text" else False,
|
|||
|
|
"multi_style": bool(arguments.get("text_styles")) if tool_name == "add_text" else False
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Tool execution error: {e}", file=sys.stderr)
|
|||
|
|
print(f"[ERROR] Traceback: {traceback.format_exc()}", file=sys.stderr)
|
|||
|
|
return {"success": False, "error": str(e)}
|
|||
|
|
|
|||
|
|
def handle_request(request_data: str) -> Optional[str]:
|
|||
|
|
"""处理JSON-RPC请求"""
|
|||
|
|
try:
|
|||
|
|
request = json.loads(request_data.strip())
|
|||
|
|
print(f"[DEBUG] Received request: {request.get('method', 'unknown')}", file=sys.stderr)
|
|||
|
|
|
|||
|
|
if request.get("method") == "initialize":
|
|||
|
|
response = {
|
|||
|
|
"jsonrpc": "2.0",
|
|||
|
|
"id": request.get("id"),
|
|||
|
|
"result": {
|
|||
|
|
"protocolVersion": "2024-11-05",
|
|||
|
|
"capabilities": {
|
|||
|
|
"experimental": {},
|
|||
|
|
"tools": {"listChanged": False}
|
|||
|
|
},
|
|||
|
|
"serverInfo": {
|
|||
|
|
"name": "capcut-api",
|
|||
|
|
"version": "1.12.3"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return json.dumps(response)
|
|||
|
|
|
|||
|
|
elif request.get("method") == "notifications/initialized":
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
elif request.get("method") == "tools/list":
|
|||
|
|
response = {
|
|||
|
|
"jsonrpc": "2.0",
|
|||
|
|
"id": request.get("id"),
|
|||
|
|
"result": {"tools": TOOLS}
|
|||
|
|
}
|
|||
|
|
return json.dumps(response)
|
|||
|
|
|
|||
|
|
elif request.get("method") == "tools/call":
|
|||
|
|
tool_name = request["params"]["name"]
|
|||
|
|
arguments = request["params"].get("arguments", {})
|
|||
|
|
|
|||
|
|
result = execute_tool(tool_name, arguments)
|
|||
|
|
|
|||
|
|
response = {
|
|||
|
|
"jsonrpc": "2.0",
|
|||
|
|
"id": request.get("id"),
|
|||
|
|
"result": {
|
|||
|
|
"content": [
|
|||
|
|
{
|
|||
|
|
"type": "text",
|
|||
|
|
"text": json.dumps(result, ensure_ascii=False, indent=2)
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return json.dumps(response)
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
error_response = {
|
|||
|
|
"jsonrpc": "2.0",
|
|||
|
|
"id": request.get("id"),
|
|||
|
|
"error": {"code": -32601, "message": "Method not found"}
|
|||
|
|
}
|
|||
|
|
return json.dumps(error_response)
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Request handling error: {e}", file=sys.stderr)
|
|||
|
|
print(f"[ERROR] Traceback: {traceback.format_exc()}", file=sys.stderr)
|
|||
|
|
error_response = {
|
|||
|
|
"jsonrpc": "2.0",
|
|||
|
|
"id": None,
|
|||
|
|
"error": {"code": 0, "message": str(e)}
|
|||
|
|
}
|
|||
|
|
return json.dumps(error_response)
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""主函数"""
|
|||
|
|
print("🚀 Starting CapCut API MCP Server (Complete Version)...", file=sys.stderr)
|
|||
|
|
print(f"📋 Available tools: {len(TOOLS)} tools loaded", file=sys.stderr)
|
|||
|
|
print("✨ Features: 视频、音频、图片、文本、字幕、特效、贴纸、关键帧", file=sys.stderr)
|
|||
|
|
print("🔌 Waiting for client connections...", file=sys.stderr)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
while True:
|
|||
|
|
try:
|
|||
|
|
line = sys.stdin.readline()
|
|||
|
|
if not line:
|
|||
|
|
print("[DEBUG] EOF received, shutting down", file=sys.stderr)
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
response = handle_request(line)
|
|||
|
|
if response:
|
|||
|
|
print(response)
|
|||
|
|
sys.stdout.flush()
|
|||
|
|
|
|||
|
|
except EOFError:
|
|||
|
|
print("[DEBUG] EOF exception, shutting down", file=sys.stderr)
|
|||
|
|
break
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Server error: {e}", file=sys.stderr)
|
|||
|
|
print(f"[ERROR] Traceback: {traceback.format_exc()}", file=sys.stderr)
|
|||
|
|
|
|||
|
|
except KeyboardInterrupt:
|
|||
|
|
print("[INFO] Server stopped by user", file=sys.stderr)
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Fatal server error: {e}", file=sys.stderr)
|
|||
|
|
print(f"[ERROR] Traceback: {traceback.format_exc()}", file=sys.stderr)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|