Penpot MCP Server
AI-powered design tool access for self-hosted Penpot via Model Context Protocol.
What is this?
An MCP server that gives AI agents (like Claude Code, Cursor, or any MCP-compatible client) full programmatic access to your self-hosted Penpot instance. AI can read, create, modify, and export design elements — from rectangles and text to full UI components — all through natural language.
Think of it as the bridge between your AI assistant and your design tool.
Problems it solves
| Problem | Solution |
|---|---|
| Manual design work | AI creates UI components, layouts, and prototypes directly in Penpot |
| No programmatic API for Penpot | 66 tools covering projects, shapes, text, exports, comments, and more |
| Design-to-code gap | Generate CSS from any shape, export to SVG/PNG, extract design tokens |
| Repetitive tasks | Batch operations — rename shapes, update colors, create variants |
| Design system maintenance | Read/write components, colors, typographies programmatically |
Architecture
graph TB
AI["AI Agent<br/>(Claude Code, Cursor, etc.)"]
MCP["penpot-mcp<br/>Python + FastMCP<br/>:8787"]
PG["PostgreSQL<br/>penpot-postgres<br/>:5432"]
BE["Penpot Backend<br/>penpot-backend<br/>:6060"]
FE["Penpot Frontend<br/>penpot-frontend<br/>:8080"]
EX["Penpot Exporter<br/>penpot-exporter<br/>:6061"]
AI -->|"Streamable HTTP"| MCP
MCP -->|"Direct SQL reads<br/>(asyncpg)"| PG
MCP -->|"RPC API writes<br/>(httpx)"| BE
MCP -->|"Export requests"| BE
BE -->|"Render"| EX
FE -->|"Proxy"| BE
style AI fill:#7c3aed,color:#fff
style MCP fill:#2563eb,color:#fff
style PG fill:#16a34a,color:#fff
style BE fill:#ea580c,color:#fff
style FE fill:#ea580c,color:#fff
style EX fill:#ea580c,color:#fff
Dual-access strategy:
- Reads go directly to PostgreSQL via
asyncpg— fast and reliable - Writes go through Penpot's RPC API via
httpx— ensures proper change tracking and undo history - Exports use Penpot's built-in exporter (headless Chromium) for pixel-perfect SVG/PNG output
Tech Stack
| Component | Technology | Purpose |
|---|---|---|
| Language | Python 3.13 | Runtime |
| MCP SDK | FastMCP | Protocol handling, tool registration |
| Database | asyncpg | Direct PostgreSQL access |
| HTTP Client | httpx | Penpot RPC API calls |
| Validation | Pydantic v2 | Automatic parameter validation |
| Package Manager | uv | Fast Python dependency management |
| Container | Docker | Deployment alongside Penpot |
Quick Start
Prerequisites
- Self-hosted Penpot running via Docker Compose (official guide)
- Docker and Docker Compose v2 installed
- Access tokens enabled in your Penpot instance (see Enable Access Tokens)
Option A: Automated Setup
git clone https://github.com/ancrz/penpot-mcp-server.git
cd penpot-mcp-server
chmod +x setup.sh
./setup.sh
The script will guide you through configuration, build the Docker image, and start the server.
Option B: Manual Setup
1. Clone the repository
git clone https://github.com/ancrz/penpot-mcp-server.git
cd penpot-mcp-server
2. Create your configuration
cp .env.example .env
Edit .env with your Penpot details:
# Your Penpot access token (see "Enable Access Tokens" below)
PENPOT_ACCESS_TOKEN=your-token-here
# Your Penpot database password (from your Penpot docker-compose.yml)
PENPOT_DB_PASS=your-db-password
# Public URL where you access Penpot in the browser
PENPOT_PUBLIC_URL=http://localhost:9001
3. Add the MCP service to your Penpot Docker stack
Add the penpot-mcp service definition to your existing Penpot docker-compose.yml. See docker-compose.penpot.yml for the complete service definition to copy.
4. Build and start
docker compose up -d --build penpot-mcp
5. Verify it's running
curl -s http://localhost:8787/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
You should see a JSON response with the server capabilities.
Connect to Claude Code
Add this to your project's .mcp.json file (or ~/.claude.json for global access):
{
"mcpServers": {
"penpot": {
"type": "streamable-http",
"url": "http://localhost:8787/mcp"
}
}
}
Restart Claude Code. You should see 66 tools from the penpot server listed when you run /mcp.
Example prompts
Once connected, you can ask Claude things like:
- "List my Penpot projects"
- "Create a login form with email/password fields and a submit button"
- "Export the Login Card frame as SVG"
- "What colors are defined in the design system?"
- "Add a comment at position (100, 200) saying 'Review this layout'"
Tools Overview
The server provides 66 tools across 11 categories. See TOOLS.md for the complete reference with all parameters.
| Category | Count | Examples |
|---|---|---|
| Projects & Teams | 4 | list_projects, list_teams, list_files, search_files |
| File Operations | 9 | create_file, get_file_pages, rename_file, duplicate_file |
| Shape Reading | 6 | get_shape_tree, get_shape_details, get_shape_css, search_shapes |
| Components & Tokens | 4 | get_design_tokens, get_colors_library, get_typography_library |
| Comments | 6 | create_comment, reply_to_comment, resolve_comment |
| Media & Fonts | 3 | upload_media, list_media_assets, list_fonts |
| Database & Advanced | 3 | query_database, get_webhooks, get_profile |
| Snapshots | 2 | create_snapshot, get_snapshots |
| Export | 2 | export_frame_png, export_frame_svg |
| Shape Creation | 8 | create_rectangle, create_frame, create_text, create_path |
| Shape Modification | 12 | set_fill, set_stroke, set_layout, move_shape, resize_shape |
| Text Operations | 5 | set_text_content, set_font, set_font_size, set_text_align |
| Advanced Analysis | 2 | get_file_raw_data, compare_revisions |
Configuration Reference
All settings are via environment variables. See .env.example for a template.
| Variable | Default | Description |
|---|---|---|
PENPOT_BASE_URL |
http://penpot-frontend:8080 |
Internal Penpot URL (Docker network) |
PENPOT_PUBLIC_URL |
http://localhost:9001 |
Public URL where you access Penpot in browser |
PENPOT_ACCESS_TOKEN |
— | API access token (preferred auth method) |
PENPOT_EMAIL |
— | Penpot login email (fallback auth) |
PENPOT_PASSWORD |
— | Penpot login password (fallback auth) |
PENPOT_DB_HOST |
penpot-postgres |
PostgreSQL host |
PENPOT_DB_PORT |
5432 |
PostgreSQL port |
PENPOT_DB_NAME |
penpot |
Database name |
PENPOT_DB_USER |
penpot |
Database user |
PENPOT_DB_PASS |
— | Database password |
MCP_HOST |
0.0.0.0 |
MCP server bind address |
MCP_PORT |
8787 |
MCP server port |
MCP_LOG_LEVEL |
info |
Log level (debug/info/warning/error) |
Enable Access Tokens
Penpot requires a feature flag to enable API access tokens.
1. Update your Penpot .env file
Add enable-access-tokens to your PENPOT_FLAGS:
PENPOT_FLAGS=enable-login-with-password enable-registration enable-access-tokens
2. Restart Penpot
docker compose restart penpot-backend penpot-frontend
3. Create a token
- Open Penpot in your browser
- Click your avatar (bottom-left) → Access Tokens
- Click "Generate new token"
- Give it a name (e.g., "MCP Server")
- Copy the token and paste it into your
.envasPENPOT_ACCESS_TOKEN
Penpot Docker Integration
The MCP server runs as a Docker container alongside your existing Penpot stack. You need to add it to your Penpot docker-compose.yml.
See docker-compose.penpot.yml for the exact service definition to add. The key points:
- It connects to the
penpotDocker network (same as other Penpot services) - It depends on
penpot-postgres(with health check) andpenpot-backend - It exposes port
8787on localhost only (127.0.0.1:8787:8787) - Environment variables reference Docker internal hostnames
Development
Running locally (outside Docker)
# Install uv if needed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync
# Run the server (needs .env configured for local access)
uv run penpot-mcp
For local development, point PENPOT_DB_HOST and PENPOT_DB_PORT to your host-mapped PostgreSQL port, and PENPOT_BASE_URL to http://localhost:9001.
Running tests
uv sync --group dev
uv run pytest tests/ -v
Project structure
penpot-mcp-server/
├── src/penpot_mcp/
│ ├── server.py # FastMCP entry point, 66 tool registrations
│ ├── config.py # Pydantic Settings configuration
│ ├── services/
│ │ ├── db.py # asyncpg connection pool
│ │ ├── api.py # httpx RPC API client
│ │ ├── changes.py # Penpot change operations builder
│ │ └── transit.py # Transit+JSON decoder
│ ├── tools/
│ │ ├── projects.py # Team & project queries
│ │ ├── files.py # File CRUD operations
│ │ ├── shapes.py # Shape reading & search
│ │ ├── create.py # Shape creation
│ │ ├── modify.py # Shape modification
│ │ ├── text.py # Text operations
│ │ ├── export.py # PNG/SVG export
│ │ ├── components.py # Components & design tokens
│ │ ├── comments.py # Comments & collaboration
│ │ ├── media.py # Media assets & fonts
│ │ ├── database.py # Raw SQL queries
│ │ └── advanced.py # File raw data & revision comparison
│ └── transformers/
│ ├── css.py # Shape → CSS conversion
│ ├── svg.py # Shape → SVG conversion
│ └── layout.py # Layout → CSS flexbox/grid
├── tests/
│ ├── conftest.py
│ ├── test_projects.py
│ ├── test_files.py
│ ├── test_shapes.py
│ └── test_e2e_login_form.py
├── pyproject.toml
├── Dockerfile
├── .env.example
├── setup.sh
├── docker-compose.penpot.yml
├── TOOLS.md
└── LICENSE
License
This project is licensed under the Apache License 2.0.
Acknowledgments
- Penpot — The open-source design platform
- Model Context Protocol — The protocol standard
- FastMCP — Python MCP SDK