# dsh-web-search-free > [**中文**](README.md) | **English** — DSH plugin ([`dsh-plugin`](https://github.com/topics/dsh-plugin)) · free web search provider + settings toggle + **MCP server (Claude Code / Codex compatible)** One-click switch from DeepSeek's **paid, token-billed web search** to the **same free search OpenCode uses** (anonymous, no API key) — and the **same free backend doubles as an MCP server** for Claude Code, Codex, or any MCP client. - 🆓 **Free backends**: Parallel (default) / Exa (backup) — the same free MCP endpoints OpenCode's built-in `websearch` tool uses. Anonymous, no key, no balance deducted. - 🔄 **DSH settings toggle**: official ↔ free, auto-maintains the profile patch. - 🔌 **MCP server**: zero-dependency, dual transport (**stdio + HTTP/SSE port**), drop-in via `claude mcp add` / `codex mcp add`. ## Background: why the default uses the DeepSeek key DSH's `web_search` tool goes through the `ctx.web` seam. The default provider is `@deepseek-ai/dsh-web-search-deepseek` (id = `deepseek-official`, pulled in by the `dsh-base` dependency): every search calls DeepSeek's Anthropic-compatible API (`https://api.deepseek.com/anthropic/v1/messages`) with the native `web_search_20250305` server tool, reuses `DEEPSEEK_API_KEY`, and is **billed per token**. ## Features ### Free backends (inside DSH) Registers two free search providers into `ctx.web`; the model-facing `web_search` tool call stays unchanged: | provider id | backend | endpoint | notes | |---|---|---|---| | `parallel-free` (**default**) | Parallel Web Search | `https://search.parallel.ai/mcp` | anonymous, no key | | `exa-free` (backup) | Exa | `https://mcp.exa.ai/mcp` | anonymous, no key; set `EXA_API_KEY` for higher limits | ### Settings toggle (DSH Settings → Plugins → Configurable → Free Web Search) - **enabled switch** (default **off** = official search): on → free; off → official. - **Free backend picker**: Parallel (default) / Exa (backup), single choice. - Changes auto-maintain the `web` row `config.searchProvider` in the profile user patch `~/.dsh/profiles/web/cordis.patch.yml` (on → writes the selected provider id; off → removes the override block). > Do **not** write `~/.dsh/.env`: dsh-app-boot rejects any `DSH_*` variable in > `.env` (bootstrap-only environment guard, fails loud at startup). - **Restart DSH to apply** (`WebRuntime` reads the `web` row config at startup). ### MCP server (Claude Code / Codex / any MCP client) A standalone zero-dependency MCP server (`lib/mcp-server.js`, independent of DSH) exposing the `web_search` tool (params: `query` required, `provider` optional `parallel`/`exa`, `numResults` optional). **Dual transport**: **stdio** (default) and **HTTP/SSE** (listening port; Streamable HTTP `POST /` plus legacy `GET /sse` + `POST /messages`). #### Transport 1: stdio (default, no port) ```bash # Local path (after clone) claude mcp add dsh-web-search -- node /abs/path/dsh-web-search-free/lib/mcp-server.js codex mcp add dsh-web-search -- node /abs/path/dsh-web-search-free/lib/mcp-server.js # Or via npx (after npm publish) claude mcp add dsh-web-search -- npx -y @dsh-external/dsh-web-search-free codex mcp add dsh-web-search -- npx -y @dsh-external/dsh-web-search-free ``` #### Transport 2: HTTP/SSE (listening port, dual-port mode) ```bash # Start the MCP server (default http://127.0.0.1:3100; tune with --host/--port) node /abs/path/dsh-web-search-free/lib/mcp-server.js --http --port 3100 ``` - **Claude Code (Streamable HTTP)**: ```bash claude mcp add --transport http dsh-web-search http://127.0.0.1:3100 ``` - **Codex (OpenAI Codex CLI)**: ```bash codex mcp add --transport http dsh-web-search http://127.0.0.1:3100 ``` - **Legacy SSE clients** (point the URL at the SSE channel): ```bash # Client config: http://127.0.0.1:3100/sse (GET /sse opens the channel, POST /messages sends requests) ``` - **Verify**: open `http://127.0.0.1:3100/` in a browser — it returns `{"name":"dsh-web-search-free","version":"0.3.0","transport":"http/sse","port":3100}`. #### Other MCP clients Any MCP client supporting **stdio** or **HTTP/SSE** transport works (command/URL as above). Optional env: `EXA_API_KEY` (higher Exa anonymous limits). ## Install (the DSH plugin itself) **Option 1: hot mount on this machine (dsh-super-injector, no restart)** ```bash dev_install_package # dir = plugin dir (package.json + lib/) ``` **Option 2: profile declaration (restart to apply)** Declare in `~/.dsh/profiles/web/package.json`: ```jsonc { "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", // ...other bundles... "@dsh-external/dsh-web-search-free" ]}}, "dependencies": { "@dsh-external/dsh-web-search-free": "link:/abs/path/dsh-web-search-free" } } ``` **Option 3: npm package (tgz)** ```bash npm run build && npm pack # Add the tgz to profile deps and reinstall (pnpm install), then restart ``` ## Verify ```bash npm run verify # patch sync + Parallel/Exa live search + provider matrix + MCP smoke (stdio & HTTP) ``` ## Revert to official Turn the enabled switch off in Settings (the override block is removed), restart DSH. (Or manually delete the block between `# >>> web-search-free` and `# <<< web-search-free` in `~/.dsh/profiles/web/cordis.patch.yml`.) ## Security / permissions - DSH plugin: once enabled, `web_search` requests go only to `search.parallel.ai` / `mcp.exa.ai`; it modifies `~/.dsh/profiles/web/cordis.patch.yml` (a boundary-marked `web` row override, format above) and nothing else. No key, no data collection, no listening. - MCP server: calls only the free endpoints above; reads none of your files/env (except optional `EXA_API_KEY`). stdio mode listens on no port; HTTP/SSE mode serves MCP endpoints only on your `--host:--port` (default `127.0.0.1:3100`), bound to the loopback address by default. ## Development ```bash bash scripts/build.sh # tsc: host + core + mcp-server; copy client into lib/ npm run verify # end-to-end verification ``` Layout: ``` src/core.ts # zero-dep search core: Parallel + Exa (shared by host & MCP) src/index.ts # host: dual provider registration + settings toggle + profile patch sync src/mcp-server.ts # MCP server: stdio + HTTP/SSE dual transport (bin: dsh-web-search-mcp) src/client/client.js # client: "Free Web Search" settings card (with backend picker) scripts/build.sh # build (auto-detects DSH_CHECKOUT) scripts/verify.mjs # end-to-end verification script cordis.patch.yml # bundle assembly layer (incl. web-row searchProvider unpin) ``` ## License MIT © sheep-programmer