# Web search The agent's knowledge of a library ends where its training did, and the two questions it cannot otherwise answer — what does this error message mean, and what is this API called now — are the ones a search box answers in seconds. The wizard's fourth question decides whether it gets one, and the honest default is no: every other capability here stays inside the working tree, and a search query is written by the model out of whatever the model has read. | `web_search` | What it does | What it costs you | | --- | --- | --- | | `none` | nothing; the tools are not registered and the model is never told they exist | — | | `claude` | declares Anthropic's hosted `web_search_20250305` tool, which runs on their side of the wire | billed per search by Anthropic; needs an endpoint that offers it | | `custom` | registers `web_search` and `web_fetch`, which run here | a search command on your PATH | **`claude`** is a line in the request's tool array and nothing else — no install, no key, no HTML parsing. The searches come back as `server_tool_use` and `web_search_tool_result` blocks, which the loop turns into ordinary `WebSearch(...)` transcript entries so a search is something you can watch rather than a pause you have to guess at. A hosted search cannot be refused at the prompt, because it happens inside a request you already approved; the setting itself is the consent. It also needs the endpoint to implement it, so a gateway or a local server speaking the Messages API will generally refuse the whole request — if that happens, switch to `custom`. **`custom`** runs a command. The default is ``` ddgs text -q {{query}} -m {{count}} -nc ``` which needs the DuckDuckGo CLI (`pipx install ddgs`) and no account at all. `{{query}}` and `{{count}}` are substituted as whole argv elements and the command is run without a shell, so a query containing `;` or backticks is a query rather than a second command. Note the absence of `-o json`, which is the flag you would reach for and the wrong one: it does not print JSON, it *writes a file* — into whatever directory the agent happens to be working in, once per search. The plain listing goes to stdout and is parsed, and `-nc` keeps the colour codes out of the titles. Any tool will do, as long as it prints to stdout: a JSON array, one JSON object per line, a `{"results": […]}` wrapper, or ddgs's own aligned listing are all read. Titles may be `title`; links `href`, `url` or `link`; snippets `body`, `snippet`, `description` or `abstract`. Edit the command in the wizard, or set `search_command` in the config. `web_fetch` is the other half, because a snippet is enough to choose a link and never enough to write code against. It resolves the host and refuses loopback, private, link-local and unique-local addresses, on the original URL and on every redirect hop — the model invents these URLs, and `169.254.169.254` is a cloud metadata endpoint. Pages come back as text, capped head and tail like `bash` output, wrapped in markers that say to treat what is between them as data rather than instructions. That does not make prompt injection impossible; it makes it visible in the transcript, which is the most a fetch tool can honestly offer. Both tools prompt, and neither is in the read-only set: they read nothing local and send the model's words off the machine, which is the opposite of what that set is for. The query and the URL are the detail on the prompt, so what is leaving is on screen before it leaves. "Always this tool" is the answer most people give once.