Introduction¶
Below is an introduction to dsh-balanced-search. It solves the problem in DSH where internet search easily relies on a single search API: when a service is unavailable, returns a failure, or the key is configured differently, the call chain is affected. This plugin puts the three search APIs—Keenable, Exa, and Tavily—into the same set of tools, calling them in a round-robin fashion; it automatically switches to the next one when a service fails and uniformly returns title, link, and content summary. It provides both a native dsh plugin and a general MCP server form.
What is it¶
dsh-balanced-search is maintained by tianmingwan and adopts the MIT license. It contains two usage modes:
- Native dsh plugin: Directly register the two dsh tools
balanced_searchandbalanced_fetch, requiring no Python. - General MCP server: Exposes
searchandfetchviaserver.pyusing stdio mode, available for any MCP client to use.
Core Features¶
This plugin mainly does two things: search and fetch.
- Search the web and return title, link, and content summary.
- Fetch the web page body of a specified URL and return clean markdown.
- Call the three search services (Keenable, Exa, Tavily) in turn; automatically switch to the next one when a single service fails.
- Configure API keys via environment variables; services without a configured key will not be enabled.
- Native dsh plugin does not require installing Python dependencies; the Python MCP server requires installing dependencies according to
requirements.txt.
Install as a dsh Plugin¶
First, confirm that DeepSeek Harness (dsh) is installed on the machine, and the Node.js version is not less than 20.
dsh plugin --profile web add github:tianmingwan/dsh-balanced-search
After installation, configure at least one search service’s API key:
KEENABLE_API_KEY=...
EXA_API_KEY=...
TAVILY_API_KEY=...
The native dsh plugin reads process environment variables directly. After completing the steps above and restarting dsh --profile web, two tools will appear in the session:
balanced_searchbalanced_fetch
Use as a General MCP Server¶
If you don’t want to use the dsh plugin, you can also use the Python MCP server. First, install dependencies:
python -m venv .venv
# Linux / macOS
.venv/bin/python -m pip install -r requirements.txt
# Windows
.venv\Scripts\python.exe -m pip install -r requirements.txt
Then run in stdio mode:
# Use the current Python directly
python server.py
# Or use the Python in the virtual environment
.venv/bin/python server.py
.venv\Scripts\python.exe server.py
Example of an MCP client connection. Here, command points to Python, args points to server.py, and three API key environment variables are injected via env:
{
"mcpServers": {
"balanced-search": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/server.py"],
"env": {
"KEENABLE_API_KEY": "...",
"EXA_API_KEY": "...",
"TAVILY_API_KEY": "..."
}
}
}
}
The Python MCP server, besides reading environment variables injected by the client, will also automatically read the .env file in the same directory.
Typical Usage¶
The parameters of native dsh tools and MCP tools are basically the same:
balanced_search/search: Parameters arequery,max_results,time_range.balanced_fetch/fetch: Parameters areurl,max_chars,live.
Parameter descriptions are as follows:
query(required): Search keywords or natural language questions.max_results: 1–20, default 8.time_range: Can takeday,week,month,year. Among them, Tavily supports it natively, Exa maps tostartPublishedDate, and Keenable maps topublished_after.max_chars: Maximum number of characters for fetched content, default 30000, upper limit 50000.live: Whether to fetch in real-time from the source site, bypassing index/cache, defaultfalse.
The JSON returned by search looks like:
{
"provider": "keenable|exa|tavily",
"count": 1,
"results": [
{"title": "...", "url": "...", "content": "...", "published_at": "...", "score": 0.5}
]
}
The JSON returned by fetch looks like:
{
"provider": "keenable|exa|tavily",
"result": {"url": "...", "title": "...", "content": "..."}
}
Use Cases and Notes¶
This plugin is suitable for integrating web search and URL fetching into DSH, or exposing the same set of search/fetch capabilities to MCP client users. Configure at least one search service’s API key; if all three services have keys configured, the plugin will call them in a round-robin manner and switch to the next one when a service fails.
It is worth noting that the dsh plugin runs with the permissions of the current dsh process. Before installation, it is recommended to check if the source code, dependencies, and MIT license fit your usage environment. If you want to add a new search service, you can add a SearchProvider subclass and register it in providers.py, or add a Provider class in index.js. The current failover strategy is round-robin + failover, and the documentation states it can be changed to weighted or health-aware.
Conclusion¶
Overall, the value of dsh-balanced-search lies in putting three search services in a balanced layer, while retaining both native dsh tools and general MCP access methods. The repository address is:
- GitHub: https://github.com/tianmingwan/dsh-balanced-search