Communitygithub.com

zeke/paper-app-skill

Travaillez dans l'application de conception Paper (paper.design) via son serveur MCP local. Utilisez-le pour concevoir, faire des maquettes, éditer des plans de travail, prendre des captures d'écran, exporter des PNG/PDF/vidéos, déplacer/cloner des fichiers, utiliser des polices/images personnalisées ou transformer un design en code.

Qu'est-ce que paper-app-skill ?

Cette compétence permet aux agents IA (comme Claude Code) d'interagir avec l'application Paper, un outil de conception dont le canevas est basé sur du HTML et CSS réels. Elle utilise le serveur MCP local de Paper pour lire le canevas et y écrire du HTML en tant que nœuds de design. Elle inclut des astuces pratiques non couvertes dans la documentation officielle, essentielles pour éviter les échecs fréquents : notamment, l'obligation d'exécuter les appels Paper en séquence (et non en parallèle) sous peine de plantages, la nécessité de fournir `fileId` dans chaque outil, et les formats spécifiques des résultats JSON. La compétence fournit des helpers prêts à l'emploi (comme `shot()` pour exporter et afficher des PNG) et explique comment vérifier la connexion au serveur, charger le guide officiel, et gérer les cas où Paper est déconnecté. Elle prend en charge la plupart des fonctionnalités de Paper : création d'artboards, écriture HTML, export, capture d'écran, et même transformation de design en code, mais ne peut pas accéder aux images de `get_screenshot` lorsqu'elle est appelée depuis un terminal (codemode).

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/zeke/paper-app-skill/tree/HEAD/skills/paper-app

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Aperçu

Depuis le README du skill

paper-app-skill preview

Documentation

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

  1. Vérifiez que les outils existent : await describeNamespace("mcp__paper") en mode code. Si l'espace de noms est vide ou que ALL_TOOLS n'a pas d'entrées mcp__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/mcp et 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 paper dans pi, ou /reload si cela ne fonctionne pas.
  2. 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 appel write_html, examinez avec des captures d'écran, et appelez finish_working_on_nodes une fois terminé. Autres sujets : mobile-status-bar, figma-import, image-generation.
  3. Trouvez le fichier. list_resources({ limit: 200 }) liste les fichiers sans qu'un soit ouvert. Passez fileId à 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 de open_file, qui ouvre un onglet dans l'application de l'utilisateur.
  4. 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, et read échouant avec ENOENT sur un export qui n'avait pas encore été écrit.
  • Chaque outil de périmètre fichier nécessite fileId dans sa déclaration de mode code, même si les instructions du serveur disent que vous pouvez l'omettre. L'omettre échoue à la validation. Seuls get_basic_info et get_selection acceptent de ne pas avoir de fileId.
  • 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_info prend familyNames: string[], set_text_content et update_styles prennent un tableau updates, et export prend une carte nodes.
  • 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_screenshot ne renvoie pas d'image en mode code : son résultat est réduit à la chaîne d'en-tête. Utilisez l'aide shot() 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 de get_screenshot arrivent bien.

Formes de résultats utiles, après analyse du corps :

OutilCorps
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_stylesListe 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 @2x ou @0.5x pour 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 le filePath renvoyé. Ne sélectionnez pas les fichiers avec ls -t ~/Downloads ou 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.5x pour les captures d'écran de révision afin d'économiser des jetons, 2x pour les livrables (les plans de travail 1920×1080 deviennent 3840×2160).
  • export_combined_pdf fusionne les plans de travail en un seul PDF, ordonné par position sur le canevas. export fait aussi de la vidéo mp4/webm, plus du svg pour les nœuds SVG, jpg, webp, avif et pdf.
  • 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 avec display: 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 avec write_html en mode replace.
  • 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 background avec des couches de dégradé ignore backgroundColor. 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 couleurs fill/stroke et 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_nodes et get_computed_styles (qui renvoie {} pour eux). Trouvez les nœuds SVGVisualElement "Path" dans get_tree_summary et recolorez-les avec update_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" avec update_styles au 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/Fonts sur 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_artboard et duplicate_nodes placent les nouveaux plans de travail dans le meilleur endroit vide, ce qui les disperse. Pour les organiser, définissez top et left sur les plans de travail avec update_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 passez pageId). 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) ou 04 · 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.md du 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") et get_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_file atterrissent en dehors des dossiers de l'utilisateur, et l'utilisateur doit les glisser dedans.
  • Déplacer des nœuds entre fichiers. move_nodes ne 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, puis delete_nodes les originaux. Confirmez avec l'utilisateur avant de supprimer.
  • Changer la page que l'utilisateur consulte.

Skills associés