Skip to content

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.

BRAVE_API_KEY=BSA... uv run kin

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 http and https URLs.
  • Resolves the hostname and validates every resolved address, rejecting loopback, link-local (including 169.254/16 cloud 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 Host header 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.