pi-web-search
by weibolu-rm
README.md
# pi-web-search
A `web_search` tool for the [pi coding agent](https://github.com/earendil-works/pi) that uses your **z.ai GLM Coding Plan's native web search** via the `web_search_prime` MCP endpoint — included in the plan, no extra cost, no separate API key — with a **free DuckDuckGo fallback**.
Zero dependencies. Zero configuration. ~350 lines of TypeScript.
## Goal
One thing, done well: lean native web search through the z.ai Coding Plan, falling back to DuckDuckGo otherwise.
It's search-only by design — there are already good `web_fetch` extensions out there, so this one doesn't ship a fetcher, and never will. Pair it with whichever you use and you get a full research pipeline: *discover → read*.
z.ai is the only native provider for now, simply because that's what I use. Other providers may be added down the line, but that's the most this extension will ever grow into.
## How it works
```
web_search(query)
│
├─ key: ZAI_API_KEY env → ~/.pi/agent/auth.json (zai)
▼
┌─────────────────────┐ fail / empty ┌──────────────────────┐
│ ZAI MCP │ ─────────────────► │ DuckDuckGo HTML │
│ web_search_prime │ │ regex scrape │
│ (raw results) │ │ (8 results) │
└─────────────────────┘ └──────────────────────┘
trim to 8 → numbered list (title / URL / snippet)
output annotated with which engine answered
```
**ZAI MCP protocol** (`https://api.z.ai/api/mcp/web_search_prime/mcp`):
1. `POST` JSON-RPC `initialize` → capture `Mcp-Session-Id` response header
2. `POST` `tools/call` with `web_search_prime` + `{ search_query }` (session header)
3. Parse the SSE `data:` line → the text payload is a **double-JSON-encoded** array of `{ title, link, content, refer }`
If native search fails or returns nothing, DuckDuckGo takes over automatically, with a visible `> zai-mcp unavailable (...)` note in the output so you always know which engine answered.
## Install
Clone into pi's extensions directory:
```bash
git clone <this-repo> ~/.pi/agent/extensions/web-search
```
Restart pi (or run `/reload`). The `web_search` tool appears alongside the built-ins.
Or load it explicitly for a one-off:
```bash
pi -e /path/to/pi-web-search/index.ts
```
## Usage
The model calls it on its own when it needs current information:
```json
{ "query": "latest stable node version" }
```
Returns:
```
1. **Node.js 24.x is now LTS ...**
https://nodejs.org/en/blog/announcements/v24-release
Node.js 24 enters long-term support ...
2. **...**
```
## Configuration
None required. Optional overrides:
| | Default | Notes |
|---|---|---|
| `ZAI_API_KEY` | `~/.pi/agent/auth.json` (`zai.key`) | Env var wins if set |
| Result count | 8 (`MAX_RESULTS`) | Client-side trim; edit constant |
| Timeouts | MCP 25s / DDG 15s | Edit constants |
## Notes
- The `Mcp-Session-Id` header from the `initialize` response is required on `tools/call`
- The MCP text payload is double-JSON-encoded and needs two rounds of `JSON.parse`
- DuckDuckGo's HTML endpoint may bot-wall curl; testing with Node fetch (real browser `User-Agent`) matches what the extension actually does
- A fresh MCP session is created per search — fine at typical agent search volume
## Future work
- Additional native provider backends (search only — a fetcher will never be part of this)
- Forward optional `count` / domain filters to the MCP endpoint once its input schema is mapped
- `allowed_domains` / `blocked_domains` tool parameters
- Fallback ladder (DDG POST variant → `lite.duckduckgo.com` → Mojeek) if DDG tightens bot detection
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues