bun.com/docs/runtime/html-rewriter
HTMLRewriter transforms HTML documents with CSS selectors.
qany queries 14 public sources, fuses their rankings, and reads pages as clean text. Use it from the CLI, over HTTP, or as an MCP server.
bun add -g @qexle/qany
bun.com/docs/runtime/html-rewriter
HTMLRewriter transforms HTML documents with CSS selectors.
Extract links from a webpage using HTMLRewriter | Bun Guides
bun.com/guides/html-rewriter/extract-links
Extract all links from the Bun website.
bun/docs/runtime/html-rewriter.mdx at main ยท oven-sh/bun
github.com/oven-sh/bun/blob/main/docs/runtime/html-rewriter.mdx
HTMLRewriter transforms HTML documents with CSS selectors. It works with Response, string, and ArrayBuffer inputs.
Sample output from a real run. Query:
bun htmlrewriter.
qany runs your query against public search, news, code, package, and paper indexes. It removes duplicate URLs, fuses the rankings with Reciprocal Rank Fusion, and adjusts the order with query overlap, source intent, and what you have read before.
Free, with no account. qany calls endpoints that already answer browsers.
DuckDuckGo, Brave, Bing, Bing News, Mwmbl, Right Dao, Hacker News, Wikipedia, MDN, Stack Overflow, GitHub, npm, arXiv, and OpenAlex.
The same binary runs as a CLI, an HTTP API, and an MCP server for Claude Code, OpenCode, Claude Desktop, and other clients.
qany fetch extracts readable text, falls back to
the Wayback Machine, and adds what it reads to a local BM25
corpus.
When not to use it. qany is a poor fit if you need sub-second latency or an SLA, if you run high query volume or pool IPs, if you need semantic search over a curated index, or if you need compliance guarantees. A paid API will beat qany on vague or invented-word queries.
qany runs on Bun 1.4.2, and the build and the test suite target that version.
Linux needs unzip. qany runs on macOS and
Linux.
curl -fsSL https://bun.com/install | bash
One command, straight from npm. The package ships ready to run.
bun add -g @qexle/qany
The first run populates the cache. Later runs reuse it for 10 minutes.
qany search "bun htmlrewriter"
The CLI prints three lines per result. Pass
--json when another program is reading.
search <query>
Search 14 public sources and rank the merged results.
8 results, max 20more <url>
Find pages like a URL you liked. qany seeds a web search with the page's distinctive terms.
8 resultsrecall <query>
Search only pages qany has read on this machine.
8 resultsfetch <url>
Read a page as text, with a Wayback Machine fallback.
20,000 char capserve
Run the HTTP API on localhost.
port 8787mcp
Run the MCP server over stdio.
stdio-n, --num <n>How many answers come back. The default is 8, the ceiling is
20.
--jsonPrint one line of JSON.--no-cacheSkip the search cache.
--rerankVerify result pages and rank by page evidence.
Question-shaped queries do this automatically.
--max-chars <n>Page text cap for fetch. Default 20,000.
-p, --port <n>Port for serve. Default 8787.
qany search "sqlite fts5 bm25" -n 5
qany more https://bun.com/docs/runtime/html-rewriter
qany fetch https://bun.com/docs/runtime --max-chars 4000
qany serve -p 8787
Question-shaped searches rerank automatically. When every engine
returns nothing, qany retries once with DuckDuckGo's autocomplete
suggestion. Set QANY_CACHE_DIR to move the cache,
QANY_NO_CACHE=1 to disable it, and
QANY_NO_LEARN=1 to stop recording pages in the local
corpus.
qany speaks MCP over stdio. It also serves a local HTTP API. There is no hosted account and no token exchange.
claude mcp add --transport stdio qany -- bunx @qexle/qany@0.3.0 mcp
Add --scope user to enable qany for all your
projects, or --scope project to write
.mcp.json into the current project. The default
scope covers this project only.
{
"mcpServers": {
"qany": {
"command": "bunx",
"args": ["@qexle/qany@0.3.0", "mcp"]
}
}
}
opencode mcp add qany -- bunx @qexle/qany@0.3.0 mcp
Or add it to opencode.jsonc, global or project.
OpenCode's protocol option defaults to
legacy and also accepts auto and
2026-07-28. qany answers all three. Tested
against OpenCode 2.0.32.
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"qany": {
"type": "local",
"command": ["bunx", "@qexle/qany@0.3.0", "mcp"]
}
}
}
}
Open Settings, go to Developer, click Edit Config, and add
qany to claude_desktop_config.json. Restart
Claude Desktop after saving.
{
"mcpServers": {
"qany": {
"command": "bunx",
"args": ["@qexle/qany@0.3.0", "mcp"]
}
}
}
qany serve -p 8787
curl "http://localhost:8787/search?q=bun+htmlrewriter"
curl "http://localhost:8787/search?q=bun+htmlrewriter&n=5&format=json"
curl "http://localhost:8787/fetch?url=https://example.com&max_chars=4000&format=json"
The server binds to 127.0.0.1, so it answers only requests
from this machine. It returns text/plain by
default and JSON with format=json. Missing
parameters return 400. Engine or fetch failures return 502
with the reason.
Any MCP client. Spawn
bunx @qexle/qany@0.3.0 mcp and speak
newline-delimited JSON-RPC 2.0 on stdin and stdout. qany
implements tools/list, tools/call for
search, more, recall, and
fetch, server/discover for MCP
2026-07-28, and the legacy initialize handshake.
qany runs fourteen engines and ranks the merged result itself, with Reciprocal Rank Fusion, a relevance vote and a freshness tie-break.
qany dedupes by normalized URL and applies Reciprocal Rank Fusion. Query overlap, local corpus reads, domain trust, and query intent adjust the final order. The rarest word in the query carries the most weight.
If an engine's top three results share no word of three or more characters with the query, qany drops that engine's set. The guard catches challenge pages that masquerade as results.
Question-shaped queries split into up to three reading angles. qany fetches the top pages, reorders them by page evidence, then searches once more for vocabulary the query lacks.
qany serializes requests per host, waits between calls, and retries with jittered backoff. DuckDuckGo and Brave get benched for 10 minutes after a challenge. The bench survives restarts.
Every page qany reads joins a BM25 index capped at 600 pages. Later searches read from the corpus, so your reading history can move a result.
qany extracts readable text and falls back to the Wayback
Machine or cache. The (direct),
(wayback), or (cache) tag tells you
which route answered.
Implement the Engine interface, add the engine to
the list in src/search.ts, and build the parser
against a saved fixture. src/engines.ts has 14
worked examples.
export interface Engine {
name: string;
minIntervalMs: number;
search(query: string, num: number): Promise<SearchResult[]>;
}
qany is free because it reads public endpoints. That trade shows up in four places.
DuckDuckGo and Brave sometimes serve challenge pages. qany benches the challenged engine, serves a stale cache entry when it has one, and falls back to the Wayback Machine when a page fails. GitHub allows 10 repository searches a minute per IP.
Run qany from one machine. Don't pool IPs or put it behind a large shared service. The endpoints will notice, and the cooldowns will get longer.
Queries go to public search engines. The cache and reading
corpus stay in ~/.cache/qany. There is no qany
account and no qany server in the middle.
Command and tool names can change until 1.0.
qany is MIT-licensed and installs from npm. If it saves you an API bill, tell someone.