MCP & Web Tools

外部服务通过标准协议变成 Agent 工具。

MCP 客户端桥 + Web 搜索/抓取缝。

85 行2 类提供者

问题

Agent 的能力不该止步于本地。现实世界在 Agent 之外:

  • 一个记忆系统、一个数据库服务、一个内部 API——它们各有各的协议;
  • 互联网搜索、网页抓取——模型训练数据里没有今天的信息。

外部世界成为工具,需要两种桥:一是标准协议桥(MCP:Model Context Protocol——外部服务器暴露工具,harness 发现并注册),二是能力缝桥(web 搜索/抓取,提供者可换)。

解决方案

第 1 类:MCP 客户端——连接一个 MCP 服务器,发现它的工具,注册进本地注册表:

// 连接 MCP 服务器(stdio 或 HTTP),发现工具
async function connectMcpServer(name, transport) {
  const client = await mcpConnect(transport);       // 启动/连接服务器
  const { tools } = await client.listTools();       // 发现工具
  for (const t of tools) {
    define(`mcp__${name}__${t.name}`, t.inputSchema, async (args) => {
      const r = await client.callTool(t.name, args); // 转发调用
      return formatMcpResult(r);
    });
  }
  return `已注册 ${tools.length} 个工具`;
}

第 2 类:Web 能力缝——搜索与抓取走同一套提供者接口:

// ctx.web 缝:注册搜索提供者
registerWebProvider("search", { name: "deepseek", async search(q) { /* ... */ } });
registerWebProvider("search", { name: "exa",      async search(q) { /* ... */ } });
registerWebProvider("fetch",  { name: "http",     async fetch(url) { /* ... */ } });

define("web_search", { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
  async ({ query }) => await webSearch(query)); // 按配置选择提供者

define("web_fetch", { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
  async ({ url }) => await webFetch(url));

工作原理

第 1 步:MCP 工具命名带服务器前缀(mcp__server__tool),避免不同服务器的同名工具冲突;stdio 服务器的生命周期挂在 harness 插件生命周期上(启动/停止跟随插件)。

第 2 步:web 缝的搜索与抓取共享提供者选择服务——选一个搜索后端(DeepSeek/Exa/Perplexity)、一个抓取后端(HTTP),工具描述不变,后端可换(s17 的哲学再次出现)。

第 3 步:外部工具的返回要规整成文本(或结构化 JSON)——工具结果的形态决定模型消费的难易。MCP 结果、搜索摘要、页面正文,最终都变成模型能读的字符串。

试一下

运行 s19:

  • 用一个本地 MCP 服务器(stdio),让模型调用它暴露的工具
  • 用 web_search 问一个需要实时信息的问题("今天 DeepSeek 发布了什么")
  • 用 web_fetch 抓一个 URL 并让模型总结

观察重点:外部工具的延迟与失败——MCP 服务器挂了会怎样?搜索超时了会怎样?(答案:进入 s11 的错误恢复与超时策略。)

以下内容基于 packages/mcppackages/web 的核查。

一、MCP 桥

packages/mcp/mcp-client 是 MCP 客户端桥:解析配置、启动 stdio 命令或连接 Streamable HTTP、发现工具并注册为 mcp__<serverName>__<tool>。可运行的参考配置在 examples/mcp-memory/(三个默认关闭的第三方记忆系统示例)。安全细节:stdio 桥在启动子进程前移除常见凭据环境变量与所有 DSH_* 变量;凭据通过配置行显式传入。

二、Web 能力缝

packages/webctx.web 定义提供者注册、选择、共享错误;提供者:web-search-deepseek(原生 DeepSeek 搜索)、web-search-exaweb-search-perplexityweb-fetch-http;消费方 tool-web 暴露 web_search / web_fetch

安全细节:带凭据的提供者请求拒绝重定向——HTTP 客户端在跟随任何重定向前失败,防止凭据被转发到其他源。这是配置层面的强制,有回归测试证明重定向目标不被接触。

你的最小实现 DSH 的真实实现
mcpConnect 示意 完整 MCP 客户端(stdio/HTTP、生命周期、凭据剥离)
registerWebProvider 内存注册 ctx.web 提供者缝 + 配置驱动选择
工具返回任意文本 结构化请求/结果类型 + 共享 WebError

一句话:外部世界接入 Agent 的两条路——标准协议桥(MCP)与能力缝(web)。它们的共同点是:外部是提供者,模型侧是稳定工具