@agntn/web

中文文档
0.9.1发布于 2026/10/29MITTypeScriptES Module构建来源已验证近 30 天下载 1.3万

Unified web search and read provider for agents and CLI.

原始使用文档
文档目录13 节
  1. Why?
  2. ✨ Features
  3. 📦 Install
  4. 🚀 First call
  5. Commands
  6. 🧠 Library
  7. 🗺️ Providers
  8. 🤖 Agents
  9. 🚫 What this does not do
  10. 🧩 Adding a provider
  11. 🛠️ Development
  12. 💛 Thanks
  13. 📄 License

@agntn/web

npm version npm downloads license Ask DeepWiki

🔎 Thirteen search APIs, one { url, title, snippet }. You ask Exa, Brave or your own SearXNG the same way, you read the page behind a hit the same way, and nobody has to remember whose key goes in which header.

Why?

Every search API sells the same thing, ten links with a title and a snippet, and every one of them takes the order differently. Exa wants a POST with x-api-key, Brave a GET with X-Subscription-Token, Tavily puts the key inside the JSON body, why not. Wire three of those into an agent and you have three clients, three response shapes and three ideas of what a date looks like. So this is one class shape in front of thirteen of them, search, read and reverse image search, same objects out no matter who answered.

Docs and a live explorer: web.agntn.dev.

✨ Features

  • 🧩 Thirteen backends, one contract. Brave, Context.dev, Exa, Firecrawl, Jina, Marginalia, Mojeek, OpenAI Codex, SearXNG, SerpAPI, SerpBase, Tavily and TinyFish, and from your side the difference is a string.
  • 🔎 Search, read, reverse image. Query to results, URL to Markdown, image URL to the pages it shows up on. Three calls, not three packages.
  • 🕵️ What the page hides. Comments, hidden form fields, invisible characters, white text on white. web reveal lists them by line, straight from the HTML.
  • 🌐 all means all. One query goes to every provider you have a key for, comes back deduplicated by URL with the UTM junk stripped, and each hit says which providers agreed on it.
  • 🛟 Fallback you don't write. Out of credits on Firecrawl, rate limited on SerpAPI? The next configured provider gets the query and the answer names who actually replied.
  • 📏 Reads have a ceiling. maxChars is exact, counted in code points, and a page that doesn't fit hands you a continuation token instead of 400 kB you didn't ask for.
  • 📄 Page two exists. Brave, Marginalia, Mojeek, SearXNG, SerpAPI, SerpBase and TinyFish page through results with an opaque token that stays pinned to the query it came from.
  • 🤖 One package, six doors. CLI, library, AI SDK tools, an MCP server, a Pi extension and an OMP extension, and the JSON coming out is the same behind every one.
  • 🔐 Keys stay out of your logs. The key is scrubbed from the URL before an error ever gets to say it out loud.
  • 🧠 Codex search on a login you already have. Signed into Codex, Pi, OMP or OpenCode? OpenAI web search runs on that, no API key.

📦 Install

pnpm add @agntn/web

Node.js 26 or newer. The AI SDK tools on @agntn/web/ai want ai 7 next to them. The main entry never touches it:

pnpm add ai

🚀 First call

npx @agntn/web "how many r in strawberry"
How many 'r's are in strawberry? And do LLMs know how to spell? - DEV Community
  https://dev.to/savannah_norem/how-many-rs-are-in-strawberry-and-do-llms-know-how-to-spell-2513
  Well the short answers are three and kind of… but not really. ... Any which way you cut it, there are three ‘r’s in stra...

Language Log » "The cosmic jam from whence it came"
  https://languagelog.ldc.upenn.edu/nll/?p=66206
  Elle Cordova offers an update from ChatGPT on the number of Rs in "strawberry": ... As of this morning, ChatGPT 4o gives...

How many r are in strawberry? | AI Roundtable by Opper
  https://opper.ai/ai-roundtable/questions/how-many-r-are-in-strawberry-fc2d3d2d
  Answer: There are exactly three "r"s in the word "strawberry". (All 4 models agreed) ... Answer: There are 3 “r” letters...

Three. All four models agreed, good for them ;)

No subcommand, no flags. Anything that isn't search, read, search-image, reveal, providers or mcp is a query. Which provider answered? The first one with a key in your env, checked in this order: Exa, Brave, Context.dev, Firecrawl, Jina, Tavily, TinyFish, SerpAPI, SerpBase, Mojeek, Marginalia, then a saved Codex login, then a SearXNG on localhost:8080. No key anywhere means search has nobody to call and says so. --provider brave if you'd rather pick.

Reading is different, that one works with nothing in your env:

web read https://example.com
[provider=jina requested=auto] read https://example.com/
Example Domain
  https://example.com/

This domain is for use in documentation examples without needing permission. Avoid use in operations.

[Learn more](https://iana.org/domains/example)

Reads start at Jina's r.jina.ai, which doesn't need a key, and move on to Context.dev, Firecrawl, TinyFish, Tavily or Exa when Jina is out of credit, rate limited, down or answers with its 409. The first line tells you who ended up doing the work.

A handful more, keys permitting:

web search "typescript 7" "node.js 26" --provider all --json
web search "rust 2027 edition" --provider brave --max-results 3
web search "typescript 7" --provider exa --summary --full-text
web search "typescript 7" --include-domains '["github.com"]' --start-published-date 2026-01-01
web read https://example.com --format markdown --max-chars 20000 --json
web search-image https://example.com/image.jpg --max-results 5
web providers

Commands

CommandWhat it doesExample
web <query>Search with the first configured providerweb "typescript 7"
web search <query...>One query or a batch, one provider or allweb search "a" "b" --provider all --json
web search-image <url>Pages that contain or resemble a public imageweb search-image https://.../logo.png
web read <url...>One URL or a batch into normalized contentweb read https://example.com --max-chars 20000
web reveal <url>What the page keeps from its readerweb reveal https://gsmg.io/theseedisplanted
web providersWho is configured and what each one can doweb providers
web mcpThe MCP server on stdioweb mcp

--provider and --max-results work on every search. Domain, source, category and date filters go to the providers that understand them and get reported as ignored on the ones that don't. Lists are JSON arrays. Without --json you read what a model reads, and --json prints the data under result, the same object MCP sends. The full flag list is in the CLI guide.

🧠 Library

import { create, readUrl, searchAll } from "@agntn/web";

const exa = await create("exa"); // reads EXA_API_KEY, loads the Exa adapter and nothing else
const results = await exa.search("typescript runtime benchmarks", { maxResults: 5 });

for (const result of results) {
  console.log(result.title, result.url);
}

const everything = await searchAll("typescript runtime benchmarks"); // every configured provider, deduplicated
console.log(everything.map((result) => [result.url, result.providers]));

const page = await readUrl("https://example.com", { format: "markdown", maxChars: 20_000 });
console.log(page.title, page.truncated, page.continuation);

That's most of it, really. create("brave") instead of create("exa") and nothing else in your code changes. Importing the package loads no adapter, create() imports the one you name and nothing else runs until then. searchWithFallback() picks the provider the CLI would and tells you in attempts who dropped out along the way. searchProviderDetailed() is the same search with pagination attached, pass its continuation back and you get page two. Errors are one family: a 401 is AuthError, spent credits are PaymentError whatever status they hide behind, a 429 is RateLimitError with retryAfter, and no response at all is HTTPError with status 0 and the real cause underneath. The details and the gotchas: Searching, Fan-out, Reading, Reverse image search.

🗺️ Providers

ProviderAuthDoesFiltersPages
braveBRAVE_API_KEYsearchdatesyes
contextCONTEXT_DEV_API_KEYsearch, readdomains
exaEXA_API_KEYsearch, readdomains, category, dates
firecrawlFIRECRAWL_API_KEYsearch, readdomains, sources, categories
jinaJINA_API_KEY, optional for readsearch, readinclude domains, category
marginaliaMARGINALIA_API_KEY, public key when namedsearchnoneyes
mojeekMOJEEK_API_KEYsearchdomains, datesyes
openai-codexExisting login, optional OPENAI_CODEX_ACCESS_TOKENsearchnone
searxngNone, your own instancesearchcategoryyes
serpapiSERPAPI_API_KEYsearch, searchImagedatesyes
serpbaseSERPBASE_API_KEYsearchcategoryyes
tavilyTAVILY_API_KEYsearch, readdomains, category, dates
tinyfishTINYFISH_API_KEYsearch, readdomains, category, datesyes

Codex is the odd one out: no key, it borrows the login you already have in Codex, Pi, OMP or OpenCode, and snippet comes back empty because the model's answer is not a page excerpt. What each one fills in, which filters it honours and where it bites: Providers.

🤖 Agents

web mcp
pi install git:github.com/agntn/web
omp install @agntn/web
{
  "mcpServers": {
    "web": { "command": "npx", "args": ["-y", "@agntn/web", "mcp"] }
  }
}

web_search, web_image_search, web_read, web_reveal and web_providers, one definition each, the same set on the AI SDK (@agntn/web/ai), MCP, Pi, OMP and the CLI. Reads stop at 20 000 characters unless the model asks for more, and the page's link list and the favicon URLs on search results stay out unless it asks for those too, so nobody stuffs a whole site into a context window by accident. Pi also gets /web and /web-providers. Schemas and envelopes for each host: Agents guide.

🚫 What this does not do

No browser. Nothing here renders JavaScript, crawls a site or takes a screenshot, and search-image sends a URL and gets pages back, there is no OCR or image analysis behind it. Yesterday's version of a page is @agntn/archives.

🧩 Adding a provider

Missing your favourite engine? Extend Provider, implement search, read or searchByImage, call register(), and the CLI, the tools and all see it without a name tuple to edit. A built in one gets an entry in the manifest at src/providers/index.ts instead, with its capabilities and a lazy import() of its file, plus its line in builtinProviders. test/index.test.ts and test/unit/providers-manifest.test.ts will tell you if you forgot. Step by step, with the contract spelled out: Custom providers.

🛠️ Development

vp install       # installs the library and docs
vp check         # format, lint and type-check
vp test
vp run build     # obuild, straight into dist/
vp run docs      # Docus on :3000, bundling src/ itself

💛 Thanks

This package exists thanks to two open source programs, Claude for Open Source at Anthropic and Codex for Open Source at OpenAI.

📄 License

MIT

版本与发布记录

@agntn/web 共发布 20 个版本,首次发布于 2026/08/26;过去 12 个月发布了 20 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.1latest2026/10/299 天前
0.9.02026/10/1523 天前
0.8.02026/10/061 个月前
0.7.52026/10/061 个月前
0.7.42026/10/011 个月前
0.7.32026/09/241 个月前

关于 @agntn/web 的常见问题

@agntn/web 是什么?

Unified web search and read provider for agents and CLI.

如何安装 @agntn/web?

在项目目录运行 npm install @agntn/web,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 web,可以用 npx web 直接运行。

@agntn/web 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@agntn/web 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@agntn/web 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=26。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@agntn/web 可以商用吗?

@agntn/web 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@agntn/web 的下载量有多少?

根据 npm 官方下载统计,@agntn/web 在 2026/10/05 – 2026/11/03 这 30 天内被下载 13,180 次,日均约 439 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@agntn/web 还在维护吗?

最新版本 0.9.1 发布于 2026/10/29(9 天前);至今共发布 20 个版本,其中过去 12 个月发布了 20 个;首次发布于 2026/08/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。