@tduyng/pi-web

Keyless web search and fetch: batched queries, arXiv, Hacker News, and Exa MCP with fallbacks. No API key, no MCP tool pile in context.

Packages

Package details

extension

Install @tduyng/pi-web from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@tduyng/pi-web
Package
@tduyng/pi-web
Version
0.0.4
Published
Nov 5, 2026
Downloads
405/mo · 405/wk
Author
tienduy-nguyen
License
Apache-2.0
Types
extension
Size
43.9 KB
Dependencies
1 dependency · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@tduyng/pi-web

Web search and fetch with no API key, no setup, and no MCP tool schemas sitting in the context. Two tools, 326 tokens a request.

A model without web access answers from memory, and the reply does not say which parts are memory. Two ways out: paste pages by hand, or install a search MCP server whose tool schemas then ride in every request, including the ones that never search.

Two tools, one hidden server

pi registers Exa's MCP server with exposure codemode, so its tools never enter the prompt. A codemode script calls them through ctx.executeTool, and a model that never writes such a script never pays for them.

web_search({queries: ["typed decision model", "pi coding agent extensions"], numResults: 5})
web_fetch({urls: ["https://example.com/post"], maxCharacters: 12000})

web_search takes up to six queries and groups the results under the query that produced them. Each group carries title, url, snippet, plus published and authors where the backend has them, and points and comments on Hacker News. The results are a declared output schema, so a codemode script gets them as data in structuredContent.groups and can compare values without parsing text.

web_fetch reads a URL as Markdown. x.com and twitter.com links go to Fixupx, which answers a page X refuses, and it returns the post together with the long-form Article it links to.

Search is a step, not an answer

Results are excerpts, so the reply ends with the ranked URLs to read. Without that list the model answers from a snippet, and a snippet is where a wrong answer sounds most confident.

Backend When What it returns
Exa MCP always, keyless prose answer plus highlights
DuckDuckGo Exa unconnected, --no-mcp, or empty result list
arXiv Atom API source: "arxiv" abstracts, authors, dates, newest first
Hacker News (Algolia) source: "hn" threads with points and comment count

A miss on arXiv or Hacker News falls back to the open web, because arXiv indexes preprints and nothing else.

Measurements

DuckDuckGo queue. DuckDuckGo answers a burst from one address with a bot check and keeps that address locked out for 10 to 15 minutes, with each retry extending it. Measured on 2026-10-26: six requests at once got three pages of results and three challenge pages at 14 KB apiece, and the challenge arrives as HTTP 202 as often as 403. So searches run one at a time, two seconds apart. A bot check pauses the provider for five minutes, doubling to an hour while it keeps answering that way, and a paused search fails at once instead of holding the tool open.

arXiv query form. arXiv ANDs nothing on its own. Measured on the live API: typed decision model returns 1,470,125 rows because the words are read as alternatives, and all:"typed decision model" returns 3 because the phrase is too strict. A plain query therefore has its words ANDed one at a time, which is the only form that ranks usefully. A query already written with arXiv field prefixes (ti:, cat:, au:) passes through untouched.

Links in a fetched page. Exa's extraction returns plain text. Measured on 2026-09-27, arxiv.org/abs/1706.03762 came back with no links at all, so a citation cannot be read out of the page that cites it. Jina Reader returns Markdown with links intact, 295 of them on a Hacker News thread, but it blocks a domain that hammered it. Exa answers first for that reason, and links survive only when Jina does. To follow a citation, search for the paper's title.

Reddit and X. Neither has a keyless search. Measured on 2026-09-27, reddit.com/search.json answers 403 on www, old and api, with a browser user agent and a plain one. DuckDuckGo's site: operator does reach both, so naming the forum in the query is the way in.

Cost

web_search is 219 tokens a request and web_fetch is 107. The Exa tools never enter the prompt, so the cold start is 326 tokens on every session, with no key, account or handshake.

Install

pi install npm:@tduyng/pi-web

Local checkout: pi install /path/to/dotfiles/pi/agent/extensions/pi-web

Optionally set EXA_API_KEY to use an own key. The extension reads it at load and puts it on the registered server URL as the exaApiKey parameter. Exa's free plan includes 1,000 requests a month, so a batch of six queries costs six of them.

Limits

  • The keyless Exa server takes a 60 second request timeout covering the connect handshake. Pi retries only HTTP 408, 429 and 5xx, so a handshake that overruns marks the server failed for the session and /mcp shows why.
  • numResults is 1 to 10, default 5. maxCharacters defaults to 8,000.
  • Fetch has a 20 second timeout, and at most six queries run per call.
  • Exa's free tier, with an own key, is 1,000 requests a month.