One query. Every engine.
One interface over Brave, Exa, Tavily, Firecrawl, Jina, SearXNG and five more. Search, reverse image search and page reading, all in the same shape. Works as a library, a CLI, an AI SDK tool or an MCP server, your pick.
- 11
- providers
- 3
- capabilities
- 4
- readers
- 4
- agent tools
Search
Query 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, Mojeek, SearXNG, SerpAPI, SerpBase and TinyFish page through an opaque continuation token
awaitsearch("TypeScript 7 native compiler")
- 1
Microsoft Releases TypeScript 7.0 with a Native Go Compiler, Delivering 10x Faster Builds - InfoQ
infoq.com
Microsoft has released TypeScript 7.0, the first stable version of the language to ship its long-in-development native compiler, a faithful port of t…
- 2
TypeScript 7.0 Native Compiler: What Breaks, What Gets 10x Faster, and How to Migrate - Developers Digest
developersdigest.tech
A practical migration guide for TypeScript 7.0's Go-based native compiler. Verified perf numbers, the full breaking-changes list, real npm commands f…
- 3
TypeScript 7 Native Compiler: 3x Faster Type Checks in a Real Monorepo
prisma.io
TypeScript 7 ships the compiler as a native Go port. We migrated a large TypeScript monorepo to it: whole-repo type checking went from ~74s to ~24s w…
- 4
TypeScript 7: The Native Compiler 10x Faster
believemy.com
TypeScript 7 is the first major version to ship a native compiler written in Go, delivering speed gains of 8x to 12x on full builds. Concretely, a pr…
5 results, same { url, title, snippet } from every provider
Fan-out
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
awaitsearchAllDetailed("TypeScript 7 native compiler")
sampleMicrosoft Releases TypeScript 7.0 with a Native Go Compiler, Delivering 10x Faster Builds - InfoQ
infoq.com/news/2026/08/typescript-7-released
BraveExaTypeScript 7.0 Native Compiler: What Breaks, What Gets 10x Faster, and How to Migrate - Developers Digest
developersdigest.tech/blog/typescript-7-native-compiler-mig…
BraveFirecrawlTavilyTinyFishTypeScript 7 Native Compiler: 3x Faster Type Checks in a Real Monorepo
prisma.io/blog/typescript-7-native-compiler-faster-type-che…
BraveTypeScript 7: The Native Compiler 10x Faster
believemy.com/en/r/typescript-7-native-go-compiler
BraveTypeScript 7.0: the native compiler, benchmarks, and what it changes for the web | PAS7 STUDIO
pas7.com.ua/blog/en/typescript-7-native-compiler-2026
Brave
6 unique URLs ·2 returned by more than one provider ·2 failures kept in errors
Read
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. Truncated page carries a token for the next slice.
- readUrl starts with Jina Reader and moves to Context.dev, Firecrawl or TinyFish 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
awaitreadUrl("infoq.com…", { maxChars: 900 })
sampleMicrosoft Releases TypeScript 7.0 with a Native Go Compiler, Delivering 10x Faster Builds
https://www.infoq.com/news/2026/08/typescript-7-released/
[BT](https://www.infoq.com/int/bt/ "bt") ## InfoQ Software Architects' Newsletter A 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 address Select your country - [x] I consent to InfoQ.com handling my data as explained in this [Privacy Notice](https://www.infoq.com/privacy-notice). [We protect your privacy.](https://www.infoq.com/privacy-notice/) Close QCon Sa…
900 code points · truncated true, continuation token issued
Providers
Eleven 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. Eleven APIs, eleven ideas about a request. Each adapter maps one of them onto the shared types and says what it can do. The rest of the library just reads that.
- Auth, endpoints and response shapes stay inside the adapter where they belong
- searchProviders(), searchImageProviders() and readProviders() come from the classes, not from a list someone forgets to update
- A custom provider is one class and one register() call
Agents
Four tools, every host
searchTool from the /ai subpath is a Vercel AI SDK tool, web mcp serves the same four over stdio. The model gets the normalized answer with the provider diagnostics attached, not prose.
- web_search, web_search_image, web_read 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
toolweb_search
@agntn/web/ai · MCP · Pi · OMP
input
{
"query": "TypeScript 7 native compiler",
"provider": "brave",
"maxResults": 2
}output
{
"provider": "brave",
"results": [
{
"url": "https://www.infoq.com/news/2026/08/typescript-7-relea…",
"title": "Microsoft Releases TypeScript 7.0 with a Na…",
"snippet": "Microsoft has released TypeScript 7.0, the…"
},
{
"url": "https://www.developersdigest.tech/blog/typescript-7-n…",
"title": "TypeScript 7.0 Native Compiler: What Breaks…",
"snippet": "A practical migration guide for TypeScript…"
}
],
"ignoredFilters": [],
"pagination": { "status": "next" }
}One interface
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
import { create } from "@agntn/web";
// reads BRAVE_API_KEY from process.env
const provider = create("brave");
const query = "TypeScript 7 native compiler";
const results = await provider.search(query, { maxResults: 5 });
results[0].url; // "infoq.com…"
results[0].title; // same shape from Brave as from every other providerStart with one command
Pre-1.0, so pin exact versions. And treat search results and page content as data you did not write, because you did not.