Hermes Agent's built-in web_search skill uses DuckDuckGo scraping which frequently fails with empty results, rate limiting, or timeouts. This tutorial replaces it with a reliable search API backend via MCP, fixing the 'web_search returned no results' error permanently.
Prerequisites
- Hermes Agent installed (v0.10+)
- A Scavio API key from scavio.dev
Walkthrough
Step 1: Diagnose the issue
Hermes web_search fails because DuckDuckGo rate-limits automated requests. Check your Hermes logs for the error pattern.
# Common error in Hermes logs:
# [WARN] web_search: DuckDuckGo returned 0 results for query 'python fastapi'
# [ERROR] web_search: Request timed out after 10s
# [WARN] web_search: Rate limited by DuckDuckGo (429)
# Check your Hermes config:
cat ~/.hermes/config.yaml | grep -A5 web_searchStep 2: Add Scavio MCP as search provider
Configure Hermes to use Scavio MCP server for web search instead of DuckDuckGo.
# In ~/.hermes/config.yaml, add MCP server:
mcp_servers:
scavio:
url: "https://mcp.scavio.dev/mcp"
headers:
x-api-key: "your_scavio_api_key"
tools:
- google_search
- reddit_search
- youtube_searchStep 3: Create a search skill override
Override the default web_search skill to use the MCP-provided google_search tool.
# Create ~/.hermes/skills/reliable_search.yaml:
name: reliable_search
description: Web search using Scavio API (replaces DuckDuckGo)
trigger: "search for|look up|find information about|web search"
action: |
Use the google_search tool from the scavio MCP server.
For Reddit-specific queries, use reddit_search instead.
For video content, use youtube_search.
Always return the top 5 results with title, URL, and snippet.Step 4: Disable default web_search
Prevent Hermes from falling back to the broken DuckDuckGo scraper.
# In ~/.hermes/config.yaml, disable built-in web_search:
skills:
disabled:
- web_search # Disable DuckDuckGo-based search
# reliable_search skill (above) will handle search queries insteadStep 5: Test the fix
Verify search works reliably with the new backend.
# In Hermes chat:
> Search for best Python web framework 2026
# Expected: Hermes uses scavio google_search tool, returns results
# Previously: Empty results or timeout from DuckDuckGo
# Test Reddit search:
> Search Reddit for Python framework recommendations
# Test YouTube search:
> Find YouTube tutorials about FastAPIPython Example
import requests, os
# Scavio has one endpoint per platform - there is no dispatcher endpoint and no
# `platform` request param, so the selector lives in your code.
SCAVIO = "https://api.scavio.dev"
SCAVIO_ENDPOINTS = {
"google": "/api/v2/google",
"reddit": "/api/v1/reddit/search",
"youtube": "/api/v1/youtube/search",
"amazon": "/api/v1/amazon/search",
"walmart": "/api/v1/walmart/search",
}
SCAVIO_QUERY_KEY = {"youtube": "search"}
SCAVIO_RESULTS_KEY = {"google": "organic_results", "reddit": "results",
"youtube": "results", "amazon": "products", "walmart": "products"}
def scavio_url(platform):
return SCAVIO + SCAVIO_ENDPOINTS[platform or "google"]
def scavio_body(platform, query):
return {SCAVIO_QUERY_KEY.get(platform or "google", "query"): query}
def scavio_payload(payload, platform="google"):
"""Google v2 passes Google's response through as-is; every other endpoint
wraps its payload in `data`. Item fields differ per platform (see
https://scavio.dev/docs), so only the result list is normalised here."""
platform = platform or "google"
out = payload if platform == "google" else payload["data"]
return {**out, "results": out.get(SCAVIO_RESULTS_KEY[platform], [])}
def hermes_search(query: str, platform: str = 'google') -> list:
resp = requests.post(scavio_url(platform), headers={'Authorization': 'Bearer ' + os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}, json=scavio_body(platform, query), timeout=10)
return scavio_payload(resp.json(), platform).get('results', [])[:5]JavaScript Example
// Scavio has one endpoint per platform - there is no dispatcher endpoint and no
// `platform` request param, so the selector lives in your code.
const SCAVIO = "https://api.scavio.dev";
const SCAVIO_ENDPOINTS = {
google: "/api/v2/google",
reddit: "/api/v1/reddit/search",
youtube: "/api/v1/youtube/search",
amazon: "/api/v1/amazon/search",
walmart: "/api/v1/walmart/search",
};
const SCAVIO_QUERY_KEY = { youtube: "search" };
const SCAVIO_RESULTS_KEY = { google: "organic_results", reddit: "results",
youtube: "results", amazon: "products", walmart: "products" };
const scavioUrl = (platform) => SCAVIO + SCAVIO_ENDPOINTS[platform || "google"];
const scavioBody = (platform, query) =>
({ [SCAVIO_QUERY_KEY[platform || "google"] || "query"]: query });
// Google v2 passes Google's response through as-is; every other endpoint wraps
// its payload in `data`. Item fields differ per platform (see
// https://scavio.dev/docs), so only the result list is normalised here.
function scavioPayload(json, platform = "google") {
const p = platform || "google";
const out = p === "google" ? json : json.data;
return { ...out, results: out[SCAVIO_RESULTS_KEY[p]] || [] };
}
async function hermesSearch(query, platform = 'google') {
const resp = await fetch(scavioUrl("google"), {
method: 'POST',
headers: {'Authorization': `Bearer ${process.env.SCAVIO_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify(scavioBody("google", query))
});
return (scavioPayload(await resp.json(), "google")).organic?.slice(0, 5) || [];
}Expected Output
Hermes Agent with reliable web search via Scavio MCP, replacing the broken DuckDuckGo scraper with a managed API backend.