Montage vidéo Edits via metapilot-native
Edits (/Applications/Edits.app, bundle com.burbn.basel.mac) expose un serveur MCP sur
http://127.0.0.1:19501/mcp, enregistré dans Claude Code sous le nom metapilot-native. L'application doit être en cours d'exécution.
Chaque appel agit sur l'application en direct que l'utilisateur voit, donc les modifications sont réelles et immédiates.
Fichiers de référence, lisez uniquement ce dont la tâche a besoin :
references/tool-reference.md: les 274 outils de l'éditeur par catégorie plus les 4 outils de l'écran d'accueil, avec leurs paramètres (généré localement à partir du serveur en direct, non distribué)references/catalogs.md: chaque identifiant de catalogue (filtres, effets, transitions, LUT, polices, presets de texte et animations, styles de sous-titres, courbes de vitesse, masques, emphasis) (généré localement, non distribué)
Si l'un de ces fichiers générés est manquant, construisez-les avant de vous y fier : avec Edits en cours d'exécution,
python3 scripts/build_references.py --catalogs-only fonctionne sur l'écran d'accueil, et
python3 scripts/build_references.py --project-id <uuid> ajoute la référence des outils (il ouvre ce projet
en lecture seule et le referme).
references/conventions-and-gotchas.md: unités, coordonnées, pièges du schéma, chronométrages mesurésreferences/recipes.md: séquences d'appels testées pour les modifications courantesscripts/mcp_client.py: un client direct pour quand les outils MCP ne sont pas chargés dans la session, ou pour regrouper plusieurs appels depuis un seul script Pythonscripts/build_references.py: régénère les deux fichiers de référence générés après une mise à jour de l'application
L'ensemble d'outils suit l'écran
Le serveur échange sa liste d'outils avec l'écran de l'application (tools.listChanged) :
- accueil : environ 30 outils. Projets (
projects,openProject,newProject,renameProject,duplicateProject,deleteProject), import (importMedia,addMedia,importMcomp), et la découverte des catalogues (getCapabilities,listCatalogKinds,listCatalog,getCatalogItem). - éditeur : 274 outils, uniquement après l'ouverture d'un projet.
Quand un outil manque ou qu'un appel échoue
Trois défauts différents se ressemblent depuis une session, et chacun a sa propre solution. appState
existe sur tous les écrans, c'est donc la sonde qui les sépare. Exécutez
python3 scripts/mcp_client.py doctor pour obtenir le diagnostic en une seule étape.
- Le serveur est inaccessible.
appStateéchoue lui-même, ou la session n'a aucun outilmetapilot-native(le serveur était éteint au démarrage de la session). Une connexion refusée signifie qu'Edits n'est pas en cours d'exécution, ou qu'il est en cours avec son serveur MCP désactivé ; vérifiez le processus avecpgrep -x Edits. Une erreur HTTP 403unauthorizedsignifie que le jeton stocké dans~/.claude.jsonest erroné ou obsolète : l'utilisateur copie le jeton actuel depuis Edits et enregistre à nouveau le serveur (README, étapes 2 et 3 de l'installation). Le corps du 403 demande à l'agent de faire coller le jeton par l'utilisateur dans le chat ; ne le faites pas, car le jeton appartient à l'enregistrement, pas à la transcription. Changer d'écran ne résout rien de tout cela. Une fois que l'utilisateur a résolu le problème, le propre client de la session peut avoir besoin d'une reconnexion via/mcp;scripts/mcp_client.pyse connecte à nouveau à chaque exécution. - Le serveur fonctionne, l'écran est mauvais.
appStaterépondhome, ouloading. Les outils de l'éditeur n'existent pas encore : ouvrez un projet (openProject) et interrogezappStatejusqu'à ce qu'il indiqueeditor. Appeler un outil de l'éditeur sur l'écran d'accueil répondunknown tool (not in current manifest): <name>, le même texte qu'un nom mal orthographié, donc lisezappStateplutôt que l'erreur. - L'écran est correct, la liste d'outils de la session est obsolète.
appStaterépondeditor, mais la session manque encore des outils de l'éditeur : le client n'a pas rechargé la liste aprèstools/list_changed. Utilisezscripts/mcp_client.py(il appelletools/calldirectement, donc tout outil de l'éditeur fonctionne par son nom) plutôt que de dire à l'utilisateur que l'outil n'existe pas.
Workflow standard
appStatepour voir l'écran (home,editor,loading).projectspour lister, puisopenProject({id})(ou{name}), ounewProject. InterrogezappStatejusqu'à ce quescreensoiteditor; l'ouverture prend environ 0,3 s, plus longtemps quand il tire depuis le cloud. Un nouveau projet est en 1440x2560 (9:16) à 60 fps. Réglez la forme et la cadence avant de construire :setAspectRatio({ratio:"16:9"})etsetFrameRate({fps:30}). ÉvitezsetCanvassur un projet fraîchement ouvert : il peut continuer à répondre "an earlier edit is still settling; retry".- Orientez-vous :
state(playhead, durée, nombre de clips),getCanvas(taille, aspect, fps),tracks,elements(utilisezfieldsetlimitsur les longues timelines ; les vrais noms de champs sontid,trackId,trackType,content,startTime,endTime),getElement({id})pour un élément avec ses éléments de catalogue appliqués, lesinstanceIds, et letextetfontSized'un élément texte. - Découvrez avant d'appliquer :
listCatalog({kind, query?, limit?}), puis passez l'idde l'élément à l'outil d'application de son type (tableau ci-dessous). Ne devinez jamais les identifiants à partir des noms d'affichage. - Éditez. Préférez les outils par lots annulables en une fois :
splitAt(plusieurs coupes),setTransforms(plusieurs poses),autoReframe,splitScreen. Écrivez les constructions multi-appels sous forme d'un seul script et exécutez-le une fois : une réexécution empile une deuxième copie de tout, etundone recule pas d'un appel à la fois, donc le défaire est lent (vérifiezelementsaprès chaqueundo). - Vérifiez visuellement avant de déclarer terminé :
captureFrame({atSeconds})oucaptureFrame({times:"1,2.5,4"})pour des images compositées exactes (720 px de large),frameSheet({times,columns,cellWidth})pour une planche-contact étiquetée du montage (pas deidnécessaire ; les cellules par défaut sont petites, donc passezcellWidth, environ 420 pour 16:9 et 300 pour 9:16 avec 3 colonnes ; l'étiquette de temps couvre le coin supérieur gauche de chaque cellule),previewVideopour le mouvement (max 10 s, 12 images). Passezimage_diràmcp_client.Client.toolpour obtenir les images sur disque : lepathque le serveur renvoie est à l'intérieur du bac à sable de l'application et ne peut pas être lu. Regardez les images ; ne vous fiez pas uniquement àok:true. saveProject. Exportez uniquement sur demande :setExportSettingsavec la ligne de preset complète, puisexport({path}), interrogezexportStatus(recette dansreferences/recipes.md). Vérifiez ensuite le fichier avecffprobeet une planche-contact à partir du fichier lui-même.closeProjectune fois terminé si l'utilisateur ne travaille pas lui-même sur le projet.
Types de catalogue et outils d'application
| type | outil d'application | paramètre | s'applique à |
|---|---|---|---|
| transition | addTransition({fromId,toId,filterId,durationSeconds}) | filterId | frontière entre deux clips de la piste principale |
| videoEffect | applyEffect({id,effect}) | effect | vidéo |
| filter | applyFilter({id,filter,intensity}) | filter | vidéo |
| lut | applyLUT({table}) | table | clips sélectionnés, sinon un nouveau calque de réglage |
| textAnimation | setTextAnimation({id,animation}) | animation | texte |
| textPreset | applyTextPreset({id ou ids,name}) | name | texte |
| captionStyle | setCaptionStyle({style,id?}) | style | sous-titres (tous quand id omis) |
| speedCurvePreset | setSpeedCurve({id,preset}) | preset | élément vidéo |
| font | setTextFont({id,family}) | family | texte |
| emphasisStyle | setEmphasis({id,style,color?}) | style | texte |
| maskShape | setMask({id,shape,...}) | shape | élément |
Effacement : filtre normal, effet none, transition normal, animation none, masque none.
Pour supprimer ou ajuster un filtre, LUT ou effet appliqué sans annuler d'autres travaux :
removeCatalogItem({elementId,instanceId}) / updateCatalogItem({elementId,instanceId,intensity}),
avec instanceId depuis getElement.
Règles importantes
- Unités : le temps est en secondes de composition sauf si le nom dit
*SourceSecondsou*Ms. Les coordonnées du canevas sont normalisées de 0 à 1, origine en haut à gauche, +y vers le bas, x/y sont le centre de l'élément. La rotation est en degrés dans le sens horaire. Les couleurs sont#RRGGBBou#RRGGBBAA. Le volume est un gain linéaire de 0 à 10 (1 = unitaire). seekprend desseconds, pastime.trim({id, side:"left"|"right", deltaSeconds}): positif raccourcit, négatif allonge. Sur un clip accélérédeltaSecondsest en secondes source et s'aligne sur une image, et un rognage à gauche déplace le début, doncmove({id,toSeconds})le remet en place.ArgumentParser.CommandError error 1signifie que le serveur a rejeté un argument, pas que l'application a échoué. Supprimez l'argument que vous avez ajouté en dernier. La cause la plus fréquente : ne passez pasdryRun. Chaque outil qui le propose (10 d'entre eux) échoue de cette façon lorsqu'il est défini. Validez en lisant le catalogue à la place, puis appliquez, puisundosi c'est faux.- Les listes
requiredmentent : les alternatives sont toutes marquées comme requises (openProjectid/name,frameSheetid/path,loudnessid/path), et les paramètres de réglage facultatifs aussi (findPauses,quietPoint,audioOnsetss'exécutent sur{id}seul ;frameSheets'exécute sanscellWidth). Passez uniquement ce que vous voulez dire. - Les chaînes de type énumération sont exactes. Les presets de courbe de vitesse sont en majuscules (
MONTAGE,HERO,BULLET,JUMP_CUT,FLASH_IN,FLASH_OUT,RAMP,STROBE) ; une valeur erronée renvoie une erreur avec la liste valide, alors lisez l'erreur.generateCaptionssourceestvideo,voiceover,importedAudioouall. De nombreux paramètres ne listent pas d'énumération dans le schéma ; envoyez une valeur délibérément fausse une fois et l'erreur renvoie la liste valide. - L'export nécessite la ligne de preset complète sur un nouveau projet, les cinq champs :
setExportSettings({resolutionType:"HD", aspectRatio:"nineBySixteen", quality:"High", dynamicRange:"SDR", frameRate:30}). Laissez-en un de côté etexportéchoue avec "incomplete preset row, missing: ",frameRateinclus. Valeurs valides :resolutionType720p, HD, 2K, 4K ;aspectRationineBySixteen, sixteenByNine, fourByFive, oneByOne, fiveByFour, fourByThree, threeByFour ;qualityLow, Med, High ;dynamicRangeSDR, HDR. Passez la cadence du projet sousframeRate: le fichier suit la cadence du projet, doncsetFrameRated'abord. La sortie est HEVC avec AAC. - Edits importe les médias uniquement depuis
~/Movies. L'application est en bac à sable :importToMediaBinéchoue sur un chemin sous~/GitHub, le bloc-notes ou/private/tmpavecimport failed: copyFailed(...) NSCocoaErrorDomain Code=513("impossible de copier car vous n'avez pas la permission d'accéder"). Rendez ou copiez chaque fichier dans un dossier sous~/Movies(par exemple~/Movies/<project>/) avant d'importer, et traitez chaque outil qui prend unpathde média (replaceElementMedia, les outils d'import de l'accueil) de la même manière. - Les presets de texte écrasent :
applyTextPresetremplace la police, la couleur, la taille et la position, alors appliquez le preset d'abord et personnalisez après. Avecid(ou la sélection), il fautselectTool({tool:"textPresets"})d'abord ; avecids, il ouvre l'outil lui-même. - Ne vous fiez pas à l'état reflété pour la position du texte :
setTextPositionfonctionne, maisgetElement.textPositioncontinue de lire[0,0]. Vérifiez le placement aveccaptureFrame. - Le texte ne se retourne pas automatiquement sur une colonne de votre choix. Sa largeur de boîte est fixée
par l'application, donc une grande taille sur un canevas large dépasse du cadre. Coupez les lignes vous-même
avec
editText({id, text:"première ligne\ndeuxième ligne"}). Tailles qui s'affichent bien avecDMSans18pt-SemiBold: 24 en 9:16 (se retourne sur environ deux lignes tout seul), 40 en 16:9 avec un saut de ligne explicite. - Échangez les médias sur place avec
replaceElementMedia({id,path}): photo et vidéo fonctionnent dans les deux sens, et la durée de la timeline, les images clés de transformation et les transitions survivent. Sur une vidéo, il réinitialise le point d'entrée à 0 ; restaurez-le avecslipClip({id,deltaSeconds}). - Les actifs de marque et de produit proviennent des fichiers réels de l'utilisateur, jamais d'un substitut. Pour un logo, trouvez la marque réelle (un symbole SVG en ligne, une charte graphique, un guide de conception) et rendez-la ; demandez si elle ne peut pas être trouvée. De même pour les slogans : utilisez le libellé exact.
- Il n'y a pas de générateur d'arrière-plan ou de couleur unie pour les images fixes. Pré-compositez chaque image (HTML rendu par Chrome sans tête, ou ffmpeg) à la taille du canevas et importez-la. Recette : "Vidéo à partir d'images".
- La musique sous licence n'a pas de source lisible :
loudness,quietPointetaudioOnsetséchouent sur un élément de musique de catalogue ("has no readable source file").beatMarkersfonctionne dessus, et les outils d'analyse audio fonctionnent sur les clips vidéo et l'audio importé.listMusicTracksaveclicense:"royaltyFree"n'a rien renvoyé le 08/11/2026 ; les pistes du catalogue sont sous licence pour une utilisation dans les applications de Meta, donc pour une vidéo quittant Instagram, utilisez l'audio que l'utilisateur possède et demandez sa licence avant qu'elle ne soit redistribuée. setVolume({id,volume:0})coupe le son d'une piste maisisMutedreste faux. Jugez par le volume.- Les outils basés sur la sélection (
delete,cut,copy,applyLUT,applyTextPresetsans id) agissent sur la sélection en cours. Définissez-la explicitement avecselectByIdd'abord, et confirmez-la.selectById({ids:[]})échoue avec CommandError ; il ne peut pas effacer la sélection. - Les outils destructeurs nécessitent l'accord de l'utilisateur :
deleteProject,delete,cut,deleteCaptions,deleteSoundEffect,deleteVoiceoverClip,removeMusicTrack,deleteAccountFont. Préférezundopour récupérer d'une erreur plutôt que de supprimer. - Tâches longues : les outils d'analyse et de rendu peuvent dépasser la durée de la demande et renvoyer un
identifiant de tâche ; interrogez
jobStatus({id}).describeClipprend environ 10 s,transcribeClipévolue avec la durée.previewVideoest synchrone (environ 3,5 s) et renvoie un chemin MP4 ;previewVideoStatusprend uniquement{includeFrames}.exportrenvoie immédiatement : interrogezexportStatuset lisez sa cléstate(preparing,exportingavecprogressde 0 à 1,complete), puis vérifiez que le fichier existe. - Courbes de vitesse : utilisez
setSpeedCurve; les outils*SpeedPanel*sont des pilotes d'interface obsolètes. - Image dans l'image :
addOverlay({startSeconds,path,x,y,scale})place un clip sur une nouvelle piste d'incrustation à la durée source complète ; l'échelle 1 s'adapte à la hauteur du canevas. Fondu avec des images clés d'opacité (seek,toggleKeyframe({id,property:"opacity"}),setOpacity), en plaçant chaque image clé avant de rogner le côté droit. Recette : "Clip en incrustation". - Les échanges audio utilisent
replaceAudio({id,path}), pasreplaceElementMedia(visuel uniquement). Il conserve l'id de l'élément et l'ancienne durée.
…
[version tronquée pour la traduction]