Paper 應用程式
Paper 是一款設計工具,其畫布是真正的 HTML 與 CSS。Paper 桌面應用程式執行一個本機 MCP 伺服器,讓代理程式能夠讀取畫布並將 HTML 寫入其中作為設計節點。
該伺服器自帶其專屬指南。此技能涵蓋該指南遺漏的內容:在實際工作階段中學到的經驗教訓。
開始之前
- 檢查工具是否存在:在 codemode 中執行
await describeNamespace("mcp__paper")。如果命名空間為空,或者ALL_TOOLS中沒有任何mcp__paper__*條目,則表示工作階段未連線,通常是因為工作階段啟動時 Paper 並未開啟。伺服器位於http://127.0.0.1:29979/mcp,並且僅在 Paper 開啟時才會回應。pi mcp list(從 bash 執行) 會顯示伺服器本身是否健康。如果是健康的,工作階段無法自行重新連線:請要求使用者執行/mcp reconnect paper(在 pi 中),如果無效,則執行/reload。 - 每個工作階段載入官方指南一次:
get_guide({ topic: "paper-mcp-instructions" })。請遵循指南:在進行新設計前發布設計簡報,每次write_html呼叫只寫入一個視覺群組,使用螢幕截圖進行審查,並在完成時呼叫finish_working_on_nodes。其他主題:mobile-status-bar、figma-import、image-generation。 - 找到檔案。
list_resources({ limit: 200 })會在沒有開啟任何檔案的情況下列出檔案。將fileId傳遞給每個檔案範圍的呼叫。get_basic_info({ fileId })可以在檔案不是前景分頁的情況下運作,因此您幾乎不需要使用open_file,它會在使用者的應用程式中開啟一個分頁。 - 如果專案中有
AGENTS.md,請在探索前先在其中查詢 Paper 檔案 ID 和畫布慣例。
從 codemode 呼叫 Paper
- 在單一腳本中使用
await或按序輪次執行相依的 Paper 呼叫。絕對不要在同一輪次中將它們作為平行工具呼叫發出。平行呼叫是造成過去工作階段中大多數失敗的原因,而且每次都被誤讀為 Paper 的錯誤:例如「找不到節點」(針對剛由同級呼叫建立的節點)、子節點以亂序排列、匯出遺漏最近的寫入內容,以及read因 ENOENT 錯誤失敗 (針對尚未寫入的匯出內容)。 - 每個檔案範圍的工具都需要在 codemode 宣告中包含
fileId,即使伺服器的說明指出可以省略它。省略它會導致驗證失敗。只有get_basic_info和get_selection可以接受不帶fileId。 - 在首次使用某個工具前,先執行
describeTool("mcp__paper__<name>")。參數型狀無法猜測:get_font_family_info接受familyNames: string[]、set_text_content和update_styles接受updates陣列,而export則接受nodes映射。 - 檔案範圍的工具會回傳一個純字串:一個
{ file, contentHash }標頭 JSON 區塊,後面接著主體。不帶檔案的工具 (list_resources、create_file、open_file、get_guide) 會回傳{ content: [{ type: "text", text }] }。不要從結果中用正則表達式擷取第一個"id":它是檔案 ID,將其作為節點 ID 傳遞會導致「找不到節點」錯誤。 - 檔案範圍的失敗和驗證錯誤會拋出例外。某些非檔案範圍的失敗 (例如「請開啟一個 Paper 檔案以使用此工具。」) 則會以
isError: true來解決。不要將 Paper 呼叫包在.catch(() => {})中:否則錯誤的參數型狀會悄悄地失敗,而編輯動作永遠不會顯示。 - 「您的 Paper 檔案目前已與伺服器中斷連線」表示寫入會失敗,但讀取仍可運作。在過去的工作階段中,等待並重試並未解決此問題。請要求使用者將 Paper 檔案帶回前景,然後重試。
get_screenshot在 codemode 中不會回傳圖片:其結果會被扁平化為標頭字串。請使用下方的shot()輔助函式,它會在同一個腳本中匯出 PNG 並讀取它。當測試工具直接呼叫 MCP 工具 (而非透過 codemode) 時,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。請檢查它。 |
輔助函式
將這些內容貼到 codemode 腳本的頂端,或儲存一次並在後續呼叫中 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 宣告輔助函式,因為 eval 內部的 const 和 let 不會洩漏出來。store() 僅在腳本成功時才會保留值,因此請在一個不做其他事情的腳本中儲存輔助函式。
螢幕截圖與匯出
- 在與編輯動作不同的 codemode 呼叫中進行螢幕截圖。如果匯出與寫入動作在同一個腳本中,它可能會在 Paper 渲染之前執行,導致 PNG 顯示遺失或過時的內容。
export會寫入到~/Downloads/<artboard name>.png,對於其他比例會加上@2x或@0.5x,而當具有該名稱的檔案已存在時,則會加上(1)、(2)等後綴。?和:等字元會變成_。始終使用回傳的filePath。不要使用ls -t ~/Downloads或名稱萬用字元來挑選檔案:這可能會選到來自較早執行的過期匯出。- 在讀取匯出檔案後移動或刪除它們,以免它們堆積在
~/Downloads中。不要動您未建立的檔案。 - 使用
0.5x進行審查螢幕截圖以節省 token,使用2x作為交付成果 (1920×1080 的畫板會變成 3840×2160)。 export_combined_pdf會將多個畫板合併成一個 PDF,並按畫布位置排序。export也支援mp4/webm影片,以及 SVG 節點的svg格式、jpg、webp、avif和pdf格式。- 若要將畫板轉換為 Google 簡報,請參閱 references/slides-export.md。
撰寫 HTML
write_html 工具說明列出了規則 (行內樣式、flex、無 margin、無 display: grid、無表格、使用 layer-name 作為圖層名稱)。其他發現:
- 空的
<div>會變成矩形節點,即使有display: flex也是如此,而矩形不能有子節點。寫入其中會失敗,並出現「無法擁有子節點」的錯誤。請將容器連同其第一個子節點一起寫入,或者使用replace模式的write_html來取代矩形。 - 不支援富文字。對於語法高亮的程式碼或包含一個彩色詞語的句子,請建立一個由獨立文字節點組成的 flex 行,每個 token 一個節點,每個節點都設定
white-space: pre。程式碼則變成每行一個 row。使用輔助函式來產生 HTML,而不是手動撰寫。 - 帶有漸層圖層的
background會忽略backgroundColor。請將基底顏色作為最後一個圖層加入:linear-gradient(#F6F7F9, #F6F7F9)。 - 行內
<svg>適用於圖示、標誌和連線線,並且可以接受fill/stroke顏色和 token 變數。請直接使用品牌標誌的 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 行中等寬的欄位需要
flex: 1; flex-basis: 0。否則,其中一個欄位的內距 (padding) 會使其比同層級的其他欄位更寬。 - 如果內容被裁剪,請使用
update_styles將畫板設為height: "fit-content",而不是猜測一個新的高度。
字型
get_font_family_info({ fileId, familyNames: [...] })會檢查機器上安裝的字型以及 Google Fonts。請在工作階段中進行第一個排版樣式設定之前呼叫它。- 在工作階段進行中安裝的字型 (例如,在 macOS 上安裝到
~/Library/Fonts) 在重新啟動 Paper 之前不會顯示。請要求使用者結束並重新開啟 Paper。不要自行終止它,因為這可能會遺失他們的狀態。 - 在將字型用於公開網站之前,請檢查其授權。許多 dafont.com 上的字型僅供個人使用。
- 若要在不重新啟動 Paper 的情況下比較多種字型,請在本機將樣本渲染為透明 PNG (例如使用 Pillow),將它們作為
paper-asset://圖片放置在樣本畫板上,然後僅安裝最終入選者。
組織畫布
- 在迭代設計時,請複製畫板並修改副本。不要直接編輯先前的畫板:使用者希望看到進展,而且經常要求回溯到較早的版本。有位使用者曾在一個工作階段中要求這樣做兩次。
create_artboard和duplicate_nodes會將新畫板放置在最合適的空位,這會導致它們分散各處。若要排列它們,請使用update_styles設定畫板的top和left屬性。畫板之間至少保留 80px 的間距。- 將設計探索與所選方向分開:放在畫布上自己的區域中,或放在自己的頁面上 (
create_page,然後傳遞pageId)。您無法切換使用者正在檢視的頁面。 - 對於投影片組和變體集,請使用固定網格 (例如,每行四個,
x = x0 + col × (width + gap))。當新增或移除畫板時,請重新排列其餘畫板,使其之間沒有空隙,並重新編號它們的名稱。 - 對於淺色和深色變體,請將淺色行放在上方,並將相應的深色版本直接放在每個淺色版本的下方。若要製作深色版本,請參閱 references/dark-mode-variants.md。
- 為畫板賦予描述性名稱,例如
首頁 · A (編輯)或04 · 什麼是 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刪除原始檔案。在刪除前請先與使用者確認。 - 切換使用者正在查看的頁面。
