非官方社区教程 · 内容整理自公开资料 · 豆包工作为字节跳动产品,本站与官方无关
豆包工作教程
首页/ Harness 拆解/CH-05 协议层:MCP 原生集成

CH-05 协议层:MCP 原生集成

图 5-1:MCP 协议层架构

图 5-1:MCP 协议层架构

5.1 为什么 MCP 重要

MCP(Model Context Protocol,模型上下文协议)是 Anthropic 于 2024 年底开源的一个标准协议,用于让 AI 模型与外部工具和数据源通信。你可以把它理解为"AI 世界的 USB-C":在 MCP 之前,每个 AI 应用接入每个外部服务都需要写定制的集成代码;有了 MCP 之后,任何兼容 MCP 的客户端都可以连接任何兼容 MCP 的服务器,工具能力可以即插即用。

MCP 定义了三个核心原语:

  • Tools(工具):服务器暴露给模型的可调用函数,有名称、描述和 JSON Schema 参数。
  • Resources(资源):服务器提供给客户端的只读数据,如文件、数据库记录、API 响应。
  • Prompts(提示词模板):服务器提供的可复用提示词模板。

对于豆包工作这样的桌面 Agent,MCP 的价值在于:它不需要为每个外部服务(飞书、GitHub、数据库、企业内部系统)写定制集成,只需要内置一个 MCP 客户端运行时,就可以连接任意 MCP 服务器。用户或管理员配置 MCP 服务器后,Agent 自动发现其工具和资源,纳入自己的能力集。

豆包工作没有用 JavaScript/TypeScript 实现 MCP 客户端(这在 Chromium 渲染进程中是最自然的选择),而是用 Rust 编写了一个独立的原生 Helper。这个选择背后有性能、安全和架构上的考量,本章逐层拆解。

5.2 mcp-helper:Rust 编写的 MCP 运行时

5.2.1 二进制组成

豆包含两个 MCP 相关的原生文件:

Contents/Helpers/mcp-helper              — 1.2 MB,可执行文件
Contents/Frameworks/libmcp_helper.dylib  — 11 MB,动态库

「源码」

mcp-helper 是一个轻量启动器,真正的 MCP 逻辑在 libmcp_helper.dylib 中。这种"启动器 + 动态库"的拆分在豆包工作的 Helper 设计中很常见(sandbox-launcher + libsandbox.dylib 也是同样模式),好处是动态库可以被多个进程加载,更新时只需要替换 dylib。

5.2.2 Rust 技术栈

对 libmcp_helper.dylib 运行 strings 分析,提取到以下关键依赖和符号:「源码」

依赖 版本/信息 用途
rmcp 3.0.0 Rust 生态的官方 MCP SDK
oauth2 5.0 OAuth 2.0 客户端实现
hyper-rustls 0.27.9 基于 rustls 的 HTTP 客户端
tokio (未显示版本) 异步运行时
serde/serde_json (未显示版本) 序列化/反序列化
tracing (未显示版本) 结构化日志

rmcp 是 Rust 生态中最成熟的 MCP SDK,由社区维护,实现了完整的 MCP 规范。版本 3.0.0 说明它跟进了 MCP 协议的较新版本(MCP 规范在 2025 年经历了快速迭代,3.0 对应 Streamable HTTP 传输等新特性)。

oauth2 5.0 是一个完整的 OAuth 2.0 客户端库,支持 Authorization Code、Client Credentials、Device Code 等流程,PKCE 扩展,以及 token 刷新。这意味着豆包工作的 MCP 客户端可以处理需要 OAuth 认证的远程 MCP 服务器。

hyper-rustls 0.27.9 是基于 rustls(纯 Rust TLS 实现)的 HTTP 客户端,不依赖系统 OpenSSL。这在沙箱环境中很重要——rustls 不需要读取系统证书存储,可以自带证书或通过配置注入。

5.2.3 为什么用 Rust

在 Chromium 桌面应用中,用 Rust 而不是 JavaScript 写 MCP 运行时,有几个合理的原因:

第一,STDIO 传输需要进程管理。 MCP 的 STDIO 传输通过子进程的标准输入/输出通信。在 JavaScript 中管理子进程(spawn、stdin/stdout 管道、进程生命周期、信号处理)是可行的,但在沙箱环境中复杂且容易出错。Rust 的 std::process 和 tokio::process 提供了更可靠的进程管理。

第二,安全性。 MCP 服务器可能是第三方代码,运行在子进程中。Rust 的内存安全特性减少了缓冲区溢出、use-after-free 等内存安全漏洞的风险。对于一个负责启动外部进程、处理认证 token、转发数据的组件来说,内存安全是重要的安全考量。

第三,性能和资源。 MCP 服务器可能是长连接(Streamable HTTP),需要同时管理多个连接。Rust 的异步运行时(tokio)在高并发场景下比 Node.js 更省内存、延迟更低。对于桌面应用来说,后台常驻的 Helper 进程内存占用直接影响用户体验。

第四,与沙箱/审计组件的集成。 libmcp_helper.dylib、libsandbox.dylib 和 libauditSboxSDK.dylib 都是原生代码,可以直接通过 FFI 互相调用。如果 MCP 运行时在 JavaScript 中,与沙箱和审计组件的集成就需要额外的 IPC 桥接。

5.3 三种传输协议

图 5-2:MCP 三种传输协议对比

MCP 规范定义了多种传输方式,豆包工作的 mcp-helper 支持全部三种主流传输:「源码」

5.3.1 STDIO

STDIO 传输通过子进程的标准输入/输出流通信。这是本地 MCP 服务器最常用的方式——服务器是一个可执行程序(如 npx @modelcontextprotocol/server-filesystem),mcp-helper 启动它,通过 stdin 发送 JSON-RPC 请求,通过 stdout 接收响应。

STDIO 传输的优点是简单、低延迟、不需要网络端口;缺点是每个服务器需要一个独立进程,进程管理开销随服务器数量增长。

5.3.2 HTTP+SSE

HTTP+SSE(Server-Sent Events)是 MCP 的早期远程传输方式。客户端通过 HTTP POST 发送请求,通过一个长连接的 SSE 流接收服务器推送。这种方式允许 MCP 服务器运行在远程主机上,支持多客户端共享。

HTTP+SSE 在 MCP 规范的 2025-03-26 版本中被标记为 deprecated,推荐使用 Streamable HTTP。但豆包工作仍然支持它,以兼容已有的 MCP 服务器生态。

5.3.3 Streamable HTTP

Streamable HTTP 是 MCP 规范 2025-03-26 版本引入的新传输方式,替代 HTTP+SSE。它使用单一 HTTP 端点,通过 Accept: application/json, text/event-stream 头让服务器决定返回普通 JSON 响应还是 SSE 流。这简化了部署(不需要两个端点),支持无状态和有状态两种模式,并允许服务器在需要时主动推送。

rmcp 3.0.0 支持 Streamable HTTP,说明豆包工作跟进了 MCP 规范的最新进展。

5.4 OAuth 2.0 认证

远程 MCP 服务器通常需要认证。mcp-helper 通过 oauth2 5.0 库实现了完整的 OAuth 2.0 客户端:「源码」

  • Authorization Code Flow with PKCE:用户在浏览器中完成授权,mcp-helper 接收 authorization code,交换 access token 和 refresh token。
  • Token 刷新:access token 过期后自动使用 refresh token 获取新 token,不需要用户重新登录。
  • Token 存储:token 存储在 macOS Keychain 中(strings 输出中包含 Keychain 相关符号),而不是明文文件。
  • Scope 管理:支持请求和管理 OAuth scope,限制 MCP 服务器的访问权限。

OAuth 流程的用户体验通常是:用户在豆包工作中添加一个远程 MCP 服务器 → 应用打开浏览器到授权页面 → 用户登录并授权 → 重定向回豆包工作(通过自定义 scheme,如 doubaowork://oauth/callback)→ mcp-helper 保存 token 并建立连接。

自定义 scheme 的证据来自 manifest.json:doubaowork-background 等 scheme 拥有 "Danger API" 权限,可以处理后台 OAuth 回调。「源码」

5.5 FFI 桥接与 Browser IPC

mcp-helper 的核心逻辑在 libmcp_helper.dylib 中,通过 C FFI(Foreign Function Interface)暴露给宿主进程。strings 分析发现了两个关键符号:「源码」

  • mcp-ffi-abi-version:FFI ABI(应用二进制接口)版本号。这确保宿主进程和 dylib 之间的二进制兼容性——如果 dylib 更新改变了接口,版本号不匹配会阻止加载。
  • RunMcpHelperApi:FFI 入口函数。这是宿主进程调用 mcp-helper 功能的统一 API 入口。

5.5.1 调用链重建

综合 FFI 符号、进程架构和 Chromium IPC 机制,可以重建 MCP 工具调用的完整链路:

模型决定调用 MCP 工具
    ↓
渲染进程(JavaScript)生成工具调用请求
    ↓
Chromium IPC(Mojo)发送到主进程
    ↓
主进程通过 FFI 调用 libmcp_helper.dylib 的 RunMcpHelperApi
    ↓
mcp-helper 根据传输类型:
  - STDIO:写入子进程 stdin
  - HTTP:通过 hyper-rustls 发送 HTTP 请求
    ↓
MCP 服务器执行工具,返回结果
    ↓
mcp-helper 接收结果(stdout / HTTP 响应)
    ↓
通过 FFI 回调返回主进程
    ↓
Chromium IPC 返回渲染进程
    ↓
渲染进程将结果注入模型上下文

「推断」

这个调用链中有几个设计要点:

  1. 渲染进程不直接接触 MCP 服务器。所有 MCP 通信都在原生层完成,渲染进程只看到"发送请求、接收结果"的抽象。这防止了渲染进程被攻破后直接访问 MCP 服务器或 token。
  2. FFI ABI 版本化。mcp-ffi-abi-version 确保 dylib 和宿主进程的接口匹配,防止版本不匹配导致的内存不安全。
  3. 结果通过 Browser IPC 返回。doubao-mcp-bridge 项目的代码证实了这一点——它通过 CDP 监听 MCP_CALL_JSON 消息,这些消息是 MCP 调用在渲染进程中的表现形式。「社区」

5.5.2 与内置工具的区别

豆包工作的工具系统中有两类工具:

  1. 内置工具(如 Read、Bash、image_gen):在系统提示词中定义,由渲染进程或主进程直接处理。
  2. MCP 工具:由外部 MCP 服务器提供,通过 mcp-helper 桥接。

从模型的角度看,两者没有区别——都是有名称、描述和参数的函数。但从执行路径看,MCP 工具多了一层原生 Helper 桥接。这层桥接带来了额外的安全性(进程隔离、token 保护)和灵活性(支持任意 MCP 服务器),代价是延迟略高(多了 IPC 跳转)。

5.6 MCP 服务器配置

豆包工作的 MCP 服务器配置存储在哪里?WorkBuddy 使用 ~/.workbuddy/mcp.json(明文 JSON,用户可编辑)。豆包工作没有在本地文件系统中发现等效的配置文件。「源码」

可能的配置位置:

  1. Chromium 存储:配置可能通过渲染进程的 UI 收集,存储在 localStorage 或 IndexedDB 中。Preferences 文件中的 saman.task_mode 相关键可能包含 MCP 配置。
  2. 服务端同步:MCP 服务器配置可能与用户账号关联,存储在服务端,登录后同步到本地。这与"技能不同步"的设置形成对比——MCP 配置可能被视为账号资产。
  3. 托管配置:企业管理员可能通过设备管理(MDM)或企业策略推送 MCP 服务器配置。

在线"连接器"功能很可能就是 MCP 服务器的图形化配置界面。用户在技能商店中添加一个连接器(如"飞书文档连接器"),背后实际是配置一个 MCP 服务器的 URL、认证信息和启用的工具。「推断」

5.7 第三方视角:doubao-mcp-bridge

GitHub 上的开源项目 doubao-mcp-bridge(wanghaoyang1995/doubao-mcp-bridge)提供了一个有趣的第三方视角。「社区」

这个项目做的事情是:通过 Chrome DevTools Protocol(CDP)连接到豆包工作的内置浏览器,监听渲染进程中的 MCP_CALL_JSON 消息,把豆包工作内部的 MCP 调用转发给外部 MCP 客户端。换句话说,它把豆包工作从"MCP 客户端"变成了"MCP 服务器"——外部工具可以通过它调用豆包工作内置的工具能力。

这个项目从侧面证实了几个事实:

  1. 豆包工作内部确实使用 MCP 协议进行工具调用(消息类型名为 MCP_CALL_JSON)。
  2. MCP 调用在渲染进程中可被 CDP 观测,说明工具调用的编排逻辑在 JavaScript 层。
  3. 内置浏览器开启了 CDP 调试端口(至少在某些配置下),这是浏览器自动化的基础。

需要注意的是,doubao-mcp-bridge 是一个逆向工程项目,它依赖 CDP 连接和内部消息格式,这些不是稳定的公共 API,可能随版本更新而失效。

5.8 MCP 协议的消息格式

MCP 使用 JSON-RPC 2.0 作为消息格式。一次典型的工具调用流程如下:

  1. 初始化:客户端发送 initialize 请求,协商协议版本和能力
  2. 工具发现:客户端发送 tools/list 请求,服务器返回可用工具列表
  3. 工具调用:客户端发送 tools/call 请求,包含工具名和参数
  4. 结果返回:服务器返回工具执行结果(content 数组,可包含文本、图片、资源)
  5. 错误处理:服务器返回 JSON-RPC error 对象,包含错误码和消息

MCP 还支持 resources/list、resources/read、prompts/list、prompts/get 等方法,分别用于资源发现、资源读取和提示词模板。

对于 STDIO 传输,这些 JSON-RPC 消息通过子进程的 stdin/stdout 逐行交换。对于 HTTP 传输,消息通过 HTTP POST 请求和 SSE 流传输。mcp-helper 负责所有这些协议细节,渲染进程只需要发送高层的工具调用请求并接收结果。

5.9 MCP 与 Function Calling 的关系

MCP 不替代大模型的 function calling 能力,而是建立在它之上:

  • Function calling 是模型生成结构化工具调用请求的能力
  • MCP 是工具的发现、描述和调用的网络协议

模型通过 function calling 决定调用哪个工具、传什么参数,MCP 负责找到工具、连接服务器、传输请求、返回结果。在豆包工作中,内置工具直接在系统提示词中定义,MCP 工具通过 mcp-helper 动态发现——但从模型的角度看,两者都是 function calling 的目标,没有区别。

这种分层设计使得新增 MCP 服务器不需要重新训练模型或修改系统提示词:mcp-helper 发现新工具后,工具定义被注入上下文,模型通过标准 function calling 调用它们。

5.10 MCP 层的架构判断

判断一:Rust MCP Helper 是豆包含 Harness 中工程含量最高的组件。 用 Rust + rmcp 3.0.0 实现完整的 MCP 客户端运行时,支持三种传输和 OAuth 2.0,通过 FFI 与 Chromium 集成,这不是简单的功能堆砌,而是一个经过深思熟虑的架构决策。它反映了字节对 MCP 作为 Agent 互联标准的战略承诺。「推断」

判断二:MCP 是连接器的底层协议。 产品界面上的"连接器"概念,在技术实现上就是 MCP 服务器。用户通过图形界面添加连接器,底层是 mcp-helper 建立 MCP 连接、发现工具、管理认证。这种"产品概念包装技术协议"的做法降低了用户的认知门槛。「推断」

判断三:MCP 工具与内置工具在模型层统一、在执行层分离。 模型看到的是统一的工具列表,不需要区分内置和 MCP。但执行路径不同:内置工具走 Chromium IPC 直接处理,MCP 工具走 Rust Helper → 外部进程/远程服务器。这种统一接口、分离执行的设计,使得新增 MCP 服务器不需要修改系统提示词或模型逻辑。「推断」

判断四:STDIO 服务器在沙箱中运行。 mcp-helper 启动的 STDIO MCP 服务器子进程很可能受到 Seatbelt 沙箱约束(与 command_helper 执行的命令一样)。这意味着第三方 MCP 服务器不能随意访问文件系统或网络,必须在沙箱策略允许的范围内操作。沙箱机制的细节在下一章展开。「推断」

下一章将进入反馈与安全层,分析豆包工作的工具执行环境——macOS Seatbelt 沙箱、审计 SDK、权限模式和虚拟钥匙串,这些是确保 Agent "能干活但不闯祸"的关键机制。

CHAPTER 06 · ZJBB-004

来源:《豆包工作 Harness 设计拆解蓝皮书》 · 作者 大鹏|智见 AI
授权:CC BY-NC-SA 4.0 (署名 · 非商业性使用 · 相同方式共享)—— 转载请保留原作者署名与相同协议。
整理:疯狂的豇豆 · 查看完整来源清单
🤖 GEO 问答 · 生成式引擎优化

❓ 什么是CH-05 协议层:MCP 原生集成?

图 5-1:MCP 协议层架构

❓ 如何理解为什么 MCP 重要?

MCP(Model Context Protocol,模型上下文协议)是 Anthropic 于 2024 年底开源的一个标准协议,用于让 AI 模型与外部工具和数据源通信。你可以把它理解为"AI 世界的 USB-C":在 MCP 之前,每个 AI 应用接入每个外部服务都需要写定制的集成代码;有了 MCP 之后,任何兼容 MCP 的客户端都可以连接任何兼容 MCP 的服务器,工具能力可以即插即用。

❓ 如何理解mcp-helper:Rust 编写的 MCP 运行时?

豆包含两个 MCP 相关的原生文件:

❓ 如何理解三种传输协议?

图 5-2:MCP 三种传输协议对比