Application Paper
Paper est un outil de conception dont le canevas est du HTML et CSS réels. L'application de bureau Paper exécute un serveur MCP local qui permet aux agents de lire le canevas et d'y écrire du HTML comme nœuds de conception.
Le serveur fournit son propre guide. Cette compétence couvre ce que ce guide omet : les choses apprises à la dure lors de sessions réelles.
Avant de commencer
- Vérifiez que les outils existent :
await describeNamespace("mcp__paper")en mode code. Si l'espace de noms est vide ou queALL_TOOLSn'a pas d'entréesmcp__paper__*, la session ne s'est pas connectée, généralement parce que Paper n'était pas ouvert au démarrage de la session. Le serveur se trouve àhttp://127.0.0.1:29979/mcpet ne répond que lorsque Paper est ouvert.pi mcp list(exécuté depuis bash) indique si le serveur lui-même est sain. S'il l'est, la session ne peut pas se reconnecter seule : demandez à l'utilisateur d'exécuter/mcp reconnect paperdans pi, ou/reloadsi cela ne fonctionne pas. - Chargez le guide officiel une fois par session :
get_guide({ topic: "paper-mcp-instructions" }). Suivez-le : publiez un brief de conception avant les nouvelles conceptions, écrivez un groupe visuel par appelwrite_html, examinez avec des captures d'écran, et appelezfinish_working_on_nodesune fois terminé. Autres sujets :mobile-status-bar,figma-import,image-generation. - Trouvez le fichier.
list_resources({ limit: 200 })liste les fichiers sans qu'un soit ouvert. PassezfileIdà chaque appel de périmètre fichier.get_basic_info({ fileId })fonctionne sur un fichier qui n'est pas l'onglet de premier plan, vous avez donc rarement besoin deopen_file, qui ouvre un onglet dans l'application de l'utilisateur. - Si le projet a un
AGENTS.md, recherchez-y l'ID du fichier Paper et les conventions du canevas avant d'explorer.
Appeler Paper depuis le mode code
- Exécutez les appels Paper dépendants dans un seul script avec
await, ou en tours séquentiels. Ne les émettez jamais comme des appels d'outils parallèles dans le même tour. Les appels parallèles ont causé la plupart des échecs dans les sessions passées, et chacun a été interprété à tort comme un bogue Paper : "Nœud non trouvé" pour un nœud qu'un appel frère venait de créer, enfants arrivant dans un ordre mélangé, exports manquant des écritures récentes, etreadéchouant avec ENOENT sur un export qui n'avait pas encore été écrit. - Chaque outil de périmètre fichier nécessite
fileIddans sa déclaration de mode code, même si les instructions du serveur disent que vous pouvez l'omettre. L'omettre échoue à la validation. Seulsget_basic_infoetget_selectionacceptent de ne pas avoir defileId. - Exécutez
describeTool("mcp__paper__<nom>")avant la première utilisation d'un outil. Les formes des arguments ne sont pas devinables :get_font_family_infoprendfamilyNames: string[],set_text_contentetupdate_stylesprennent un tableauupdates, etexportprend une cartenodes. - Les outils de périmètre fichier renvoient une seule chaîne de texte : un en-tête JSON
{ file, contentHash }, puis le corps. Les outils sans fichier (list_resources,create_file,open_file,get_guide) renvoient{ content: [{ type: "text", text }] }. N'extrayez pas le premier"id"d'un résultat par expression régulière : c'est l'ID du fichier, et le passer comme ID de nœud échoue avec "Nœud non trouvé". - Les échecs de périmètre fichier et les erreurs de validation génèrent une exception. Certains échecs hors fichier (comme "Ouvrez un fichier Paper pour utiliser cet outil.") se résolvent avec
isError: trueà la place. N'enveloppez pas les appels Paper dans.catch(() => {}): une forme d'argument incorrecte échoue alors silencieusement et la modification n'apparaît jamais. - "Votre fichier Paper est actuellement déconnecté du serveur" signifie que les écritures échouent tandis que les lectures fonctionnent encore. Dormir et réessayer n'a pas résolu le problème lors des sessions passées. Demandez à l'utilisateur de ramener le fichier Paper au premier plan, puis réessayez.
get_screenshotne renvoie pas d'image en mode code : son résultat est réduit à la chaîne d'en-tête. Utilisez l'aideshot()ci-dessous, qui exporte un PNG et le lit dans le même script. Lorsqu'un harnais appelle directement les outils MCP au lieu de passer par le mode code, les images deget_screenshotarrivent bien.
Formes de résultats utiles, après analyse du corps :
| Outil | Corps |
|---|---|
create_artboard | { id, name, worldX, worldY, width, height } |
write_html | { createdNodes: [{ id, name, component }] }, nœuds de niveau supérieur uniquement |
get_tree_summary | { summary }, un arbre texte indenté avec Type "nom" (id) L×H |
get_children | { children: [{ id, name, component, childCount, x, y }], count } |
find_nodes | { nodes: [{ id, name, component, matched: [{ styleName, styleValue }] }] } |
duplicate_nodes | { duplicatedNodes: [{ sourceId, newId, descendantIdMap }] } |
export | { exports: [{ nodeId, filePath }] } |
update_styles | Liste les ignoredStyles lorsqu'un style est inerte dans ce contexte. Vérifiez-le. |
Aides
Collez-les en haut d'un script de mode code, ou stockez-les une fois et exécutez-les (eval) dans des appels ultérieurs :
var F = "<fileId>";
var txt = r => typeof r === "string" ? r : (r?.content ?? []).map(c => c.text ?? "").join("\n");
// Supprime l'en-tête { file, contentHash } que les outils de périmètre fichier ajoutent.
var body = r => { const t = txt(r); const i = t.indexOf("\n}\n"); return t.startsWith('{\n "file"') && i > -1 ? t.slice(i + 3) : t; };
var json = r => JSON.parse(body(r));
var w = async (target, html, mode = "insert-children") =>
json(await tools.mcp__paper__write_html({ fileId: F, targetNodeId: target, mode, html })).createdNodes;
// Exporte un nœud en PNG, l'affiche, puis supprime le fichier de ~/Downloads.
var shot = async (id, scale = "1x") => {
const { exports } = json(await tools.mcp__paper__export({ fileId: F, nodes: { [id]: [{ format: "png", scale }] } }));
const p = exports[0].filePath;
image(await tools.read({ path: p, offset: null, limit: null }));
await tools.bash({ command: `rm -f "${p}"`, timeout: 5 });
};
Pour réutiliser les aides entre appels : store("paper", helperSource), puis eval(load("paper")) en haut de chaque script ultérieur. Déclarez les aides avec var, car const et let à l'intérieur de eval ne fuient pas. store() ne conserve les valeurs que lorsque le script réussit, alors stockez les aides dans un script qui ne fait rien d'autre.
Captures d'écran et exports
- Prenez les captures d'écran dans un appel de mode code séparé des modifications. Un export dans le même script que les écritures peut s'exécuter avant que Paper ne les rende, donc le PNG affiche un contenu manquant ou obsolète.
exportécrit dans~/Downloads/<nom du plan de travail>.png, avec@2xou@0.5xpour d'autres échelles et des suffixes(1),(2)lorsqu'un fichier de ce nom existe déjà. Les caractères comme?et:deviennent_. Utilisez toujours lefilePathrenvoyé. Ne sélectionnez pas les fichiers avecls -t ~/Downloadsou des globs de noms : cela a déjà récupéré des exports obsolètes d'exécutions antérieures.- Déplacez ou supprimez les exports après les avoir lus pour qu'ils ne s'accumulent pas dans
~/Downloads. Laissez tranquilles les fichiers que vous n'avez pas créés. - Utilisez
0.5xpour les captures d'écran de révision afin d'économiser des jetons,2xpour les livrables (les plans de travail 1920×1080 deviennent 3840×2160). export_combined_pdffusionne les plans de travail en un seul PDF, ordonné par position sur le canevas.exportfait aussi de la vidéomp4/webm, plus dusvgpour les nœuds SVG,jpg,webp,avifetpdf.- Pour transformer des plans de travail en Google Slides, voir references/slides-export.md.
Écrire du HTML
La description de l'outil write_html liste les règles (styles en ligne, flex, pas de margin, pas de display: grid, pas de tableaux, layer-name pour les noms de calque). Constats supplémentaires :
- Une
<div>vide devient un nœud Rectangle, même avecdisplay: flex, et les Rectangles ne peuvent pas avoir d'enfants. Écrire dans l'un d'eux échoue avec "cannot have children". Écrivez un conteneur avec son premier enfant, ou remplacez le Rectangle avecwrite_htmlen modereplace. - Le texte enrichi n'est pas pris en charge. Pour du code avec coloration syntaxique ou une phrase avec un mot coloré, construisez une rangée flex de nœuds de texte séparés, un par jeton, chacun avec
white-space: pre. Le code devient une rangée par ligne. Générez le HTML avec une aide plutôt que de l'écrire à la main. - Un
backgroundavec des couches de dégradé ignorebackgroundColor. Ajoutez la couleur de base comme dernière couche :linear-gradient(#F6F7F9, #F6F7F9). - Le
<svg>en ligne fonctionne pour les icônes, logos et lignes de connexion, et accepte les couleursfill/strokeet les variables de jeton. Intégrez le SVG d'un logo de marque plutôt que de faire un lien vers celui-ci. - Les remplissages de chemin SVG sont invisibles pour
find_nodesetget_computed_styles(qui renvoie{}pour eux). Trouvez les nœudsSVGVisualElement "Path"dansget_tree_summaryet recolorez-les avecupdate_styles({ styles: { fill } }), qui fonctionne. - Les images locales nécessitent un chemin absolu avec le schéma
paper-asset://:<img src="paper-asset:///private/tmp/spec/foo.png">. - Des colonnes de largeur égale dans une rangée flex nécessitent
flex: 1; flex-basis: 0. Le remplissage sur une colonne la rend autrement plus large que ses voisines. - Si le contenu est rogné, réglez le plan de travail sur
height: "fit-content"avecupdate_stylesau lieu de deviner une nouvelle hauteur.
Polices
get_font_family_info({ fileId, familyNames: [...] })vérifie les polices installées sur la machine et les Google Fonts. Appelez-le avant le premier style typographique dans une session.- Les polices installées en cours de session (par exemple dans
~/Library/Fontssur macOS) n'apparaissent pas tant que Paper n'a pas redémarré. Demandez à l'utilisateur de quitter et de rouvrir Paper. Ne le tuez pas vous-même, car cela pourrait perdre son état. - Vérifiez la licence d'une police avant qu'elle ne soit utilisée sur un site public. De nombreuses polices de dafont.com sont gratuites pour un usage personnel uniquement.
- Pour comparer de nombreuses polices sans redémarrer Paper, générez des spécimens en PNG transparents localement (par exemple avec Pillow), placez-les comme images
paper-asset://sur un plan de travail de spécimens, puis n'installez que les finalistes.
Organiser le canevas
- Lors d'itérations sur une conception, dupliquez le plan de travail et modifiez la copie. Ne modifiez pas les plans antérieurs sur place : les utilisateurs veulent voir la progression et demandent souvent de revenir à une version antérieure. Un utilisateur a dû demander cela deux fois dans une session.
create_artboardetduplicate_nodesplacent les nouveaux plans de travail dans le meilleur endroit vide, ce qui les disperse. Pour les organiser, définisseztopetleftsur les plans de travail avecupdate_styles. Maintenez au moins 80px entre les plans de travail.- Gardez les explorations de conception séparées de la direction choisie : dans leur propre région du canevas, ou sur leur propre page (
create_page, puis passezpageId). Vous ne pouvez pas changer la page que l'utilisateur consulte. - Pour les jeux de diapositives et les variantes, utilisez une grille fixe (par exemple quatre par ligne,
x = x0 + col × (largeur + espacement)). Lorsqu'un plan de travail est ajouté ou supprimé, réorganisez le reste pour qu'il n'y ait pas de trous, et renumérotez leurs noms. - Pour les variantes claires et sombres, placez la rangée claire en haut et la version sombre correspondante directement en dessous de chacune. Pour créer les versions sombres, voir references/dark-mode-variants.md.
- Donnez aux plans de travail des noms descriptifs comme
Page d'accueil · A (Éditorial)ou04 · Que sont les Workers ?. Les noms de fichiers d'exportation en proviennent. - Les ID de nœuds ne survivent pas à la session. Écrivez l'ID du fichier, l'URL, le schéma de nommage, la formule de grille et les règles de style dans l'
AGENTS.mddu projet afin que la prochaine session puisse reprendre.
De la conception au code
- Lisez les valeurs exactes avec
get_jsx(format: "inline-styles"ou"tailwind") etget_computed_styles. Ne mesurez jamais les tailles ou les couleurs à partir d'une capture d'écran. - Les couleurs calculées peuvent revenir sous forme
oklab(...). Copiez-les textuellement au lieu de les convertir. - Après avoir construit la page, comparez-la dans un vrai navigateur avec un export Paper, en modes clair et sombre.
Ce que le MCP ne peut pas faire
- Partager des fichiers ou gérer les autorisations. L'utilisateur partage depuis l'application Paper : invitez des personnes dans l'équipe pour leur permettre de modifier, ou envoyez l'URL du fichier pour un accès en lecture seule.
- Déplacer des fichiers dans des dossiers. Les nouveaux fichiers de
create_fileatterrissent en dehors des dossiers de l'utilisateur, et l'utilisateur doit les glisser dedans. - Déplacer des nœuds entre fichiers.
move_nodesne déplace que dans un fichier, entre pages. Pour déplacer le contenu d'un fichier entier,create_file({ cloneFileId, name }), vérifiez que les comptes de nœuds et de plans de travail correspondent, puisdelete_nodesles originaux. Confirmez avec l'utilisateur avant de supprimer. - Changer la page que l'utilisateur consulte.
