--- name: codebuddy-ide-mcp-upgrade description: 升级 CodeBuddy IDE(genie 扩展)内置的 CloudBase MCP,以及 MCP 发版时同步 IDE 侧白名单。当用户提到「更新 IDE 里的 MCP」「内置 MCP 版本太老」「IDE 集成的 CloudBase 功能不足」「改工具白名单 toolWhiteList」「把新 bundle 打进 CodeBuddy」「白名单漂移」「MCP 发版要同步什么」时使用。覆盖:解包定位内置 bundle 与内嵌配置、重新构建 mcp bundle、生成新的工具白名单与系统提示词、安全注入 IDE 并备份、用 MCP 协议验证工具清单、人工端到端验收、一键回滚。 description_zh: 升级 CodeBuddy IDE 内置 CloudBase MCP description_en: Upgrade the CloudBase MCP bundled inside CodeBuddy IDE disable: false agent_created: true --- # codebuddy-ide-mcp-upgrade ## When to use - 需要把 CloudBase MCP 新版本推进 CodeBuddy IDE 的内置集成 - 线上反馈「IDE 里集成的 CloudBase 功能不足」(大概率是白名单过期,不是 MCP 能力不够) - 需要修改 IDE 内置的 `toolWhiteList` / `systemPrompt` / `attatchPrompt` - 需要定位「IDE 里的 MCP 到底装的哪个版本、能用哪些工具」 ## 集成结构(先读,别猜) CodeBuddy IDE 的内置 CloudBase MCP 由 **genie 扩展**承载,改一处不生效,**必须同时改两个文件**: | 文件 | 内容 | 等价来源 | | --- | --- | --- | | `Contents/Resources/app/extensions/genie/integration-mcp/tcb/index.cjs` | MCP Server bundle | 仓库 `mcp/dist/cli.cjs` 改名 | | `Contents/Resources/app/extensions/genie/out/extension/index.js` | 内嵌的 tcb 集成配置(webpack module,`ir.exports=JSON.parse('{...}')`) | 无仓库对版,需就地解包 | 默认 IDE 路径:`/Applications/CodeBuddy CN.app`。同目录还有 `anydev`、`eop`、`lighthouse` 三个集成,别改错。 加载与启动契约: ```js // TcbIntegration mcpServer: { path: path.join("integration-mcp", "tcb", "index.cjs"), envMapper: (r) => ({ TENCENTCLOUD_SECRETID: r.tmp_secret_id, TENCENTCLOUD_SECRETKEY: r.tmp_secret_key, TENCENTCLOUD_SESSIONTOKEN: r.token }), toolWhiteList: config.toolWhiteList, } // StdioClientTransport { command: process.execPath, args: [mcpPath], env: { ...envMapper(), INTEGRATION_IDE: "CodeBuddy", ELECTRON_RUN_AS_NODE: "1", WORKSPACE_FOLDER_PATHS } } ``` **不传任何命令行参数**(`--cloud-mode` / `--integration-ide` 都没用),凭据全靠环境变量,MCP 侧 `mcp/src/auth.ts` 直接读 `TENCENTCLOUD_SECRETID/SECRETKEY`。 ## Steps ### 1. 解包拿到线上基线(第一步必做) 配置内嵌在 21MB 的 `out/extension/index.js` 里,用 `JSON.parse('...')` 包着,**必须按 JS 字符串语义 eval 才能解析**: ```js const i = s.indexOf('"id":"tcb"'); const st = s.lastIndexOf("JSON.parse('", i) + 12; let cursor = st, cfg; for (;;) { cursor = s.indexOf("')", cursor + 1); try { cfg = JSON.parse(eval("'" + s.slice(st, cursor) + "'")); break; } catch {} } ``` 拿到后先数一遍白名单,并和仓库 `scripts/tools.json` 比对。**九成问题出在这里**:白名单停留在旧版本,里面全是已被 MCP 改名的死条目。 ### 2. 构建新 bundle ```bash cd /mcp && npm run build:webpack # 产物 dist/cli.cjs,约 4.6 MiB ``` 只跑 `build:webpack`,不要跑 `npm run build`(会触发 `prebuild` 的 `rm -rf dist`,可能被 safe-delete hook 拦截)。 ### 3. 生成新配置 白名单真源是 `scripts/tools.json`,**不要手写清单**。配置改动落在: - `toolWhiteList` ← `tools.json` 全部工具名(**全量,不要裁剪**,理由见「白名单裁剪的前提已不存在」) - `systemPrompt.login` / `.logout`、`userPrompt.*`、`attatchPrompt.*` ← 提示词 - 其余字段(`id`、`displayName`、`description`、`descriptionMap`、`types`、`ruleZipUrl`、`loginOnlyChinese`、`loginType`、`toolTimeout`)**保持原值** ### 3.1 写提示词前必须知道的两件事 **(1)PG 模式 = Supabase 同构,不是「多了一种数据库」** 判定为 PG 环境后,认证、存储、权限、迁移**四项全部改道**: | 能力层 | Supabase | CloudBase PG 模式 | 工具 | | --- | --- | --- | --- | | 数据库 | Postgres | PostgreSQL | `queryPgDatabase` / `managePgDatabase` | | Schema 变更 | Migration | `applyMigration`(须带 `migrationVersion`) | `managePgDatabase` | | 行级授权 | RLS Policies | RLS | `managePgDatabase` + `rls-patterns.md` | | 存储 | Storage Buckets | **pgstore(与 legacy COS 是两套系统)** | `queryPgStorage`(不是 `queryStorage`) | | 认证 | anon/service key | 应用认证(publishable key / API key) | `queryAppAuth` / `manageAppAuth` | **PG 环境里引导错路径(用 NoSQL/MySQL 工具,或用 `queryStorage` 而非 `queryPgStorage`)是最高频的跑偏方式。** **(2)提示词看配重,不看总长** - **总长不是问题**:`systemPrompt.login` 约 10.6k 字符 ≈ 3.5k token,在 Tool Search + 长上下文下不构成负担。为「看起来短」删引导 = 丢掉关键分叉点的判断质量。 - **要看常量 vs 变量的配比**。实测一次改版的占比: - 静态索引(rule 文件路径清单 + 控制台 URL 清单)占 **28.8%**,但模型随时可查、规则文件里本来就有完整版(提示词自己都写着 "see platform rule for full list"); - 真正决定走向的分支变量(如 PG 主线)只占 **6.6%**,且散落在互不相邻的章节,需要模型自行拼接 —— 这是「提示词写了但模型没照做」的典型成因。 - **改法**: 1. 下沉常量索引(可省 ~23%),腾出的空间上提变量主线; 2. 分支判定后**紧跟一张「改道表」**,把散落约束收敛成一处; 3. 「三选一」式的并列列表,若各分支会改变后续多项决策,应改写成「两条主线」各自自包含。 改完提示词用这个脚本量化配重,别靠感觉: ```bash node -e ' const s=require("fs").readFileSync("config/prompts/systemPrompt.login.md","utf8"),L=s.split("\n"); let c="(开头)",a={[c]:0},o=[c]; for(const l of L){if(/^## /.test(l)){c=l.slice(3);if(!(c in a)){a[c]=0;o.push(c)}continue} if(/^### /.test(l)){c=l.slice(4);if(!(c in a)){a[c]=0;o.push(c)}continue}a[c]+=l.length+1} for(const k of o)console.log(String(a[k]).padStart(6),(a[k]/s.length*100).toFixed(1).padStart(5)+"% ",k.slice(0,50))' ``` ### 4. 注入 IDE(先 dry-run) ```bash node scripts/apply-to-ide.mjs --dry-run # 只看差异 node scripts/apply-to-ide.mjs # 备份到 backup/<时间戳>/ 后写入 node scripts/patch-tool-timeout.mjs --timeout 300000 # 接通 toolTimeout(见 Pitfalls) ``` 写配置的替换逻辑:生成**紧凑 JSON**(`JSON.stringify(cfg)`,无裸换行),再按 JS 单引号字符串转义(先 `\\` 再 `'`),替换 `JSON.parse('...')` 区间。转义顺序错了会破坏 JS 字符串。 **边界语义(踩过坑,勿改错)**:`start` = raw 起点(`slice(0, start)` 里**已包含** `JSON.parse('`),`end` = `')` 之后。所以替换时**只能拼 escaped raw + `')`,绝不能再拼一次 `JSON.parse('`**。 ### 5. 验证 ```bash node scripts/verify-ide-config.mjs # 从 IDE 回读配置,逐字段比对 node scripts/verify-bundle.mjs # 按 IDE 方式启动 bundle,拉 tools/list ``` `verify-bundle.mjs` 复刻 IDE 的启动参数(stdio + `INTEGRATION_IDE=CodeBuddy` + `ELECTRON_RUN_AS_NODE=1` + 临时密钥占位值),比对三件事:暴露的工具是否全在白名单内、白名单是否有悬空条目、PG 工具是否注册。 ### 6. 回滚 ```bash node scripts/rollback-ide.mjs --latest ``` ## MCP 发版时的强制同步项(防漂移) **白名单漂移是「IDE 里 CloudBase 功能不足」的唯一根因**,不是 MCP 能力问题。线上实测:21 条白名单里 12 条是已被 MCP 删除或改名的死条目,用户实际只能用 9 个。 因此 **MCP 每次发版(工具增删改名)都必须重新生成 IDE 侧白名单**,否则新版本 MCP 发得再勤,IDE 里还是老的。 **发版 checklist:** 1. `scripts/tools.json` 是否已更新(工具清单真源) 2. 用 `scripts/build-config.mjs` 重新生成 IDE 配置,产出新 `toolWhiteList` 3. 检查**新增/改名**的工具是否在提示词里有对应引导 —— 提示词里引用已删除的工具名会导致模型调用不存在的工具 4. 把新配置同步给 IDE 侧(或直接执行本 skill 的 Steps 打进本机 IDE 验证) 5. 在交付文档里记录「本次新增了哪些工具」,便于 IDE 侧理解变更 **建议把这个 checklist 挂到 MCP 发版流程里(release workflow 或发版 checklist 文档),不要靠人工记忆。** 靠人记的后果就是这次的 12 条死条目。 ### 白名单裁剪的前提已不存在 - **CodeBuddy 已支持 Tool Search**:MCP 工具按需检索,不再全量塞进上下文;MCP server 配置层也支持 `defer_loading`。 - **当初给 tcb 加 `toolWhiteList` 的唯一理由就是省上下文,这个前提现在没了。** - 结论:白名单回归「安全边界」单一职责,按 `tools.json` **全量生成**。继续裁剪的唯一后果就是随 MCP 发版漂移成死条目。 - ⚠️ 判断「IDE 是否支持 Tool Search」时**不要 grep genie 的 `out/extension/index.js`** —— 那里搜不到 `ToolSearch` 字符串(实测 0 命中)。Tool Search 属 Agent CLI 内核层,证据在 CLI 进程参数(`--tools` 白名单含 `ToolSearch`)和 mcp-config 的 `defer_loading` 里。 ## Pitfalls - **白名单过滤在 IDE 侧,不在 bundle 内。** 只换 bundle 不换白名单 = 新工具被静默过滤,用户侧零变化。这是最容易踩的坑。 - **写入后必须完全退出并重启 IDE** 才生效,运行中的进程已把旧 bundle 加载进内存。 - 解包时配置字符串里可能含 `')` 序列,必须用「eval + JSON.parse 能否成功」来判断结束位置,不能用第一个 `')`。 - **定位 tcb 块必须用 `"id":"tcb"` 做锚点。** 全文 `toolWhiteList` 出现 13 次,用 `toolWhiteList` 搜会抓到 eop(EdgeOne)的配置块——症状是解出来的 raw 只有 1,015 字符(正常应 ~16,000)。 - 插件类工具(如 `msg-push`)不在 `DEFAULT_PLUGINS` 里,白名单写了也不会注册,需注入 `CLOUDBASE_MCP_PLUGINS_ENABLED=msg-push`。白名单 40 条、实际暴露 38 条是**正常现象**,不是 bug。 ### ⚠️ 头号陷阱:JSON 回读全绿 ≠ 文件可用 曾发生的事故:替换时重复拼接 `JSON.parse('` 前缀,生成 `JSON.parse('JSON.parse('{...}')`,第二个 `'` 提前闭合字符串,整文件 `SyntaxError`。**但 verify 脚本的 JSON 字段比对全部显示 ✅** —— 因为定位用 `lastIndexOf("JSON.parse('")`,恰好命中了第二个前缀,照样能解析出正确 JSON。 **铁律**:改动这种大打包产物后,**必须对整文件做真实编译**: ```js import vm from "node:vm"; try { new vm.Script(source, { filename: "index.js" }); } catch (e) { /* 立即回滚备份 */ } ``` - 写入脚本要内置编译校验 + 失败自动回滚 - verify 脚本的结构/语法检查必须**硬阻断 `exit 1`**,只打印 ❌ 而不改变退出码等于没有检查 - 交付前再独立跑一次 `node --check `,不要只信自己的脚本 - **反向测试**:拿一个已知损坏的备份喂给 verify,确认它真的报失败(否则检测是摆设) ### 已知 IDE 侧缺陷:`toolTimeout` 未接通,实际只有 60 秒 - `TcbIntegration` 的配置对象**没有** `toolTimeout` 字段(`EopIntegration` 传了) - `callTool` 用 `this.config.toolTimeout` → `undefined` - MCP SDK:`const Sn = sn?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC`,而 `DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4` - ⇒ 配置 JSON 里写的 `"toolTimeout":120000` **从未生效**,实际 60 秒就掐断 PG `applyMigration` / CloudRun 部署 修复(`scripts/patch-tool-timeout.mjs`): - **Patch A**:给 `TcbIntegration` 配置对象补 `toolTimeout:hn.toolTimeout`(锚点 `attatchPrompt:hn.attatchPrompt,loginOnlyChinese:hn.loginOnlyChinese}`,全文唯一 1 处) - **Patch B**:把配置值从 120000 提到 300000 ### 白名单可以放心多留位(源码实证) ```js ((ir?.tools) || []).filter((ir) => this.config.mcpServer.toolWhiteList.includes(ir.name)) ``` 遍历的是 **server 实际返回的 `tools/list`**,白名单只做 `includes` 判定。多出的条目静默跳过、不报错、不产生悬空工具。所以白名单按 `tools.json` 全量下发是安全的,插件后续启用也无需再改配置。 - `mcp/src/server.ts` 用 `ide === "CodeBuddy"` 判定 logging capability,大小写敏感;IDE 传的正是 `"CodeBuddy"`,别改成小写。 - 老版本 bundle 用旧的 MySQL / 云函数 / 存储工具名(`executeReadOnlySQL`、`createFunction`、`uploadFiles`、`writeSecurityRule` 等),新 bundle 里这些名字已全部消失,提示词里如果还在引用就会引导模型调用不存在的工具。 ## Verification 交付前必须同时满足: 1. **独立跑 `node --check "/out/extension/index.js"` 通过**(最关键,能抓住回读校验掩盖的语法错误) 2. `apply-to-ide.mjs` 输出「语法有效」+「回读校验通过」 3. `verify-ide-config.mjs` 结构完整性三项 ✅ + 七个字段 ✅,`echo $?` 为 0 4. `verify-bundle.mjs` 显示「所有暴露的工具都在白名单内」且 PG 三件套(`queryPgDatabase` / `managePgDatabase` / `queryPgStorage`)已注册 5. `verify-ide-config.mjs` 反向测试:喂已知损坏文件必须 `exit 1` 6. 重启 IDE 后完成下方的人工端到端验收(E1–E10) ### 人工端到端验收用例(自动化证明不了的那一层) 脚本只能证明「bundle 与配置文件本身是对的」,**证明不了 IDE 加载后用户真的能用**。重启后逐项跑: | # | 用例 | 预期 | | --- | --- | --- | | E1 | 完全退出后重启 IDE | 集成面板正常渲染,无 `SyntaxError`、genie 扩展不报错 | | E2 | 集成面板连接 CloudBase | 登录成功,显示环境信息 | | E3 | 让 Agent 列出可用的 CloudBase 工具 | 数量与新白名单一致(不是旧版数量) | | E4 | PG 环境让 Agent 建表 | 走 `managePgDatabase` 的 `applyMigration`,提示词先引导读 `postgresql-development-cloudbase` 规则 | | E5 | 执行一条只读 SQL | 走 `queryMysqlDatabase`(不再是 `executeReadOnlySQL`) | | E6 | 部署一个 Node.js 云函数 | 走 `manageFunctions`(不再是 `createFunction`) | | E7 | PG 模式下访问存储 | 走 `queryPgStorage` 而非 `queryStorage` | | E8 | 查看/修改安全规则 | 走 `queryPermissions` / `managePermissions`(不再是 `writeSecurityRule`) | | E9 | PG 执行耗时 >1 分钟的迁移 | 不中断,5 分钟超时生效(验证 `toolTimeout` 修复) | | E10 | 正常对话观察上下文占用 | 工具全量放开后无明显膨胀(验证 Tool Search 结论) | **验证时的两个坑:** - **tcb 临时密钥会过期**:日志表现为 `Authorization cache loaded for tcb, tempKey expires at: <过去时间>`,必须在集成面板重新登录,MCP 才起来。 - **MCP 进程按需启动**:tcb 的 MCP Server 只有集成面板连上后才拉起,IDE 刚启动时日志里没有 tcb 的 `tools/list` 属正常,**别据此判定 bundle 没生效**。 日志位置:`~/Library/Application Support/CodeBuddy CN/logs/<时间戳>/window1/exthost/Tencent-Cloud.coding-copilot/腾讯云代码助手.log`(搜 `[Integration]` / `tcb`)。 ### MCP 服务端质量的合格基线(顺带可测) 如果要顺带评估 MCP 工具层本身,这几项是实测通过的基线,达不到说明有回归: - **只读承诺**:`queryPgDatabase(action=sql)` 必须拦截 DELETE / UPDATE / DROP / 多语句注入,且返回带 `nextActions` 的可执行建议 - **confirm 闸门**:`managePgDatabase(execute)`、`manageFunctions(deleteFunction)` 缺 `confirm` 时必须拒绝 - **负向路径零崩溃**:不存在的函数名 / 集合 / envId / topic 都返回结构化错误或正常语义,不出裸 stack trace - **能力边界明示**:PG 环境下 `queryPermissions` 应返回「不支持 PostgreSQL 类型环境」,而不是假装成功 首次实操的完整交付物(文档 + 配置 + 脚本)模板在 CloudBase-MCP 仓库的 `specs/cb-ide-mcp-upgrade/`(worktree `chore/cb-ide-mcp-upgrade`)。 ## 交付前的一致性自查(易漏) **凡「改配置 + 再打独立 patch」的两步流程,patch 改的标量必须回流到配置生成脚本**。 实例:本任务里 `toolTimeout` 先从 120000 提到 300000 是靠 `patch-tool-timeout.mjs` 单独 patch 的,而 `build-config.mjs` 生成的 `tcb-config.new.json` 里仍是 120000。交付物自带旧值,IDE 侧直接拿配置去用就会退回两分钟。 自查项: 1. 对比「交付配置 JSON 的标量值」与「IDE 内实际生效值」,逐项相等 2. 白名单条数、各提示词长度、所有标量字段都要对,不能只看回读脚本报绿 3. 文档里的数值表格(变更项、建议项)与配置源保持一致 ## 交付文档的可读性(易被忽略) **Markdown 交付物不要放在点开头的隐藏目录下**。git worktree 常用 `.worktrees//`,预览器常因安全策略拒绝加载隐藏目录资源,表现是「文件能读到、点击却打不开/报错」。 交付前做两件事: 1. 把文档产物镜像到非隐藏路径(本次用 `~/Projects/cb-ide-mcp-upgrade/`),`present_files` 指向该路径 2. 生成自包含 HTML 版,`present_files` 第一个传它(HTML 会同时开预览面板 + 列 artifact card,最稳) 渲染脚本在本 skill 的 `scripts/render-html.mjs`,依赖 `marked`: ```bash mkdir -p /tmp/mdrender && cd /tmp/mdrender echo '{"name":"mdrender","private":true}' > package.json npm install marked NODE_PATH=/tmp/mdrender/node_modules node /scripts/render-html.mjs \ "<交付目录>/README.md" "<交付目录>/README.html" "文档标题" ``` 注意:`npm install` 别在 `~/.workbuddy/binaries/node/workspace` 里跑——没有 package.json 时 npm 会向上找到 `~/node_modules` 并因 ENOTEMPTY 失败。装到带 package.json 的临时目录最省事。 产物自带侧边目录导航(从 h2/h3 生成)、表格与代码高亮样式、`@media print` 打印规则(可直接导出 PDF 交给外部团队)。