codebuddy-ide-mcp-upgrade · git:20260902.d44293e · 2026-09-02 · sha256 fa538c6d69c533c7
codebuddy-ide-mcp-upgrade git:20260902.d44293eA
Immutable. This exact content is served forever at /api/v1/blob/fa538c6d69c533c7.
---
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 <repo>/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 <file>`,不要只信自己的脚本
- **反向测试**:拿一个已知损坏的备份喂给 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 "<genie>/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/<name>/`,预览器常因安全策略拒绝加载隐藏目录资源,表现是「文件能读到、点击却打不开/报错」。
交付前做两件事:
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 <skill>/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 交给外部团队)。