【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
本指南围绕 opencodex(Universal provider proxy for OpenAI Codex & Claude Code)Cursor 适配器中的一次关键加固展开:修复AgentRunRequest.mcp_tools工具通告通道与RequestContext.tools、事件状态clientToolNames之间可见集不一致的缺陷,并通过回归测试锁定边界行为。读完本文,你将理解 Cursor 路由下客户端工具为何无法被模型调用、正确的 protobuf 封装形状如何避免解析崩溃,以及如何用同一份过滤后的可见集保证多条通告通道的一致性。
背景:为什么 Cursor 路由下的客户端工具"不可见"
在 Cursor 桥接中,Codex 的客户端工具(如mcp__node_repl__js、mcp__codex_apps__*系列)由 opencodex 通过合成 Provider 标识opencodex-responses通告(见 tool-naming.ts 中OCX_RESPONSES_TOOL_PROVIDER = "opencodex-responses")。根因分析(001_wp1_root_cause.md)确认:这不是权限问题,而是"未注册 / 合成 Provider 路由缺口"——Cursor 只会把挂载在可路由的provider.mcpServers下的 MCP 工具暴露给模型的可调用目录,而合成 Provideropencodex-responses无法被 Cursor 路由,于是其下的工具被隐藏/丢弃。
此前唯一通告通道是 native-exec 的RequestContext.tools(即requestContextArgs),实测中模型会回复"我没有这个工具"并回退到 Cursor 原生 Shell。而顶层AgentRunRequest.mcp_tools通道(McpTools封装)才是 Cursor 真正注册进模型可调用目录的通道。
修复主线:用正确的 McpTools 封装形状恢复 wire 兼容性
早期 Phase 42 曾尝试把工具镜像进mcp_tools,但 Cursor 实况解析器直接崩溃:
parse binary: illegal tag: field no 13 wire type 7wire type 7 是非法类型,这是序列化形状错误而非通道被拒绝。加固文档(004_fix_mcp_tools_channel.md)确认正确姿势是用McpToolsSchema封装赋值:
const mcpToolDefs = buildCursorToolDefinitions(request.tools, request.toolChoice); // ... ...(mcpToolDefs.length > 0 ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) } : {}),实现在 protobuf-request.ts 的encodeCursorRunRequest/prepareCursorRunRequest中:buildCursorToolDefinitions把request.tools映射为McpToolDefinition(name、toolName、providerIdentifier、description、inputSchema,见 tool-definitions.ts),再以create(McpToolsSchema, { mcpTools: defs })封装进runRequest.mcpTools。该形状与生成的 protobuf 定义McpTools.mcp_tools = repeated McpToolDefinition严格对应。修复后实况端到端验证通过:cursor/gpt-5.6-luna与cursor/claude-4.5-sonnet均产生了对注入工具的FINAL function_call,而修复前所有变体(裸名、mcp_前缀名、mcp_instructions等)都产生零次工具调用。
RequestContext.tools通道被保留作为第二条通告通道,两通道同时填充。
加固核心:三通道可见集必须来自同一过滤结果
A-gate 评审折叠了一个 BLOCKER:mcp_tools原先直接取自原始request.tools,而另外两处——RequestContext.tools(live-transport.ts 附近的requestContextArgs)与事件状态clientToolNames(live-transport.ts 的clientToolDefs.map(tool => tool.toolName || tool.name))——都来自经cursorToolsForActivePrompt(...)过滤后的可见集。
以通用的"工具计数演示"类 prompt(如 "use any 3 tools")为例:这类 prompt 会把客户端工具收窄到裸exec_command。若mcp_tools仍通告非 exec 工具,模型一旦调用其中某个工具,就会被当作未知 Responses 工具拒绝(见 protobuf-events.ts 的recordToolCall未知工具判定)。因此加固后的encodeCursorRunRequest改为从同一份cursorToolsForActivePrompt(request.tools, activePromptText(request), request.toolChoice)可见集构建mcp_tools,使三条通道(mcp_tools、RequestContext.tools、事件状态名称)保持一致:
// Hoisted out of the mcp_tools spread below so the estimate can read the same // filtered definitions the wire carries. Both helpers are pure. const mcpToolDefs = buildCursorToolDefinitions(visibleTools, request.toolChoice);cursorToolsForActivePrompt:可见集的裁决逻辑
过滤规则位于 tool-guidance.ts:当shouldUseNativeExecOnlyForGenericToolUse判定为通用工具计数演示时,可见集收窄为仅执行路径工具(isCursorExecutionPathTool),否则返回完整工具集。识别逻辑isGenericToolUseCountDemoPrompt覆盖中英文与韩文模式(如/\buse\s+any\s+tools?\b/i、/\b\d+\s+tools?\b/i等,见 tool-guidance.ts),并支持从 prompt 中解析期望的工具数量(requestedCursorToolUseCount,上限 50)。
正因为buildCursorToolDefinitions接收的是过滤后的visibleTools,mcp_tools的最终内容才与事件状态clientToolNames完全一致,杜绝了"通告了但调用即被拒"的跨通道漂移。
非阻塞结论审计:无需修改的确认项
加固评审同时确认了四项"无需改动"的边界:
- 无双重执行:返回的客户端工具调用只被 surfacing 一次,并按 call id 去重——事件状态中的
completedToolCalls: Set<string>(protobuf-events.ts)在recordToolCall、toolCallCompleted、dropInvalidFreeformCall等路径统一先查重再处理;OCX_RESPONSES的mcpArgs由 Responses bridge 拦截,不会在本地重复执行。 - wire 形状正确:
create(McpToolsSchema, { mcpTools: defs })与McpTools.mcp_tools = repeated McpToolDefinition一一对应。 - 空工具与
toolChoice:"none"均产出空列表:mcpToolDefs为空时字段保持 unset(除非显式设置suppressDefaultCursorToolCatalog,此时序列化一个显式空McpTools封装以抑制 Cursor 默认原生目录)。 - 性能可忽略:仅为一次小规模的同步 filter/map 加编码。
回归测试:用测试锁定每条边界
加固在 cursor-blob.test.ts(tests/providers/cursor/目录下)新增了describe("Cursor AgentRunRequest.mcp_tools channel")测试组,逐一锁定行为:
| 场景 | 输入 | 断言 |
|---|---|---|
| 普通 prompt | 含mcp__node_repl命名空间工具 | mcp_tools = ["mcp__node_repl__js"] |
| 通用工具计数 prompt | "use any 3 tools" +exec_command+ 非 exec 工具 | mcp_tools = ["exec_command"](过滤器一致性,即 BLOCKER 的测试) |
| 通用 prompt + 统一 exec | "use any 3 tools" +exec+wait+ node_repl | mcp_tools = ["exec"] |
| 空工具 | tools: [] | mcpToolsunset |
| 抑制默认目录 | tools: []+suppressDefaultCursorToolCatalog: true | mcpTools.mcpTools = [](显式空封装) |
toolChoice: "none" | 含 node_repl 工具 | mcpToolsunset |
测试辅助函数mcpToolNames(bytes)从编码后的AgentClientMessage反解run.mcpTools.mcpTools.map(def => def.toolName)(cursor-blob.test.ts),直接断言 wire 层真实载荷。
验证与交付
加固交付验证分三层:
- 类型检查:
bunx tsc --noEmit退出码 0; - 单元测试:
bun test tests/cursor-blob.test.ts13 项全部通过;完整bun test tests/cursor-*.test.ts265 项通过 / 0 失败(此前为 261 项,本次净增 4 项); - 实况验证:本轮不占用实况 Cursor probe,线上代理(端口 10100)不受影响,改动在代理重启后生效。
最终状态为 DONE:channel 一致性缺陷已修复、边界行为由测试锁定、经 sol 评审后提交到分支。对于运行中的线上代理,重启前现有会话行为不变;工具 RESULT 返回路径未改动(既有 client-tool bridge:function_callsurfacing → Codex 执行 → 结果作为历史在下一轮回放),如需完整多轮浏览器往返(node_repl → result → 下一次调用)可另行做一次端到端冒烟验证。
延伸阅读
- 修复发现与实况验证全过程:004_fix_mcp_tools_channel.md
- 根因分析(合成 Provider 路由缺口):001_wp1_root_cause.md
- 工具定义与 wire 编码:tool-definitions.ts、protobuf-request.ts
- 可见集过滤与 prompt 识别:tool-guidance.ts
- 事件状态与工具调用去重:protobuf-events.ts
【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
相关推荐
opencodex PR 73 Cherry-Pick 与传输加固:Codex/Cursor 桥接层的错误分类与取消语义修复
opencodex PR 73 Cherry Pick 与传输加固:Codex/Cursor 桥接层的错误分类与取消语义修复 导读 本文基于 opencodex
Composio 回归测试指南:从复现缺陷到提交可验证的修复
Composio 回归测试指南:从复现缺陷到提交可验证的修复 导读 本文面向 Composio SDK 的贡献者与维护者,系统讲解在仓库中开展回归测试(Regr
人工智能AI Agent工具调用MCP 服务MCP ClientsAptos MoveFlow State-Label 验证修复:Move Prover 后置条件中间状态断言缺陷的修复与回归验证
Aptos MoveFlow State Label 验证修复:Move Prover 后置条件中间状态断言缺陷的修复与回归验证 导读 本指南围绕 Aptos
区块链Web3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考