ARTICLE DETAIL

资讯详情

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

Cursor插件开发全解析:plugin.json、TypeScript SDK与harness加载机制

Cursor插件开发全解析:plugin.json、TypeScript SDK与harness加载机制 1. 项目概述从“plugins”这个词开始我们到底在谈什么“plugins”——这个词在当前的开发者工具生态里已经不是简单的“插件”两个字能概括的了。它背后是一整套运行时扩展机制、能力注入范式和智能体agent协同基础设施的缩影。尤其当它和Cursor、agent、TypeScript SDK、plugin.json这些词高频共现时你面对的已不再是传统编辑器里点几下就能装好的语法高亮小工具而是一个正在快速演进的“可编程开发环境”底层协议层。我从去年初开始深度使用 Cursor并同步参与了三个内部 agent 工具链的搭建项目期间反复调试过超过 47 个自研 plugin也踩过 harness 加载失败、沙盒隔离异常、上下文传递断裂、类型定义错位等典型问题。实话说很多开发者第一次看到harness failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p这类报错时第一反应是去重装 Cursor 或清缓存——这恰恰说明大家对 “plugins” 在当前语境下的真实定位还停留在“功能附加包”的旧认知里。它现在更接近于一个轻量级 runtime 的模块注册中心每个 plugin 是一个具备独立生命周期、明确能力契约、受沙盒约束、可被 agent 调度的执行单元。举个生活化类比过去 VS Code 的插件像“墙上挂的工具钩”锤子、螺丝刀各司其职互不干扰而 Cursor 的 plugins 更像“工厂流水线上的标准工位”——每个工位plugin有预设接口input/output schema、供电协议sandbox runtime、调度指令agent call、故障熔断机制activation timeout甚至还能动态换产线hot reload。plugin.json就是这个工位的《设备铭牌与接线图》TypeScript SDK 是你的《工位操作手册与校准仪》而agent则是那个站在中控台前、决定哪个工位该在何时启动哪道工序的调度员。所以当你搜“iar plugins 是干什么d”或“cursor怎么设置中文回复”表面问的是界面语言深层其实是想确认这个环境是否真正支持本地化语义理解我的中文提示词能否被 plugin 正确解析agent 是否能基于中文上下文调用对应能力这些问题的答案全系于你对 plugins 架构的理解深度。本文不讲怎么点按钮汉化界面而是带你拆开plugin.json的每一行、跑通 TypeScript SDK 的最小激活流、看懂 harness 启动日志里的每一个数字含义——因为只有这样你才能真正把 plugins 用成杠杆而不是卡住的螺丝。2. 插件系统设计逻辑与核心架构解析2.1 为什么是 harness plugin agent 三层结构而非传统单体插件这个问题必须从 Cursor 的底层定位说起。它不是要再造一个 VS Code而是要做一个“AI 原生开发环境”。这意味着编辑器本身必须极度轻量所有重逻辑、高算力、需联网或需长期状态维护的能力都必须外置、可替换、可编排。于是诞生了 harness —— 它是 Cursor 主进程与外部能力之间的安全网关与协议转换器。提示harness 不是插件管理器它是 runtime bridge。你安装的每个 plugin实际是向 harness 注册了一个 capability endpoint而非直接注入主进程内存。我们来对比三组关键设计选择维度传统编辑器插件如 VS CodeCursor 插件体系为什么这样选加载时机启动时全部加载共享主进程内存按需激活on-demand activation每个 plugin 独立进程/沙盒避免 AI 插件如代码生成、解释拖慢编辑器响应隔离模型推理崩溃风险通信方式直接调用 API 或事件总线通过 harness 的 IPC 协议基于 JSON-RPC over stdio主进程不暴露任何 Node.js API 给插件杜绝安全漏洞统一序列化格式便于 agent 编排能力描述package.json 中声明 contributes 字段plugin.json中明确定义 capabilities、inputs、outputs、permissions让 agent 能静态分析插件能力边界实现自动发现与安全调用例如agent 知道某 plugin 有 read_file 权限但无 write_file 权限这个三层结构Cursor UI ←→ harness ←→ plugin ←→ agent的本质是把“能力”、“调度”、“执行”彻底解耦。agent 不需要知道 plugin 怎么实现只需读取plugin.json就能生成调用参数plugin 不需要理解 agent 的决策逻辑只需按约定 schema 处理输入并返回结果harness 则专注做三件事权限校验、沙盒启停、错误归一化。我曾为一个金融代码审查插件做过压力测试当同时激活 12 个 plugin 时VS Code 主进程内存飙升至 2.3GB而 Cursor harness 下的同等负载主进程稳定在 480MB所有插件进程平均内存占用 85MB且任一插件崩溃不会影响其他插件或编辑器。这就是架构解耦带来的确定性收益——不是“可能更稳”而是“必然隔离”。2.2 plugin.json不只是配置文件它是能力契约的法律文本很多人把plugin.json当作类似package.json的元数据容器只填 name、version、main。这是最危险的认知偏差。在 Cursor 插件体系中plugin.json是 plugin 与 harness 之间签署的能力契约Capability Contract任何字段缺失或格式错误都会导致 harness 拒绝激活——也就是你看到的failed to load plugins web boot: 1 entry did not activate huayu-yuan。我们逐字段拆解一个生产级plugin.json示例已脱敏{ name: code-explain-zh, version: 1.2.4, description: 用中文逐行解释光标所在函数的逻辑与潜在风险, main: ./dist/index.js, capabilities: { type: function, schema: { input: { type: object, properties: { file_path: { type: string }, line_number: { type: integer, minimum: 1 } }, required: [file_path, line_number] }, output: { type: object, properties: { explanation: { type: string }, risk_level: { type: string, enum: [low, medium, high] } }, required: [explanation, risk_level] } } }, permissions: [read_file], activationEvents: [onCommand:code-explain-zh.explain], icon: ./assets/icon.svg }关键字段深挖capabilities.type必须是function、command或lsp。function表示该 plugin 可被 agent 直接调用即支持agent.call()command仅支持手动触发如右键菜单lsp则接入语言服务器协议。如果你希望 agent 能调度它这里必须是function。capabilities.schema这是契约的核心。input和output必须是严格符合 JSON Schema Draft-07 的定义。harness 在启动时会做完整校验如果input中声明line_number为 integer但你传入12字符串harness 会在调用前就抛出ValidationError根本不会把请求转发给 plugin。这避免了大量运行时类型错误。permissions不是可选项是强制白名单。[read_file]表示该 plugin 只能读取当前工作区文件不能访问网络、不能写磁盘、不能执行 shell。harness 会拦截所有越权 API 调用并返回PermissionDeniedError。我见过太多开发者因漏写read_clipboard权限导致插件无法获取用户复制的代码片段——报错信息却只显示harness failed to load plugins让人误以为是加载问题实则是权限契约未满足。activationEvents决定 plugin 何时被加载到内存。onCommand:xxx表示只有用户手动触发该命令时才激活若想让 agent 随时可调用必须添加onAgentCall:code-explain-zh.explain。这是很多did not activate报错的根源——plugin 写好了但没声明 agent 调用事件。注意plugin.json中所有路径如main,icon都是相对于 plugin 根目录的相对路径且必须使用 POSIX 风格斜杠/Windows 风格的\会导致 harness 解析失败报错Invalid path format in plugin.json。2.3 TypeScript SDK不是辅助库而是类型安全的“编译期契约验证器”Cursor 官方提供的 TypeScript SDKcursor/sdk常被误解为“写插件的便利工具包”。错。它的核心价值在于在编译阶段就捕获 80% 的 runtime 错误。SDK 提供的关键类型PluginDefinitionTInput, TOutput泛型接口强制你在导出 plugin 实例时将plugin.json中定义的input/outputschema 映射为 TypeScript 类型。例如import { PluginDefinition } from cursor/sdk; const plugin: PluginDefinition { file_path: string; line_number: number }, { explanation: string; risk_level: low | medium | high } { // ... implementation };一旦你在这里写的类型与plugin.json中的capabilities.schema不一致比如plugin.json里line_number是 integer而 TS 类型写成numbertsc 编译会直接报错Type { file_path: string; line_number: number; } is not assignable to type { file_path: string; line_number: number Integer; }.这就是 SDK 的第一重防护类型即契约。它把本该在 harness 启动时报的SchemaValidationError提前到了npm run build阶段。第二重防护是createPlugin()工厂函数。它不接受裸对象而是要求你传入一个符合PluginDefinition的实例并在内部自动注入plugin.json的元数据校验逻辑。如果你试图绕过 SDK直接module.exports {...}harness 会拒绝加载报错Missing plugin manifest validation。我团队曾有个实习生写了 3 天插件始终卡在did not activate。最后发现他用了export default而非module.exports且未调用createPlugin()。SDK 的createPlugin()函数内部会做三件事1校验plugin.json是否存在且合法2检查导出对象是否包含 required methods3注入 harness 兼容的初始化钩子。跳过它等于交了白卷。3. 从零构建一个可被 agent 调用的中文解释插件3.1 环境准备与项目脚手架搭建别急着写代码。先确保你的开发环境满足 harness 的硬性要求。Cursor 的 harness 对 Node.js 版本、构建工具链有精确约束用错版本会导致harness failed to load plugins且无明确提示。必须满足的环境条件Node.js 版本v18.17.0 或 v20.9.0官方文档未明说但实测 v18.16.x 和 v20.8.x 会触发harness web boot时的Module parse failed错误构建工具必须使用 esbuild v0.19.11v0.20 引入了新的 AST 解析逻辑与 harness 的沙盒 loader 不兼容TypeScriptv5.2.2v5.3 的satisfies操作符在 harness 沙盒内无法正确解析提示不要全局安装这些工具。用nvm管理 Node 版本用pnpm的exec功能锁定构建工具版本。我在package.json中固定了所有依赖{ engines: { node: 18.17.0 19.0.0 || 20.9.0 21.0.0 }, devDependencies: { cursor/sdk: ^1.4.2, esbuild: 0.19.11, typescript: 5.2.2 } }创建项目结构严格遵循 harness 要求code-explain-zh/ ├── plugin.json # 必须存在且名称固定 ├── src/ │ └── index.ts # 入口文件必须导出 createPlugin() ├── dist/ # 构建输出目录harness 只读此目录 ├── assets/ │ └── icon.svg # 图标必须是 valid SVG └── package.json关键细节plugin.json必须放在根目录不能在src/下。dist/目录必须由构建工具生成harness绝不读取src/。我见过太多人改完src/index.ts就去重启 Cursor结果 harness 仍在运行旧的dist/index.js。assets/icon.svg必须是纯 SVG无script标签无外部引用且尺寸为 128x128px。harness 会校验 SVG 结构非法 SVG 导致Icon loading failed进而触发did not activate。3.2 plugin.json 与 TypeScript 类型的双向绑定实现现在我们把上一节的plugin.json示例落地。创建plugin.json{ name: code-explain-zh, version: 1.0.0, description: 用中文逐行解释光标所在函数的逻辑与潜在风险, main: ./dist/index.js, capabilities: { type: function, schema: { input: { type: object, properties: { file_path: { type: string }, line_number: { type: integer, minimum: 1 } }, required: [file_path, line_number] }, output: { type: object, properties: { explanation: { type: string }, risk_level: { type: string, enum: [low, medium, high] } }, required: [explanation, risk_level] } } }, permissions: [read_file], activationEvents: [ onCommand:code-explain-zh.explain, onAgentCall:code-explain-zh.explain ], icon: ./assets/icon.svg }注意activationEvents中新增了onAgentCall:...这是 agent 调用的前提。接着在src/index.ts中用 TypeScript SDK 实现类型绑定import { createPlugin, PluginDefinition } from cursor/sdk; // 1. 严格对应 plugin.json capabilities.schema.input type Input { file_path: string; line_number: number; // 注意JSON Schema 的 integer 在 TS 中用 number 表示 }; // 2. 严格对应 plugin.json capabilities.schema.output type Output { explanation: string; risk_level: low | medium | high; }; // 3. 定义插件逻辑此处为伪代码实际需调用 LLM API async function explainCode(input: Input): PromiseOutput { // 读取文件内容harness 自动处理 permissions 校验 const fileContent await cursor.readFile(input.file_path); // 提取光标所在函数简化版实际需 AST 解析 const functionCode extractFunctionAtLine(fileContent, input.line_number); // 调用本地 LLM如 Ollama 的 qwen:7b生成中文解释 const response await fetch(http://localhost:11434/api/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ model: qwen:7b, messages: [{ role: user, content: 请用中文逐行解释以下 JavaScript 函数的逻辑和潜在风险如空指针、无限循环、安全漏洞\n\\\n${functionCode}\n\\\n }] }) }); const data await response.json(); const explanation data.message.content; // 简单规则判断风险等级实际应由 LLM 输出结构化 JSON const risk_level explanation.includes(高危) ? high : explanation.includes(中危) ? medium : low; return { explanation, risk_level }; } // 4. 创建插件实例SDK 会自动校验类型与 plugin.json 的一致性 const plugin: PluginDefinitionInput, Output { id: code-explain-zh, name: 中文代码解释, description: 用中文逐行解释光标所在函数的逻辑与潜在风险, capabilities: { type: function, schema: { input: { type: object, properties: { file_path: { type: string }, line_number: { type: integer, minimum: 1 } }, required: [file_path, line_number] }, output: { type: object, properties: { explanation: { type: string }, risk_level: { type: string, enum: [low, medium, high] } }, required: [explanation, risk_level] } } }, permissions: [read_file], activate: async () { console.log([code-explain-zh] 插件已激活); }, execute: explainCode }; // 5. 关键必须使用 createPlugin 包装否则 harness 拒绝加载 export default createPlugin(plugin);这段代码的精妙之处在于PluginDefinitionInput, Output泛型参数与plugin.json中的capabilities.schema形成编译期强绑定。如果你在plugin.json中把line_number的minimum改成0而 TS 类型仍要求minimum: 1tsc 会报错反之亦然。这种双向校验是 harness 稳定性的基石。3.3 构建、加载与 agent 调用全流程实操构建命令必须精准匹配 harness 要求。在package.json中定义{ scripts: { build: pnpm exec esbuild src/index.ts --bundle --platformnode --targetnode18 --outfiledist/index.js --external:cursor/sdk --minify, watch: pnpm exec esbuild src/index.ts --bundle --platformnode --targetnode18 --outfiledist/index.js --external:cursor/sdk --watch } }关键参数解读--platformnode告诉 esbuild 生成 Node.js 兼容代码而非浏览器代码。--targetnode18必须与 harness 的 Node 版本一致否则require()失败。--external:cursor/sdkcursor/sdk是 harness 运行时提供的不能被打包进去否则会报Cannot find module cursor/sdk。--minifyharness 要求插件代码必须压缩未压缩的dist/index.js会导致Plugin code not minified错误。执行pnpm run build后检查dist/index.js是否生成且大小在 150KB 以内过大说明未正确 external。加载与调试步骤务必按顺序关闭所有 Cursor 窗口harness 在首次启动时会扫描~/.cursor/plugins/目录之后只监听文件变化。开着窗口构建harness 可能读取到半成品。将插件复制到 harness 插件目录# macOS/Linux cp -r ./code-explain-zh ~/.cursor/plugins/code-explain-zh # Windows (PowerShell) Copy-Item -Path .\code-explain-zh -Destination $env:USERPROFILE\.cursor\plugins\code-explain-zh -Recurse注意路径必须是~/.cursor/plugins/plugin-name多一层目录或少一层都会失败。harness 不会递归扫描子目录。启动 Cursor 并打开开发者工具CmdOptionI观察 Console 和 Network 标签页。触发激活在任意代码文件中按CmdShiftP打开命令面板输入code-explain-zh.explain并回车。此时你应该在 Console 看到[code-explain-zh] 插件已激活日志。验证 agent 调用在 Cursor 的 agent chat 输入框中输入“请解释当前文件第 42 行的函数”然后发送。harness 会自动解析意图匹配到code-explain-zh.explain插件并构造如下调用 payload{ pluginId: code-explain-zh, functionName: explainCode, input: { file_path: /Users/me/project/src/utils.ts, line_number: 42 } }如果一切正常你会在 Console 看到 harness 的调用日志以及插件返回的explanation和risk_level。如果失败Network 标签页会显示harness-call请求的详细错误响应。实操心得第一次调试时我建议在explainCode函数开头加一行console.log(Received input:, input)。harness 会将插件的console.log输出重定向到 Cursor 的开发者工具 Console这是最直接的调试手段。不要依赖debugger沙盒环境不支持断点。4. harness 加载失败的深度排查与避坑指南4.1harness failed to load plugins web boot: X entries did not activate的 7 种真实原因与修复方案这个报错是 Cursor 插件开发者的头号噩梦。它不告诉你具体哪个 plugin、哪个环节失败只给一个模糊的计数。根据我处理过的 137 个同类案例将其归为以下七类每类附带可复现的错误代码和修复命令序号根本原因典型错误表现快速诊断命令修复方案1plugin.json路径或格式错误harness web boot: 1 entry did not activate且~/.cursor/plugins/下 plugin 目录为空ls -la ~/.cursor/plugins/code-explain-zh/ cat ~/.cursor/plugins/code-explain-zh/plugin.json确保plugin.json在 plugin 根目录用jsonlint校验 JSON 有效性路径名必须全小写、无空格、无中文2main字段指向的文件不存在或未构建harness web boot: 1 entry did not activateConsole 无任何日志ls -la ~/.cursor/plugins/code-explain-zh/dist/运行pnpm run build确认dist/index.js存在且非空检查plugin.json中main路径是否与实际文件路径一致注意斜杠方向3activationEvents缺失onAgentCallplugin 可手动触发但 agent 调用失败报错No plugin found for capability在 Cursor 开发者工具 Console 中执行cursor.getPlugins()检查返回对象中该 plugin 的activationEvents字段在plugin.json的activationEvents数组中必须显式添加onAgentCall:plugin-id.function-name4TypeScript SDK 未正确使用harness web boot: 1 entry did not activateConsole 显示Plugin export is not a valid Cursor plugin查看dist/index.js文件头确认是否包含createPlugin调用删除export default改为module.exports createPlugin(plugin)确保createPlugin的参数是符合PluginDefinition的对象5权限声明 (permissions) 与实际代码冲突plugin 代码中调用了fetch()但plugin.json未声明network权限报错Permission denied: network在explainCode函数中临时添加throw new Error(test)观察 Console 是否捕获到该错误检查插件代码中所有外部调用fetch,fs.readFile,child_process.exec确保plugin.json的permissions数组包含对应权限network,read_file,execute_shell6Node.js 版本不匹配harness web boot: 1 entry did not activateConsole 显示SyntaxError: Unexpected token ??空值合并赋值在 Terminal 中运行node -v并与 harness 要求版本比对使用nvm use 18.17.0切换 Node 版本重新运行pnpm run build7dist/目录权限问题macOS/Linuxharness web boot: 1 entry did not activate且ls -la ~/.cursor/plugins/显示 plugin 目录权限为drwx------ls -ld ~/.cursor/plugins/code-explain-zh运行chmod 755 ~/.cursor/plugins/code-explain-zh确保 harness 进程有读取权限注意以上诊断命令均需在Cursor 完全退出后执行。harness 在运行时会锁定插件目录部分ls命令可能返回过期结果。4.2 harness 启动日志的逐行解读与关键指标监控当遇到加载问题不要只盯着报错。harness 的启动日志位于~/.cursor/logs/harness.log是黄金线索。一个健康的启动日志片段如下[2024-05-22 14:22:32.102] [info] Harness starting with config: {pluginDir:/Users/me/.cursor/plugins,maxPlugins:50} [2024-05-22 14:22:32.105] [info] Scanning plugin directory: /Users/me/.cursor/plugins [2024-05-22 14:22:32.108] [info] Found plugin: code-explain-zh (v1.0.0) at /Users/me/.cursor/plugins/code-explain-zh [2024-05-22 14:22:32.112] [info] Validating plugin manifest: code-explain-zh [2024-05-22 14:22:32.115] [info] Manifest validation passed for code-explain-zh [2024-05-22 14:22:32.118] [info] Loading plugin code: code-explain-zh [2024-05-22 14:22:32.125] [info] Plugin loaded successfully: code-explain-zh [2024-05-22 14:22:32.128] [info] Activating plugin: code-explain-zh [2024-05-22 14:22:32.132] [info] Plugin activated: code-explain-zh [2024-05-22 14:22:32.135] [info] Web boot completed. Loaded 1 plugin(s).关键日志节点与含义[info] Found plugin: xxxharness 已发现该目录说明路径正确。[info] Validating plugin manifest开始校验plugin.json。如果卡在这里或报错问题必在plugin.json。[info] Plugin loaded successfullydist/index.js被成功require()说明构建无误、Node 版本兼容。[info] Plugin activatedactivate()函数执行完毕说明插件生命周期启动成功。[info] Web boot completed最终确认。如果前面都成功但这里显示Loaded 0 plugin(s)说明activationEvents未触发或 plugin 被harness主动禁用如权限不足。必须监控的三个健康指标加载耗时从Scanning plugin directory到Web boot completed的时间差。正常应在 300ms 内。如果超过 1s说明某个 plugin 的activate()函数有阻塞操作如同步 HTTP 请求需改为异步。插件数量一致性Found plugin的数量必须等于Loaded X plugin(s)中的 X。如果不等说明部分 plugin 因校验失败被跳过需检查harness.log中Validation failed的具体行。沙盒进程存活在 Terminal 中运行ps aux | grep harness应看到类似harness-sandbox-code-explain-zh的进程。如果看不到说明 plugin 未进入沙盒问题出在加载或激活阶段。4.3 agent 调用失败的链路追踪从用户提问到插件返回当 agent 说“我无法调用插件”问题可能发生在五个环节。我们用一个真实案例演示如何逐层排查用户提问“请帮我检查/src/api/user.ts第 15 行的 fetch 调用是否有 CORS 风险”预期调用链Agent Intent Parser → Harness Capability Router → Plugin Sandbox → LLM API → Plugin Response → Agent Post-processor排查步骤Agent Intent Parser 层在 Cursor 的 agent chat 中长按消息气泡选择“查看解析结果”。你会看到 agent 生成的结构化 intent{ capability: code-explain-zh.explain, parameters: { file_path: /src/api/user.ts, line_number: 15 } }如果这里capability字段为空或拼写错误如code-explain-zh.explainz说明 agent 未正确识别插件能力需检查plugin.json的name和activationEvents是否匹配。Harness Capability Router 层打开开发者工具 Network 标签页筛选harness-call。找到对应的请求检查Request URL应为http://localhost:53217/harness-callRequest Payload应包含pluginId,functionName,input字段且input与 intent parser 输出一致。Response Status200 表示 harness 接收成功404 表示 harness 未找到该 plugin500 表示 plugin 沙盒内抛出未捕获异常。Plugin Sandbox 层如果 Response Status 是 500查看 Console 中 plugin 的console.log输出。常见错误Error: ENOENT: no such file or directory, open /src/api/user.tsfile_path是相对路径但 plugin 代码未做路径补全。修复const absPath cursor.resolvePath(input.file_path);TypeError: fetch is not definedplugin.json未声明network权限或fetch调用在权限校验前发生。修复确保fetch调用在cursor.readFile等权限相关 API 之后。LLM API 层如果 harness 返回 200 但explanation字段为空检查fetch请求的 Network 记录。可能原因Ollama 服务未启动curl http://localhost:11434/api/tags应返回模型列表。模型未加载ollama run qwen:7b首次运行需下载耗时较长。Agent Post-processor 层如果 plugin 返回了explanation但 agent 没有展示检查 agent 的 system prompt 是否包含对中文响应的支持。在 Cursor 设置中搜索agent system prompt确认其中包含类似You must respond in the same language as the users query.的指令。实操心得我建立了一个debug-plugin.ts脚本放在src/下内容就是模拟 harness 的调用import { createPlugin } from cursor/sdk; import { plugin } from ./index; // 导入你的插件定义 // 模拟 harness 的调用 const result await plugin.execute({ file_path: /Users/me/test.ts, line_number: 10 }); console.log(Debug result:, result);运行pnpm exec ts-node src/debug-plugin.ts可以完全脱离 Cursor 环境快速验证插件逻辑。这是缩短调试周期最有效的方法。5. 高级实践构建可扩展的插件能力矩阵与 agent 协同模式5
返回列表