Skip to main content
Glama

export_draft

Export video drafts as editable JianYing (CapCut) project files to local directories for further editing and production.

Instructions

导出草稿为剪映项目,导出到本地剪映的草稿路径下

Args: draft_id: 草稿ID,必须是已存在的草稿 jianying_draft_path: 导出路径

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
draft_idYes
jianying_draft_pathNo/app/output

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
dataNo
messageYes
successYes

Implementation Reference

  • Primary MCP tool handler for 'export_draft'. Registered via @mcp.tool() decorator. Validates draft existence, instantiates ExportDraft, performs export, processes logs, and returns structured ToolResponse with success status and details.
    @mcp.tool()
    def export_draft(draft_id: str, jianying_draft_path: str = OUTPUT_PATH) -> ToolResponse:
        """
        导出草稿为剪映项目,导出到本地剪映的草稿路径下
    
        Args:
            draft_id: 草稿ID,必须是已存在的草稿
            jianying_draft_path: 导出路径
        """
        try:
            # 验证草稿是否存在
            draft_data_path = os.path.join(SAVE_PATH, draft_id)
            if not os.path.exists(draft_data_path):
                return ToolResponse(
                    success=False,
                    message=f"草稿不存在: {draft_id}"
                )
    
            # 验证草稿数据文件是否存在
            draft_json_path = os.path.join(draft_data_path, "draft.json")
            if not os.path.exists(draft_json_path):
                return ToolResponse(
                    success=False,
                    message=f"草稿数据文件不存在: {draft_id}/draft.json"
                )
    
            # 创建导出器
            exporter = ExportDraft(jianying_draft_path)
    
            # 执行导出
            export_result = exporter.export(draft_id)
    
            if export_result and isinstance(export_result, dict):
                return ToolResponse(
                    success=True,
                    message="草稿导出成功",
                    data={
                        "draft_id": draft_id,
                        "output_path": export_result.get("output") + f"/{export_result.get("draft_name")}",
                        "draft_name": export_result.get("draft_name"),
                        "export_logs": export_result.get("export_logs", []),
                        "summary": export_result.get("summary", {}),
                        "processing_details": {
                            "total_operations": len(export_result.get("export_logs", [])),
                            "successful_operations": len(
                                [log for log in export_result.get("export_logs", []) if log.get("level") == "info"]),
                            "warnings": len(
                                [log for log in export_result.get("export_logs", []) if log.get("level") == "warning"]),
                            "errors": len(
                                [log for log in export_result.get("export_logs", []) if log.get("level") == "error"])
                        }
                    }
                )
            else:
                return ToolResponse(
                    success=False,
                    message="草稿导出失败,请检查草稿数据完整性"
                )
    
        except FileNotFoundError as e:
            return ToolResponse(
                success=False,
                message=f"文件不存在: {str(e)}"
            )
        except Exception as e:
            return ToolResponse(
                success=False,
                message=f"导出失败: {str(e)}"
            )
  • Core helper method ExportDraft.export() that implements the draft export logic: reads JSON data (draft/track/video/text/audio), creates pyJianYingDraft script and segments, copies material files, adds tracks and segments, and saves the Jianying draft folder structure.
    def export(self, draft_id: str) -> dict:
        """
        导出草稿
        
        Args:
            draft_id: 草稿ID
            
        Returns:
            bool: 导出是否成功
        """
        try:
            self._log(f"开始导出草稿: {draft_id}")
    
            # 验证草稿数据是否存在
            draft_data_path = f"{SAVE_PATH}/{draft_id}"
            if not os.path.exists(draft_data_path):
                raise FileNotFoundError(f"草稿数据不存在: {draft_data_path}")
    
            # 验证导出路径是否存在
            if not os.path.exists(self.output_path):
                raise FileNotFoundError(f"导出路径不存在: {self.output_path}")
    
            # 1. 读取所有数据
            draft_info = self._load_draft_data(draft_id)
            track_data = self._load_track_data(draft_id)
            video_data = self._load_video_data(draft_id)
            text_data = self._load_text_data(draft_id)
            audio_data = self._load_audio_data(draft_id)
    
            if not draft_info:
                raise FileNotFoundError(f"未找到草稿信息文件: {draft_id}/draft.json")
    
            # 2. 验证目标草稿是否已存在
            draft_name = draft_info.get("draft_name", f"Draft_{draft_id}")
            # 3. 创建草稿
            width = draft_info.get("width", 1920)
            height = draft_info.get("height", 1080)
            if os.path.exists(os.path.join(self.output_path, draft_name)):
                # 删除文件夹
                shutil.rmtree(os.path.join(self.output_path, draft_name))
            script = self.draft_folder.create_draft(draft_name, width, height)
    
            # 3. 复制素材文件
            self._copy_material_files(draft_id, draft_name)
    
            # 4. 添加轨道
            self._create_tracks(script, track_data)
    
            # 5. 处理视频片段
            video_segments = self._process_video_segments(video_data, draft_name)
    
            # 6. 处理文本片段
            text_segments = self._process_text_segments(text_data)
    
            # 7. 处理音频片段
            audio_segments = self._process_audio_segments(audio_data, draft_name)
    
            # 8. 添加视频片段到轨道
            self._add_video_segments_to_tracks(script, video_segments)
    
            # 为文本片段分配轨道
            self._add_text_segments_to_tracks(script, text_segments)
    
            # 为音频片段分配轨道
            self._add_audio_segments_to_tracks(script, audio_segments)
    
            # 9. 保存草稿
            script.save()
    
            data = {
                "output": self.output_path,
                "draft_name": draft_name,
                "export_logs": self.export_logs,
                "summary": {
                    "total_logs": len(self.export_logs),
                    "info_count": len([log for log in self.export_logs if log["level"] == "info"]),
                    "warning_count": len([log for log in self.export_logs if log["level"] == "warning"]),
                    "error_count": len([log for log in self.export_logs if log["level"] == "error"])
                }
            }
            return data
    
        except Exception as e:
            self._log(f"导出草稿失败: {e}", "error")
            import traceback
            traceback.print_exc()
            return {
                "output": self.output_path,
                "draft_name": draft_name,
                "export_logs": self.export_logs,
                "summary": {
                    "total_logs": len(self.export_logs),
                    "info_count": len([log for log in self.export_logs if log["level"] == "info"]),
                    "warning_count": len([log for log in self.export_logs if log["level"] == "warning"]),
                    "error_count": len([])
                }
            }
  • Wrapper method in Draft class for exporting draft, which delegates to ExportDraft.export().
    def export_draft(self, draft_id: str, output_path: str = ''):
        """
        导出草稿
    
        Args:
            draft_id: str, 草稿ID
            output_path: str, 导出路径,可选,默认"./output"
    
        Returns:
            str: 导出结果信息
        """
        export_manager = ExportDraft(output_path)
        return export_manager.export(draft_id)
  • The draft_tools function that sets up the MCP server and registers all draft-related tools including export_draft.
    def draft_tools(mcp: FastMCP):
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions exporting to a local path, implying a write operation, but doesn't disclose critical behavioral traits such as whether this overwrites existing files, requires specific permissions, has rate limits, or what happens on failure. The description is too sparse for a mutation tool with no annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured: a clear purpose statement followed by an 'Args:' section listing parameters with brief explanations. There's no wasted text, and information is front-loaded. However, the Chinese-only text might limit accessibility in some contexts, slightly affecting structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema (which should cover return values), no annotations, and low schema description coverage, the description is moderately complete. It covers the basic purpose and parameters but lacks behavioral details and usage context. For a mutation tool with 2 parameters, it's adequate but has clear gaps in transparency and guidelines.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema provides no parameter descriptions. The description adds some semantics: it explains that 'draft_id' must be an existing draft ID, and 'jianying_draft_path' is the export path (with a default noted in the schema). However, it doesn't fully compensate for the coverage gap—e.g., no details on path format or constraints. With 0% coverage, baseline is lower, but the description adds marginal value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: '导出草稿为剪映项目' (export draft as Jianying project). It specifies the verb (export) and resource (draft), and mentions the destination (local Jianying draft path). However, it doesn't explicitly differentiate from sibling tools like 'create_draft', which creates rather than exports drafts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides minimal usage guidance. It states that the draft must already exist ('必须是已存在的草稿'), which is a prerequisite, but offers no explicit guidance on when to use this tool versus alternatives like 'create_draft' or other editing tools. There's no mention of when-not-to-use scenarios or comparisons with sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hey-jian-wei/jianying-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server