metapilot-native를 통한 Edits 비디오 편집
Edits (/Applications/Edits.app, 번들 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초가 걸리며, 클라우드에서 가져올 때는 더 오래 걸립니다. 새 프로젝트는 60fps의 1440x2560 (9:16)입니다. 제작 전에 모양과 속도를 설정하십시오:setAspectRatio({ratio:"16:9"})및setFrameRate({fps:30}). 새로 연 프로젝트에서setCanvas사용을 피하십시오. "이전 편집이 아직 안정화 중입니다. 다시 시도하십시오."라는 응답을 계속 반환할 수 있습니다.- 방향 파악:
state(플레이헤드, 지속 시간, 클립 수),getCanvas(크기, 종횡비, fps),tracks,elements(긴 타임라인에서는fields및limit사용. 실제 필드 이름은id,trackId,trackType,content,startTime,endTime),getElement({id})로 단일 요소에 적용된 카탈로그 항목,instanceIds 및 텍스트 요소의text및fontSize를 확인합니다. - 적용하기 전에 검색:
listCatalog({kind, query?, limit?}), 그런 다음 항목id를 해당 종류의 적용 도구에 전달합니다 (아래 표). 표시 이름에서 ID를 추측하지 마십시오. - 편집합니다. 단일 실행 취소 배치 도구를 선호하십시오:
splitAt(여러 컷),setTransforms(여러 포즈),autoReframe,splitScreen. 다중 호출 빌드는 하나의 스크립트로 작성하고 한 번만 실행하십시오: 다시 실행하면 모든 것이 두 번째 복사본으로 쌓이고undo는 한 번에 한 호출씩 되돌릴 수 없으므로 되돌리는 속도가 느립니다 (각undo후elements확인). - 완료했다고 선언하기 전에 시각적으로 확인:
captureFrame({atSeconds})또는captureFrame({times:"1,2.5,4"})로 정확한 합성 프레임 (720px 너비)을,frameSheet({times,columns,cellWidth})로 편집 내용의 레이블이 지정된 콘택트 시트 (기본 셀은 작으므로cellWidth전달, 3열의 경우 16:9는 약 420, 9:16은 약 300; 시간 레이블이 각 셀의 왼쪽 상단 모서리를 덮습니다)를,previewVideo로 모션 (최대 10초, 12프레임)을 확인합니다.mcp_client.Client.tool에image_dir을 전달하여 이미지를 디스크에 저장하십시오: 서버가 반환하는path는 앱의 샌드박스 내부에 있어 읽을 수 없습니다. 이미지를 보고;ok:true만 믿지 마십시오. saveProject. 요청 시에만 내보내기: 전체 프리셋 행으로setExportSettings를 호출한 다음,export({path})를 호출하고exportStatus를 폴링합니다 (레시피는references/recipes.md에 있음). 그런 다음ffprobe와 파일 자체의 콘택트 시트로 파일을 확인합니다.- 사용자가 프로젝트에서 직접 작업하지 않는 경우 완료되면
closeProject.
카탈로그 종류 및 적용 도구
| 종류 | 적용 도구 | 매개변수 | 적용 대상 |
|---|---|---|---|
| 전환 | addTransition({fromId,toId,filterId,durationSeconds}) | filterId | 두 메인 트랙 클립 사이의 경계 |
| 비디오 효과 | applyEffect({id,effect}) | effect | 비디오 |
| 필터 | applyFilter({id,filter,intensity}) | filter | 비디오 |
| LUT | applyLUT({table}) | table | 선택된 클립, 또는 새 조정 레이어 |
| 텍스트 애니메이션 | setTextAnimation({id,animation}) | animation | 텍스트 |
| 텍스트 프리셋 | applyTextPreset({id or ids,name}) | name | 텍스트 |
| 캡션 스타일 | setCaptionStyle({style,id?}) | style | 캡션 (id 생략 시 모두) |
| 속도 곡선 프리셋 | setSpeedCurve({id,preset}) | preset | 비디오 요소 |
| 글꼴 | setTextFont({id,family}) | family | 텍스트 |
| 강조 스타일 | setEmphasis({id,style,color?}) | style | 텍스트 |
| 마스크 모양 | 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는time이 아닌seconds를 사용합니다.trim({id, side:"left"|"right", deltaSeconds}): 양수는 짧게, 음수는 길게 합니다. 속도가 빨라진 클립의 경우deltaSeconds는 소스 초 단위이며 프레임에 스냅되고, 왼쪽 트림은 시작점을 이동시키므로move({id,toSeconds})로 다시 이동하십시오.ArgumentParser.CommandError error 1은 서버가 인수를 거부했음을 의미합니다. 앱이 실패한 것이 아닙니다. 마지막에 추가한 인수를 제거하십시오. 가장 일반적인 원인:dryRun을 전달하지 마십시오. 이를 제공하는 모든 도구 (10개)는 이 값이 설정되면 이런 방식으로 실패합니다. 대신 카탈로그를 읽어 검증하고, 적용한 다음 잘못된 경우undo를 사용하십시오.required목록은 거짓말을 합니다: 대안이 모두 필수로 표시되고 (openProjectid/name,frameSheetid/path,loudnessid/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가 "불완전한 프리셋 행, 누락됨: " 오류와 함께 실패하며,frameRate도 포함됩니다. 유효한 값:resolutionType720p, HD, 2K, 4K;aspectRationineBySixteen, sixteenByNine, fourByFive, oneByOne, fiveByFour, fourByThree, threeByFour;qualityLow, Med, High;dynamicRangeSDR, HDR. 프로젝트 속도를frameRate로 전달하십시오: 파일은 프로젝트 속도를 따르므로setFrameRate를 먼저 호출하십시오. 출력은 AAC가 포함된 HEVC입니다. - 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:"첫 번째 줄\n두 번째 줄"}).DMSans18pt-SemiBold로 잘 읽히는 크기: 9:16에서는 24 (자체적으로 약 두 줄로 줄 바꿈), 16:9에서는 명시적 줄 바꿈과 함께 40. replaceElementMedia({id,path})로 제자리에서 미디어 교체: 사진과 비디오 간 교환이 모두 작동하며, 타임라인 지속 시간, 변환 키프레임 및 전환이 유지됩니다. 비디오의 경우 인포인트가 0으로 재설정됩니다;slipClip({id,deltaSeconds})로 복원하십시오.- 브랜드 및 제품 에셋은 사용자의 실제 파일에서 가져오며, 대체 파일을 사용하지 않습니다. 로고의 경우 실제 마크(인라인 SVG 기호, 브랜드 키트, 디자인 가이드)를 찾아 렌더링하고, 찾을 수 없으면 물어보십시오. 태그라인도 마찬가지입니다: 정확한 문구를 사용하십시오.
- 스틸 이미지용 배경 또는 단색 생성기가 없습니다. 각 프레임을 (헤드리스 Chrome으로 렌더링된 HTML 또는 ffmpeg) 캔버스 크기로 미리 합성하고 가져오십시오. 레시피: "스틸 이미지에서 비디오".
- 라이선스 음악은 읽을 수 있는 소스가 없습니다:
loudness,quietPoint및audioOnsets는 카탈로그 음악 요소에서 실패합니다 ("읽을 수 있는 소스 파일이 없음").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,progress0~1의exporting,complete)를 읽은 다음 파일이 존재하는지 확인하십시오. - 속도 곡선:
setSpeedCurve를 사용하십시오.*SpeedPanel*도구는 더 이상 사용되지 않는 UI 드라이버입니다. - 화면 속 화면:
addOverlay({startSeconds,path,x,y,scale})는 전체 소스 길이의 클립을 새 오버레이 트랙에 배치합니다. scale 1은 캔버스 높이에 맞습니다. 불투명도 키프레임으로 페이드하십시오 (seek,toggleKeyframe({id,property:"opacity"}),setOpacity). 모든 키프레임을 먼저 배치한 후 오른쪽을 트리밍하십시오. 레시피: "화면 속 화면 클립". - 오디오 교체는
replaceAudio({id,path})를 사용합니다.replaceElementMedia(시각적 전용)가 아닙니다. 요소 ID와 이전 지속 시간을 유지합니다.