Skip to content

adapters: write a unique evox-product MCP config and stdio shim - #619

Open
autogame-17 wants to merge 1 commit into
evox/product-bridge-contract-20260825from
evox/product-bridge-shim-20260825
Open

adapters: write a unique evox-product MCP config and stdio shim#619
autogame-17 wants to merge 1 commit into
evox/product-bridge-contract-20260825from
evox/product-bridge-shim-20260825

Conversation

@autogame-17

Copy link
Copy Markdown
Member

Summary

setup-hooks now owns a unique evox-product MCP writer for Claude Code and Codex, plus a stdio shim that proxies to Desktop only after a loopback grant file exists.

What changed

  • write .mcp.json mcpServers.evox-product and Codex [mcp_servers.evox-product] with an ownership marker
  • uninstall removes only managed evox-product entries and leaves user servers/tables in place
  • add evox-product-shim.js as an on-demand stdio proxy; it never listens on a port
  • fail closed when ~/.evox/product-bridge.json (or EVOX_PRODUCT_BRIDGE_GRANT_FILE) is missing, not loopback, or has the wrong schema
  • send X-Evox-Product-Bridge-Grant on every RPC and a fresh nonce only on tools/call

How to test

  1. node --test test/productBridgeMcp.test.js test/adapters.test.js test/adaptersSyntax.test.js
  2. Expected: 104 passed
  3. evolver setup-hooks --runtime=claude-code --root="$PWD" --force then inspect .mcp.json
  4. Expected: managed evox-product command points at src/adapters/scripts/evox-product-shim.js
  5. Start the shim with no grant file and send initialize
  6. Expected: JSON-RPC error that EvoX Desktop is not publishing a product-bridge grant

Risk

Medium -- this changes the adapter execution bridge used by Claude Code and Codex setup-hooks. It writes a new MCP server entry into user runtime configs and introduces a loopback HTTP proxy. Desktop still does not write .mcp.json.

Harness/evaluator governance

Upstream governance surface: adapter execution bridge (src/adapters/* setup-hooks install/verify/uninstall plus the evox-product stdio shim)
Downstream EvoX impact: desktop AgentBridge Connect can now get a real evox-product MCP entry from setup-hooks; Desktop later publishes ~/.evox/product-bridge.json and should treat evox-product as the connected needle
Rollout-local scope: opt-in operator command only; existing installs stay unchanged until the operator reruns setup-hooks
Promotion boundary: lands as default setup-hooks behavior on merge; no shadow cohort and no automatic rewrite of existing user MCP servers
Evaluator mismatch sets: verification now distinguishes missing, user-owned, and managed evox-product entries; observation/action/repair/evidence/belief evaluator sets are unchanged
Non-regression evidence: test/productBridgeMcp.test.js covers merge/uninstall ownership, loopback grant rejection, grant/nonce headers, and stdio fail-closed; test/adapters.test.js still covers Claude/Codex hook install and Codex TOML [features] edits
Fix-severity review: medium
Owner approval: adapter/setup-hooks owner review required before merge
Security boundary: writes only a marked evox-product MCP entry; shim refuses non-loopback URLs, symlink grant files, and oversized bodies; grant token is sent as a header and not logged; no new secrets store
Rollback: revert this PR; operators can rerun setup-hooks --uninstall to remove only managed evox-product entries
Live promotion: no
Autonomous evaluator self-editing: no

Test plan

  • node --test test/productBridgeMcp.test.js test/adapters.test.js (92 passed)
  • node --test test/adaptersSyntax.test.js (entry-point scripts parse)
  • Desktop grant publisher and connected-needle follow-up still needed after this PR

Self-check

  • New adapter files ship as plain source under src/, matching sibling hook scripts. public.manifest.json is not present in this checkout or git tree; package.json already includes src/.
  • No new runtime dependencies added.
  • Tests added or updated to cover the new behavior; focused adapter suites passed locally.

Related

Stacked on #618

Desktop Connect already shells out to setup-hooks, but the installer
only wrote hooks. External Claude/Codex need a managed evox-product
stdio proxy that fails closed until Desktop publishes a loopback grant.
@cursor

cursor Bot commented Aug 25, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@evomap-aiqa-bot

evomap-aiqa-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

Sentinel 已审核此变更,并发现 4 个已核验问题

另有 14 个需要人工确认的风险;它们不阻塞合并,但开发 agent 必须 review advisory 评论并逐条确认。

开发 agent 查询详情: 调用 MCP 工具 sentinel_pr_review_context,传入 owner_repo="evomap/evolver"pr_number=619head_sha="df5dcb6e5095956db1cc845c34fb55815861b8df",并按条目追加 finding_idfinding_key

由 Sentinel 审核 · 目标提交 df5dcb6e5095

@evomap-aiqa-bot

Copy link
Copy Markdown

Sentinel 保留了 14 个尚未确认的审核候选。它们不阻塞合并;单一猜测、缺少变更行证据或纯 checklist 的候选会统一降为 P3。
开发 agent:请 review 以下 advisory,并逐条给出“已验证无风险 / 已修复 / 转为后续事项”的结论;不要只做整体确认。

  • [P2] localhost 解析结果未校验,grant 可能被发送到非回环地址isLoopbackHttp() 只判断 URL 中的 hostname 字符串是否为 localhost,而 http.request() 随后再次通��...(truncated) · src/adapters/scripts/evox-product-shim.js · advisory_d7f273baa27bfd3891d4cb76
  • [P2] 原子写入的临时文件可绕过符号链接保护 — 代码仅校验目标 .mcp.json 不是符号链接,但以固定名称 <filePath>.tmp 写入临时文件。若该��...(truncated) · src/adapters/productBridgeMcp.js · advisory_31f258f8724b57ec5838583f
  • [P3] Claude MCP 配置解析失败时会覆盖原配置installClaudeJson 对读取或解析 .mcp.json 的所有异常执行 catch,随后把 data 重置为 { mcpServ...(truncated) · ``src/adapters/productBridgeMcp.js`` · advisory_1b5713aba4c0b0fcab7ceb26`
  • [P3] MCP 写入失败仍被适配器报告为安装成功installClaudeJson/installCodexToml 在 shim 不存在时返回 {changed:false,error:...};两个适配器只处��...(truncated) · src/adapters/claudeCode.js · advisory_d0bf5a5bb17adf54f50754f8
  • [P3] shim 缺失时安装仍返回成功 — MCP 安装函数返回 { changed: false, error: ... },调用方既不检查 error,也不将安装结果标记为...(truncated) · src/adapters/productBridgeMcp.js · advisory_3a1f5abb9ba3597cf410a464
  • [P3] 仅凭 shim 文件名会接管并删除用户自有 MCP 条目isOwnedServer 不要求 _evox_product_managed: true,只要任一参数具有相同 basename 就判定为 Evolver �...(truncated) · src/adapters/productBridgeMcp.js · advisory_ed4c17d8213b01352b412663
  • [P3] 原子替换会丢失现有配置文件权限 — 临时文件使用默认创建权限,未继承目标文件 mode;renameSync 替换后,最终文件权限来自临...(truncated) · src/adapters/productBridgeMcp.js · advisory_6a5177bb5b21955597ca0b68
  • [P3] 无效 JSON 会被静默覆盖 — 读取或解析已有配置失败后,代码将数据重置为 { mcpServers: {} },随后继续写回文件,覆盖...(truncated) · src/adapters/productBridgeMcp.js · advisory_ab51e507a114ed25c93c9038
  • [P3] MCP stdio 传输使用了不兼容的 Content-Length 分帧 — 标准 MCP stdio transport 使用每行一个 JSON-RPC 消息(JSON Lines);该 shim 仅识别 \r\n\r\n 分隔的 ...(truncated) · src/adapters/scripts/evox-product-shim.js · advisory_53ebc348d0cf2547e71187d3
  • [P3] 并发安装会覆盖其他进程刚写入的配置installClaudeJson() 先读取整个 .mcp.json,修改内存对象,再通过固定路径 .mcp.json.tmp 写入并...(truncated) · src/adapters/productBridgeMcp.js · advisory_2e9a6db77f3987a155b98c7c
  • [P3] 所有 MCP 通知都被静默丢弃dispatch() 在发现 req.id === undefined 后直接返回 null,且 main() 仅在返回值非空时写回,因...(truncated) · src/adapters/scripts/evox-product-shim.js · advisory_3849140b759e27e64da2bf68
  • [P3] reset/clear 后可能缺少 in-flight completion 防护 — 状态链探针:reset/clear path touches async state but no abort/generation fence is visible for in-flight completion...(truncated) · src/adapters/productBridgeMcp.js · slice:scope_api_contract:state_chain_0350489c5d5bc14a
  • [P3] 安装结果契约允许“成功但未安装” — 配置写入方在输入为顶层 JSON 数组时不写入有效配置,但仍向 Claude/Codex 适配器返回安装成�...(truncated) · src/adapters/productBridgeMcp.js · advisory_bc8070559a9a27bfbbf8dc77
  • [P3] 通知丢失被重复记录并错误降级security_boundary_01 已确认“所有 JSON-RPC 通知都被静默丢弃”,同一行为又以“所有 MCP 通知�...(truncated) · src/adapters/scripts/evox-product-shim.js · advisory_a736b0905e4923d6ed4a8ad8

需要完整证据时,可用行末 finding_key 查询 Sentinel PR review context。

由 Sentinel 审核 · 目标提交 df5dcb6e5095

if (!req || req.jsonrpc !== '2.0' || !req.method) {
return rpcError(req && req.id, 'invalid JSON-RPC request');
}
if (req.id === undefined) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

所有 JSON-RPC 通知都被静默丢弃

🟠 严重程度:中(P2)
dispatch() 在发现 req.id === undefined 时直接返回,导致所有 JSON-RPC 通知都不会经过 readGrant()postJson()。因此 Desktop 桥接端只能收到带 ID 的请求,收不到用于推进或回滚会话状态的初始化、取消等通知。

Finding:finding_0186b48a46b22a089ee1c1dd

try {
if (fs.existsSync(filePath)) data = readJsonFile(filePath);
} catch {
data = { mcpServers: {} };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON 解析失败时安装会静默覆盖整个现有配置

🟠 严重程度:中(P2)
readJsonFile 的任意解析异常都会把 data 重置为仅含空 mcpServers 的默认值,随后正常写回;原文件中的其他服务器配置和原始内容被不可逆覆盖,返回值仍报告 changed: true

Finding:finding_66bd0497b647cbf97f3537e7

} catch {
data = { mcpServers: {} };
}
if (!data || typeof data !== 'object') data = {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

顶层 JSON 数组会导致安装假成功但配置未写入

🟠 严重程度:中(P2)
顶层结构校验只检查 typeof data === 'object',没有排除数组。代码向数组对象添加 mcpServers 命名属性,但 JSON.stringify 不序列化数组的命名属性,因此落盘仍为 [],安装函数却返回成功。

Finding:finding_60637b4baaca2fcd216cb06a

lines.splice(start, section.end - start, ...blockLines);
} else {
// Prepend so a later user append still lands in the existing [features]
// table, and so our ownership comment is never inside that table.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

重复安装 Codex 配置会删除 managed table 中的用户扩展字段

🟠 严重程度:中(P2)
找到已托管 section 后,安装逻辑用新 block 替换从 section 开始到下一 section 的全部行,而新 block 仅包含 commandargs。同一 table 内的 startup_timeout_sec 等非 Evolver 字段会被静默删除。

Finding:finding_3f1d8cba3d74220ae42b5fd9

@autogame-17 autogame-17 reopened this Aug 27, 2026
@cursor

cursor Bot commented Aug 27, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant