Skip to content

MCP 集成与工具装配

本章目标:

  1. 理解 get_available_tools() 这条"工具装配总线"如何把四类工具汇成 Agent 的工具集
  2. 看懂 MCP 集成:多 server 管理、三种 transport、OAuth、mtime 缓存失效
  3. 弄清哪些工具是条件装配(vision/subagent/host_bash)及其开关

TL;DR

Agent 的工具集不是写死的,而是 get_available_tools()(tools/tools.py:35)每次请求装配:① config.yaml 定义的工具(resolve_variable 反射)② MCP 工具(从启用的 MCP server,懒加载 + mtime 缓存失效)③ 内置工具(present_files/ask_clarification,vision 时加 view_image)④ 子代理工具(subagent_enabled 时加 task)。MCP 用 MultiServerMCPClient,build_server_params 支持 stdio/sse/http 三种 transport,HTTP/SSE 支持 OAuth token 刷新;工具缓存靠 extensions_config.json 的 mtime 比对自动失效。

Overview(为什么工具要"装配"而不是固定)

不同请求需要的工具不同:vision 模型才需要 view_image,开了子代理才需要 task,装了 filesystem MCP server 才有读 llm-wiki 的工具,allow_host_bash 决定 bash 能不能用宿主 shell。如果工具集是固定的,要么给 LLM 一堆它用不了的工具(浪费 token、误导选择),要么硬编码所有组合。

deer-flow 用一条装配总线 get_available_tools():每次 make_lead_agent 调它,按当前 model/subagent/config 现场拼工具集。MCP 是其中最动态的部分——外部 MCP server 的工具是运行时发现的,且 server 配置(extensions_config.json)可经 Gateway API 在线改,所以需要懒加载 + 缓存失效机制,既不每次重连(慢)也不用过期配置(错)。这套机制是 a-cdm 链路 B(Agent 通过 MCP filesystem 读 llm-wiki)的技术底座。

Architecture:工具装配总线

get_available_tools(groups, include_mcp, model_name, subagent_enabled)(tools/tools.py:35)四类来源:

#来源机制条件Source
config.yaml tools[]resolve_variable(tool.use, BaseTool) 反射按 group 过滤tools/tools.py:62
MCP server 工具get_cached_mcp_tools() 懒加载include_mcptools/tools.py(MCP 段)
内置工具BUILTIN_TOOLS(present_files/ask_clarification)始终tools/tools.py:65
③'view_imagemodel supports_vision条件tools/tools.py:81-85
③''skill_manage技能管理条件tools/tools.py:70
子代理 taskSUBAGENT_TOOLSsubagent_enabledtools/tools.py:72-75

resolve_variable(tool.use, BaseTool)(tools/tools.py:62)是反射加载——这就是 config.yaml 里 use: deerflow.sandbox.tools:bash_tool 能变成可调用工具的原因(见配置体系章)。_is_host_bash_tool(tools/tools.py:24)用于按 allow_host_bash 决定 bash 工具是否纳入。内置工具:present_file_tool(把 outputs 文件对用户可见)、ask_clarification_tool(被 ClarificationMiddleware 拦截成中断)、task_tool(子代理委派,见下章);注意 task_status_tool 已不暴露给 LLM(后端内部轮询,tools/tools.py:20)。

Components:MCP 集成

build_server_params(server_name, config)(mcp/client.py:11)按 config.type(默认 stdio)分支:

transport必需字段用途Source
stdiocommand + args本地子进程 MCP server(如 filesystem)deer-flow/backend/packages/harness/deerflow/mcp/client.py:24-30
sseurlSSE 长连deer-flow/backend/packages/harness/deerflow/mcp/client.py:32
httpurlHTTP(+ OAuth)deer-flow/backend/packages/harness/deerflow/mcp/client.py:32

build_servers_config(extensions_config)(mcp/client.py:45-63)遍历启用的 server 拼 MultiServerMCPClient 配置。mcp/oauth.py 处理 HTTP/SSE 的 OAuth:client_credentials/refresh_token 流,自动刷新 + 注入 Authorization 头。

缓存失效(mcp/cache.py):_mcp_tools_cache 缓存工具,_get_config_mtime()(:17)取 extensions_config.json mtime,_is_cache_stale()(:31-50)比对——文件 mtime 增大即判定 stale 并失效(:49-50)。这就是为什么 Gateway PUT /api/mcp/config 改完 MCP 配置,LangGraph 进程无需重启也能感知:它写文件改了 mtime,下次 get_cached_mcp_tools 检测到 stale 自动重载。a-cdm 的 extensions_config.jsonfilesystem server(stdio,指向 llm-wiki)默认启用,这是链路 B 的入口。

MCP 工具缓存状态机:

Configuration

配置位置含义Source
mcpServers.<name>extensions_config.jsonenabled/type/command/url/headers/oauthdeer-flow/extensions_config.json
tool_search.enabledconfig.yaml:647延迟加载 MCP 工具(只列名,运行时 tool_search 发现)deer-flow/config.yaml:647-648
tools[] / tool_groups[]config.yaml:509-636config 定义工具 + 分组deer-flow/config.yaml:509-636
filesystem serverextensions_config.json链路 B:读 llm-wiki(默认 enabled)deer-flow/extensions_config.json

tool_search(config.yaml:647)关闭时所有 MCP 工具直接进上下文;开启时只把工具名列进系统提示、运行时靠 tool_search 工具按需发现——多 MCP server 暴露大量工具时减 token、提升选择准确性(对应中间件链 #16 DeferredToolFilterMiddleware)。

扩展指南:加一个 MCP server

json
// extensions_config.json → mcpServers
"my-server": {
  "enabled": true,
  "type": "http",
  "url": "https://my-mcp.example.com/mcp",
  "headers": {},
  "oauth": { "token_url": "...", "client_id": "...", "grant_type": "client_credentials" },
  "description": "给 LLM 看的用途说明"
}

约束(从代码读出):

  • type 决定必需字段:stdio 必须 command(client.py:24-26),sse/http 必须 url(client.py:32)。
  • extensions_config.json无需重启:mtime 失效机制自动重载(cache.py:49);也可经 Gateway PUT /api/mcp/config 在线改。
  • OAuth 走 mcp/oauth.py:支持 client_credentials/refresh_token,自动刷新 + 注入 Authorization 头。
  • 想加 config 工具(非 MCP)则在 config.yamltools[] 加一条 use: pkg.mod:tool_var(必须是 BaseTool 实例,tools/tools.py:62 反射校验)。

Common Pitfalls / 实战 Tips

  • 改 extensions_config 不必重启,但 mtime 必须真的变:某些编辑器原子写可能不更新 mtime,改完确认 os.path.getmtime 变了(cache.py:27)。
  • MCP 工具懒加载:首次用才连 server,首次调用会慢一点(要 list_tools),这是设计不是 bug。
  • tool_search 开关影响 token:多 server 大量工具时不开 tool_search 会把工具 schema 全塞进上下文,token 爆。
  • config 工具 use 必须返回 BaseTool:resolve_variable(tool.use, BaseTool) 有类型校验,写错会启动期 ImportError/ValueError(fail-fast)。
  • task_status 不给 LLM:子代理状态由后端轮询,别期望 LLM 能调 task_status(tools/tools.py:20)。

References

  • deer-flow/backend/packages/harness/deerflow/tools/tools.py:35-85 — get_available_tools 装配总线(本章主源)
  • deer-flow/backend/packages/harness/deerflow/mcp/client.py:11-63 — build_server_params/servers_config(三 transport)
  • deer-flow/backend/packages/harness/deerflow/mcp/cache.py:11-50 — mtime 缓存失效
  • deer-flow/backend/packages/harness/deerflow/mcp/oauth.py — HTTP/SSE OAuth token 刷新
  • deer-flow/extensions_config.json — MCP server 配置(filesystem 默认启用)
  • deer-flow/config.yaml:509-648 — tools/tool_groups/tool_search 配置
PageRelationship
Lead Agent 设计与图构建本章装配总线由该章 make_lead_agent 调用
deer-flow 引擎配置体系本章 resolve_variable 反射机制在该章详解
子代理委派与确定性 Pipeline本章 task 工具委派到该章子代理
沙箱架构与文件工具本章 ① 类工具含该章文件/bash 工具
系统整体架构本章 filesystem MCP 是该章链路 B 的底座

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析