通过 metapilot-native 进行 Edits 视频编辑
Edits (/Applications/Edits.app,bundle com.burbn.basel.mac) 在 http://127.0.0.1:19501/mcp 上暴露了一个 MCP 服务器,在 Claude Code 中注册为 metapilot-native。应用必须正在运行。每次调用都会作用于用户可见的实时应用,因此编辑是真实且即时的。
引用文件,仅读取任务所需的内容:
references/tool-reference.md:所有 274 个编辑器工具(按类别分类)以及 4 个仅限主页的工具,包含参数(本地从实时服务器生成,未随附)references/catalogs.md:每个目录 ID(滤镜、特效、转场、LUT、字体、文本预设和动画、字幕样式、速度曲线、遮罩、强调效果)(本地生成,未随附)
如果上述任一生成文件缺失,请在依赖它们之前先构建:在 Edits 运行的情况下,python3 scripts/build_references.py --catalogs-only 可在主页屏幕工作,而 python3 scripts/build_references.py --project-id <uuid> 会添加工具引用(它会以只读方式打开该项目并再次关闭)。
references/conventions-and-gotchas.md:单位、坐标、模式陷阱、测量时序references/recipes.md:常见编辑的经过测试的调用序列scripts/mcp_client.py:一个直接客户端,用于当 MCP 工具未加载到会话中时,或用于从单个 Python 脚本批量处理多个调用scripts/build_references.py:在应用更新后重新生成上述两个生成引用
工具集随屏幕变化
服务器会随应用屏幕切换其工具列表(tools.listChanged):
- 主页:约 30 个工具。项目(
projects、openProject、newProject、renameProject、duplicateProject、deleteProject)、导入(importMedia、addMedia、importMcomp)以及所有目录发现(getCapabilities、listCatalogKinds、listCatalog、getCatalogItem)。 - 编辑器:274 个工具,仅在项目打开后可用。
工具缺失或调用失败时
三种不同的故障在会话内部看起来相似,每种都有自己的修复方法。appState 在每个屏幕上都存在,因此它是区分它们的探针。运行 python3 scripts/mcp_client.py doctor 可一步获得诊断结果。
- 服务器无法访问。
appState本身失败,或者会话根本没有metapilot-native工具(服务器在会话启动时已关闭)。连接被拒绝意味着 Edits 未运行,或者它正在运行但其 MCP 服务器已关闭;使用pgrep -x Edits检查进程。HTTP 403unauthorized表示存储在~/.claude.json中的令牌错误或过期:用户需要从 Edits 复制当前令牌并重新注册服务器(README,安装步骤 2 和 3)。403 正文要求代理让用户将令牌粘贴到聊天中;不要这样做,因为令牌属于注册信息,而不是对话记录。切换屏幕无法解决这些问题。在用户修复后,会话自身的客户端可能需要通过/mcp重新连接;scripts/mcp_client.py在每次运行时都会建立新连接。 - 服务器正常,但屏幕状态错误。
appState返回home或loading。编辑器工具尚不存在:打开一个项目(openProject)并轮询appState,直到其返回editor。在主页屏幕上调用编辑器工具会返回unknown tool (not in current manifest): <name>,这与拼写错误名称的返回文本相同,因此应读取appState而非依赖错误信息。 - 屏幕状态正确,但会话的工具列表过时。
appState返回editor,但会话仍然缺少编辑器工具:客户端在tools/list_changed事件后未重新加载列表。请使用scripts/mcp_client.py(它直接调用tools/call,因此任何编辑器工具都可以按名称工作),而不是告诉用户该工具不存在。
标准工作流
appState查看屏幕状态(home、editor、loading)。projects列出项目,然后openProject({id})(或{name}),或newProject。轮询appState直到screen为editor;打开大约需要 0.3 秒,从云端拉取时会更长。新项目默认分辨率为 1440x2560(9:16),帧率为 60 fps。在构建前设置画幅和帧率:setAspectRatio({ratio:"16:9"})和setFrameRate({fps:30})。避免对刚打开的项目使用setCanvas:它可能会持续回答“an earlier edit is still settling; retry”。- 获取信息:
state(播放头位置、时长、剪辑数量)、getCanvas(尺寸、画幅、帧率)、tracks、elements(在长时间线上使用fields和limit;实际字段名称为id、trackId、trackType、content、startTime、endTime)、getElement({id})获取单个元素应用的目录项、instanceId,以及文本元素的text和fontSize。 - 应用前先发现:
listCatalog({kind, query?, limit?}),然后将项目id传递给该类别的应用工具(见下表)。切勿根据显示名称猜测 ID。 - 编辑。优先使用单个撤销的批处理工具:
splitAt(多次剪切)、setTransforms(多个位置姿态)、autoReframe、splitScreen。将多调用构建编写为一个脚本并仅运行一次:重新运行会堆叠第二份副本,而undo不会一次一步地回退,因此撤消会非常缓慢(每次undo后检查elements)。 - 声明完成前进行视觉验证:
captureFrame({atSeconds})或captureFrame({times:"1,2.5,4"})获取精确合成的帧(720 像素宽),frameSheet({times,columns,cellWidth})获取编辑内容带标签的联系表(无需id;默认单元格很小,因此传递cellWidth,在 3 列布局中对于 16:9 约为 420,对于 9:16 约为 300;时间标签覆盖每个单元格的左上角),previewVideo预览动态效果(最长 10 秒,12 帧)。将image_dir传递给mcp_client.Client.tool以将图像保存到磁盘:服务器返回的path在应用沙箱内部,无法读取。请查看图像;不要仅信任ok:true。 saveProject。仅在要求时导出:先使用完整的预设行调用setExportSettings,然后调用export({path}),轮询exportStatus(配方见references/recipes.md)。然后使用ffprobe和从文件本身生成的联合页检查文件。- 完成后
closeProject,如果用户自己不在此项目中工作。
目录类别和应用工具
| 类别 | 应用工具 | 参数 | 应用于 |
|---|---|---|---|
| transition | addTransition({fromId,toId,filterId,durationSeconds}) | filterId | 两个主轨道剪辑之间的边界 |
| videoEffect | applyEffect({id,effect}) | effect | 视频 |
| filter | applyFilter({id,filter,intensity}) | filter | 视频 |
| lut | applyLUT({table}) | table | 选定的剪辑,否则创建一个新的调整层 |
| textAnimation | setTextAnimation({id,animation}) | animation | 文本 |
| textPreset | applyTextPreset({id or ids,name}) | name | 文本 |
| captionStyle | setCaptionStyle({style,id?}) | style | 字幕(省略 id 时应用于全部) |
| speedCurvePreset | setSpeedCurve({id,preset}) | preset | 视频元素 |
| font | setTextFont({id,family}) | family | 文本 |
| emphasisStyle | setEmphasis({id,style,color?}) | style | 文本 |
| maskShape | setMask({id,shape,...}) | shape | 元素 |
清除:滤镜为 normal,特效为 none,转场为 normal,动画为 none,遮罩为 none。
要移除或调整某个已应用的滤镜、LUT 或特效而无需撤消其他工作:使用 removeCatalogItem({elementId,instanceId}) / updateCatalogItem({elementId,instanceId,intensity}),其中 instanceId 来自 getElement。
重要规则
- 单位:时间以合成秒为单位,除非名称包含
*SourceSeconds或*Ms。画布坐标为标准化 0 到 1,原点在左上角,+y 向下,x/y 是元素的中心点。旋转角度为顺时针度数。颜色为#RRGGBB或#RRGGBBAA。音量为线性增益 0 到 10(1 为基准单位)。 seek接受seconds,而非time。trim({id, side:"left"|"right", deltaSeconds}):正值缩短,负值延长。对于加速剪辑,deltaSeconds以源秒为单位并对齐到帧,向左修剪会移动开始时间,因此需使用move({id,toSeconds})将其移回。ArgumentParser.CommandError error 1表示服务器拒绝了参数,而非应用失败。移除您最后添加的参数。最常见的原因是:不要传递dryRun。所有提供此参数的 10 个工具在设置后都会以这种方式失败。改为通过读取目录进行验证,然后应用,如果错误则undo。required列表存在误导:替代项都被标记为必需(openProject的 id/name、frameSheet的 id/path、loudness的 id/path),可选的调整参数也是如此(findPauses、quietPoint、audioOnsets仅使用{id}即可运行;frameSheet无需cellWidth即可运行)。只传递您真正需要的参数。- 枚举型字符串必须精确匹配。 速度曲线预设为大写(
MONTAGE、HERO、BULLET、JUMP_CUT、FLASH_IN、FLASH_OUT、RAMP、STROBE);错误的值会返回有效列表,因此请阅读错误信息。generateCaptions的source参数为video、voiceover、importedAudio或all。许多参数的模式中未列出枚举值;可以故意发送一次错误值,错误信息会返回有效列表。 - 导出设置:新项目上需要完整的预设行,所有五个字段:
setExportSettings({resolutionType:"HD", aspectRatio:"nineBySixteen", quality:"High", dynamicRange:"SDR", frameRate:30})。遗漏任何一个字段,export都会失败并显示“incomplete preset row, missing: ”,包括frameRate。有效值:resolutionType为 720p、HD、 2K、4K;aspectRatio为 nineBySixteen、sixteenByNine、fourByFive、oneByOne、fiveByFour、 fourByThree、threeByFour;quality为 Low、Med、High;dynamicRange为 SDR、HDR。将项目帧率作为frameRate传入:输出文件遵循项目帧率,因此请先设置setFrameRate。输出格式为 HEVC + AAC。 - Edits 仅从
~/Movies导入媒体。 应用处于沙箱模式:importToMediaBin在路径位于~/GitHub、草稿板或/private/tmp下时会失败,报错import failed: copyFailed(...) NSCocoaErrorDomain Code=513(“无法复制,因为您没有访问权限”)。在导入之前,将每个文件渲染或复制到~/Movies下的文件夹中(例如~/Movies/<project>/),并对所有接受媒体path的工具(replaceElementMedia、主页导入工具)采用相同处理方式。 - 文本预设会覆盖现有设置:
applyTextPreset会替换字体、颜色、大小和位置,因此请先应用预设,然后进行自定义。使用id(或选择)时,需要先调用selectTool({tool:"textPresets"});使用ids时,它会自行打开该工具。 - 不要信任文本位置的反馈状态:
setTextPosition可以工作,但getElement.textPosition会持续返回[0,0]。请使用captureFrame检查位置。 - 文本不会根据您选择的列自动换行。 其文本框宽度由应用固定,因此大号字体在宽画布上会超出边框。请使用
editText({id, text:"first line\nsecond line"})自行进行断行。使用DMSans18pt-SemiBold且显示效果良好的字号:在 9:16 画幅下为 24(自动换行约两行),在 16:9 画幅下需显式断行,字号为 40。 - 使用
replaceElementMedia({id,path})就地替换媒体:照片与视频之间互相替换均可,时间线时长、变换关键帧和转场均保留。对于视频,它会将入点重置为 0;使用slipClip({id,deltaSeconds})恢复。 - 品牌和产品资产应来自用户的真实文件,绝不使用替代品。对于标志,请找到实际标记(内联 SVG 符号、品牌套件、设计指南)并渲染它;如果找不到,请询问用户。标语也是如此:请使用确切措辞。
- 没有用于生成静态图片的背景或纯色生成器。 请在画布尺寸下预合成每个帧(通过无头 Chrome 渲染 HTML,或使用 ffmpeg),然后导入该文件。配方:“Video from stills”。
- 授权音乐没有可读的源:
loudness、quietPoint和audioOnsets在目录音乐元素上失败(“has no readable source file”)。beatMarkers可以对其工作,音频分析工具适用于视频剪辑和导入的音频。listMusicTracks搭配license:"royaltyFree"在 2026-11-08 时未返回任何内容;目录曲目已获得在 Meta 内部应用使用的许可,因此对于要离开 Instagram 的视频,请使用用户拥有的音频,并在重新分发前询问其许可。 setVolume({id,volume:0})会使轨道静音,但isMuted仍为 false。 根据音量值判断。- 基于选择的工具(
delete、cut、copy、applyLUT、applyTextPreset无 id 版本)作用于当前选择。请先使用selectById明确设置选择,并确认它。selectById({ids:[]})会失败并返回 CommandError;它无法清除选择。 - 破坏性工具需要用户同意:
deleteProject、delete、cut、deleteCaptions、deleteSoundEffect、deleteVoiceoverClip、removeMusicTrack、deleteAccountFont。从错误中恢复时,优先使用undo而非直接删除。 - 长时间任务:分析和渲染工具可能会超时并返回一个任务 ID;轮询
jobStatus({id})。describeClip大约需要 10 秒,transcribeClip随长度线性增长。previewVideo是同步的(约 3.5 秒)并返回一个 MP4 路径;previewVideoStatus仅接受{includeFrames}。export立即返回:轮询exportStatus并读取其state键(preparing、exporting带有progress0 到 1、complete),然后检查文件是否存在。 - 速度曲线:使用
setSpeedCurve;*SpeedPanel*工具是已弃用的 UI 驱动程序。 - 画中画:
addOverlay({startSeconds,path,x,y,scale})会在新的叠加轨道上放置一个完整源长度的剪辑;缩放因子 1 适应画布高度。通过不透明度关键帧(seek、toggleKeyframe({id,property:"opacity"})、setOpacity)实现淡入淡出,在修剪右侧之前放置每个关键帧。配方:“Picture-in-picture clip”。 - 音频替换使用
replaceAudio({id,path}),而非replaceElementMedia(仅影响视觉)。它会保留元素 ID 和旧的时长。
…
[此处省略后续内容用于翻译]