Skip to content

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:

extensions_config.json
{
"mcpServers": {
"github": {
"enabled": true,
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "$GITHUB_TOKEN"
}
}
}
}

Step 2: Set the Environment Variable

.env
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

Step 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 set

I had set the token in .env, but DeerFlow wasn’t loading it. The fix was to either:

  1. Export the variable in the shell: export GITHUB_TOKEN=ghp_xxx
  2. Use a .env loader in the startup script

I went with option 2 and added this to my startup:

Terminal window
source .env && python -m deerflow.main

Now 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.

stdio-config.json
{
"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.

http-config.json
{
"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-config.json
{
"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:

oauth-config.json
{
"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:

refresh-token-config.json
{
"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:

  1. Lazy Initialization: Tools are loaded only when first used. No startup delay.

  2. Cache Invalidation: DeerFlow checks the modification time (mtime) of extensions_config.json. If it changed, the cache is cleared.

  3. Runtime Updates: I can edit the config file without restarting. DeerFlow detects changes and reloads.

The Python code looks something like this:

mcp/tools.py
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_tools

Multiple Server Configuration

I ended up with this configuration for my project:

extensions_config.json
{
"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 ENOENT

The npx command wasn’t found. The fix was to ensure Node.js is installed and in PATH:

/usr/bin/npx
which npx

If it’s missing, install Node.js:

Terminal window
# macOS
brew install node
# Ubuntu
sudo apt install nodejs npm

Issue 2: Tools Not Appearing

The config was correct, but tools didn’t show up. I checked:

  1. Is the server enabled: true?
  2. Are environment variables set?
  3. Is the server process running?

I used this command to debug:

Terminal window
# Test MCP server directly
npx @modelcontextprotocol/server-github

If 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

Terminal window
curl http://localhost:2026/api/mcp/config

Response:

{
"mcp_servers": {
"github": {
"enabled": true,
"type": "stdio"
}
}
}

Update Config

Terminal window
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

mcp_client_example.py
from deerflow.client import DeerFlowClient
client = DeerFlowClient()
# Get current config
config = client.get_mcp_config()
# Add a new server
client.update_mcp_config({
"mcpServers": {
"my-server": {
"enabled": True,
"type": "stdio",
"command": "my-mcp-server"
}
}
})

Here are servers I found useful:

ServerPurposeInstall
@modelcontextprotocol/server-githubGitHub operationsnpx -y @modelcontextprotocol/server-github
@modelcontextprotocol/server-filesystemFile system accessnpx -y @modelcontextprotocol/server-filesystem /path
@modelcontextprotocol/server-postgresPostgreSQL queriesuvx mcp-server-postgres <connection>
@modelcontextprotocol/server-slackSlack integrationnpx -y @modelcontextprotocol/server-slack
mcp-server-browserbaseBrowser automationnpx -y mcp-server-browserbase

Why This Matters

Before MCP integration, extending my agent meant:

  1. Writing Python code
  2. Defining tool schemas
  3. Handling errors manually
  4. Redeploying the agent

With MCP:

AspectWithout MCPWith MCP
Tool extensibilityCode changesJSON config
External data sourcesCustom integrationsStandard protocol
AuthenticationManual handlingOAuth support
Runtime updatesRestart requiredHot reload
Tool ecosystemLimited to built-inAny 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:

  1. Configure servers in extensions_config.json
  2. Set environment variables for secrets
  3. Choose the right transport (stdio, HTTP, SSE)
  4. Use OAuth for secured APIs
  5. 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:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments