Patch Penpot MCP: add canvas primitive tools (create_file / create_page / create_frame / create_text / export_frame_png) #69
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#69
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
User story
As the designer agent (from #56), I want the patched Penpot MCP to
expose canvas-primitive creation tools — file, page, frame, text, and
PNG export — so that I can actually build UI/UX mockups programmatically
instead of aborting every dispatch with "no
create_*tools" (assurfaced on #62 issuecomment-5443).
Context
The claude-hooks fork of
penpot-mcpatpenpot-mcp-server/currentlyexposes 10 tools, all of which either read files or mutate
design tokens:
Every tool requires a
file_idand operates on pre-existing Penpotfiles via the
update-fileRPC withadd-token/mod-token/del-tokenchange-ops.Not present (what this ticket adds):
create_file— bootstrap a file the agent can write tocreate_page,create_frame,create_text— the primitives thedesign-implement.mdskill assumes existexport_frame_png— the sanity-check step the skill runs beforeposting its handoff comment
Without these, the designer dispatched on #62 correctly aborts: it has
no way to create a
smoke-hellopage, an 800×600hello-frame, or thecentred
HELLOtext the AC calls for, and refuses to invent work itcan't verify.
Infrastructure already in place
As of PRs #66, #67, and #68, the pipeline below the MCP tool layer is
fully wired:
area:design → designerwebhook routing (tested)designer/design-reviewerwithpenpot_mcp: true(dispatch intoclaude-hooks:dev)Accept: application/jsonon every RPC call (transit-json bug)seedContainerClaudeJsonensures the CLI has anoauthAccountbinding in its bind-mount dir
So the moment these tools exist, re-dispatching the designer on #62
should land the full AC in one run.
Acceptance criteria
MCP — new tools (
penpot-mcp-server/src/penpot_mcp/tools/canvas.py)Pattern mirrors
tokens.py: each tool assembles the appropriatechange-op(s) and dispatches via
api.apply_changes.create_file(project_id, name)— top-level RPC (create-file),returns
{file_id, name}.create_page(file_id, name)— emitsadd-pagechange-op;returns
{page_id, name}.create_frame(file_id, page_id, name, x, y, width, height, fill_color?)— emits
add-objwith typeframe; returns{shape_id}.create_text(file_id, page_id, parent_id, content, x, y, font_family, font_size, font_weight, fill_color)— emits
add-objwith typetext+ the Penpot text content tree;returns
{shape_id}.export_frame_png(file_id, page_id, frame_id, scale?=1)—renders the frame (RPC command TBD during implementation —
probably
get-file-object-thumbnailor the dedicated exportcommand); returns
{png_bytes_base64, width, height}.MCP — supporting plumbing
list_projects(team_id)andlist_teams()— needed bydesign-implementto find a target project beforecreate_file. Simple RPC wrappers.list_files(project_id)orsearch_files(name)— so the skill's"reuse existing file by name" rule is reachable.
Tests
api.apply_changesthat captures the change-op and verifies the shape.
pytest.mark.livetest against theexisting
claude-hooks — dashboardfile(
689d7fa4-f94b-81d4-8007-e39c5c82f66c) that creates a newtemp page, adds a frame + text, exports a PNG, and deletes
the page. Gated behind
PENPOT_ACCESS_TOKENenv — no-opwhen unset.
Smoke (closes the #56 loop)
smoke-hellopage,the 800×600
hello-framewith fill#1A1B26, theHELLOtext with fill
#C0CAF5on the existing file UUID(
689d7fa4-f94b-81d4-8007-e39c5c82f66c), then posts ahandoff comment with the deep-link and token hexes.
scripts/smoke-creds.sh designerwith a shape-toolpresence assertion (grep for
mcp__penpot__create_frameinthe tool listing).
Documentation
penpot-mcp-server/README.md: new Canvas tools section.penpot-mcp-server/CHANGELOG.md: bump to 0.5.0 with the fulltool list.
Out of scope
from #60's acceptance criteria.
The five primitives above cover the
design-implementskill'shappy path; richer shapes can come later per-demand.
(see penpot-mcp-server/README.md § Patch history).
References
— designer's 4th run, same tool-gap diagnostic.
.claude.jsonseedclaude-hooks — dashboard(
file-id = 689d7fa4-f94b-81d4-8007-e39c5c82f66c), created on #55.penpot-mcp-server/src/penpot_mcp/services/changes.py(existing pattern for token change-ops) and Penpot's upstream source
for
add-obj/add-pageop shapes.Dependencies
#67, #68 merge).
designeragent producing any mockup that goesbeyond tokens; closes the operational smoke on #62.
main.Suggested breakdown (for
boss)Roughly one PR per bullet:
list_teams/list_projects/list_filesreads (smallest,unlocks exploration).
create_file+create_page(RPC +add-pagechange-op).create_frame(add-objchange-op for frame type).create_text(add-objwith the text-content subtree — probablythe trickiest; Penpot text is a nested paragraph/run structure).
export_frame_png(separate RPC, no change-op).create_file(project_id, name)#73export_frame_png(file_id, page_id, frame_id, scale?)#74Mostly superseded — the canvas primitives landed before this ticket opened:
list_teams,list_projects,list_files,create_page,create_frame,create_textcreate_file(project_id, name, features?)penpot-mcp-server/src/penpot_mcp/tools/canvas.py(377 lines) exposes all of the above, andscripts/smoke-creds.shalready probes their presence.Remaining scope =
export_frame_png, which is carved out into #74. Closing this parent in favour of tracking the last bit there.