Penpot Docker 部署与 MCP 服务配置文档

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_codehigh_level_overviewpenpot_api_infoexport_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):

  1. 打开 http://192.168.36.136:9001 并登录;
  2. 点击右上角 头像 → 你的账户(Your account);
  3. 进入 Integrations → MCP Server;
  4. 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;
  • 握手顺序:initializenotifications/initializedtools/list / tools/call;
  • 支持的工具:execute_codehigh_level_overviewpenpot_api_infoexport_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,外部客户端连不上。

原因:

  1. 前后端缺 enable-access-tokens flag(见 7.3);
  2. 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/callexecute_codehigh_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 反代配置(挂载进前端容器)

相关推荐
天蓝不会忘记021 小时前
k8s集群部署的方法原理
云原生·容器·kubernetes
九硕智慧建筑一体化厂家1 小时前
直流智能照明|全场景节能升级!打造安全低碳的智慧建筑光环境
运维·笔记·安全·智慧城市
蓝速科技2 小时前
蓝速科技丨数智人终端选型指南:后台运维如何决定长期运营成本
运维·科技
奇树谦2 小时前
HDD 为什么适合顺序大文件读写:从 RAID 5 到 RAID 50 的原理与性能分析
linux·运维·网络
hi_ro_a2 小时前
Linux基础开发工具详解
linux·运维·服务器
xrlfreedom2 小时前
大厂 MCP 面试实录:本地 Server 远程化改造中的技术选型与落地实践
docker·json schema·mcp
嵌入式阿蔡2 小时前
Linux_01:交叉编译与开发环境实战——从单片机跨到 Linux 的第一道坎
linux·运维·网络·单片机·嵌入式硬件·嵌入式实时数据库
红球yyds3 小时前
用anisble搭建k8s集群及原理
云原生·容器·kubernetes
Pocker_Spades_A3 小时前
终端里也能斗地主:Linux部署Ratel,从本地对战到公网联机
linux·运维·服务器