MCP Integration
Model Context Protocol (MCP) lets DeerFlow connect to any external tool server. Once connected, MCP tools are available to the Lead Agent exactly like built-in tools.
The Model Context Protocol (MCP) is an open standard for connecting language models to external tools and data sources. DeerFlow’s MCP integration allows you to extend the agent with any tool server that implements the MCP protocol — without modifying the harness itself.
Configuration
MCP servers are configured in extensions_config.json, a file separate from config.yaml. This separation allows MCP and skill configurations to be managed independently and updated at runtime through the Gateway API.
The default location is the project root (same directory as config.yaml). The path is determined by ExtensionsConfig.resolve_config_path().
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "@my-org/my-mcp-server"],
"enabled": true
},
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/path/to/db.sqlite"],
"enabled": false
}
}
}Do not add an MCP filesystem server for DeerFlow workspace files. DeerFlow
already provides built-in file tools for thread-scoped workspace access, and
overlapping file tools with different path semantics can make LLM tool
selection and file access behavior unstable. DeerFlow does not currently
adapt MCP Roots mode for filesystem servers: it does not publish per-thread
MCP roots or map sandbox paths such as /mnt/user-data/… to
paths accepted by @modelcontextprotocol/server-filesystem.
Each server entry supports:
command: the executable to run (e.g.,npx,uvx,python)args: command arguments as an arrayenabled: whether the server is active (can be toggled without removing the entry)env: optional environment variables injected into the server process
How tools are loaded
Startup initialization
When the DeerFlow server starts, initialize_mcp_tools() is called. This connects to all enabled MCP servers, retrieves their tool schemas, and caches the results.
Lazy initialization fallback
If the server starts before MCP tools are initialized (e.g., in LangGraph Studio), get_cached_mcp_tools() performs lazy initialization on the first tool call.
Cache invalidation
The MCP tools cache tracks the modification time (mtime) of extensions_config.json. When the file changes — for example, when a server is enabled or disabled through the Gateway API — the cache is marked stale and tools are reloaded on the next request.
This means MCP server changes take effect without restarting the DeerFlow server.
Tool availability
Once loaded, MCP tools appear in the Lead Agent’s tool list alongside built-in and community tools. The agent selects and calls them using the same mechanism as any other tool.
Tool search integration
When many MCP servers expose a large number of tools, loading all of them into the agent’s context at once can increase token usage and reduce tool selection accuracy.
Enable tool search to load MCP tools on demand instead:
# config.yaml
tool_search:
enabled: trueWith tool search enabled, MCP tools are listed by name in the system prompt but not included in the full tool schema. The agent discovers them using the tool_search built-in tool and loads only the ones it needs for a given task.
OAuth support
Some MCP servers require OAuth authentication. DeerFlow’s mcp/oauth.py handles the OAuth flow for servers that declare OAuth requirements in their capability headers.
When an OAuth-protected MCP server is connected, DeerFlow will:
- Detect the OAuth requirement from the server’s capability headers
- Build the appropriate authorization headers using
get_initial_oauth_headers() - Wrap tool calls with an OAuth interceptor via
build_oauth_tool_interceptor()
The OAuth flow is transparent to the Lead Agent — it simply calls the tool, and DeerFlow handles the authentication.
Per-user credentials
A single HTTP/SSE MCP server entry can serve several DeerFlow users, each
authenticated to the remote service with their own credential. Declare a
user_auth block mapping DeerFlow user ids to credential header values:
{
"mcpServers": {
"shared-service": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": { "Authorization": "$SERVICE_DISCOVERY_TOKEN" },
"user_auth": {
"header": "Authorization",
"users": {
"alice-user-id": "$SERVICE_TOKEN_ALICE",
"bob-user-id": "$SERVICE_TOKEN_BOB"
}
}
}
}
}On every tool call, the built-in user-scoped auth interceptor resolves the
authenticated DeerFlow user and injects that user’s credential into the
configured header. The entry’s static headers are used only for startup tool
discovery.
- Values support the same
$ENV_VARresolution as the rest of the file, so raw secrets can stay in the process environment. - Fail-closed by default: a user with no mapped credential — or a mapped
$ENV_VARthat is unset — gets a clear error instead of another user’s credential. Set"on_missing": "passthrough"to instead forward such calls with the server’s static headers. - Combined with per-
(user, thread)MCP session scoping, users cannot reach each other’s authenticated sessions or credentials. - The Gateway API masks
user_auth.usersvalues in GET responses and preserves stored credentials when masked values are round-tripped through PUT.
Finding the right users key. The resolved user id depends on how the
deployment authenticates: with LangGraph Server auth it is the safe-slug form
of the authenticated identity (e.g. alice-example-com-ab12cd34), while the
embedded Gateway resolves the DeerFlow user’s raw id (a UUID). The simplest way
to get the exact key is to have the user attempt a call before being mapped:
the fail-closed error message includes their resolved id verbatim, ready to
copy into user_auth.users.
Per-request credentials
user_auth binds a credential to a configured DeerFlow user. When the caller
picks the credential at request time instead — a multi-tenant gateway, a per-run
API key, one shared MCP server fronting several environments — declare a
headers_from_context block. Each entry maps an HTTP header name to a key of
the run request’s config.context.secrets carrier:
{
"mcpServers": {
"shared-api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": { "Authorization": "$SERVICE_DISCOVERY_TOKEN" },
"headers_from_context": {
"headers": {
"X-Tenant-Id": "tenant_id",
"Authorization": "tenant_token"
},
"on_missing": "deny"
}
}
}
}The caller supplies the values with each run request, out of band from the conversation:
{
"config": {
"context": {
"secrets": {
"tenant_id": "acme",
"tenant_token": "Bearer <request-scoped credential>"
}
}
}
}- The config file stores names only, never a credential, so the block is returned unmasked by the config API. The values travel with each run and are stripped from persisted run configuration, API responses, and traces.
- The entry’s static
headersare used only for startup tool discovery; a mapped header replaces the static one for the tool call. Header names are matched case-insensitively, so a mappedAuthorizationreplaces a staticauthorizationrather than travelling alongside it. - Fail-closed by default: if the run carries no value for a mapped key, the
call fails with an error naming the missing key rather than falling back to
the discovery credential — which would send one tenant’s request under
another tenant’s authority. Set
"on_missing": "passthrough"to opt out. - For a server declaring several sources, precedence is static
headers<oauth<user_auth<headers_from_context: the value chosen for this one request is the most specific, so it wins. - On a server that also declares
task_toolsets, the durable submit is awaited inside the run and carries these headers, but the later status and cancel polls run after the run ends and fall back to the server’s own credentials — so the fail-closed guarantee covers the submit, not those polls.
Managing MCP servers
MCP servers can be managed in several ways:
- Through the DeerFlow App UI: the extensions panel shows connected MCP servers and lets you enable/disable them.
- Through the Gateway API:
POST /api/extensions/mcp/{name}/enableand/disable. - By editing
extensions_config.jsondirectly: useful for scripted or programmatic configuration.
Changes are picked up automatically due to the file mtime-based cache invalidation.