feat(penpot-mcp): add export_frame_png wrapping Penpot's exporter RPC directly #185
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#185
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?
Goal
Give the
designer/design-revieweragents a workingmcp__penpot__export_frame_pngtool so they can export Penpot frames as PNG (and SVG via the same path) and commit them todesign/mockups/*. Implementers on downstream stories can thenReadthe PNG directly (Claude Code's multimodal Read handles images) and build UI against a locked visual reference.Today our fork (
penpot-mcp-server) does not implement export at all —canvas.py:27explicitly defers it with the note that "recent Penpot builds render PNGs client-side via wasm; there's no stable server-side export RPC on the instance we target". That note is now stale: the self-hosted Penpot exporter (CT 245) was brought online inproxmox-iac@9dc800b+@94a0ffa(2026-04-20). Logs confirm the exporter renders PNGs successfully:The remaining gap is an MCP wrapper in our fork. The upstream Penpot MCP does expose
export_frame_png, but it uses a stale/assets/by-id/<id>URL pattern that 404s on Penpot 2.14. Implementing our own wrapper lets us call Penpot's exporter RPC directly over the documented interface, bypass the stale URL-rewrite bug, and keep the feature in our fork's MIT-licensed control.Acceptance criteria
New MCP tool
penpot-mcp-servergains a new toolexport_frame_png(file_id, page_id, object_id, scale=1.0)insrc/penpot_mcp/tools/canvas.py(or a newexport.pyif cleaner)${PENPOT_BASE_URL}/api/export— posts the export shape spec, waits for the rendered resource, downloads bytes, returns{"content_base64": str, "mime_type": "image/png", "width": int, "height": int}export_frame_svgtool for vector export (same shape,mime_type: "image/svg+xml",contentas a string not base64)services/api.py— reuse the existingAuthorization: Token <PAT>headerURL / routing resilience
/assets/by-id/<id>fetch pattern that upstream MCP uses — that path 404s on our Penpot build. Either poll the exporter's response directly for bytes, OR use the documented/api/rpc/command/<command>//api/export-resourcepaths that match what the Penpot frontend uses internallyexporter-unavailable(502 from/api/export) — surface clearly in the tool response so the agent doesn't keep retryingunable-to-upload-resource— surface the backend assets-permission hintAllowlist + integration
apps/server/src/agent-runner.ts::FORGEJO_TOOLS_ALLOWLIST-analogue for Penpot — wait, that's forgejo. The Penpot MCP doesn't have a per-tool allowlist on our side; the fork registers every defined tool unconditionally. Just make sure the new tools register cleanly.scripts/smoke-creds.sh(the container smoke probe) adds a check thatmcp__penpot__export_frame_pngreturns successfully against a seeded test frame. Runs only ondesigner/design-reviewercontainers (the ones withpenpot_mcp: true)Tests
penpot-mcp-server/tests/tools/test_export.py: mock the Penpot HTTP API, fireexport_frame_png, assert bytes match a fixture PNGfbb66a40-7b05-40bc-88a6-886a66a80a05in file689d7fa4-f94b-81d4-8007-e38d1256f1ae, confirm PNG writes to disk and visually matches the Penpot UI renderingDocs
penpot-mcp-server/src/penpot_mcp/tools/canvas.py:27comment block — remove the "export deferred" note, point at the new toolpenpot-mcp-server/CHANGELOG.mdwith an entry describing the new toolexport_frame_png/_svgavailability on design agentspyproject.tomlto0.7.0+claude-hooks.<n>Downstream work (separate stories)
design/mockups/m19/<frame-slug>.{png,svg}after this tool shipspenpot-mcp-serverDocker build stage to pick up the new version; rebuild designer + design-reviewer imagesOut of scope
/assets/by-id/<id>bug — not our problem, and we route around it by using Penpot's internal RPCs directly.export_frame_pdf/ other formats — PNG + SVG are sufficient for the dashboard/frame handoff flow.References
/var/lib/penpot/assets→ UID 1001:1001. Should be added as an ansible task in a follow-up IaC commit./assets/by-id/968c1582-…returns 404 on Penpot 2.14.penpot-mcp-server/src/penpot_mcp/tools/canvas.py:27-30("recent Penpot builds render PNGs client-side via wasm; there's no stable server-side export RPC…")