跳转至

12 · MCP、Hook bridges、Mods、浏览器与电脑使用

这一章研究外部能力如何接入同一个ToolRuntime,以及接入后仍有哪些功能没有实现。读“bridge支持Claude/Codex/MCP”时必须分开命令hook、JS mods、external subagent、模型provider和浏览器tool;这些不是一条万能兼容层。源码固定main da00f7f,实验固定rc.2;真正第三方server/GUI行为需要额外实测,本课程离线实验不冒充这些平台验收。

12.1 MCP client是一实例一server的effect-owned连接

mcp-client没有给loop加专用分支。每个插件实例声明serverName与transport,连接发现tools后注册到ctx.tools;多个实例多server,caller-visible scope控制隔离。serverName限制 [A-Za-z0-9_-]{1,32},相同scope重复被拒绝,另一Agent scope可以复用同namespace。常见tools也来自同一registry pipeline,MCP不绕过guards/approval/PTC。

transport 配置 实际执行所有者
stdio command、args、env、cwd MCP SDK StdioClientTransport启动child
streamable-http url、headers MCP SDK StreamableHTTPClientTransport

共同配置有toolCallTimeoutMs(默认60000)、failOnStartupError(默认false)、maxInstructionBytes(32768)、reconnect。stdio MCP spawn不走ctx.subprocess.spawn,只是复用scrubbedParentEnv再加explicitenv;managedprocess range、localfile sandbox不会因你有这些DSH services就自动施加到MCPserver。server executable与endpoint是部署trustedconfig,不是随意接受model参数。源码:index.ts · export interface StdioConfig、transport.ts · export function createTransport。

# 示例组成:需要你实际安装并审核这个server。此处命令是占位,
# 不是未经下载就已可运行的DSH内置工具。
- insert:
    - id: my-notes-mcp
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        transport: stdio
        serverName: my-notes
        command: /absolute/path/to/your-server
        args: []
        env: {}
        failOnStartupError: true
        toolCallTimeoutMs: 10000
        reconnect:
          enabled: true
          initialDelayMs: 500
          maxDelayMs: 10000
          maxAttempts: 3

12.2 discovery、命名与完整代交换

publicToolName以tuple(serverName,rawName)为identity,cleancase mcp__server__raw;DeepSeek function names最多64char且限定alphabet,replacement/truncation时加12hex SHA-256identity摘要。wire始终用rawName,不能从normalizedpublicname反向猜rawName。有限摘要并非数学上绝无碰撞,registryduplicate检查仍重要。源码:tools.ts · export function publicToolName。

syncTools()是两阶段:先fetch整份catalog并builddefinitions,期间失败留下previousgeneration;随后disposeprevious并registerallnew。swap阶段遇foreignnamespace conflict回滚partialnew,结果这个server为zero tools并report,strict initial模式可throw;不是恢复previous所有工具的事务。重复raw/publicname也拒绝,不保留半份catalog。notifications/reconnectsync通过syncChain serial,防两次dispose/register交错泄露代。源码:tools.ts · export async function syncTools、connection.ts · function enqueueSync。

flowchart TB Connect["connect + negotiate capabilities"] --> List["tools/list: fetch完整next generation"] List --> Build["normalized public names / schema definitions"] Build --> Swap["dispose previous registrations"] Swap --> Register["register next全部tools"] Register --> Live["当前generation服务调用"] List -->|fetch失败| Keep["previous generation保留"] Build -->|duplicate/schema失败| Keep Register -->|conflict| Zero["回滚partial next: 本server zero tools"] Live -->|list changed| List Live -->|connection lost| Enabled{"reconnect enabled?"} Enabled -->|是| Closed{"旧 connection 关闭已确认?"} Enabled -->|否| Disconnected["不重连;previous tool registrations 可能保留并调用失败"] Closed -->|否| Stopped["停止恢复,记录错误;避免重叠连接"] Closed -->|是| Budget{"重连预算仍可用?"} Budget -->|否| Remove["移除本 server 的工具注册;需 reload/restart"] Budget -->|是| Backoff["bounded backoff;旧工具注册等完整 swap"] Backoff --> Connect

MCP 工具宣告的 outputSchema 若属于 Harness 支持的 JSON Schema 子集,会保留为结构化输出约束;若包含不支持的词汇,则降级成通用 JSON 值,不会因此拒绝整个工具。声明 taskSupport: required 的工具仍可以出现在 catalog,但执行时明确拒绝,因为这个 bridge 不实现 MCP task-based execution。接入 MCP 需要逐项核对这些能力边界。源码:supportedOutputSchema。

12.3 原始canonical值与model content是两条结果线

createMcpToolDefinition()用MCP spec CallToolResult验证wire result;isError=true在image storage之前throw,ToolRuntime于是记录failed/isError;普通successfultyped value只含content与可选structuredContent,不把所有transportmetadata塞进模型history。outputschema若支持structuredschema,则structuredContent也成为required并runtime验证;不能把plainJSON成功当作structuredschema已合规。源码:tools.ts · export function createMcpToolDefinition、tools.ts · function createExecutor。

native model projection:text合并、resource_link变名称/URItext;audio和embeddedresource暂只生成unsupportedplaceholder,raw数据仍供programmaticcaller使用,不自动等价于模型可理解音频/文件。未知block也明确提示。

image结果需符合MIME、canonicalbase64、所有imagebatch的preflight、当前exact route正向image-capability proof、attachmentsdurable save。任意imagebatch拒绝时model projection全部fallbacktext;rawimagecanonicalvalue仍可给PTCcaller。attachmentsstore和modelimagecapability缺失不意味着整个MCP业务调用必须失败,可能是imagecontext不能接纳。成功admission保持图像原position,tool-output后置policy改了value/content会使preparedprojection失配而不偷偷覆盖policy。源码:tools.ts · async function resolveImageAdmission、tools.ts · async function prepareImageProjection。

12.4 reconnect恢复连接,不自动重做有副作用的call

reconnectdefaults enabled true、initial500、maxDelay30000、maxAttempts10。一次outage共享连续failedbudget,brieflyconnect又crash不会无限重置;uptime超过stabilitywindow(maxDelay)才视作outage结束。firstready供activation等待,failOnStartupError决定是否rejectplugin;默认false会log并继续supervisor,不等于server已ready。

generation必须仍current且pluginlive才能同步tools或操作state。旧transportclose确认有5秒barrier,不能确认会停reconnect,避免多个stdiochild重叠。budgetexhaust移除server工具,需reload/restart再启;reconnectdisabled时既有失效工具可能留下并失败,不能宣称只要断开就即时从prompt消失。源码:connection.ts · export function startConnection、connection.ts · function settleFailedGeneration。

MCP connection restart与tool invocationretry分开。HTTPtimeout、streamloss或userabort可能发生在server已完成写操作之后;Host不能安全判定是否未执行。你自己的server应接受幂等key或提供operationstatus/reconcile工具;恢复后先读取真实状态,避免自动重送write/delete/send。

12.5 Resources不是伪装成独立工具的每个URI

ctx.mcpResources是scope-awareproviderregistry。mcp-client连接注册一server的资源request能力,resource consumer提供三个shared tools:list_mcp_resources、list_mcp_resource_templates、read_mcp_resource。firstserver注册时拥有本scope的tools,lastserver卸载时移除;一server卸载不能误删兄弟server的sharedtools。sourceprompt列出caller可见servernames,request按agent scope解析server,不接受不可见服务。源码:index.ts · register(server:。

cursor/URI由server拥有;resource内容不自然获得systeminstructionpriority。instructions由servercontext有归属且受UTF-8bytes上限,插件extension把model-visibletext送prompt/history让log能重建。资源返回JSON、attachmentimageadmission、tooltypedvalue、contextinjection是不同过程;不要把read resource等同于自动开图片识别。

12.6 命令Hooks桥接复用DSH扩展点,兼容范围是明确子集

hook-protocol共享matcher、codec、merge、runHook、detachedtracking与durablehookevents;claude-code/codex桥自己生成stdinpayload、env/substitution并映射decision。runHook经ctx.shell.resolve/execute,payload由bridgebuild,带operation signal/timeout;default600000ms、perhook timeoutSec要换为ms。配置configPath在processload读一次,relative相对processcwd,不自动按每个session发现项目hooks.json。源码:runner.ts · export async function runHook。

sequenceDiagram participant Event as DSH扩展点 participant Bridge as CC / Codex Bridge participant Proto as HookProtocol participant Shell as ctx.shell Event->>Bridge: pre-step / pre-tool / post-tool / stopping Bridge->>Bridge: matcher + dialect payload Bridge->>Proto: runHook(payload, signal, timeout) Proto->>Shell: JSON stdin + command Shell-->>Proto: exit/stdout/stderr Proto-->>Bridge: parsed neutral outcome Bridge->>Bridge: merge + event-specific mapping Bridge-->>Event: reject / deny / ask / block / context / continuation
行为 Claude Code command bridge Codex command bridge
常见events SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、Stop、SubagentStart/Stop 前五种,不含CC subagent events
stdin framing JSON末尾newline 无newline
command variables CLAUDE_PLUGIN_ROOT/PROJECT_DIR替换与project env 无额外env/substitution
matcher CC dialect规则 regex-only
PreToolUse deny、ask,其他委托 只honor blocking deny,不当成allow/ask
updatedInput warning+ignored 无rewrite路径
transcript_path 空string null
systemMessage warning+ignored warning+ignored
continue:false 记录neutralstop,目前缺runhalt机制 同样不能当全run已停止

源码:index.ts · export function apply、index.ts · export function apply。

workspace取sessioncwd,CLAUDE_PROJECT_DIR默认sessionworkspace,可有部署explicitoverride。CC subagent_type目前general-purpose,配置匹配code-reviewer等specifickind不会神奇工作。Codex payload 保留 model、turn_id,tool_name 使用真实工具名;tool_input 只抽 command,不声称传所有Harness args;last_assistant_message目前null。

runHook基础设施无法执行会转nonblockingerroroutcome(undefinedexit+stderr),turn继续;configread/parse失败会log并不注册任何hooks。命令hook桥不是默认fail-closed安全policy。如果你的安全需求是任何校验程序出错都禁止执行,应写native typedguard明确deny,不要把桥接的容错规则当审计保障。

12.7 Pre、Post和Stop三类决定不应混淆

PreTool deny发生业务体之前;ask转审批;普通没有决定应next(),让后续policies仍运行。PostTool block发生执行之后,给modelfeedback或context,不撤销sideeffect。只添context的bridge先delegate再spreaddownstreamdecision,保留后续block与其他metadata,不能凭第一hookallow覆盖更严格下一hook。

Stop映射到agent/turn-stopping,在边界steer一条typedcontext使loop再请求;source stop_hook_active现在总false,无条件blockinghook可能永远续step。自己Agent应加budget/明确stopcondition,而不是写“hook说deny后立刻结束整个task”。SessionStart/subagent生命周期有detachedruns,dispose abort+drain;open-turn有hook/invoked/result对,detachedlifecycle不强造turn记录。

桥接解决“复用一部分已有命令hooks”,本地插件以typedextensionpoints实现新政策更容易清楚表达错误、幂等性和日志。别为了复用旧payload,把本来一个tools/pre-execute listener能解决的逻辑再包成shell子进程。

12.8 Experimental Claude Mods与“Codex Mods”的核对结论

当前仓库存在 experimental/claude-code-mods、client-ui-claude-code-mods,没有名为experimental/codex-mods的实现包。Codex相关已实现的是hooks-codex与外部subagentdriver等,不能按请求措辞发明另一JS mods插件。

Claude mods支持JS register(on,options)、orderedhooks/matchers、next chaining、catchhandler、hostprocess/HTTP等API和UIband。SERVEDEVENTS明确只有session.start/end、prompt.submit、turn.start/complete、tool.call、command.run、ui.render;已知但unserved event可以注册却会被报告,不会在DSH自动产生。可重写directhumanprompttext且保留nontextblocks,追加context;tool-inputrewrite仍deferred,不同于promptrewrite。源码:index.ts · export const SERVED_EVENTS、index.ts · function rewritePromptText。

HookRegistry按modload与registrationorder选择chain;mod自己发起的API事件只被更早加载mod观察,避免递归interception。超时/catchdeadline限制业务hook等待,但它是受信Host扩展,不应称为执行任意陌生JSmod的OS沙箱。prototype-safevalue、surfaceadapter和typedresultvalidation仍只保护指定边界。源码:module.ts · select(event:。

这是opt-in experimental publishedprototype,默认Web不应自动加载所有experimental。UIbandclient、hostengine与moddistribution需要一起按manifest部署;有包目录不等于默认preset已有工具。真正creatorAgent改插件时,先看profile/currentroster,再操作explicitbundle。

12.9 browser-use/computer-use registry只保留一个provider席位

服务定义browserUse与computerUse均为exclusive namedregistration:一个provider占位,重复同name也拒绝;provider必须先stoptools、awaitownedwork再release,以防旧browser/desktop仍工作时下一provider重入。registry没有内置一个万能browser.click方法,provider自己注册工具,并负责session资源。源码:index.ts · register(name:、index.ts · register(name:。

experimental provider 真实实现 生命周期/权限要点
browser-use-playwright-mcp pinned Playwright MCP,Nodecli,Chromium launch perliveSession或exclusiveattach;清理上游PLAYWRIGHT_MCP env避免绕开配置
browser-use-chrome-devtools-mcp pinned Chrome DevTools MCP launch/attach,禁用usage-statistics;CDPendpoint部署指定
browser-use-stagehand-native native Stagehand + connectionWorker + ownedChromium modelcredentials独立于sessionmodel,某些actions实际另有模型费用
computer-use-cua-driver-mcp 已安装cua-driver mcp命令 startupdiscovery严格,exclusiveprovider,shutdown后release
computer-use-cua-driver-native @trycua/cua-driver same-process SDK platform权限与nativehandle,tools + screenshotprojection,shutdown/uniffiDestroy

源码:index.ts · export function apply、index.ts · export function apply、index.ts · export function apply、index.ts · export async function apply、index.ts · export async function apply。

12.10 浏览器资源属于一次live Agent activation,不是durable Sessionid

SessionResources Map的key是exact Agentobject;available检查activeRegistry仍持有该Agent。resume同sessionid不会接回旧浏览器资源;launchsession浏览器state不从Sessionlogrestore。attach一次只能给一个liveowner,busyactivation可被maskbrowsertools让其他conversation继续。每Session操作串行,其他ownedSessions可独立推进;caller取消等待不能随意取消Session-ownedstartup。

MCPbrowsermount在agent/created等待scope-ownedclient,strictstartup且reconnect false;它在scope检查tool不得被另Agentborrow,blockedscope有restrictions防继承泄露。资源框架的 unload 会先 await 每个资源的 close,再释放 provider reservation;只有 close Promise 真正 reject 时,SessionResources 才保留 entry/exclusive ownership 并拒绝 disposal。MCP 路径的 close 是 scope.dispose,而其中 ConnectionHandle.dispose 在无法确认 transport 已关闭时只是 log error 后继续 resolve。因此不能把资源框架的通用规则升级成“MCP child 未确认退出就一定保留席位”的保证;attach 场景需要关注该错误并核验旧 server/浏览器实际状态。源码:index.ts · export class SessionResources、mcp.ts · export function mountSessionMcp。

Stagehand的act/observe/extract可能调用独立配置model,cancel后等activeworkdrain,推理/browserinput可在drain期间继续;completedclick无法回滚,失败cleanup阻reuse。attachedbrowser还可能由human或其他app改变。所以自己的Agent应先freshsnapshot再action,action后freshstate验收;recovery不依赖旧dom token。源码:index.ts · const GUIDANCE。

Cua native在Hostdesktop运行,element_token从freshwindowsnapshot取得;新snapshot使旧token失效,target和legacy pid/window_id不要混用。backgroundinput优先,但refusal不授权foregroundretry;macOS cursoroverlayfacility可能unavailable,即使screenshots/input可工作。OSpermissions、可见desktop、nativeSDK安装是部署条件,静态课程网站没有开放这样的控制入口。

12.11 自己的Agent应该怎样接这些能力

外部notes/ticket/search优先用受控MCPserver或native tool;需要复杂policy用DSH typedguards,在开call之前明确failclosed。已有CC/Codexcommandhooks可复用但逐项核对兼容子集;creator扩展用profile-ownedbundle并独立审查hostJS。浏览器/电脑能力放单独受限profile/机器,明确provider席位、owned/attached资源、freshsnapshot与真实model费用。

所有集成都要分别验收catalogdiscovery、callfailuretaxonomy、sideeffectstatus、contextprojection与quiescentcleanup。await成功只代表对应Promise已settle;不保证server动作成功、图像已被模型接纳、session资源能恢复或所有trace记录完整。