ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Penpot Docker 部署与 MCP 服务配置文档:TaoToken 统一 Key 接入实践

Penpot Docker 部署与 MCP 服务配置文档:TaoToken 统一 Key 接入实践 1. 为什么要在 Penpot 自托管里接一层统一 KeyPenpot 是一个开源的设计协作平台支持自托管用 Docker 就能拉起来。它从 2.14 版本开始内置了 MCPModel Context Protocol服务意味着 AI 客户端可以读取甚至写入画布内容——这对做原型、做设计稿自动化的人来说很实用。但真正把它跑起来之后你会发现一个绕不开的问题MCP 客户端连接时需要 userToken而如果你同时用 Cursor、Claude Code、VS Code 好几个工具每个工具都要单独配一遍 tokentoken 一多就散得到处都是轮换一次要改五六个地方。这篇文档要解决的就是这个场景Penpot 用 Docker Compose 自托管MCP 服务通过前端 Nginx 反代暴露然后所有 AI 工具的调用统一走 TaoToken 的 Key/API 通道。这样你只需要维护一份 Key换工具、换机器都不用重新翻 Penpot 后台去复制 token。适合已经在跑 Penpot、或者准备自托管 Penpot 并想接 AI 工作流的人。下面从容器编排、MCP 反代配置、TaoToken 接入到连通性验证一步步给可复制的片段。2. TaoToken 前置准备拿到统一 Key 和接入地址在动 Penpot 的 compose 文件之前先把 TaoToken 这边的通道准备好。TaoToken 的作用是给多个 AI 工具提供统一的 Key 和 API 入口你不需要在每个客户端里分别填不同的凭证。先去官网注册并登录https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。登录后在控制台里创建一个 API Key这个 Key 就是后面所有 MCP 客户端要用的统一凭证。创建 Key 的入口在控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。点新建给它起个能认出来的名字比如penpot-mcp生成后立刻复制保存——多数平台只在创建时显示一次。API 的基础地址是https://taotoken.net/api 注意这个地址不带任何查询参数直接作为 base_url 使用。如果你用的是 Claude Code 这类需要 Anthropic 兼容端点的工具接入文档在这里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有不同客户端的配置示例。注意TaoToken 的 Key 等同于你的调用凭证不要写进会提交到 Git 的 compose 文件里。建议用环境变量或.env文件加载后面配置片段里我会用${TAOTOKEN_API_KEY}这种占位方式。如果你只是想先验证模型通道通不通可以打开模型对话页面直接发一条消息试试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。确认能正常返回之后再往下做 Penpot 这边的配置。3. Penpot Docker 部署与 MCP 反代配置3.1 目录结构与 compose 骨架先在服务器上建好部署目录。假设你用的是一个内网地址比如192.168.36.136实际替换成你自己的 IP 或域名。sudo mkdir -p /data/penpot/frontend-overrides cd /data/penpotdocker-compose.yaml的核心是七个容器postgres、redis、backend、frontend、exporter、assetsMinIO、mcp。MCP 容器不直接对外暴露端口由 frontend 的 Nginx 统一反代。下面是可以直接用的骨架重点看 backend 和 frontend 的 flags以及 mcp 服务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: 换成你自己的随机密钥 PENPOT_DATABASE_URI: postgresql://penpot/penpot?hostpenpot-postgresport5432 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: 换成你自己的随机密钥 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]这里有两个关键点。第一enable-mcp和enable-access-tokens必须前后端都加只加 backend 的话浏览器里看不到 Integrations 菜单拿不到 token。第二MCP 镜像版本要和后端版本对齐别用latest否则 API 契约可能对不上。3.2 MCP 的 Nginx 反代配置MCP 容器内部监听两个端口4401 是 Streamable HTTP 端点4402 是 PluginBridge WebSocket。外部统一通过 frontend 的 9001 端口访问。新建/data/penpot/frontend-overrides/mcp.conf# Streamable HTTP 端点外部 /mcp/stream - 容器 4401 的 /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; } # PluginBridge WebSocket浏览器端 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; }proxy_buffering off和proxy_cache off是必须的MCP 用 SSE 流式返回Nginx 默认缓冲会把流卡住。location /mcp/stream用的是精确匹配优先级高于前缀匹配的/mcp/这样 Streamable HTTP 请求不会被错误转发到 4402。3.3 启动与状态检查cd /data/penpot sudo docker compose up -d sudo docker compose ps正常情况下你会看到七个容器都是 Uppostgres 显示 healthy。如果哪个起不来先看日志sudo docker compose logs --tail50 penpot-mcp sudo docker compose logs --tail50 penpot-frontend4. TaoToken 统一 Key 接入 MCP 客户端4.1 获取 Penpot 的 userToken打开http://192.168.36.136:9001登录点右上角头像进入账户设置找到 Integrations → MCP Server把开关切到 ON复制生成的连接 URL 和 token。这个 token 只在开关打开时显示一次先存好。4.2 在 MCP 客户端里配置统一通道以 Cursor 或 Claude Code 为例MCP 服务端的配置骨架长这样。这里的关键是把 Penpot 的 MCP 端点和 TaoToken 的 Key 都放进配置让客户端通过统一通道调用{ mcpServers: { penpot: { url: http://192.168.36.136:9001/mcp/stream?userToken你的penpot_userToken, env: { TAOTOKEN_API_KEY: ${TAOTOKEN_API_KEY}, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }如果你用的是 Claude Code需要 Anthropic 兼容端点参考接入文档里的写法https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。长期跑编码或 Agent 任务的话Coding Plan 会更划算配置入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。4.3 协议要点MCP 的 Streamable HTTP 有几个必须遵守的点请求头要带Accept: application/json, text/event-streaminitialize握手成功后响应头会返回Mcp-Session-Id后续请求必须复用这个会话 ID握手顺序是initialize→notifications/initialized→tools/list或tools/call。Penpot MCP 支持的工具包括execute_code、high_level_overview、penpot_api_info、export_shape等。5. 验证 MCP 连通性与成功结果配置完之后用 curl 从外部验证一下 MCP 端点是否可达curl -v -X POST http://192.168.36.136:9001/mcp/stream \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:test,version:1.0}}}预期返回里能看到serverInfo类似{ jsonrpc: 2.0, id: 1, result: { protocolVersion: 2024-11-05, serverInfo: { name: penpot, version: 1.0.0 }, capabilities: { tools: {} } } }握手成功后用返回的Mcp-Session-Id发tools/listcurl -X POST http://192.168.36.136:9001/mcp/stream \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -H Mcp-Session-Id: 上一步返回的会话ID \ -d {jsonrpc:2.0,id:2,method:tools/list,params:{}}能返回工具清单execute_code、high_level_overview等就说明 MCP 服务通了。再调一次tools/call用high_level_overview应该能拿到完整的 Penpot API 文档。userToken 鉴权有效的话这一步不会返回 401。6. 本篇常见错误排查6.1 MCP 容器端口不通客户端连不上MCP 容器在 compose 里没有映射端口宿主机直接访问 4401 是不通的。正确做法是统一走 frontend 的 Nginx 反代只对外开 9001。反代配好后要重启 frontend 容器生效。6.2 SSE 流式响应卡住握手无响应initialize发出去长时间没反应或者tools/list返回一半就挂起。原因是 Nginx 默认开了proxy_buffering把 SSE 流缓冲了。解决就是在两个 location 里都显式关掉proxy_buffering和proxy_cache并延长读写超时到 3600s。6.3 只在 backend 加 enable-mcp 无效浏览器里看不到 Integrations → MCP Server 菜单拿不到 userToken。MCP 开关需要前后端同时开启frontend 的PENPOT_FLAGS也要加enable-mcp enable-access-tokens然后重建两个容器。6.4 MCP 镜像版本与 Penpot 版本不匹配MCP 容器起来后握手失败或者tools/list返回 404。MCP 是随 Penpot 版本发布的独立镜像要求 Penpot ≥ 2.14。把后端升到对应版本MCP 镜像也用相同版本号别用latest。6.5 Nginx location 匹配顺序导致 404/mcp/stream被 404或者返回的 JSON 不符合 MCP 协议。原因是精确匹配写错被前缀匹配的/mcp/覆盖请求被转发到了 4402。Streamable HTTP 端点必须用location /mcp/streamproxy_pass要带/mcp后缀。6.6 改完 mcp.conf 不生效mcp.conf是以只读方式挂载进容器的改宿主机文件后 Nginx 不会热加载。必须重建 frontend 容器cd /data/penpot sudo docker compose up -d --force-recreate penpot-frontend验证配置有没有加载进去sudo docker compose exec penpot-frontend nginx -T | grep -A5 mcp6.7 拿不到 userToken 或 URL 不可用Integrations 里开关打开后没有 token或者拿到的 URL 指向 localhost。检查PENPOT_PUBLIC_URI是不是配成了内网正确地址backend 和 exporter 都要配。token 只在开关 ON 时显示一次务必先复制保存。6.8 防火墙没放行 9001宿主机上curl localhost:9001正常但局域网其他机器访问不通。按实际环境放行端口sudo ufw allow 9001/tcp # 或 sudo firewall-cmd --permanent --add-port9001/tcp sudo firewall-cmd --reload6.9 execute_code 调用报错或无文件可操作工具能列出来但实际调用报错或返回空。MCP 服务端只是入口真正读写设计文件需要浏览器端的 MCP 插件和当前打开的项目建立桥接。在 Penpot 编辑器左下角进插件市场装 MCP 插件打开目标设计文件通过 File → MCP Connect 连接。6.10 修改前没备份改坏无法回滚YAML 对缩进敏感改坏了docker compose up -d会报解析失败。养成先备份的习惯sudo cp /data/penpot/docker-compose.yaml /data/penpot/docker-compose.yaml.bak改完先做语法检查cd /data/penpot sudo docker compose config --quiet出问题直接回滚备份文件再up -d。7. 把 Key 收拢到一处之后整套跑通之后你手里其实只有两份凭证Penpot 的 userToken 和 TaoToken 的 API Key。前者管画布读写后者管模型调用通道。多工具切换时不用再翻 Penpot 后台复制 token换机器也只需要把环境变量带过去。如果后面要加新的 AI 客户端接入文档里有现成的配置模板可以抄https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。需要新建或轮换 Key 的时候控制台在https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。长期跑 Agent 任务的话Coding Plan 的额度模型比按次调用更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。
返回列表