Penpot MCP 插件开发指南:搭建 WebSocket 桥接层并运行本地调试服务器
Penpot MCP 插件开发指南搭建 WebSocket 桥接层并运行本地调试服务器【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot导读Penpot MCP Plugin 是一个运行在 Penpot 编辑器内部的官方配套插件它通过 WebSocket 连接 Penpot MCP Server让服务端能够借助 Penpot 的 Plugin API 在真实文档中执行任务例如执行 JS 代码操作画布对象。本文将完整说明该插件的定位与双向消息链路、三种核心命令依赖安装、构建、启动本地调试服务器的用法并结合仓库源码剖析其双运行模式、任务分发、断线重连与心跳机制帮助你在本地把插件跑起来并理解它与 MCP Server 的协作边界。一、插件在 Penpot MCP 架构中的角色该插件是 Penpot MCP Server 的前端执行代理插件 README 对它的定位描述得非常清晰它随 MCP Server 一同交付通过 WebSocket 与 MCP Server 建立连接从而使 MCP Server 能借助 Penpot Plugin API 在 Penpot 中执行任务。从仓库结构看MCP 相关能力分为三部分彼此依赖关系明确模块位置职责MCP Servermcp/packages/server/面向 LLM/客户端暴露 MCP 工具维护 nREPL 等连接MCP Pluginmcp/packages/plugin/本文主体在 Penpot 内部运行执行 Server 下发的任务共享类型mcp/packages/common/定义插件与 Server 之间的任务参数、响应类型其中 ExecuteCodeTaskHandler 从共享包导入ExecuteCodeTaskParams、ExecuteCodeTaskResultData等类型说明 Plugin 与 Server 通过mcp/packages/common/src约定的同一套任务协议通信参数结构由common包统一定义。因此插件本身的源码位于 mcp/packages/plugin/其工程为一个基于 Vite TypeScript 的独立前端构建单元入口被拆成插件主体与UI 页面两部分src/plugin.tsPenpot 插件主入口注册任务处理器并打开 UIsrc/main.tsUI 页面逻辑负责 WebSocket 连接的建立与维护index.html插件 iframe UI连接按钮、状态胶囊、执行状态面板。二、环境准备与前置条件插件使用 pnpm 作为包管理器并复用仓库根级 workspace。其独立依赖声明在 package.json运行时依赖penpot/plugin-types与penpot/plugin-styles版本均为 1.5.0前者提供 Penpot Plugin API 的类型定义后者提供与 Penpot 官方界面一致的样式开发依赖vite、typescript、cross-env、vite-live-preview用于构建、类型检查与本地调试。确保仓库顶层已经安装过依赖该仓库在根目录及各子工程均以 pnpm workspace 组织即可进入插件目录执行安装命令。三、三步快速上手安装、构建与启动文档给出一组非常精简的命令流程实际对应三种不同开发状态1. 安装依赖pnpm install该命令在mcp/packages/plugin/目录下执行会基于 pnpm-lock.yaml 拉取插件的类型库、样式库以及 Vite 构建工具链。2. 构建项目pnpm run build对应 package.json 中的build: tsc vite build --config vite.release.config.ts即先运行 TypeScript 类型检查再以发布配置执行 Vite 构建。vite.release.config.ts通过mergeConfig继承基础配置并清空插件列表产物将按 vite.config.ts 中rollupOptions.input的约定输出plugin.js插件入口与index.htmlUI并写入dist/。构建产物可直接上传至 Penpot 作为插件加载其元信息由 public/manifest.json 描述{ name: Penpot MCP Plugin, code: plugin.js, icon: icon.jpg, version: 2, description: This plugin enables interaction with the Penpot MCP server, permissions: [content:read, content:write, library:read, library:write, comment:read, comment:write] }从清单可见该插件属于 Penpot 插件清单 v2 格式向 Penpot 申请了内容content、资源库library与评论comment的读写权限这些权限正是 MCP Server 通过任务指令操作文档所必需的。3. 启动本地开发服务器pnpm run startstart命令实际执行vite build --watch --config vite.config.ts见 package.json即以 watch 模式构建并启动 Vite preview 服务器文档明确说明其地址为http://localhost:4400端口与主机由 vite.config.ts 中preview.host/preview.port控制默认localhost:4400并开启了cors: true以便 Penpot 插件 iframe 正常加载资源。四、构建时的关键编译期配置插件构建依赖两组编译期注入常量理解它们对调试本地连接至关重要见 vite.config.ts常量来源默认值含义PENPOT_MCP_WEBSOCKET_URL环境变量WS_URIhttp://localhost:4402插件 UI 默认连接的 MCP Server WebSocket 地址PENPOT_MCP_VERSION仓库根package.json的version随包版本用于与 Penpot 客户端版本做一致性校验例如要让插件连接其它地址的 MCP Server可通过WS_URI环境变量覆盖默认端点后重新构建。插件的plugin.ts中还会读取另一个编译期常量PENPOT_MCP_VERSION与当前 Penpot 版本做比对以提示版本不匹配风险。五、双运行模式集成式远程 MCP 与本地显式加载plugin.ts源码开篇即判断插件运行环境const isIntegratedRemoteMcp !!mcp;这揭示了插件的两种运行模式其核心区别在 UI 是否可见hidden: isIntegratedRemoteMcp集成式远程 MCP 模式Penpot 官方集成了远程 MCP Server插件被以隐藏方式自动加载penpot.ui.open的hidden置为true连接 URL 与 Token 由集成环境通过mcp.getServerUrl()/mcp.getToken()提供不需要用户手动干预。本地显式加载模式无集成环境mcp全局对象不存在插件 UI 正常显示用户通过 Connect MCP Server 按钮手工连接到本地 Server默认PENPOT_MCP_WEBSOCKET_URL。集成模式下插件还会通过mcp.setMcpStatus(...)反向把连接状态connecting/connected 等同步回宿主环境并监听disconnect/connect事件驱动 UI 侧启停连接见 plugin.ts。也就是说WebSocket 的真正承载方是 UI 页面main.ts而插件主体只负责任务执行与状态转发两者通过postMessage在父 iframe 与子 iframe 间通信。六、WebSocket 连接的生命周期管理UI 侧 main.ts 是连接管理的核心实现值得逐块理解连接建立与鉴权connectToMcpServer(baseUrl, token)在已有连接或正在连接时直接返回若传入 token则将 URL 改写为wsUrl ?userToken${encodeURIComponent(token)}Server 侧据此完成用户鉴权。连接过程中会即时把状态胶囊status-pill切换到connecting/connected/error并通过parent.postMessage通知插件主体同步状态。心跳保活heartbeat插件每 10 秒发送一次 JSON 心跳帧HEARTBEAT_INTERVAL_MS 10_000。源码注释明确指出浏览器协议层的 ping/pong 并不能证明页面 JS 仍在运行——页面被冻结时浏览器仍可能应答 ping。因此心跳承担了应用层存活探测职责让 MCP Server 能及时发现插件事件循环已停摆的标签页。指数退避重连断开后由scheduleReconnect()以 1 秒为基数、30 秒封顶执行指数退避RECONNECT_BASE_DELAY_MS 1_000、RECONNECT_MAX_DELAY_MS 30_000兼顾短暂抖动快速恢复与服务器不可用时不打爆连接。该逻辑被disconnect/stop-server场景下的shouldReconnect false精确终止。页面冻结与恢复针对浏览器对后台标签页的冻结机制main.ts监听三类事件Chromefreeze发送{ type: freeze }通知 ServerChromeresume与 Firefox/Chrome 的visibilitychange恢复可见后立即补发心跳或重连防止因页面休眠导致的假死连接。源码注释说明这一设计对 Firefox 的兼容性作了权衡Firefox 不支持 freeze/resume但支持 visibilitychange配合过期心跳仍能检测标签页挂起。响应大小上限集成式远程模式下任务响应会被回传给 LLMJSON 体积过大会拖垮模型上下文同时海量超大响应会挤爆集中式 MCP Server 内存存在 OOM 风险。因此main.ts定义了MAX_TASK_RESPONSE_SIZE_REMOTE_MCP 15_000_000约 15 MB超过该值不发送原始响应而是改写为仅含id、success: false、error的错误帧见 main.ts。七、消息协议任务请求与响应的完整链路插件与 MCP Server 之间的协作遵循一套清晰的任务协议任务请求方向Server → UI 的每条消息被 JSON 解析后若含有task字段UI 会先更新 Current task 展示与已执行代码预览然后通过parent.postMessage(request, *)转发给插件主体plugin.ts 中message.task message.id分支接手处理。处理器注册与分发plugin.ts 维护处理器注册表并按键分发const taskHandlers: TaskHandler[] [new ExecuteCodeTaskHandler()];每个处理器继承 TaskHandler 抽象基类通过taskType task.taskType匹配任务类型目前插件内置的处理器是executeCode。任何未注册的task类型都会收到Unknown task type: ...错误响应。响应方向与结果封装Task类见 TaskHandler.ts封装了响应状态机sendSuccess/sendError经由penpot.ui.sendMessage发回 UI再由main.ts的sendTaskResponse经 WebSocket 送回 MCP Server。其中isResponseSent标志保证同一任务只应答一次若某个 handler 执行成功后忘了发响应plugin.ts会兜底发送generic success。若成功响应的序列化传输本身失败例如试图把 shape、token 等非可序列化对象回传插件会将其转换为错误响应并附带给用户的排查提示。版本兼容校验插件初始化收到 UI 的ui-initialized消息后会比较penpot.version与编译期常量PENPOT_MCP_VERSION的major.minor.patch前缀通过extractVersionPrefix提取。两者不一致且 Penpot 版本不是本地开发版0.0.0时UI 会展示 Version mismatch detected 警告横幅——因为服务端能力与客户端 API 版本绑定错配可能导致任务执行失败或产出次优结果见 plugin.ts 与 main.ts。八、核心任务executeCode 的实现细节executeCode是当前插件唯一注册的任务类型其处理器 ExecuteCodeTaskHandler 的设计体现了让 LLM 以代码方式操作文档的目标持久上下文构造器中建立跨多次执行共享的上下文对象this.context { penpot: penpot, storage: {}, console: new ExecuteCodeTaskConsole(), penpotUtils: PenpotUtils, };penpot暴露官方 Plugin APIstorage让多次任务间可暂存中间结果console是自定义捕获实现penpotUtils见 PenpotUtils.ts提供文档操作的便捷工具。安全代码执行每次执行把代码包进new Function(...)构造的异步函数体并将上下文对象展开为具名参数传入保证代码只能访问这 4 个受控对象。标志位管控执行期间强制开启penpot.flags.naturalChildOrdering与penpot.flags.throwValidationErrors确保子对象按自然顺序生成、校验错误及时抛出执行完的finally中恢复原值。若 Penpot 版本过旧连flags都不存在处理器会直接报错并引导用户核对 MCP Server 与 Penpot 的配套版本。日志捕获ExecuteCodeTaskConsole实现了console的常用接口log/warn/error/info/debug/trace/table/time/group/count/assert 等把输出累积为[LEVEL] 内容格式的字符串随任务结果一并返回便于 LLM 看到运行日志。二进制结果优化若顶层返回值是Uint8Array处理器会分块0x8000 字节编码为{ __type: base64, data }信封再回传。源码注释指出直接JSON.stringify类型化数组会膨胀约 10 倍体积且一次性String.fromCharCode(...bytes)可能栈溢出所以采用分块 base64 方案服务端据此还原图片字节数据如导出 PNG 的场景。九、测试与类型检查除运行外工程还提供质量保障命令package.jsonpnpm run test # node --experimental-strip-types --test src/*.test.ts pnpm run types:check # tsc --noEmit pnpm run clean # 清理 dist/test基于 Node 原生测试运行器执行插件单元测试如 ErrorUtils.test.ts、PenpotUtils.test.ts无需引入额外测试框架types:check则以tsconfig.json为基准做严格类型验证避免在编辑期漏掉 Plugin API 的类型错误。十、常见问题与排查提示结合源码实现可将日常使用中可能遇到的问题归纳如下连接失败且 UI 显示 Connection error确认 MCP Server 的 WebSocket 端点可达默认指向http://localhost:4402可用WS_URI环境变量覆盖后重新构建见 vite.config.ts。出现 Version mismatch 警告横幅插件/Server 包版本与 Penpot 客户端版本前缀不一致建议对齐到配套版本后再使用本地开发版 Penpot版本为0.0.0会跳过该警告。任务无响应检查 WebSocket 是否仍处于OPEN状态——页面休眠可能造成假死插件已通过心跳、freeze/resume 事件与指数退避重连加以缓解若sendTaskResponse在连接断开时被调用控制台会打印WebSocket not connected, cannot send response。任务返回超大结果集成式远程模式下超过约 15 MB 的序列化响应会被替换为错误帧返回图片时应控制导出尺寸或降低分辨率。结语Penpot MCP Plugin 以不到一个标准前端工程的体量完成了LLM/MCP Server → WebSocket → Penpot 插件环境这条关键链路的落地plugin.ts负责任务分发与状态同步main.ts负责稳定可靠的 WebSocket 长连接ExecuteCodeTaskHandler提供受控的代码执行沙箱。理解它的构建命令与消息协议是二次开发、调试或集成 Penpot MCP 能力最直接的起点。【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

最新新闻

日新闻

周新闻

月新闻