@counterposition/pi-web-search
Multi-provider web search and markdown page fetch for Pi
Package details
Install @counterposition/pi-web-search from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@counterposition/pi-web-search- Package
@counterposition/pi-web-search- Version
0.4.1- Published
- May 5, 2026
- Downloads
- 764/mo · 53/wk
- Author
- harishkukreja
- License
- GPL-3.0-only
- Types
- extension
- Size
- 103.7 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"extensions/web-search.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Pi Web Search
A Pi extension that gives the agent two tools for working with the open web:
web_searchfor querying multiple search providers with automatic fallback.web_fetchfor reading pages as clean markdown.
The extension manages three search backends (Brave, Tavily, Exa) behind a single interface. It selects the best available provider for each request based on the capabilities the request needs, falls back on transient failures, and tells the agent when a result has been degraded. Page fetching is backed by Jina Reader.
Install
pi install npm:@counterposition/pi-web-search
Tools
web_search
Returns titles, URLs, snippets, and dates. Parameters:
| Parameter | Description |
|---|---|
query |
Search query. |
depth |
basic (default) returns snippets. thorough uses content-capable search and may include one inline excerpt. |
freshness |
Optional recency filter: day, week, month, or year. |
domains |
Optional allowlist of bare hostnames to restrict results (max 10). |
max_results |
1--20, default 5. |
web_fetch
Fetches a URL through Jina Reader and returns the page content as markdown. Long pages are paginated: use offset and max_chars to page through content in 8,000-character windows by default (up to 20,000 per call). Fetched pages are cached in an LRU cache (20 entries, 5-minute TTL) so repeated reads of the same URL within a session are free.
Providers
Each search provider has different capabilities. The extension routes requests to the provider best suited for the job:
| Provider | Capabilities | Best for |
|---|---|---|
| Brave | search, freshness | Fast basic queries; time-sensitive searches |
| Tavily | search, content, semantic, freshness, domain filter, dates | Thorough searches; domain-scoped research |
| Exa | search, content, semantic, freshness, domain filter, dates | Thorough searches; domain-scoped research |
| Jina | page fetch | Reading full pages as markdown |
How provider resolution works: When a search comes in, the extension ranks available providers by how well they match the request. A thorough search needs the content capability, so Tavily and Exa are preferred. A basic search with a freshness filter prefers Brave. If the top-ranked provider fails transiently (network error, rate limit), the next provider in the ranking is tried. If no provider can serve the requested depth, a thorough search degrades to basic and the agent is told.
You can override the automatic ranking by setting a preferred provider per depth level (see Settings below).
Configuration
API keys
Set at least one search provider key. Keys can be set as environment variables or in the global Pi settings file (~/.pi/agent/settings.json under webSearch.apiKeys). Project-level API keys are intentionally ignored.
| Variable | Provider | Required |
|---|---|---|
BRAVE_API_KEY |
Brave Search | For basic/fresh searches |
TAVILY_API_KEY |
Tavily | For thorough searches |
EXA_API_KEY |
Exa | For thorough searches |
JINA_API_KEY |
Jina Reader | Optional (works without a key at lower rate limits) |
Recommended minimum: BRAVE_API_KEY plus either TAVILY_API_KEY or EXA_API_KEY. Brave covers basic and freshness-filtered searches. Tavily or Exa covers thorough searches that need content-capable discovery. With only one provider, thorough searches may silently degrade to basic.
Settings
Optional overrides in settings.json (global or project-level) under webSearch:
{
"webSearch": {
"preferredBasicProvider": "brave",
"preferredThoroughProvider": "tavily"
}
}
When set, the preferred provider is tried first for that depth level before falling back to the default ranking.
Security
The extension takes two precautions around untrusted web content:
- SSRF protection.
web_fetchvalidates URLs before fetching. Private and reserved IP ranges (RFC 1918, link-local, loopback), cloud metadata endpoints, and.local/.internalhostnames are all blocked. Onlyhttpandhttpsschemes are allowed; embedded credentials are rejected. - Prompt injection mitigation. The extension appends a system prompt instructing the agent to treat web content as untrusted data, not as instructions to follow.