# Web Search Missing — Diagnosis

Symptom: `web_search`/`web_extract` tools not available in agent tools list, despite `"web"` being in `platform_toolsets.cli`.

## Two Root Causes (both must be ruled in/out)

### 1. Toolset not in platform_toolsets.cli

```bash
grep 'cli:' ~/.hermes/config.yaml | grep -o '"web"'
```

If `"web"` is missing, fix:
```bash
hermes tools enable web
```

Then `/new` — tools are loaded at session init only.

### 2. Web backend not configured

```bash
grep -A3 '^web:' ~/.hermes/config.yaml
```

Expected output:
```yaml
web:
  backend: ddgs
  extract_backend: ''
  search_backend: ddgs
```

If `backend` or `search_backend` are empty (`''`), the web search provider registry returns `None` and `web_search`/`web_extract` are silently stripped from tools — even when the toolset is enabled.

Fix:
```bash
hermes config set web.backend ddgs
hermes config set web.search_backend ddgs
```

Then `/new`.

Available free backends (no API key): `ddgs` (DuckDuckGo Search), `brave_free` (if Brave Search API key in env).

**⚠️ `ddgs` requires `pip install ddgs`.** The ddgs Python package is not a Hermes dependency — it must be installed manually. Without it, `web.search_backend: ddgs` configures the backend but the provider plugin silently fails to import (no error logged). Always run:
```bash
uv pip install ddgs
```

After installing, verify the session has the tool by starting a new session (tools load at session init) and running `web_search`. If it still doesn't appear, check the `.env` doesn't have a stale FIRECRAWL_API_KEY that might override auto-detection.

**Per-capability split recommended:** Set separate search and extract backends to combine free search (ddgs) with a paid/API-key extract provider:
```bash
hermes config set web.search_backend ddgs
hermes config set web.extract_backend firecrawl
```
This lets search run free while only paying for URL extraction when needed.

### Verification

After fixing, start a new session and check the tool list is present:
```bash
hermes tools list | grep web
```

Should show: `✓ enabled  web  🔍 Web Search & Scraping`

Then in the new session, try:
```
web_search query="test"
```

### Direct Terminal Test (without starting a session)

To test `web_search`/`web_extract` from the shell without starting a Hermes session, import the tool functions directly from the Hermes venv. **The `.env` file is NOT loaded by standalone Python** — Hermes loads it at session init. You must export the relevant env vars manually:

```bash
cd ~/.hermes/hermes-agent
source venv/bin/activate
export SEARXNG_URL=http://127.0.0.1:8080
export FIRECRAWL_API_KEY=***
python -c "
from tools.web_tools import web_search_tool, web_extract_tool
import json, asyncio

# Test search
result = json.loads(web_search_tool('test query', limit=3))
print('Search results:', len(result.get('data',{}).get('web',[])))

# Test extract
result = asyncio.run(web_extract_tool(['https://example.com'], use_llm_processing=False))
data = json.loads(result)
print('Extract success:', data.get('results',[{}])[0].get('title',''))
"
```

Alternatively load all env vars from `.env`:
```bash
export $(grep -v '^#' ~/.hermes/.env | xargs)
```

This technique works for **any** Hermes tool function — import from `tools/` and call directly. Useful for debugging without the session overhead.

### SearXNG-Specific Verification

If using SearXNG as search backend, verify JSON format is enabled on the instance:

```bash
curl -s "http://<SEARXNG_URL>:<port>/search?q=test&format=json" | python3 -c '
import sys, json
d = json.load(sys.stdin)
count = len(d.get("results", []))
print(f"{count} results returned")
if d.get("results"):
    print(f"First: {d[\"results\"][0].get(\"title\", \"?\")}")
'

If you get a 403 Forbidden or HTML response instead of JSON, the `formats:` list in SearXNG's `settings.yml` needs `- json` added (SearXNG ships with `json` disabled by default). See the SearXNG setup docs.

## Why This Happens Repeatedly

The `web:` config section is fragile — it tends to get wiped by:
- Config audits that rewrite the file
- `hermes config` operations that dump/re-serialize YAML (string arrays become quoted strings, etc.)
- Updates that reset parts of config.yaml
- Manual edits that omit the section

Pin it in your mental checklist after any config operation. The `hermes-diagnostics` skill audit procedure now includes this check.

## See Also

See `references/hermes-web-browsing-ecosystem.md` for the full landscape: all 8 search backends, 6 browser automation modes, MCP addons, plugin architecture, and recommended configurations by use case.
