Skip to content
mercury framework
GitHub

Web search

Package
@mercury-fw/plugin-web-search
Role
Tool plugin
Version
0.4.1
Terminal window
bun add @mercury-fw/plugin-web-search
VariableWhat it is
SEARXNG_URLSearXNG, the compose service the web search asks (default http://searxng:8080)
SEARXNG_SECRETRequired. SearXNG's secret key, any long random string (openssl rand -hex 32)

The agent gets two tools: web_search, which returns the title, address and a short excerpt of each result, and web_fetch, which reads a page and returns its text (the main content, without menus and footers). An answer built on pages ends with the list of what was read, written by the plugin, so the model can’t leave it out or make it up; a list the model writes at the end of its answer anyway is replaced by it.

The model searches when the question shows it needs to: something that changes over time (news, versions, prices, who holds a role), something recent compared with the date of the message (the agent tells the model the date, see Languages), a precise detail it would otherwise guess, a name it doesn’t recognize, or a request to look something up. Lasting facts, reasoning and writing need no search. A model can’t tell what it doesn’t know, so it isn’t asked to.

It needs no login, and everyone may use it (actsAs: "anyone").

What the person reads in its words (the two choices, the status lines, the Sources: heading) is English, under web-search. keys, and comes out in their language when the app translates them: see Languages.

The plugin doesn’t search by itself: it takes a backend that turns a query into results. The one Mercury ships is @mercury-fw/web-search-searxng, for SearXNG, a search engine you run yourself that asks the big ones and merges what they answer, with no API key. mfw create --plugins web-search adds a SearXNG service next to the app; fill in SEARXNG_SECRET in .env with a long random string:

Terminal window
openssl rand -hex 32
mercury.config.ts
import { webSearchPlugin } from "@mercury-fw/plugin-web-search";
import { searxngBackend } from "@mercury-fw/web-search-searxng";
plugins: [
webSearchPlugin({
backend: searxngBackend({ url: process.env.SEARXNG_URL! }),
allowedHosts: ["bun.sh", "developer.mozilla.org"],
}),
],

A page can carry text written to trick the model, and the model also reads private data (your wiki, your Jira), so reading any page it finds would let a hostile one make it send that data away. So a page is read right away only when its host is allowed:

  • by the app, in allowedHosts (an entry covers its subdomains: bun.sh covers docs.bun.sh);
  • or by the person asking. For any other host the agent asks them first, with a choice: read this page only, or always allow the host. Each choice is a confirmation, a button on Google Chat and a token elsewhere, and a host they allow stays on their own list.

Some addresses are never read, allowed or not: anything other than http and https, and any host that resolves to an address inside your network (the app’s own database, the model’s server). A read downloads at most 2 MB, gives up after 15 seconds, and hands the model at most 20,000 characters. No JavaScript runs, so a page that builds itself in the browser comes back nearly empty.