MCP server for self-hosted Penpot — provides AI agents with full access to design files, shapes, components, design tokens, comments, and more. Reads from PostgreSQL for speed, writes via Penpot RPC API for safety.
The Penpot MCP server demonstrates moderate definition quality with consistent naming patterns and documented parameters. However, there are significant gaps in output schema documentation, parameter descriptions lack depth, and error handling is not visible in the source provided. 15 tools are registered with names starting with action verbs (list_, get_, create_, delete_, search_, rename_, duplicate_), which follows the naming convention baseline (90% of A+ tools). Descriptions are present for all tools (range 50-180 chars, within the 10-1024 baseline) and all input parameters have type declarations and descriptions. However, parameter descriptions are brief and generic (averaging 40-60 chars vs the 72 char baseline), lacking constraint details, format specifications, and dependency hints. The most critical gap: no output schemas are documented for any tool, violating the core pattern that 'LLMs need to know what fields to expect so they can plan downstream tool calls.' All tools return JSON-serialized strings, but the internal structure (field names, types, nesting) is not declared. Additionally, the source code provided is incomplete, tool implementations are imported from separate modules (e.g., `from penpot_mcp.tools.projects import list_teams`) but the actual implementation logic is not visible, preventing verification of error handling, security controls, and data transformation. Given this incomplete visibility, per-tool scores are conservative.
Create a new design file in a project.
Create a new project in a team.
Delete a file (moves to trash).
Duplicate an existing file.
Get revision history of a file.
List shared libraries linked to a file.
Get all pages in a file with their object counts.
Output schemas completely undocumented. No documentation of what fields, types, or structure each tool returns. All tools serialize to JSON strings but internal schema is opaque.
Parameter descriptions are generic and lack constraint details. Examples: 'The file UUID' (no format/length specified), 'Filter by team UUID' (no guidance on how to obtain team IDs), 'Max entries to return' (no range specified for limit). Descriptions average 40-60 chars vs 72 char baseline.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 52 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 55 | - | v1 |
Get detailed metadata for a file (counts, team, project, versions).
List all objects on a page, optionally filtered by type.
Get the hierarchical tree of shapes on a page.
List all files in a project.
List projects, optionally filtered by team ID.
List all teams in the Penpot instance with member and project counts.
Rename an existing file.
Search files by name across all projects.
No error handling guidance visible in source. No documentation of what errors tools return, how to recover, or actionable next steps. Agents cannot distinguish retryable from fatal errors.
No pagination parameters on list tools. list_teams, list_projects, list_files, get_page_objects lack limit, offset/page, or cursor parameters. Large result sets will blow context window.
Tool implementations not visible in provided source. Core logic is imported from separate modules (e.g., `from penpot_mcp.tools.projects import list_teams`). Cannot verify error handling, input validation, security controls, or actual output schemas.
No confirmation/dry-run support for destructive operations. delete_file has DESTRUCTIVE risk but no description of confirmation flow, undo capability, or recovery steps. rename_file, duplicate_file, and create operations lack idempotency documentation.
Tool annotations missing. No toolAnnotations in FastMCP definition. Tools like delete_file (DESTRUCTIVE) and get_file_summary (READ_ONLY) should carry readOnlyHint and destructiveHint annotations per current spec.
No dependency hints between tools. Many tools return IDs (file_id, project_id, team_id) that other tools require as input, but no documentation of the lookup sequence or prerequisite calls. Agents will waste rounds discovering which tool produces which ID.
shape_type parameter in get_page_objects uses free-form string instead of enum. Allowed values (rect, circle, frame, text, group, path, image, svg-raw, bool) are listed in description only, inviting LLM hallucination of invalid types.