hermes - 💡(How to fix) Fix [Bug]: check_web_api_key() hardcodes built-in backends — third-party web search plugins silently disabled [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

web_search and web_extract are missing from the tool list. The tool registry's check_fn (check_web_api_key) returns False because it doesn't recognize "kagi" as a valid backend and none of the hardcoded fallback backends have credentials set. No warning or error is logged — the tools are silently dropped.

Additional Logs / Traceback (optional)

Root Cause

Third-party web search provider plugins (installed under ~/.hermes/plugins/web/) are silently disabled because check_web_api_key() in tools/web_tools.py only knows about built-in backends. Even when a plugin successfully registers its provider via ctx.register_web_search_provider(), the web_search and web_extract tools are filtered out by the tool registry because the check_fn returns False.

Fix Action

Fix / Workaround

  • PR #29425 (open) — fixes the same class of bug for xAI only, by adding xai to the hardcoded list. Doesn't solve the general problem for third-party plugins.
  • Issue #19198 (closed) — capability-based provider architecture proposal. Tasks 5-8 (registry, dispatch rewrite, check_fn split) were never implemented.
  • PR #23478 (closed, not merged) — auto-discovering ProviderRegistry that would have fully fixed this by making check_web_api_key() delegate to the registry.

Code Example

plugins:
  enabled:
    - web-kagi
web:
  backend: kagi

---

Contains PII/PHI and not relevant as far as I can tell.

---



---

def check_web_api_key() -> bool:
    configured = _load_web_config().get("backend", "").lower().strip()
    if configured in {"exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs"}:
        return _is_backend_available(configured)
    return any(
        _is_backend_available(backend)
        for backend in ("exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs")
    )

---

if configured in {"parallel", "firecrawl", "tavily", "exa", "searxng", "brave-free", "ddgs", "xai"}:
    return configured

---

def check_web_api_key() -> bool:
    """Check whether any registered web backend is available."""
    # First check if any plugin-registered provider is available
    from agent.web_search_registry import get_registry
    registry = get_registry()
    configured = _load_web_config().get("backend", "").lower().strip()
    if configured:
        provider = registry.get(configured)
        if provider and provider.is_available():
            return True
    if any(p.is_available() for p in registry.all_providers()):
        return True
    # Fall back to legacy built-in check
    return any(
        _is_backend_available(backend)
        for backend in ("exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs")
    )
RAW_BUFFERClick to expand / collapse

Bug Description

Third-party web search provider plugins (installed under ~/.hermes/plugins/web/) are silently disabled because check_web_api_key() in tools/web_tools.py only knows about built-in backends. Even when a plugin successfully registers its provider via ctx.register_web_search_provider(), the web_search and web_extract tools are filtered out by the tool registry because the check_fn returns False.

This affects any custom WebSearchProvider plugin that uses its own API key (e.g. KAGI_API_KEY) rather than one of the hardcoded env vars. The plugin system is designed to be extensible — but the tool availability gate defeats that extensibility.

Steps to Reproduce

  1. Install a third-party web search plugin (e.g. a Kagi provider) into ~/.hermes/plugins/web/kagi/
  2. Set the plugin's API key: export KAGI_API_KEY="..."
  3. Do not set any built-in backend keys (no EXA_API_KEY, TAVILY_API_KEY, etc.)
  4. Configure ~/.hermes/config.yaml:
plugins:
  enabled:
    - web-kagi
web:
  backend: kagi
  1. Run hermes tools or start a chat

Expected Behavior

web_search and web_extract should appear in the tool list, backed by the Kagi plugin. The plugin registered successfully and its is_available() returns True.

Actual Behavior

web_search and web_extract are missing from the tool list. The tool registry's check_fn (check_web_api_key) returns False because it doesn't recognize "kagi" as a valid backend and none of the hardcoded fallback backends have credentials set. No warning or error is logged — the tools are silently dropped.

Affected Component

Tools (terminal, file ops, web, code execution, etc.)

Messaging Platform (if gateway-related)

No response

Debug Report

Contains PII/PHI and not relevant as far as I can tell.

Operating System

Debian 13

Python Version

3.11

Hermes Version

Current main

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

Three functions in tools/web_tools.py have hardcoded backend lists that don't account for plugin-registered providers:

  1. check_web_api_key() (line 1367-1375) — the direct cause. This is the check_fn for both web_search and web_extract tool registrations (lines 1540, 1556). It only checks 7 built-in names:
def check_web_api_key() -> bool:
    configured = _load_web_config().get("backend", "").lower().strip()
    if configured in {"exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs"}:
        return _is_backend_available(configured)
    return any(
        _is_backend_available(backend)
        for backend in ("exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs")
    )

When backend is "kagi" (or any custom plugin name), it falls through to the any(...) fallback which also only probes the same 7 built-in backends.

  1. _is_backend_available() (line 205-231) — hardcoded if/elif chain with return False for any unrecognized backend name (line 231). Even if check_web_api_key passed "kagi" through, this function would still return False.

  2. _get_backend() (line 142-143) — the config allowlist rejects custom names:

if configured in {"parallel", "firecrawl", "tavily", "exa", "searxng", "brave-free", "ddgs", "xai"}:
    return configured

So web.backend: kagi in config is ignored and the function falls through to env-var auto-detection.

The fundamental disconnect: the plugin system in hermes_cli/plugins.py is designed to be extensible (any plugin can call ctx.register_web_search_provider()), but the tool availability gate never consults the web search provider registry. Each registered provider already implements is_available(), but that method is never called during the check.

Proposed Fix (optional)

check_web_api_key() should query the web search provider registry instead of (or in addition to) checking a hardcoded list:

def check_web_api_key() -> bool:
    """Check whether any registered web backend is available."""
    # First check if any plugin-registered provider is available
    from agent.web_search_registry import get_registry
    registry = get_registry()
    configured = _load_web_config().get("backend", "").lower().strip()
    if configured:
        provider = registry.get(configured)
        if provider and provider.is_available():
            return True
    if any(p.is_available() for p in registry.all_providers()):
        return True
    # Fall back to legacy built-in check
    return any(
        _is_backend_available(backend)
        for backend in ("exa", "parallel", "firecrawl", "tavily", "searxng", "brave-free", "ddgs")
    )

Similarly, _is_backend_available() and _get_backend() should delegate to the registry for unknown backend names rather than returning False.

Related issues/PRs:

  • PR #29425 (open) — fixes the same class of bug for xAI only, by adding xai to the hardcoded list. Doesn't solve the general problem for third-party plugins.
  • Issue #19198 (closed) — capability-based provider architecture proposal. Tasks 5-8 (registry, dispatch rewrite, check_fn split) were never implemented.
  • PR #23478 (closed, not merged) — auto-discovering ProviderRegistry that would have fully fixed this by making check_web_api_key() delegate to the registry.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING