Paper App
Paper ist ein Design-Tool, dessen Leinwand echtes HTML und CSS ist. Die Paper-Desktop-App führt einen lokalen MCP-Server aus, der es Agenten ermöglicht, die Leinwand zu lesen und HTML als Design-Knoten darauf zu schreiben.
Der Server bringt eine eigene Anleitung mit. Diese Fähigkeit deckt ab, was diese Anleitung auslässt: Dinge, die auf die harte Tour in echten Sitzungen gelernt wurden.
Vorbereitung
- Überprüfen Sie, ob die Tools existieren:
await describeNamespace("mcp__paper")im Codemode. Wenn der Namespace leer ist oderALL_TOOLSkeinemcp__paper__*-Einträge enthält, war die Sitzung nicht verbunden, normalerweise, weil Paper nicht geöffnet war, als die Sitzung startete. Der Server läuft unterhttp://127.0.0.1:29979/mcpund antwortet nur, solange Paper geöffnet ist.pi mcp list(aus bash ausgeführt) zeigt, ob der Server selbst fehlerfrei ist. Wenn ja, kann sich die Sitzung nicht von selbst neu verbinden: Bitten Sie den Benutzer,/mcp reconnect paperin pi auszuführen, oder/reload, falls das nicht funktioniert. - Laden Sie die offizielle Anleitung einmal pro Sitzung:
get_guide({ topic: "paper-mcp-instructions" }). Folgen Sie ihr: Posten Sie ein Design-Briefing vor neuen Designs, schreiben Sie eine visuelle Gruppe prowrite_html-Aufruf, überprüfen Sie mit Screenshots und rufen Siefinish_working_on_nodesauf, wenn Sie fertig sind. Weitere Themen:mobile-status-bar,figma-import,image-generation. - Finden Sie die Datei.
list_resources({ limit: 200 })listet Dateien auf, ohne dass eine geöffnet ist. Übergeben SiefileIdan jeden dateibezogenen Aufruf.get_basic_info({ fileId })funktioniert mit einer Datei, die nicht im Vordergrund-Tab ist, daher benötigen Sie seltenopen_file, das einen Tab in der App des Benutzers öffnet. - Wenn das Projekt eine
AGENTS.mdhat, suchen Sie dort zuerst nach der Paper-Datei-ID und den Leinwand-Konventionen.
Paper aus dem Codemode aufrufen
- Führen Sie abhängige Paper-Aufrufe in einem Skript mit
awaitoder in sequenziellen Schritten aus. Geben Sie sie niemals als parallele Tool-Aufrufe im selben Schritt aus. Parallele Aufrufe haben in der Vergangenheit die meisten Fehler verursacht, und jeder wurde als Paper-Bug fehlinterpretiert: "Node not found" für einen Knoten, den ein paralleler Aufruf gerade erstellt hatte, Kinder landeten in durcheinandergebrachter Reihenfolge, Exporte fehlten bei kürzlichen Schreibvorgängen, undreadschlug mit ENOENT fehl, weil der Export noch nicht geschrieben war. - Jedes dateibezogene Tool erfordert
fileIdin seiner Codemode-Deklaration, auch wenn die Anleitung des Servers sagt, dass man es weglassen kann. Das Weglassen schlägt bei der Validierung fehl. Nurget_basic_infoundget_selectionakzeptieren keinfileId. - Führen Sie
describeTool("mcp__paper__<name>")vor der ersten Verwendung eines Tools aus. Argumentstrukturen sind nicht erratbar:get_font_family_infonimmtfamilyNames: string[]entgegen,set_text_contentundupdate_stylesnehmen einupdates-Array, undexportnimmt einenodes-Map. - Dateibezogene Tools geben einen einfachen String zurück: einen JSON-Header
{ file, contentHash }, dann den Body. Tools ohne Datei (list_resources,create_file,open_file,get_guide) geben{ content: [{ type: "text", text }] }zurück. Extrahieren Sie nicht das erste"id"aus einem Ergebnis: Es ist die Datei-ID, und die Übergabe als Knoten-ID schlägt fehl mit "Node not found". - Dateibezogene Fehler und Validierungsfehler werfen Ausnahmen. Einige Fehler ohne Datei (wie "Open a Paper file to use this tool.") werden stattdessen mit
isError: trueaufgelöst. Umwickeln Sie Paper-Aufrufe nicht mit.catch(() => {}): Ein falsches Argument führt dann zu einem stillen Fehler, und die Bearbeitung wird nie angezeigt. - "Your Paper file is currently disconnected from the server" bedeutet, dass Schreibvorgänge fehlschlagen, während Lesevorgänge noch funktionieren. In früheren Sitzungen halfen Schlafen und erneutes Versuchen nicht. Bitten Sie den Benutzer, die Paper-Datei wieder in den Vordergrund zu bringen, und versuchen Sie es dann erneut.
get_screenshotgibt im Codemode kein Bild zurück: Sein Ergebnis wird auf den Header-String reduziert. Verwenden Sie den unten stehendenshot()-Helfer, der ein PNG exportiert und im selben Skript liest. Wenn ein Aufrufer MCP-Tools direkt anstelle des Codemodes aufruft, werdenget_screenshot-Bilder jedoch durchgereicht.
Nützliche Ergebnisstrukturen nach Parsen des Bodys:
| Tool | Body |
|---|---|
create_artboard | { id, name, worldX, worldY, width, height } |
write_html | { createdNodes: [{ id, name, component }] }, nur Top-Level-Knoten |
get_tree_summary | { summary }, ein eingerückter Textbaum mit Type "name" (id) W×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 | Listet ignoredStyles auf, wenn ein Stil in diesem Kontext wirkungslos ist. Prüfen Sie es. |
Helfer
Fügen Sie diese am Anfang eines Codemode-Skripts ein oder speichern Sie sie einmal und werten Sie sie in späteren Aufrufen mit eval aus:
var F = "<fileId>";
var txt = r => typeof r === "string" ? r : (r?.content ?? []).map(c => c.text ?? "").join("\n");
// Entfernt den { file, contentHash }-Header, den dateibezogene Tools voranstellen.
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;
// Exportiert einen Knoten als PNG, zeigt ihn an und löscht die Datei aus ~/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 });
};
Um Helfer über Aufrufe hinweg wiederzuverwenden: store("paper", helperSource), dann eval(load("paper")) am Anfang jedes späteren Skripts. Deklarieren Sie Helfer mit var, da const und let innerhalb von eval nicht nach außen dringen. store() behält Werte nur, wenn das Skript erfolgreich ist. Speichern Sie Helfer daher in einem Skript, das nichts anderes tut.
Screenshots und Exporte
- Machen Sie Screenshots in einem separaten Codemode-Aufruf von den Bearbeitungen. Ein Export im selben Skript wie die Schreibvorgänge kann ausgeführt werden, bevor Paper sie rendert, sodass das PNG fehlende oder veraltete Inhalte zeigt.
exportschreibt nach~/Downloads/<artboard name>.png, mit@2xoder@0.5xfür andere Skalierungen und(1),(2)-Suffixen, wenn eine Datei mit diesem Namen bereits existiert. Zeichen wie?und:werden zu_. Verwenden Sie immer den zurückgegebenenfilePath. Wählen Sie Dateien nicht mitls -t ~/Downloadsoder Namens-Globs aus: Das hat bereits veraltete Exporte aus früheren Läufen aufgegriffen.- Verschieben oder löschen Sie Exporte nach dem Lesen, damit sie sich nicht in
~/Downloadsansammeln. Lassen Sie Dateien in Ruhe, die Sie nicht erstellt haben. - Verwenden Sie
0.5xfür Screenshots zur Überprüfung, um Tokens zu sparen,2xfür Liefergegenstände (1920×1080-Artboards werden zu 3840×2160). export_combined_pdffasst Artboards in einem PDF zusammen, geordnet nach Leinwand-Position.exporterstellt auchmp4-/webm-Videos sowiesvgfür SVG-Knoten,jpg,webp,avifundpdf.- Informationen zum Umwandeln von Artboards in Google Slides finden Sie in references/slides-export.md.
HTML schreiben
Die write_html-Tool-Beschreibung listet die Regeln auf (Inline-Styles, Flex, kein margin, kein display: grid, keine Tabellen, layer-name für Ebenennamen). Zusätzliche Erkenntnisse:
- Ein leeres
<div>wird zu einem Rechteck-Knoten, selbst mitdisplay: flex, und Rechtecke können keine Kinder haben. Das Schreiben in einen solchen Knoten schlägt fehl mit "cannot have children". Schreiben Sie einen Container zusammen mit seinem ersten Kind, oder ersetzen Sie das Rechteck mitwrite_htmlim Modusreplace. - Rich-Text wird nicht unterstützt. Für syntax-hervorgehobenen Code oder einen Satz mit einem farbigen Wort bauen Sie eine Flex-Zeile aus separaten Text-Knoten, einen pro Token, jeweils mit
white-space: pre. Code wird zu einer Zeile pro Code-Zeile. Generieren Sie das HTML mit einem Helfer, anstatt es von Hand zu schreiben. - Ein
backgroundmit Farbverlaufsebenen ignoriertbackgroundColor. Fügen Sie die Grundfarbe als letzte Ebene hinzu:linear-gradient(#F6F7F9, #F6F7F9). - Inline-
<svg>funktioniert für Symbole, Logos und Verbindungslinien und akzeptiertfill-/stroke-Farben und Token-Variablen. Betten Sie das SVG eines Markenlogos anstatt es zu verlinken. - SVG-Pfad-Füllungen sind für
find_nodesundget_computed_stylesunsichtbar (was{}für sie zurückgibt). Finden Sie dieSVGVisualElement "Path"-Knoten inget_tree_summaryund färben Sie sie mitupdate_styles({ styles: { fill } })um, was funktioniert. - Lokale Bilder benötigen einen absoluten Pfad mit dem
paper-asset://-Schema:<img src="paper-asset:///private/tmp/spec/foo.png">. - Gleich breite Spalten in einer Flex-Zeile benötigen
flex: 1; flex-basis: 0. Andernfalls macht Padding in einer Spalte sie breiter als ihre Geschwister. - Wenn Inhalte abgeschnitten werden, setzen Sie das Artboard mit
update_stylesaufheight: "fit-content", anstatt eine neue Höhe zu erraten.
Schriftarten
get_font_family_info({ fileId, familyNames: [...] })prüft auf dem Rechner installierte Schriftarten und Google Fonts. Rufen Sie es vor der ersten typografischen Gestaltung in einer Sitzung auf.- Schriftarten, die während einer Sitzung installiert werden (z. B. in
~/Library/Fontsauf macOS), werden erst nach einem Neustart von Paper angezeigt. Bitten Sie den Benutzer, Paper zu beenden und erneut zu öffnen. Beenden Sie es nicht selbst, da dies seinen Zustand verlieren kann. - Überprüfen Sie die Lizenz einer Schriftart, bevor sie auf einer öffentlichen Website erscheint. Viele Schriftarten von dafont.com sind nur für den persönlichen Gebrauch kostenlos.
- Um viele Schriftarten zu vergleichen, ohne Paper neu zu starten, rendern Sie Schriftproben lokal als transparente PNGs (z. B. mit Pillow), platzieren Sie sie als
paper-asset://-Bilder auf einem Schriftproben-Artboard und installieren Sie nur die Finalisten.
Organisation der Leinwand
- Wenn Sie an einem Design iterieren, duplizieren Sie das Artboard und ändern Sie die Kopie. Bearbeiten Sie frühere Boards nicht direkt: Benutzer möchten den Fortschritt sehen und bitten oft darum, zu einer früheren Version zurückzukehren. Ein Benutzer musste dies in einer Sitzung zweimal anfordern.
create_artboardundduplicate_nodeslegen neue Artboards an der besten leeren Stelle ab, was sie verstreut. Um sie anzuordnen, setzen Sietopundleftauf den Artboards mitupdate_styles. Halten Sie mindestens 80px Abstand zwischen Artboards.- Halten Sie Design-Explorationen getrennt von der gewählten Richtung: in ihrem eigenen Bereich der Leinwand oder auf einer eigenen Seite (
create_page, dannpageIdübergeben). Sie können nicht die Seite wechseln, die der Benutzer gerade ansieht. - Für Folien und Variantensätze verwenden Sie ein festes Raster (z. B. vier pro Zeile,
x = x0 + col × (width + gap)). Wenn ein Artboard hinzugefügt oder entfernt wird, ordnen Sie die restlichen neu an, sodass keine Lücken entstehen, und nummerieren Sie ihre Namen neu. - Für helle und dunkle Varianten setzen Sie die helle Reihe oben und die passende dunkle Version direkt darunter. Informationen zum Erstellen der dunklen Versionen finden Sie in references/dark-mode-variants.md.
- Geben Sie Artboards beschreibende Namen wie
Homepage · A (Editorial)oder04 · What are Workers?. Exportdateinamen werden daraus abgeleitet. - Knoten-IDs überdauern die Sitzung nicht. Schreiben Sie die Datei-ID, URL, das Benennungsschema, die Rasterformel und die Stilregeln in die
AGENTS.mddes Projekts, damit die nächste Sitzung darauf aufbauen kann.
Design zu Code
- Lesen Sie exakte Werte mit
get_jsx(format: "inline-styles"oder"tailwind") undget_computed_styles. Messen Sie niemals Größen oder Farben aus einem Screenshot. - Berechnete Farben können als
oklab(...)zurückkommen. Kopieren Sie sie wörtlich, anstatt sie umzuwandeln. - Vergleichen Sie die Seite nach dem Erstellen in einem echten Browser mit einem Paper-Export, im hellen und dunklen Modus.
Was der MCP nicht kann
- Dateien freigeben oder Berechtigungen verwalten. Der Benutzer teilt aus der Paper-App: Laden Sie Personen zum Team ein, damit sie bearbeiten können, oder senden Sie die Datei-URL für schreibgeschützten Zugriff.
- Dateien in Ordner verschieben. Neue Dateien von
create_filelanden außerhalb der Ordner des Benutzers, und der Benutzer muss sie hineinziehen. - Knoten zwischen Dateien verschieben.
move_nodesverschiebt nur innerhalb einer Datei, zwischen Seiten. Um den gesamten Inhalt einer Datei zu verschieben,create_file({ cloneFileId, name }), überprüfen Sie, ob Knoten- und Artboard-Anzahl übereinstimmen, und löschen Sie dann die Originale mitdelete_nodes. Bestätigen Sie dies mit dem Benutzer vor dem Löschen. - Die Seite wechseln, die der Benutzer gerade ansieht.
