Web search and page reading for AI agents, as an MCP server and a CLI. Two tools:
search— real search engine result pages, read honestly. DuckDuckGo lite by default, with no key and no account. Google (and Google's AI Mode) when you list them, and then every Google query is shown to you first and runs only when you accept it.fetch— a page as clean markdown that keeps code blocks and tables. Long pages by section, by relevance, or by regex. PDFs, feeds, GitHub, PyPI, npm and Stack Overflow read through their APIs.
It identifies itself, honours robots.txt, waits between requests, never solves a bot check, and
treats a refusal as final — unless you, present, say "open it in my browser", in which case the page
is your browsing, opened in an incognito tab and read back labelled. Your queries go from your own browser to the engine you chose and nowhere
else. The full rules are in docs/POLICY.md.
Node 22.5 or newer. Add to your MCP client's config:
{ "mcpServers": { "fearch": { "command": "npx", "args": ["-y", "fearch-mcp"] } } }Claude Code: claude mcp add fearch -- npx -y fearch-mcp. For the fearch command on your PATH:
npm install -g fearch-mcp. (The package is fearch-mcp because npm reserves names this close to
fetch; the command is fearch.)
On macOS, pair the extension so that engine pages open quietly in your own Chrome:
npx fearch-mcp extension install # or: fearch extension installIt registers fearch's native messaging host with your browsers, writes the pairing token, opens
chrome://extensions, and tells you the three clicks: Developer mode, Load unpacked, choose the
folder it printed (~/fearch-extension). (A Chrome Web Store listing is in preparation; see
docs/extension/STORE.md and the extension's privacy policy.) Optional: allow it in
Incognito, which is where Google result pages open by default. After upgrading fearch, run the
command again and reload the extension. Without the extension, engine pages open in a background window of
your installed Chrome, and on macOS that window comes forward on every search (a Chromium bug).
From an agent, the two tools are enough; the server sends its own guidance to the model. From the shell:
fearch search "asyncio cancel task" --fetch-top 1
fearch fetch https://docs.python.org/3/library/asyncio-task.html --mode section --query Timeouts
fearch doctor # config, network, extension, one search, one render
fearch clear-profile --yes # forget the tool-owned browser profileEvery command takes --help and --json. Exit codes: 0 ok, 1 refused (with a Diagnosis), 2
failed.
Engines run in the order listed until one answers; an agent can ask for one (engine="google-ai"),
or ask for the form on any query (ask=true). On fetch, ask=true reads a page as your own
browsing with your yes. Your flags always win: the agent can add a question, never remove one.
Google needs a person: --engines google,duckduckgo in the args, and each query then appears in
your client as a form (edit it, pick the engine, incognito or not) before it runs. google-ai adds
Google's AI Mode: its reply comes back labelled as Google's unverified text, and its cited pages are
the results. Google shows those sources only to a signed-in session, so the form defaults AI Mode
to your profile and plain Google to incognito; signed out, the reply comes without sources and the
next engine supplies the links. From the CLI, which has no form, the search box opens in your browser and you press Enter;
for AI Mode the question is put on your clipboard for you to paste.
| DuckDuckGo lite | Google (Web tab) | Google AI Mode | fetch |
|
|---|---|---|---|---|
Default (--engines duckduckgo) |
runs, no question | not listed | not listed | plain client, then headless browser |
| Listed, you at the screen (MCP client with prompts) | runs, no question | form first: query, engine picker, incognito (default on), run on accept | same form, incognito default off so sources come | a bot check or a robots.txt "no" is offered to you (incognito or your profile, ask again or not); a sign-in wall in incognito earns a second offer, signed in |
| Listed, you away | runs | form waits 45 s, then the next engine runs and the result says so | same | refusal stands (Diagnosis, retryable) |
Agent passes ask=true |
form for that query, whatever else is set | form (also when "ask again" was turned off) | same | "open this page as your own browsing?" with the profile choice; no or nobody there means no read |
| "Ask me again next time" off | runs | runs as your remembered choice, no form | same | unchanged |
--human-search |
form first too; unanswered runs nothing | same as above | same | unchanged |
Terminal (fearch search, no form) |
runs | search box opens in your Chrome, you press Enter | AI Mode opens, question on your clipboard, you paste and press Enter | a robots.txt "no" is final |
--incognito |
your Chrome, incognito | incognito by default | incognito by default: reply without sources | escalations incognito (already the default) |
--browser headless or no display |
no search | no search | no search | headless only; checks and robots "no" final |
--no-handoff |
runs | not eligible | not eligible | checks final |
| A bot check appears | you are asked, then you pass it or not | same | same | same |
- Send a browser User-Agent from its own HTTP client, or hide
navigator.webdriverin a browser it drives. It isfearch/<version> (+https://github.com/funkyfunc/fearch#bot-info), or your own Chrome. - Retry a refusal with different headers, cookies, an IP, a proxy, or a third-party reader.
- Solve a CAPTCHA or any bot check. When one appears you are asked whether to see it, and you pass it or you don't.
- Follow links on its own, or fetch more than one page per host at a time, faster than once a second.
- Send a query anywhere but the engine you chose. No third-party search services, no telemetry.
Flags go in the MCP config's args; each is also a FEARCH_* environment variable. fearch --help
has the full table.
--engines duckduckgo,google,google-ai engines in order (default: duckduckgo)
--browser auto|headless|extension|off who renders pages (default: auto)
--robots default|strict default: `*` and the fearch token; strict adds vendor AI-agent and training-crawler tokens
--incognito your own Chrome: engine pages in an incognito window
--human-search show every query in the form, DuckDuckGo included (an unanswered form then runs nothing)
--allow-domains a,b --deny-domains c host lists
--search off fetch only
Behind a corporate proxy: HTTPS_PROXY/NO_PROXY are honoured everywhere; behind a TLS-intercepting
proxy, point NODE_EXTRA_CA_CERTS at the proxy's CA bundle for the plain client (the browser tier
uses the OS trust store). GITHUB_TOKEN in the environment raises GitHub API limits.
search(query, max_results=8, recency?: d|w|m|y, site?, allowed_domains?, engine?, ask=false,
fetch_top=0..3, raw=false)
fetch(url | urls[≤5], mode=read|focus|section|pattern|raw, query?, max_chars=12000,
cursor?, include_links=false, context_chars=200, archive=false, fresh=false, ask=false)
A page comes back with a one-line header (source, robots decision, date, licence, char window),
the content, and an outline of the sections not shown. A refusal comes back as a Diagnosis: the
kind (captcha_or_challenge, paywall, robots_disallowed, …), what was attempted, and what to do
instead.
If you operate a website and see this agent in your logs:
- User-Agent:
fearch/<version> (+https://github.com/funkyfunc/fearch#bot-info)from the plain client;HeadlessChrome/…withFrom:andX-Agent:headers naming the tool when a page needed a browser. Engine result pages are opened in the person's own Chrome and carry no tool identity. - What it is: a locally-run tool fetching individual pages a person's AI assistant asked for right now. Agent traffic, not a crawler: it never follows links and never trains models.
- Volume: one request at a time per host, at least a second apart, capped per session.
- To block it:
User-agent: fearch+Disallow: /in robots.txt.Crawl-delayandContent-Signal: ai-input=noare honoured;Claude-User/ChatGPT-UserandGPTBot-class tokens are honoured under--robots strict. A 402/403 is final. - Content negotiation: it sends
Accept: text/markdown, text/html;q=0.9, …. Serve markdown and it never touches your HTML.
npm test # unit, fixture and golden tests (no network)
npm run test:live # live smoke tests
npm run audit:render # per page: paragraphs, headings, code, tables kept vs. the page (live)
npm run lint && npm run format && npm run typecheckDocs: POLICY.md (the rules, for reviewers), SPECTRUM.md
(why), CASES.md, ROADMAP.md,
AGENT-GUIDANCE.md, and the outside reviews under docs/reviews/.