ID@agntn/webv0.9.0
One query. Every engine.
One TypeScript interface over Brave, Exa, Tavily, Firecrawl, Jina, SearXNG and the rest of the catalog. Search, reverse image search and page reading come back in the same shape, from a library, a CLI, an AI SDK tool or an MCP server, your pick.
- Providers
- 13
- one class shape
- Capabilities
- 3
- 13 search · 1 image
- Read
- 6 readers
- fallback from Jina
$ pnpm add @agntn/webQuery in, results out
create("brave") reads the key from env and gives you a provider with one search(). Change the string, the rest of your code stays. This panel walks through 4 queries and replaces each recorded sample with the live answer from the docs worker.
- Every provider answers { url, title, snippet }. Score, dates, highlights and full text come along when the engine has them
- A filter the provider cannot do lands in ignoredFilters. Nothing gets dropped quietly
- Brave, Marginalia, Mojeek, SearXNG, SerpAPI, SerpBase and TinyFish page through an opaque continuation token
Brave
5 results, next page through a continuation token.
- 01Microsoft Releases TypeScript 7.0 with a Native Go Compiler, Delivering 10x Faster Builds - InfoQinfoq.com
- 02TypeScript 7.0 Native Compiler: What Breaks, What Gets 10x Faster, and How to Migrate - Developers Digestdevelopersdigest.tech
- 03TypeScript 7 Native Compiler: 3x Faster Type Checks in a Real Monorepoprisma.io
- 04TypeScript 7: The Native Compiler 10x Fasterbelievemy.com
Ask them all, keep the evidence
One query, every key you have. UTM junk is stripped before URLs are compared, the first provider in order gives the representative record, and one engine hitting a paywall, a rate limit or a timeout does not empty the list. That was the whole point.
- searchAll asks every configured provider at once and deduplicates by normalized URL
- Each result remembers which providers returned it and keeps every record as evidence
- A provider that fails goes to errors. The rest still answer
Asked
- Braveanswered
- Context.devnot asked: no key on the docs worker
- Exaanswered
- Firecrawlanswered
- Jinanot asked: no key on the docs worker
- Marginalianot asked: no key on the docs worker
- Mojeeknot asked: no key on the docs worker
- OpenAI Codexnot asked: no key on the docs worker
- SearXNGnot asked: no key on the docs worker
- SerpAPIRate limited. Retry after 60s
- SerpBaseSerpBase insufficient credits: insufficient credits
- Tavilyanswered
- TinyFishanswered
URL in, page out
You have a URL, you want the page. readUrl gives it back as Markdown, text or HTML. The bound is measured after the provider answers, so a page is never bigger than the agent asked for. A truncated page carries a token for the next slice.
- readUrl starts with Jina Reader and moves to Context.dev, Firecrawl, TinyFish, Tavily or Exa when Jina fails in a way that makes sense to retry
- maxChars is an exact bound in code points, same on every reader, with a continuation for the rest
- readUrlDetailed tells you which reader answered and which ones it tried first
Microsoft Releases TypeScript 7.0 with a Native Go Compiler, Delivering 10x Faster Builds
infoq.com/news/2026/08/typescript-7-released
[BT](https://www.infoq.com/int/bt/ "bt")## InfoQ Software Architects' NewsletterA monthly overview of things you need to know as an architect or aspiring architect.[View an example](https://www.infoq.com/software-architects-newsletter#placeholderPastIssues)Enter your e-mail addressThirteen adapters, one shape
Exa wants a POST with x-api-key, Brave a GET with X-Subscription-Token, Tavily puts the key in the body. Different APIs, different ideas about a request. Each adapter maps one of them onto the shared types and says what it can do, and the rest of the library just reads that. A custom provider is one class and one register() call.
Import@agntn/web/providers/<name>
| Key | What it talks to | ||
|---|---|---|---|
| Brave | BRAVE_API_KEY | The Brave Search API, one GET per page, with extra snippets joined into text and a continuation that carries the offset. | search |
| Context.dev | CONTEXT_DEV_API_KEY | The Context.dev API for search with Markdown ranked by relevance and for scraping pages into Markdown or HTML. | search · read |
| Exa | EXA_API_KEY | The Exa neural search API, the richest result shape of the built-ins, with highlights by default and summaries and full text on request, and Exa Contents for reading pages. | search · read |
| Firecrawl | FIRECRAWL_API_KEY | The Firecrawl v2 API for search with scraped passages and for scraping a page into Markdown or HTML. | search · read |
| Jina | JINA_API_KEY | Jina's s.jina.ai for search and r.jina.ai for reading, the default reader, with an optional key. | search · read |
| Marginalia | MARGINALIA_API_KEY | Marginalia Search, an independent index of small personal sites and plain text pages, usable without a key when you name it. | search |
| Mojeek | MOJEEK_API_KEY | The Mojeek Search API, an independent index with classic SERP metadata, domain and date filters and paging. | search |
| OpenAI Codex | OPENAI_CODEX_ACCESS_TOKEN | Hosted web search through the ChatGPT Codex backend using an existing Pi, OMP, Codex or OpenCode login. Native sources, not snippets invented by the model. | search |
| SearXNG | none, self-hosted | A self-hosted SearXNG metasearch instance, no key, with a reachability probe and the engines behind each result in metadata. | search |
| SerpAPI | SERPAPI_API_KEY | SerpAPI's Google endpoints for classic SERP results and Google Lens for reverse image search. | search · image |
| SerpBase | SERPBASE_API_KEY | SerpBase's Google SERP endpoints, with a category that picks the web, images, news or videos endpoint. | search |
| Tavily | TAVILY_API_KEY | The Tavily search API, with an optional generated answer in response metadata, and Tavily Extract for reading pages. | search · read |
| TinyFish | TINYFISH_API_KEY | TinyFish Search for news and research results with publisher metadata, and TinyFish Fetch for reading pages. | search · read |
Every tool, every host
searchTool from the /ai subpath is a Vercel AI SDK tool, web mcp serves the same set over stdio. The model reads a short header and numbered results, your code gets the data underneath.
- web_search, web_image_search, web_read, web_reveal and web_providers, same schema on every surface
- Provider names are checked against the live registry, so a custom provider works in a tool call too
- The host abort signal cancels the provider request. Reads default to 20 000 characters
Brave
The model reads a header and numbered results. Your code gets the same answer as data, diagnostics and all.
- provider
- "brave"
- results.length
- 5
- pagination
- { status: "next" }
Same calls, every adapter
Type guards like isReadProvider tell you what a provider can do, the registry tells you which ones exist. Upstream quirks, Brave's extra snippets, Exa's highlights, all of that stays inside the adapter. The public types never see it.
- search(), searchByImage() and read() on the providers that have them
- AuthError, RateLimitError with retryAfter, HTTPError with the key already redacted from the URL
- Every network call takes a signal, a deadline and a concurrency bound
Search
import { create } from "@agntn/web";// reads BRAVE_API_KEY from process.env, imports one adapterconst provider = await create("brave");const results = await provider.search("TypeScript 7 native compiler", { maxResults: 5 });results[0].url; // "infoq.com…"results[0].title; // same shape from Brave as from every other oneresults[0].publishedDate; // "2026-08-03"results.length; // 5Start with one command
One install gives you the library, the web CLI and the MCP server. Name a provider, ask it something and read the same shape whoever answered.
- PinPre-1.0, so pin exact versions.
- KeysEach provider reads its key from its own env var. A self-hosted SearXNG needs none.
- DataResults and pages come from sites you did not write. Treat them as data.
First search
$ pnpm add @agntn/webimport { create } from "@agntn/web";const brave = await create("brave");const results = await brave.search("TypeScript 7 native compiler");results[0].url; // "https://www.infoq.com/news/2026/08/typescript-7-released/"