How to Connect MCP Servers to DeerFlow for Extended Tool Access
The Problem
I built a DeerFlow agent to automate some workflows. It worked great for basic tasks, but then I needed it to:
- Read files from my local filesystem
- Create GitHub issues and pull requests
- Query a PostgreSQL database
Each new capability meant writing Python code, adding new tool definitions, and redeploying. The tools were hardcoded in the agent definition. I thought: there must be a better way.
Discovery: MCP Integration
I found that DeerFlow supports Model Context Protocol (MCP) servers out of the box. MCP is an open protocol for connecting AI models to external tools and data sources.
The key insight: instead of writing code, I configure MCP servers in a JSON file. DeerFlow auto-discovers tools from these servers.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ DeerFlow │────▶│ MCP Client │────▶│ MCP Server ││ Agent │ │ Manager │ │ (tools) │└─────────────┘ └─────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ Config JSON │ │ (no code!) │ └─────────────┘My First MCP Server Connection
Step 1: Create the Configuration File
I created extensions_config.json in my project root:
{ "mcpServers": { "github": { "enabled": true, "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" } } }}Step 2: Set the Environment Variable
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxStep 3: Restart DeerFlow
I restarted the DeerFlow server. The tools from the GitHub MCP server were automatically discovered.
But wait - nothing happened. I checked the logs and found:
Error: GITHUB_TOKEN environment variable not setI had set the token in .env, but DeerFlow wasn’t loading it. The fix was to either:
- Export the variable in the shell:
export GITHUB_TOKEN=ghp_xxx - Use a
.envloader in the startup script
I went with option 2 and added this to my startup:
source .env && python -m deerflow.mainNow it worked. The agent could use GitHub tools.
Understanding Transport Types
MCP servers communicate via different transports. DeerFlow supports three:
stdio Transport
The server runs as a local process. DeerFlow spawns it and communicates via stdin/stdout.
{ "filesystem": { "enabled": true, "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"] }}This is what I used for GitHub. The npx command runs the server package directly.
HTTP Transport
The server exposes an HTTP endpoint. DeerFlow sends requests to it.
{ "custom-api": { "enabled": true, "type": "http", "url": "https://api.example.com/mcp", "headers": { "X-Custom-Header": "value" } }}This is useful for remote MCP servers or custom implementations.
SSE Transport
Server-Sent Events for real-time communication.
{ "sse-server": { "enabled": true, "type": "sse", "url": "https://sse.example.com/mcp" }}Adding OAuth Authentication
My next challenge was connecting to a secured API that uses OAuth. The MCP config supports OAuth flows.
Client Credentials Flow
For server-to-server authentication:
{ "secure-api": { "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_CLIENT_ID", "client_secret": "$MCP_CLIENT_SECRET", "scope": "read write", "refresh_skew_seconds": 60 } }}The OAuth flow works like this:
┌─────────────┐ 1. Request Token ┌─────────────┐│ DeerFlow │────────────────────────▶│ OAuth Server ││ │ │ ││ │◀──── 2. Access Token ───│ ││ │ └─────────────┘│ ││ │ 3. API Request + Bearer Token│ │────────────────────────▶┌─────────────┐│ │ │ MCP Server ││ │◀──── 4. Response ───────│ │└─────────────┘ └─────────────┘The refresh_skew_seconds tells DeerFlow to refresh the token 60 seconds before it expires. This prevents “token expired” errors during long-running operations.
Refresh Token Flow
For user-authorized access:
{ "user-api": { "enabled": true, "type": "http", "url": "https://api.example.com/mcp", "oauth": { "enabled": true, "token_url": "https://auth.example.com/oauth/token", "grant_type": "refresh_token", "refresh_token": "$MCP_REFRESH_TOKEN", "client_id": "$MCP_CLIENT_ID", "client_secret": "$MCP_CLIENT_SECRET" } }}How Tools Are Loaded
I was curious about the internal mechanism. Here’s what happens:
-
Lazy Initialization: Tools are loaded only when first used. No startup delay.
-
Cache Invalidation: DeerFlow checks the modification time (mtime) of
extensions_config.json. If it changed, the cache is cleared. -
Runtime Updates: I can edit the config file without restarting. DeerFlow detects changes and reloads.
The Python code looks something like this:
async def get_cached_mcp_tools(): config_mtime = get_config_mtime() cache_mtime = get_cache_mtime()
if config_mtime > cache_mtime: # Config changed, reinitialize client = MultiServerMCPClient(config) tools = await client.list_tools() cache_tools(tools) update_cache_mtime()
return cached_toolsMultiple Server Configuration
I ended up with this configuration for my project:
{ "mcpServers": { "github": { "enabled": true, "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" } }, "filesystem": { "enabled": true, "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"] }, "postgres": { "enabled": true, "type": "stdio", "command": "uvx", "args": ["mcp-server-postgres", "postgresql://localhost/mydb"] } }}Now my agent can read files, create GitHub issues, and query PostgreSQL - all without writing any Python code.
Common Issues and Fixes
Issue 1: Server Not Starting
I got this error:
Error: spawn npx ENOENTThe npx command wasn’t found. The fix was to ensure Node.js is installed and in PATH:
which npxIf it’s missing, install Node.js:
# macOSbrew install node
# Ubuntusudo apt install nodejs npmIssue 2: Tools Not Appearing
The config was correct, but tools didn’t show up. I checked:
- Is the server
enabled: true? - Are environment variables set?
- Is the server process running?
I used this command to debug:
# Test MCP server directlynpx @modelcontextprotocol/server-githubIf it fails, the issue is with the server, not DeerFlow.
Issue 3: OAuth Token Not Refreshing
Tokens expired during long operations. The refresh_skew_seconds wasn’t enough. I increased it:
{ "oauth": { "refresh_skew_seconds": 300 }}Now tokens refresh 5 minutes before expiry.
Programmatic Configuration
DeerFlow provides an API for managing MCP config:
Get Current Config
curl http://localhost:2026/api/mcp/configResponse:
{ "mcp_servers": { "github": { "enabled": true, "type": "stdio" } }}Update Config
curl -X PUT http://localhost:2026/api/mcp/config \ -H "Content-Type: application/json" \ -d '{ "mcpServers": { "slack": { "enabled": true, "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "$SLACK_BOT_TOKEN" } } } }'Using the Python Client
from deerflow.client import DeerFlowClient
client = DeerFlowClient()
# Get current configconfig = client.get_mcp_config()
# Add a new serverclient.update_mcp_config({ "mcpServers": { "my-server": { "enabled": True, "type": "stdio", "command": "my-mcp-server" } }})Popular MCP Servers
Here are servers I found useful:
| Server | Purpose | Install |
|---|---|---|
@modelcontextprotocol/server-github | GitHub operations | npx -y @modelcontextprotocol/server-github |
@modelcontextprotocol/server-filesystem | File system access | npx -y @modelcontextprotocol/server-filesystem /path |
@modelcontextprotocol/server-postgres | PostgreSQL queries | uvx mcp-server-postgres <connection> |
@modelcontextprotocol/server-slack | Slack integration | npx -y @modelcontextprotocol/server-slack |
mcp-server-browserbase | Browser automation | npx -y mcp-server-browserbase |
Why This Matters
Before MCP integration, extending my agent meant:
- Writing Python code
- Defining tool schemas
- Handling errors manually
- Redeploying the agent
With MCP:
| Aspect | Without MCP | With MCP |
|---|---|---|
| Tool extensibility | Code changes | JSON config |
| External data sources | Custom integrations | Standard protocol |
| Authentication | Manual handling | OAuth support |
| Runtime updates | Restart required | Hot reload |
| Tool ecosystem | Limited to built-in | Any MCP server |
The biggest benefit: I can use any MCP server from the growing ecosystem. No custom integration code needed.
Summary
Connecting MCP servers to DeerFlow changed how I extend my agents:
- Configure servers in
extensions_config.json - Set environment variables for secrets
- Choose the right transport (stdio, HTTP, SSE)
- Use OAuth for secured APIs
- Tools load lazily with hot reload
The configuration-based approach means I spend less time writing integration code and more time building workflows.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Model Context Protocol Specification
- 👨💻 DeerFlow Documentation
- 👨💻 MCP Server Registry
- 👨💻 LangChain MCP Adapters
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments