Web tools¶
kin can read the open web through three tools. One works out of the box; the other two need a Brave Search key.
Enabling¶
web_fetch is always available. The two Brave-backed tools, web_search and web_context, are registered only when a Brave key is present — so the model is never offered a tool it cannot use.
The key is read from, in order:
| Source | Key |
|---|---|
| Environment | BRAVE_API_KEY |
| Environment | KIN_BRAVE_API_KEY |
| Global settings | brave_api_key in ~/.kin/settings.toml |
brave_api_key is a global-only setting (it is stripped from a project file), like other secrets. See Configuration and Environment variables.
With no key set, only web_fetch appears in the toolset.
web_search vs web_context vs web_fetch¶
The three tools cover three jobs. Pick by what you want back:
| Tool | Returns | Use when |
|---|---|---|
web_search |
A ranked list of results — title, URL, snippet, date. | You want links to choose from, then read one with web_fetch. |
web_context |
Pre-extracted, re-ranked readable snippets per URL (Brave LLM Context). | You want grounding context for a question, not just links. |
web_fetch |
The main content of one specific URL as markdown (or extracted PDF text). | You already have a URL — a search result, or one you were given. |
web_search takes a query, an optional count (1–20, default 10), and an optional freshness filter (pd/pw/pm/py or a YYYY-MM-DDtoYYYY-MM-DD range). web_context takes a query, a count of results to consider (1–50, default 5), and a max_tokens budget (1024–32768, default 8192). Both use the same Brave Search plan and key.
web_fetch takes a url and an optional max_chars cap (default 50,000). It routes by content type: HTML is extracted to markdown, PDFs are extracted to text, and other text types (JSON, XML, CSV, …) pass through verbatim.
A common pattern is web_search to find candidates, then web_fetch to read the best one. When you only need grounding, web_context does both in one call.
None of the three can interact with a page — no clicking, no filling a form, no waiting on JS-rendered content. For that, see the browser tool.
A fourth, narrower web tool, cite_check, verifies that a batch of already-cited URLs still resolve (HEAD-first, never downloading bodies, with a Wayback-snapshot substitute for dead ones). It is always registered, reuses web_fetch's SSRF guard on every hop, and backs the deep-research citation-liveness gate.
SSRF safety¶
web_fetch only reaches public internet hosts. It blocks internal and loopback targets, so the model cannot use it to probe your network or a cloud metadata endpoint.
Concretely, the tool:
- Allows only
httpandhttpsURLs. - Resolves the hostname and validates every resolved address, rejecting loopback, link-local (including
169.254/16cloud metadata), private, CGNAT, multicast, reserved, and unspecified ranges — and unwraps IPv4-mapped IPv6 so a wrapped loopback cannot slip through. - Connects to the validated, pinned IP while preserving the
Hostheader and TLS SNI, so a DNS-rebinding response cannot redirect the connection to an internal address after the check. - Follows redirects manually and re-validates every hop, and caps the response size and redirect count.
A blocked address returns a plain error: blocked for safety result the model reads and moves on from — it is not a crash.
All three tools frame their output as untrusted data before it enters the conversation, since titles, snippets, and page bodies are attacker-influenceable. For how that framing works, see the "Untrusted content" section of REFERENCE.md.