mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-10 14:58:46 +00:00
* feat(mcp): add per-server tool_call_timeout for MCP tool calls Add a configurable timeout for individual MCP tool calls to prevent agent runs from blocking indefinitely when an MCP server becomes unresponsive (e.g., rate-limited HTTP API, hung subprocess). Uses the MCP SDK's built-in read_timeout_seconds parameter on ClientSession.call_tool, which handles the timeout within the session's own task — avoiding cross-task cancellation issues with the session pool (ref #3379, #3203). Config field is named tool_call_timeout (not timeout) to avoid collision with langchain-mcp-adapters' existing timeout field on HTTP/SSE connections. Closes #3840 * fix(mcp): read tool_call_timeout from McpServerConfig, not connection dict The previous implementation put tool_call_timeout into the connection dict returned by build_server_params, which langchain's create_session then passed to _create_stdio_session(), causing TypeError. Now reads the timeout directly from ExtensionsConfig.mcp_servers where the wrapper is built, keeping it out of the connection dict entirely. Fixes P1 bug from review on #3843. * test(mcp): regression test for tool_call_timeout not leaking into connection dict Adds two tests: - test_build_server_params_excludes_tool_call_timeout: verifies the connection dict returned by build_server_params() does NOT contain tool_call_timeout - test_stdio_tool_call_timeout_does_not_raise_typeerror: end-to-end test that get_mcp_tools() with a stdio server having tool_call_timeout configured loads tools without TypeError from _create_stdio_session() Regression for PR #3843 P1 bug. * fix(mcp): only pass read_timeout_seconds when tool_call_timeout is set When tool_call_timeout is None, don't pass read_timeout_seconds=None to session.call_tool(). This avoids breaking existing tests that assert on exact call_tool arguments without the extra kwarg. * docs(mcp): clarify stdio tool timeout
136 lines
4.8 KiB
Markdown
136 lines
4.8 KiB
Markdown
# MCP (Model Context Protocol) Configuration
|
||
|
||
DeerFlow supports configurable MCP servers and skills to extend its capabilities, which are loaded from a dedicated `extensions_config.json` file in the project root directory.
|
||
|
||
## Setup
|
||
|
||
1. Copy `extensions_config.example.json` to `extensions_config.json` in the project root directory.
|
||
```bash
|
||
# Copy example configuration
|
||
cp extensions_config.example.json extensions_config.json
|
||
```
|
||
|
||
2. Enable the desired MCP servers or skills by setting `"enabled": true`.
|
||
3. Configure each server’s command, arguments, and environment variables as needed.
|
||
4. Restart the application to load and register MCP tools.
|
||
|
||
## Per-Tool Timeout (Stdio MCP Servers)
|
||
|
||
For `stdio` MCP servers, set `tool_call_timeout` to limit each individual MCP tool call in seconds:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"github": {
|
||
"enabled": true,
|
||
"type": "stdio",
|
||
"command": "npx",
|
||
"args": ["-y", "@modelcontextprotocol/server-github"],
|
||
"env": {
|
||
"GITHUB_TOKEN": "$GITHUB_TOKEN"
|
||
},
|
||
"tool_call_timeout": 60
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
`tool_call_timeout` only applies to `stdio` servers. `http` and `sse` servers use transport-level timeouts, and DeerFlow logs a warning if `tool_call_timeout` is configured for those transports.
|
||
|
||
## Filesystem MCP Servers
|
||
|
||
DeerFlow already provides built-in file tools for thread-scoped workspace access.
|
||
Do not add an MCP filesystem server for the same DeerFlow workspace. The
|
||
overlapping file tools use different path semantics, which can make LLM tool
|
||
selection and file access behavior unstable.
|
||
|
||
DeerFlow does not currently adapt the MCP Roots mode for filesystem servers. In
|
||
particular, it does not publish per-thread MCP roots or map DeerFlow sandbox
|
||
paths such as `/mnt/user-data/...` to paths accepted by
|
||
`@modelcontextprotocol/server-filesystem`. Use DeerFlow's built-in file tools
|
||
for DeerFlow workspace files.
|
||
|
||
## OAuth Support (HTTP/SSE MCP Servers)
|
||
|
||
For `http` and `sse` MCP servers, DeerFlow supports OAuth token acquisition and automatic token refresh.
|
||
|
||
- Supported grants: `client_credentials`, `refresh_token`
|
||
- Configure per-server `oauth` block in `extensions_config.json`
|
||
- Secrets should be provided via environment variables (for example: `$MCP_OAUTH_CLIENT_SECRET`)
|
||
|
||
Example:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"secure-http-server": {
|
||
"enabled": true,
|
||
"type": "http",
|
||
"url": "https://api.example.com/mcp",
|
||
"oauth": {
|
||
"enabled": true,
|
||
"token_url": "https://auth.example.com/oauth/token",
|
||
"grant_type": "client_credentials",
|
||
"client_id": "$MCP_OAUTH_CLIENT_ID",
|
||
"client_secret": "$MCP_OAUTH_CLIENT_SECRET",
|
||
"scope": "mcp.read",
|
||
"refresh_skew_seconds": 60
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Custom Tool Interceptors
|
||
|
||
You can register custom interceptors that run before every MCP tool call. This is useful for injecting per-request headers (e.g., user auth tokens from the LangGraph execution context), logging, or metrics.
|
||
|
||
Declare interceptors in `extensions_config.json` using the `mcpInterceptors` field:
|
||
|
||
```json
|
||
{
|
||
"mcpInterceptors": [
|
||
"my_package.mcp.auth:build_auth_interceptor"
|
||
],
|
||
"mcpServers": { ... }
|
||
}
|
||
```
|
||
|
||
Each entry is a Python import path in `module:variable` format (resolved via `resolve_variable`). The variable must be a **no-arg builder function** that returns an async interceptor compatible with `MultiServerMCPClient`’s `tool_interceptors` interface, or `None` to skip.
|
||
|
||
Example interceptor that injects auth headers from LangGraph metadata:
|
||
|
||
```python
|
||
def build_auth_interceptor():
|
||
async def interceptor(request, handler):
|
||
from langgraph.config import get_config
|
||
metadata = get_config().get("metadata", {})
|
||
headers = dict(request.headers or {})
|
||
if token := metadata.get("auth_token"):
|
||
headers["X-Auth-Token"] = token
|
||
return await handler(request.override(headers=headers))
|
||
return interceptor
|
||
```
|
||
|
||
- A single string value is accepted and normalized to a one-element list.
|
||
- Invalid paths or builder failures are logged as warnings without blocking other interceptors.
|
||
- The builder return value must be `callable`; non-callable values are skipped with a warning.
|
||
|
||
## How It Works
|
||
|
||
MCP servers expose tools that are automatically discovered and integrated into DeerFlow’s agent system at runtime. Once enabled, these tools become available to agents without additional code changes.
|
||
|
||
## Example Capabilities
|
||
|
||
MCP servers can provide access to:
|
||
|
||
- **Databases** (e.g., PostgreSQL)
|
||
- **External APIs** (e.g., GitHub, Brave Search)
|
||
- **Browser automation** (e.g., Puppeteer)
|
||
- **Custom MCP server implementations**
|
||
|
||
## Learn More
|
||
|
||
For detailed documentation about the Model Context Protocol, visit:
|
||
https://modelcontextprotocol.io
|