主题
MCP 集成与工具装配
本章目标:
- 理解
get_available_tools()这条"工具装配总线"如何把四类工具汇成 Agent 的工具集- 看懂 MCP 集成:多 server 管理、三种 transport、OAuth、mtime 缓存失效
- 弄清哪些工具是条件装配(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_mcp | tools/tools.py(MCP 段) |
| ③ | 内置工具 | BUILTIN_TOOLS(present_files/ask_clarification) | 始终 | tools/tools.py:65 |
| ③' | view_image | model supports_vision | 条件 | tools/tools.py:81-85 |
| ③'' | skill_manage | 技能管理 | 条件 | tools/tools.py:70 |
| ④ | 子代理 task | SUBAGENT_TOOLS | subagent_enabled | tools/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 |
|---|---|---|---|
stdio | command + args | 本地子进程 MCP server(如 filesystem) | deer-flow/backend/packages/harness/deerflow/mcp/client.py:24-30 |
sse | url | SSE 长连 | deer-flow/backend/packages/harness/deerflow/mcp/client.py:32 |
http | url | HTTP(+ 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.json 里 filesystem server(stdio,指向 llm-wiki)默认启用,这是链路 B 的入口。
MCP 工具缓存状态机:
Configuration
| 配置 | 位置 | 含义 | Source |
|---|---|---|---|
mcpServers.<name> | extensions_config.json | enabled/type/command/url/headers/oauth | deer-flow/extensions_config.json |
tool_search.enabled | config.yaml:647 | 延迟加载 MCP 工具(只列名,运行时 tool_search 发现) | deer-flow/config.yaml:647-648 |
tools[] / tool_groups[] | config.yaml:509-636 | config 定义工具 + 分组 | deer-flow/config.yaml:509-636 |
filesystem server | extensions_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);也可经 GatewayPUT /api/mcp/config在线改。 - OAuth 走
mcp/oauth.py:支持client_credentials/refresh_token,自动刷新 + 注入 Authorization 头。 - 想加 config 工具(非 MCP)则在
config.yaml的tools[]加一条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 配置
Related Pages
| Page | Relationship |
|---|---|
| Lead Agent 设计与图构建 | 本章装配总线由该章 make_lead_agent 调用 |
| deer-flow 引擎配置体系 | 本章 resolve_variable 反射机制在该章详解 |
| 子代理委派与确定性 Pipeline | 本章 task 工具委派到该章子代理 |
| 沙箱架构与文件工具 | 本章 ① 类工具含该章文件/bash 工具 |
| 系统整体架构 | 本章 filesystem MCP 是该章链路 B 的底座 |