Penpot Docker 部署与 MCP 服务配置文档
操作对象:192.168.36.136(SSH 用户 hello,Linux)
部署方式:Docker Compose
文档整理日期:根据实际服务器操作记录整理
0. 背景:原型工具的 AI 能力边界
0.1 为什么选 Penpot 做这次试验
AI 目前能够胜任从 ASCII 示意图、手绘草图、线稿到可交互 HTML 原型页面的全链路生成,产出可预览、可演示的网页原型。
但对于 Figma、Axure 这类专业原型工具:AI 不能直接原生生成 .rp(Axure)工程文件;Figma 有原生能力,但二者的能力边界存在明显差异。
Figma 生态:Figma 内置 Figma‑Make AI 能力,支持通过自然语言描述直接生成具备真实布局、图层、组件与交互逻辑的 Figma 设计稿,生成结果是画布内可编辑图层,可直接在 Figma 编辑器内继续修改迭代。同时配套官方 MCP,支持文本 / 代码写入 Figma 画布,也支持读取 Figma 文件输出前端代码。
Axure 兼容现状 :当前各类 AI 原型工具仅能输出网页原型、截图、设计文档等产物,无法直接生成 Axure 原生 .rp 工程文件,也不存在可以一键导入 Axure 即可二次编辑的兼容原型文件;AI 输出结果仅可作为设计参考,页面、元件、交互逻辑仍需要人工在 Axure 内重新搭建还原。
0.2 支持全流程的原型工具选型对比
针对「AI 全链路生成 + 可二次编辑」的需求,调研了其他支持全流程的原型工具:
| 工具 | 部署方式 | 费用 | MCP 能力 | 私有化部署 |
|---|---|---|---|---|
| 腾讯 Ardot | 在线编辑 | 目前免费,后期收费 | MCP 流程可以全打通 | 无私有化部署版 |
| Penpot | 开源免费,自己 Docker 部署 | 免费 | MCP 可读可写画布 | 支持(自托管) |
结论 :综合「开源免费、可私有化部署、MCP 可读可写画布」等条件,本期选择 Penpot 进行试验------即本文档所述在 192.168.36.136 上通过 Docker 部署 Penpot 并打通 MCP 服务。
1. 概述
本服务器通过 Docker Compose 部署了 Penpot(开源设计协作平台) 及其 MCP(Model Context Protocol)服务。
- Penpot 前端访问地址 :
http://192.168.36.136:9001 - Penpot MCP 端点 :
http://192.168.36.136:9001/mcp/stream - 数据目录 :
/data/penpot - MCP 容器镜像 :
penpotapp/mcp:2.17.1
部署完成后,MCP 服务已通过端到端验证:
- 握手(initialize)成功,返回
serverInfo: penpot 1.0.0 - 工具列表可获取(
execute_code、high_level_overview、penpot_api_info、export_shape等) tools/call调用成功,userToken 鉴权有效
2. 服务架构(容器清单)
| 容器名 | 镜像 | 端口映射 | 状态 | 作用 |
|---|---|---|---|---|
| penpot-penpot-postgres-1 | postgres:16 | 5432(内部) | Up(healthy) | 数据库 |
| penpot-penpot-redis-1 | redis:7 | 6379(内部) | Up | 缓存/消息 |
| penpot-penpot-backend-1 | penpotapp/backend:latest | 6060->6060 | Up | 后端 API |
| penpot-penpot-frontend-1 | penpotapp/frontend:latest | 9001->8080 | Up | 前端 + Nginx 反代(含 MCP 反代) |
| penpot-penpot-exporter-1 | penpotapp/exporter:latest | --- | Up | 文件导出 |
| penpot-penpot-assets-1 | minio/minio:latest | 9000(内部) | Up | 对象存储(S3 兼容) |
| penpot-penpot-mcp-1 | penpotapp/mcp:2.17.1 | ---(走前端反代) | Up | MCP 服务 |
网络拓扑要点:
- MCP 容器不直接对外暴露端口,由前端容器的 Nginx 统一反代到
/mcp/*路径; - 前端
9001端口对外提供 Penpot 网页与 MCP 端点。
3. 部署步骤
3.1 创建部署目录
bash
sudo mkdir -p /data/penpot/frontend-overrides
cd /data/penpot
3.2 编写 docker-compose.yaml
文件位置:/data/penpot/docker-compose.yaml
yaml
networks:
penpot:
driver: bridge
volumes:
penpot_data:
penpot_assets:
services:
penpot-postgres:
image: postgres:16
restart: always
networks:
- penpot
environment:
POSTGRES_INITDB_ARGS: "--data-checksums"
POSTGRES_DB: penpot
POSTGRES_USER: penpot
POSTGRES_PASSWORD: penpot
volumes:
- penpot_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U penpot"]
interval: 10s
timeout: 5s
retries: 5
penpot-redis:
image: redis:7
restart: always
networks:
- penpot
penpot-backend:
image: penpotapp/backend:latest
restart: always
networks:
- penpot
depends_on:
- penpot-postgres
- penpot-redis
environment:
PENPOT_FLAGS: "disable-email-verification disable-signup-on-invite disable-onboarding-on-invite disable-register enable-prepl-server enable-mcp enable-access-tokens"
PENPOT_PUBLIC_URI: "http://192.168.36.136:9001"
PENPOT_SECRET_KEY: "3f6867d47cefa13d829afa04fd193529cf723889a425d60a9e970be6a43b96a9"
PENPOT_DATABASE_URI: "postgresql://penpot/penpot?host=penpot-postgres&port=5432"
PENPOT_DATABASE_USERNAME: "penpot"
PENPOT_DATABASE_PASSWORD: "penpot"
PENPOT_REDIS_URI: "redis://penpot-redis:6379"
PENPOT_ASSETS_STORAGE_BACKEND: "assets-s3"
PENPOT_ASSETS_S3_BUCKET: "assets"
PENPOT_ASSETS_S3_REGION: "us-east-1"
PENPOT_ASSETS_S3_ENDPOINT: "http://penpot-assets:9000"
PENPOT_ASSETS_S3_ACCESS_KEY_ID: "penpot"
PENPOT_ASSETS_S3_SECRET_ACCESS_KEY: "penpot123"
ports:
- "6060:6060"
penpot-frontend:
image: penpotapp/frontend:latest
restart: always
environment:
PENPOT_FLAGS: "enable-mcp enable-access-tokens"
networks:
- penpot
depends_on:
- penpot-backend
ports:
- "9001:8080"
volumes:
- ./frontend-overrides/mcp.conf:/etc/nginx/overrides/server.d/mcp.conf:ro
penpot-exporter:
image: penpotapp/exporter:latest
restart: always
networks:
- penpot
depends_on:
- penpot-backend
environment:
PENPOT_PUBLIC_URI: "http://192.168.36.136:9001"
PENPOT_REDIS_URI: "redis://penpot-redis:6379"
PENPOT_SECRET_KEY: "3f6867d47cefa13d829afa04fd193529cf723889a425d60a9e970be6a43b96a9"
penpot-assets:
image: minio/minio:latest
restart: always
networks:
- penpot
environment:
MINIO_ROOT_USER: "penpot"
MINIO_ROOT_PASSWORD: "penpot123"
command: server /data
volumes:
- penpot_assets:/data
penpot-mcp:
image: "penpotapp/mcp:2.17.1"
restart: always
networks:
- penpot
3.3 MCP Nginx 反代配置
文件位置:/data/penpot/frontend-overrides/mcp.conf
该文件以只读方式挂载到前端容器的
/etc/nginx/overrides/server.d/mcp.conf,随前端 Nginx 一起生效。
nginx
# Penpot MCP - Streamable HTTP (client URL: /mcp/stream -> container /mcp)
location = /mcp/stream {
proxy_pass http://penpot-mcp:4401/mcp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# Penpot MCP - PluginBridge WebSocket (browser File->MCP Connect)
location /mcp/ {
proxy_pass http://penpot-mcp:4402;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
说明:
/mcp/stream:MCP Streamable HTTP 端点,反代到 MCP 容器4401端口的/mcp;/mcp/:MCP PluginBridge WebSocket 端点(浏览器端 File → MCP Connect 使用),反代到容器4402端口;- 必须关闭
proxy_buffering/proxy_cache(SSE 流式响应需要),并延长读写超时。
3.4 启动服务
bash
cd /data/penpot
sudo docker compose up -d
3.5 查看运行状态
bash
sudo docker compose ps
# 或
sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}'
4. MCP 服务配置
4.1 启用 MCP 相关 Flags
MCP 功能依赖两个关键 Flag:
| 位置 | Flag | 说明 |
|---|---|---|
| penpot-backend | enable-mcp |
启用后端 MCP 支持 |
| penpot-backend | enable-access-tokens |
允许生成/使用 userToken 访问令牌 |
| penpot-frontend | enable-mcp |
启用前端 MCP(显示 Integrations 菜单) |
| penpot-frontend | enable-access-tokens |
前端允许 MCP 令牌功能 |
4.2 获取 userToken(访问令牌)
操作路径(浏览器 UI):
- 打开
http://192.168.36.136:9001并登录; - 点击右上角 头像 → 你的账户(Your account);
- 进入 Integrations → MCP Server;
- 将 MCP Server 开关切换为 ON,复制生成的连接 URL / Token。
⚠️ 注意:userToken 是访问 Penpot API 的凭证,等同于账号权限,请妥善保管,不要提交到代码仓库或公开文档。
4.3 MCP 客户端连接配置
在支持 MCP 的客户端(Cursor / Claude Code / VS Code 等)中配置:
json
{
"mcpServers": {
"penpot": {
"url": "http://192.168.36.136:9001/mcp/stream?userToken=<你的userToken>"
}
}
}
协议要点(Streamable HTTP):
- 请求头需携带
Accept: application/json, text/event-stream; initialize握手成功后,响应头会返回Mcp-Session-Id,后续请求必须复用该会话 ID;- 握手顺序:
initialize→notifications/initialized→tools/list/tools/call; - 支持的工具:
execute_code、high_level_overview、penpot_api_info、export_shape等。
5. 验证结果(实测)
| 验证项 | 结果 |
|---|---|
| 容器全部运行 | ✅ 7 个容器 Up(postgres healthy) |
| 前端 9001 端口可访问 | ✅ http://192.168.36.136:9001 |
| MCP initialize 握手 | ✅ 返回 serverInfo: penpot 1.0.0 |
| tools/list 获取工具列表 | ✅ 成功返回工具清单 |
| tools/call(high_level_overview) | ✅ 返回完整 Penpot API 文档 |
| userToken 鉴权 | ✅ 有效 |
6. 使用注意与前提
- execute_code 生效前提 :AI 要能读写设计文件,还需要用户在 Penpot 编辑器内安装 MCP 插件(编辑器左下角 → 插件市场 → MCP 插件),打开目标设计项目并让插件连接到本 MCP 服务器(浏览器端 File → MCP Connect)。
- 备份文件 :修改 compose 前已保留备份:
/data/penpot/docker-compose.yaml.bak(最初版本)/data/penpot/docker-compose.yaml.bak2(加入 MCP 配置前)
- 安全建议:如部署到公网,建议为 9001 端口配置防火墙/反向代理 HTTPS,并定期轮换 SECRET_KEY 与 userToken。
7. 配置中遇到的坑与解决方法
按实际配置过程踩坑顺序整理,均为本次部署中真实遇到并已解决的问题。
7.1 MCP 容器端口不通,客户端连不上
现象 :MCP 客户端访问 http://192.168.36.136:9001/mcp/stream 超时/连接失败;直接访问 MCP 容器 4401 端口也不通。
原因 :MCP 容器本身没有对外映射端口(compose 中未配置 ports),且在容器网络 penpot 内,宿主机/外部无法直接访问。
解决 :不暴露 MCP 容器端口,统一由前端容器的 Nginx 反代。在 mcp.conf 中把 /mcp/stream 转发到 http://penpot-mcp:4401/mcp(容器间通过 compose 网络名 penpot-mcp 互通),对外只需开放前端 9001 端口。反代配好后重启前端容器生效。
7.2 SSE 流式响应卡住/握手无响应(proxy_buffering 未关闭)
现象 :initialize 握手请求发出后长时间无响应,或 tools/list 只返回部分内容后挂起。
原因 :MCP 的 Streamable HTTP 使用 SSE(Server-Sent Events)流式返回,而 Nginx 默认开启 proxy_buffering,会把响应缓冲起来,流式数据无法实时推给客户端。
解决:在两个 location 中都显式关闭缓冲并关闭缓存:
nginx
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
同时在 proxy_pass 时保留 proxy_http_version 1.1 与 Upgrade/Connection 头,保证 SSE 与后续 WebSocket 均正常。
7.3 只在 backend 加 enable-mcp 无效(前端没有 Integrations 菜单)
现象 :backend 已加 enable-mcp,但浏览器里账户页面看不到 Integrations → MCP Server,拿不到 userToken。
原因 :MCP 的开关需要 前后端同时开启 。前端容器缺 enable-mcp 时,UI 不渲染 MCP 相关菜单;缺 enable-access-tokens 时,即使后端开了也不生成 token。
解决 :backend 与 frontend 的 PENPOT_FLAGS 都加上 enable-mcp enable-access-tokens(见 4.1 对照表),然后 docker compose up -d 重建两个容器。
7.4 MCP 镜像版本与 Penpot 版本不匹配
现象 :MCP 容器起来后握手失败,或 tools/list 返回 404/工具不完整。
原因 :MCP 服务是随 Penpot 版本发布的独立镜像(penpotapp/mcp:<版本>),MCP 功能要求 Penpot ≥ 2.14;若 MCP 镜像版本与后端版本差距过大,API 契约对不上。
解决 :先把 Penpot 升级到 ≥ 2.14(本机为 2.17.1),再让 MCP 镜像与后端版本保持一致,即 penpotapp/mcp:2.17.1,避免用 latest 造成版本漂移。
7.5 Nginx location 匹配顺序导致 404 / 转发到错误端口
现象 :/mcp/stream 请求被 404,或返回的 JSON 不符合 MCP 协议。
原因 :location /mcp/(前缀匹配)与 location = /mcp/stream(精确匹配)并存时,若精确匹配写错/被前缀规则覆盖,/mcp/stream 会被转发到 4402(PluginBridge WebSocket),而 4402 不处理 Streamable HTTP 请求。
解决 :Streamable HTTP 端点必须用精确匹配 location = /mcp/stream,确保其优先级高于前缀匹配的 /mcp/;并用 proxy_pass http://penpot-mcp:4401/mcp;(注意带 URI 后缀 /mcp),把 4401 的 /mcp 路径暴露成外部 /mcp/stream。
7.6 改完 mcp.conf 不生效
现象 :修改 frontend-overrides/mcp.conf 后刷新页面/重连 MCP,行为仍是旧的。
原因 :mcp.conf 以只读 方式挂载(:ro)进前端容器,文件被 Nginx 加载进内存;只改宿主机文件,运行中的 Nginx 不会热加载。
解决:修改后必须重建/重启前端容器使其重新挂载并加载配置:
bash
cd /data/penpot
sudo docker compose up -d --force-recreate penpot-frontend
# 或全量重建
sudo docker compose up -d
验证方式:sudo docker compose exec penpot-frontend nginx -T | grep -A5 mcp
7.7 拿不到 userToken / 生成的 URL 不可用
现象 :Integrations 里 MCP Server 开关打开后没有 token,或拿到的 URL 指向 localhost,外部客户端连不上。
原因:
- 前后端缺
enable-access-tokensflag(见 7.3); PENPOT_PUBLIC_URI配成了localhost或内网错误地址,生成的连接 URL 基于该地址拼接。
解决 :确保 PENPOT_PUBLIC_URI: "http://192.168.36.136:9001"(backend、exporter 均配置);MCP 客户端连接时如 URL 不对,直接改用 http://192.168.36.136:9001/mcp/stream?userToken=<token> 格式手动拼接。token 只在开关 ON 时显示一次,务必先复制保存。
7.8 防火墙/端口未放行,外部访问 9001 失败
现象 :宿主机上 curl localhost:9001 正常,但局域网其它机器访问 http://192.168.36.136:9001 不通。
原因:服务器防火墙(ufw/firewalld/云安全组)未放行 9001 端口。
解决:按实际环境放行端口:
bash
# ufw 示例
sudo ufw allow 9001/tcp
# firewalld 示例
sudo firewall-cmd --permanent --add-port=9001/tcp && sudo firewall-cmd --reload
放行后用 curl -v http://192.168.36.136:9001/mcp/stream 从外部验证。
7.9 execute_code 调用报错 / 无文件可操作
现象 :tools/call 中 execute_code、high_level_overview 等工具能列出,但实际调用报错或返回空。
原因 :MCP 服务端只是入口,真正读写设计文件需要浏览器端的 MCP 插件 与当前打开的项目建立桥接;未安装插件或未打开项目时,工具无对象可操作。
解决 :在 Penpot 编辑器内安装 MCP 插件 (左下角 → 插件市场),打开目标设计文件,通过 File → MCP Connect 让插件连接到本服务器 MCP;连接成功后,AI 的 execute_code 才能读写当前项目。
7.10 修改前未备份,改坏后无法回滚
现象 :编辑 compose 时缩进/语法出错,docker compose up -d 报 YAML 解析失败,服务起不来。
原因:YAML 对缩进敏感,新增 service 或环境变量时层级写错很常见。
解决:养成先备份再修改的习惯,本次操作保留了两份备份:
bash
sudo cp /data/penpot/docker-compose.yaml /data/penpot/docker-compose.yaml.bak # 最初版本
sudo cp /data/penpot/docker-compose.yaml /data/penpot/docker-compose.yaml.bak2 # 加 MCP 前
改坏时直接回滚: sudo cp /data/penpot/docker-compose.yaml.bak2 /data/penpot/docker-compose.yaml && sudo docker compose up -d
另外修改后先做语法检查:cd /data/penpot && sudo docker compose config --quiet
8. 目录结构速览
/data/penpot/
├── docker-compose.yaml # 主编排文件(当前生效)
├── docker-compose.yaml.bak # 备份:最初版本
├── docker-compose.yaml.bak2 # 备份:加 MCP 前
└── frontend-overrides/
└── mcp.conf # MCP Nginx 反代配置(挂载进前端容器)