Communitygithub.com

zeeke/paper-app-skill

Paperデザインアプリ(paper.design)のローカルMCPサーバーを介して作業を行います。Paperでのデザイン、モックアップ、バリアントの探索、アートボードやスライドの編集、スクリーンショットの取得やPNG/PDF/ビデオへの書き出し、ファイルの移動や複製、カスタムフォントやローカル画像の使用、デザインのコード化などに使用します。コードモードの癖、結果の解析、書き出しとスクリーンショットの回避策、キャンバスの整理、MCPでできないことをカバーします。

paper-app-skill とは?

このスキルは、Paperデザインツール(paper.design)のローカルMCPサーバーと連携して、AIエージェントがPaperのキャンバスを読み取り、HTMLをデザインノードとして書き込むことを可能にします。PaperはHTMLとCSSをベースにしたキャンバスを持つデザインツールで、このスキルは公式ガイドではカバーされていない、実際のセッションで得られた知見(コードモードでの呼び出し方、結果のパース方法、スクリーンショットや書き出しの回避策、キャンバスの整理方法、MCPではできないことなど)を提供します。主にClaude Codeなどのコードモード対応エージェントで使用され、設計の反復、バリアントの作成、デザインからコードへの変換、フォント管理など、Paperを使ったデザインワークフロー全体をエージェントが効率的に実行できるようになります。このスキルは特に、PaperのMCPサーバーとコードモード間の非互換性や並行呼び出しの落とし穴に対する実践的な解決策に重点を置いており、安定した自動化を実現します。

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

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

プレビュー

スキル README より

paper-app-skill preview

ドキュメント

Paperアプリ

Paperは、キャンバスが実際のHTMLとCSSで構成されたデザインツールです。PaperデスクトップアプリはローカルMCPサーバーを実行し、エージェントがキャンバスを読み取り、その中にデザインノードとしてHTMLを書き込むことを可能にします。

サーバーには独自のガイドが付属しています。このスキルは、そのガイドで省略されている部分、つまり実際のセッションを通じて苦労して学んだことをカバーします。

始める前に

  1. ツールが存在することを確認します。コードモードで await describeNamespace("mcp__paper") を実行します。名前空間が空の場合、または ALL_TOOLS に mcp__paper__* エントリがない場合、セッションは接続されていません。通常、セッション開始時にPaperが開いていなかったためです。サーバーは http://127.0.0.1:29979/mcp にあり、Paperが開いている間だけ応答します。pi mcp list(bashから実行)は、サーバー自体が正常かどうかを示します。正常な場合、セッションは自動的に再接続できません。ユーザーにpiで /mcp reconnect paper を実行するか、それが機能しない場合は /reload を実行するよう依頼してください。
  2. セッションごとに一度公式ガイドを読み込みます。get_guide({ topic: "paper-mcp-instructions" }) を実行します。それに従います。新しいデザインの前にデザインブリーフを投稿し、write_html の呼び出しごとに1つのビジュアルグループを書き込み、スクリーンショットで確認し、完了したら finish_working_on_nodes を呼び出します。その他のトピック:mobile-status-bar、figma-import、image-generation。
  3. ファイルを見つけます。list_resources({ limit: 200 }) は、ファイルを開いていなくてもファイルを一覧表示します。すべてのファイルスコープの呼び出しに fileId を渡します。get_basic_info({ fileId }) はフォアグラウンドタブではないファイルでも機能するため、ユーザーのアプリでタブを開く open_file が必要になることはほとんどありません。
  4. プロジェクトに AGENTS.md がある場合は、探索する前にPaperファイルIDとキャンバス規則を確認します。

コードモードからPaperを呼び出す

  • 依存関係のあるPaperの呼び出しは、1つのスクリプト内で await を使用して、または順次ターンで実行します。同じターン内で並行ツール呼び出しとして発行しないでください。並行呼び出しは過去のセッションでほとんどの失敗の原因となっており、それぞれがPaperのバグと誤解されていました。兄弟呼び出しが作成したばかりのノードが見つからない、子ノードがスクランブルされた順序で配置される、最近の書き込みが欠落したエクスポート、まだ書き込まれていないエクスポートで read がENOENTで失敗するなどです。
  • すべてのファイルスコープツールは、サーバーの指示では省略可能とされていますが、コードモード宣言に fileId が必要です。省略すると検証に失敗します。fileId を受け付けないのは get_basic_info と get_selection のみです。
  • ツールを最初に使用する前に describeTool("mcp__paper__<name>") を実行します。引数の形状は推測できません。get_font_family_info は familyNames: string[] を取り、set_text_content と update_styles は updates 配列を取り、export は nodes マップを取ります。
  • ファイルスコープのツールは1つのプレーン文字列を返します。{ file, contentHash } ヘッダーJSONブロックと、それに続く本文です。ファイルがないツール(list_resources、create_file、open_file、get_guide)は { content: [{ type: "text", text }] } を返します。結果から最初の "id" を正規表現で抽出しないでください。それはファイルIDであり、ノードIDとして渡すと「Node not found」で失敗します。
  • ファイルスコープの失敗と検証エラーはスローされます。一部の非ファイルの失敗(「このツールを使用するにはPaperファイルを開いてください。」など)は、代わりに isError: true で解決します。Paperの呼び出しを .catch(() => {}) でラップしないでください。間違った引数形状がサイレントに失敗し、編集が表示されなくなります。
  • 「Paperファイルが現在サーバーから切断されています」は、読み取りは機能するが書き込みが失敗することを意味します。過去のセッションでは、スリープと再試行では修正されませんでした。ユーザーにPaperファイルをフォアグラウンドに戻すよう依頼し、その後再試行してください。
  • get_screenshot はコードモードでは画像を返しません。その結果はヘッダー文字列にフラット化されます。以下の shot() ヘルパーを使用してください。これはPNGをエクスポートし、同じスクリプト内で読み取ります。ハーネスがコードモードを介さずにMCPツールを直接呼び出す場合は、get_screenshot 画像は届きます。

本文を解析した後の便利な結果の形状:

ツール本文
create_artboard{ id, name, worldX, worldY, width, height }
write_html{ createdNodes: [{ id, name, component }] }、トップレベルノードのみ
get_tree_summary{ summary }、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スタイルがそのコンテキストで無効な場合に ignoredStyles をリストします。確認してください。

ヘルパー

コードモードスクリプトの先頭に貼り付けるか、一度保存して後続の呼び出しで eval します:

var F = "<fileId>";
var txt = r => typeof r === "string" ? r : (r?.content ?? []).map(c => c.text ?? "").join("\n");
// ファイルスコープのツールが先頭に付加する { file, contentHash } ヘッダーを削除します。
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;
// ノードをPNGにエクスポートし、表示してから ~/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 });
};

呼び出し間でヘルパーを再利用するには:store("paper", helperSource) を実行し、後続の各スクリプトの先頭で eval(load("paper")) を実行します。ヘルパーは var で宣言します。const と let は eval 内から漏れ出さないためです。store() はスクリプトが成功した場合にのみ値を保持するため、ヘルパーは他に何もしないスクリプトで保存します。

スクリーンショットと書き出し

  • スクリーンショットは編集とは別のコードモード呼び出しで行います。書き込みと同じスクリプト内でのエクスポートは、Paperがそれらをレンダリングする前に実行される可能性があり、PNGにコンテンツが欠落または古くなる可能性があります。
  • export は ~/Downloads/<アートボード名>.png に書き出します。他のスケールの場合は @2x または @0.5x が付き、同じ名前のファイルが存在する場合は (1)、 (2) のサフィックスが付きます。? や : などの文字は _ になります。常に返された filePath を使用してください。ls -t ~/Downloads や名前のグロブでファイルを選択しないでください。以前の実行からの古いエクスポートを取得する可能性があります。
  • 読み取り後はエクスポートを移動または削除して、~/Downloads に溜まらないようにします。自分で作成していないファイルはそのままにします。
  • 確認用のスクリーンショットには 0.5x を使用してトークンを節約し、成果物には 2x(1920×1080のアートボードは3840×2160になります)を使用します。
  • export_combined_pdf は、キャンバス上の位置順にアートボードを1つのPDFに結合します。export は mp4/webm ビデオ、SVGノードの場合は svg、jpg、webp、avif、pdf も行います。
  • アートボードをGoogleスライドに変換するには、references/slides-export.md を参照してください。

HTMLの書き込み

write_html ツールの説明にはルール(インラインスタイル、フレックスボックス、margin なし、display: grid なし、テーブルなし、レイヤー名の layer-name)がリストされています。追加の知見:

  • 空の <div> は、display: flex でもRectangleノードになり、Rectangleは子を持つことができません。そこに書き込むと「cannot have children」で失敗します。コンテナとその最初の子を一緒に書き込むか、write_html を replace モードで使用してRectangleを置き換えます。
  • リッチテキストはサポートされていません。シンタックスハイライトされたコードや、1つの単語だけ色が異なる文の場合は、トークンごとに個別のテキストノードのフレックス行を作成し、それぞれに white-space: pre を設定します。コードは1行につき1行になります。手動で記述するのではなく、ヘルパーを使用してHTMLを生成します。
  • グラデーションレイヤーを持つ background は backgroundColor を無視します。基本色を最後のレイヤーとして追加します:linear-gradient(#F6F7F9, #F6F7F9)。
  • インライン <svg> は、アイコン、ロゴ、接続線に使用でき、fill/stroke の色とトークン変数を受け入れます。ブランドロゴのSVGはリンクする代わりにインライン化します。
  • SVGパスの塗りは、find_nodes と get_computed_styles(空の {} を返す)からは見えません。get_tree_summary で SVGVisualElement "Path" ノードを見つけ、update_styles({ styles: { fill } }) で色を変更します。これは機能します。
  • ローカル画像には、paper-asset:// スキームを使用した絶対パスが必要です:<img src="paper-asset:///private/tmp/spec/foo.png">。
  • フレックス行の等幅列には flex: 1; flex-basis: 0 が必要です。そうしないと、1つの列にパディングがあると兄弟列よりも幅が広くなります。
  • コンテンツがクリップされる場合は、新しい高さを推測する代わりに、update_styles でアートボードを height: "fit-content" に設定します。

フォント

  • get_font_family_info({ fileId, familyNames: [...] }) は、マシンにインストールされているフォントとGoogleフォントをチェックします。セッションでの最初のタイポグラフィスタイリングの前に呼び出します。
  • セッションの途中(macOSで ~/Library/Fonts にインストールするなど)でインストールされたフォントは、Paperを再起動するまで表示されません。ユーザーにPaperを終了して再度開くよう依頼します。自分で強制終了しないでください。ユーザーの状態が失われる可能性があります。
  • 公開サイトで使用する前に、フォントのライセンスを確認してください。dafont.com の多くのフォントは個人使用のみ無料です。
  • Paperを再起動せずに多くのフォントを比較するには、ローカルで(Pillowなどを使用して)透明PNGに見本をレンダリングし、paper-asset:// 画像として見本アートボードに配置し、最終候補のみをインストールします。

キャンバスの整理

  • デザインを反復するときは、アートボードを複製してコピーを変更します。以前のボードをその場で編集しないでください。ユーザーは進行状況を確認したいと考えており、以前のバージョンに戻すように依頼することがよくあります。あるユーザーはセッションでこれを2回依頼しなければなりませんでした。
  • create_artboard と duplicate_nodes は、新しいアートボードを最適な空のスポットにドロップするため、散在します。これらを整理するには、update_styles でアートボードに top と left を設定します。アートボード間は少なくとも80pxの間隔を保ちます。
  • デザインの探索は、選択した方向とは別に、キャンバスの独自の領域、または独自のページ(create_page を実行し、pageId を渡す)に保管します。ユーザーが表示しているページを切り替えることはできません。
  • デッキやバリアントセットには、固定グリッド(例:1行あたり4つ、x = x0 + col × (width + gap))を使用します。アートボードが追加または削除されたら、残りを再フローしてギャップがなくなるようにし、名前を付け直します。
  • ライトモードとダークモードのバリアントの場合、ライトモードの行を上部に、対応するダークモードのバージョンをそれぞれの真下に配置します。ダークモードバージョンを作成するには、references/dark-mode-variants.md を参照してください。
  • アートボードには、Homepage · A (Editorial) や 04 · What are Workers? などの説明的な名前を付けます。エクスポートのファイル名はこれらから生成されます。
  • ノードIDはセッションを超えて存続しません。ファイルID、URL、命名規則、グリッド計算式、スタイルルールをプロジェクトの AGENTS.md に書き込んで、次のセッションが引き継げるようにします。

デザインからコードへ

  • get_jsx(format: "inline-styles" または "tailwind")と get_computed_styles で正確な値を読み取ります。スクリーンショットからサイズや色を測定しないでください。
  • 計算された色は oklab(...) として返されることがあります。変換せずにそのままコピーします。
  • ページを構築したら、実際のブラウザでPaperのエクスポートと比較し、ライトモードとダークモードの両方で確認します。

MCPでできないこと

  • ファイルの共有や権限の管理。ユーザーはPaperアプリから共有します。編集を許可するにはチームにユーザーを招待するか、ファイルURLを送信して表示専用アクセスを許可します。
  • ファイルをフォルダに移動。create_file からの新しいファイルはユーザーのフォルダ外に作成され、ユーザーがドラッグする必要があります。
  • ファイル間でのノードの移動。move_nodes はファイル内でのみ、ページ間での移動が可能です。ファイル全体の内容を移動するには、create_file({ cloneFileId, name }) を実行し、ノードとアートボードの数が一致することを確認してから、delete_nodes で元のノードを削除します。削除する前にユーザーに確認します。
  • ユーザーが見ているページの切り替え。

関連スキル