web_search, web_extract, and (optionally) deep-crawl tool calls. Built-in providers — Firecrawl, SearXNG, Tavily, Perplexity, Exa, Parallel, Keenable, Brave Search (free tier), xAI, and DDGS — all ship as plugins under plugins/web/<name>/. You can add a new one, or override a bundled one, by dropping a directory next to them.
How discovery works
Mibyan scans for web-search backends in three places:- Bundled —
<repo>/plugins/web/<name>/(auto-loaded withkind: backend, always available) - User —
~/.mibyan/plugins/web/<name>/(opt-in viaplugins.enabledormibyan plugins enable <name>) - Pip — packages declaring a
mibyan_agent.pluginsentry point
register(ctx) function calls ctx.register_web_search_provider(...) — that puts the instance into the registry in agent/web_search_registry.py. The active provider for each capability is picked by config:
When neither key is set, Mibyan auto-detects the backend from whichever API key/URL is present in the environment.
mibyan tools walks users through selection.
Directory structure
brave_free/ and ddgs/ are the smallest in-tree references — brave_free for an API-key-gated search-only provider, ddgs for a no-key provider that lazy-installs its SDK.
The WebSearchProvider ABC
Subclassagent.web_search_provider.WebSearchProvider. The only required members are name, is_available(), and whichever of search() / extract() you implement. (Deep crawling is not a separate method — it’s a mode of extract().)
plugin.yaml
ABC reference
Full contract inagent/web_search_provider.py. Methods you may override:
Providers can advertise multiple capabilities from a single class — Firecrawl, Tavily, Perplexity, Keenable, Exa, and Parallel all implement both search and extract. Brave Search and DDGS are search-only; SearXNG is search-only with a documented “pair me with an extract provider” workflow.
Response shape
The tool wrapper expects a fixed envelope so it doesn’t have to translate between backends. Search success:search() and extract() may be async def — the dispatcher detects coroutine functions via inspect.iscoroutinefunction and awaits accordingly. Sync implementations that do blocking I/O (HTTP, SDK calls) are fine for small backends; the dispatcher handles threading.
Capability flags
Mibyan routes calls to the right provider based on thesupports_* flags. A common multi-provider setup:
web.search_backend or web.extract_backend aren’t set, both fall through to web.backend. When that’s also unset, Mibyan picks the first available provider that supports the requested capability based on env-var presence.
If your provider only supports one capability, leave the other flags at their default (False) and the registry will skip it for that tool — users won’t see misleading “provider X failed” errors when they’re using X only for search and asking the agent to extract.
How Mibyan wires it into the tools
Theweb_search and web_extract tools live in tools/web_tools.py. At call time they:
- Read the relevant config key (
web.search_backendforweb_search,web.extract_backendforweb_extract) - Ask the registry for the provider with that
name - Check
is_available()and the matchingsupports_*()flag - Dispatch to
search()/extract()(deep crawl runs as a mode insideextract()), awaiting if the method is a coroutine - JSON-serialize the response envelope and hand it back to the LLM
mibyan tools.
Lazy-installing optional dependencies
Keep availability checks read-only. For an SDK covered by a Mibyan extra, usepm.available("extra-name") in is_available(). Request
pm.ensure_import("extra-name") from the operation that needs it. Report
InstallError, including a required restart, to the caller.
Declare a third-party plugin’s own dependencies in its manifest or
pyproject.toml rather than inventing a Mibyan extra. See
Build a Mibyan Plugin → Lazy-install.
Reference implementations
plugins/web/brave_free/— small, API-key-gated, search-only HTTP provider. Good starting template.plugins/web/ddgs/— no-key provider that lazy-installs its SDK. Useful pattern for backends that wrap a Python package.plugins/web/firecrawl/— full multi-capability provider (search + extract + crawl) with multiple format modes.plugins/web/searxng/— self-hosted, URL-configured backend with no auth.plugins/web/xai/— LLM-backed search via Grok’s server-sideweb_searchtool. Shows how to reuse an existing OAuth/env-var credential surface (tools/xai_http.py) without adding new env vars, and how to write a cheapis_available()that honors the no-network contract.
Distribute via pip
my_backend_web_package must expose a top-level register function. See Distribute via pip in the general plugin guide for the full setup.
Related pages
- Web Search — user-facing feature documentation and per-backend configuration
- Plugins overview — all plugin types at a glance
- Build a Mibyan Plugin — general tools/hooks/slash commands guide

