MyWebSearch
MyWebSearch is a keyless, multi-engine web search and high-purity content-extraction MCP server for AI coding agents, usable over MCP STDIO/HTTP, CLI, or a local daemon.
search— Federated search across 16 engines (Bing, Baidu, Sogou, DuckDuckGo, Brave, Startpage, Yahoo, Exa, CSDN, Juejin, Bing News, GitHub, StackOverflow, Hacker News, arXiv, grep.app code) with language-aware auto-routing, up to 4 parallel queries,timeRange/sitefilters,minResultscascade fallback,fetchTopinline article previews, andsearchMode(HTTP / auto / headless browser) — no API keys required.fetchWebContent— Turn public pages or PDFs into clean Markdown/text: batch 1–3 URLs, CSSselectorscoping, BM25focuskeyword targeting,startIndexpaging, disk cache (reloadto bypass), selector-based JSONextractwithout an LLM, numberedcitations, andretainLinkscontrol.fetchGithubContent— Read GitHub or Gitee READMEs, single source files (withlinesslicing), directory trees, issues/PR discussions, commits, compare views, and releases.queryDocs— Pull official library/package documentation via Context7, with fallback to npm/PyPI/crates.io/Go/Maven/GitHub README, optional version pinning, and a 24h cache.codeSymbol— Locate a symbol's declaration and call sites in a remote GitHub repo (signature, code block, reference lines); requiresGITHUB_TOKEN.Plus built-in diagnostics (
doctor), Prometheus/metrics, and MCP prompt/resource templates for research and code-lookup workflows.
Search the web using Baidu as the search engine, returning structured results with titles, URLs, and descriptions.
Search the web using Brave Search, returning structured results with titles, URLs, and descriptions.
Search the web using CSDN and fetch individual article content from CSDN pages.
Search the web using DuckDuckGo, returning structured results with titles, URLs, and descriptions.
Fetch README files from GitHub repositories as content.
Search the web using Juejin, returning structured results with titles, URLs, and descriptions.
Search the web using Sogou, returning structured results with titles, URLs, and descriptions.
Search the web using Startpage, returning structured results with titles, URLs, and descriptions.
🔍 MyWebSearch
Keyless Multi-Engine AI Web Search & High-Purity Content Extraction Engine
MCP Server · CLI · Local HTTP Daemon · Skill-Guided Agent Workflows
One server, four ways to use it: connect an AI client over MCP, run a one-shot CLI query, keep a local daemon alive, or use the built-in diagnostics before going live.
Jump to: Quick start · MCP tools · CLI · Configuration · Diagnostics · Development
✨ Features
my-websearch is a full-stack search and technical documentation engine built specifically for AI coding agents (Claude Desktop, Cursor, Cherry Studio, Windsurf, Cline, ZCode, etc.). Out of the box, ready for multiple mainstream AI clients:
🌐 16-Engine Federated Search & Smart Routing
Aggregates direct domestic engines (Bing, Baidu, CSDN, Juejin, Sogou), global engines (DuckDuckGo, Brave, Startpage, Yahoo, Exa, Bing News), and specialized developer platforms (GitHub, StackOverflow, Hacker News, arXiv). Features automatic language-aware routing (auto), parallel multi-query execution (queries: string[]), URL deduplication, and automaticminResultscascading fallback. When DuckDuckGo is unhealthy, Yahoo automatically joins the English primary group from the fallback pool.⚡ Native Web Protocol & Proof-of-Work Solvers (Browser-Free Fast Path, Browser Fallback Only When Needed)
Built onwreq-jswith native modern TLS/HTTP2 handshake emulation and persistent session cookies. Includes pure-JS/Rust solvers for Startpage's Anubis SHA-256 PoW challenge and DuckDuckGo'sd.js(isJsaChallenge) HTML5/arithmetic challenge, so most challenges resolve without launching a browser. Request stacks are engine-specific: DuckDuckGo requires the wreq Chrome profile via GET (axios POST is challenged with HTTP 202), while Brave defaults to curl HTTP/1.1 (Windows Schannel; tested curl 7/10 before rate limiting, axios 0/10, wreq HTTP/2 429). When a Brave 429 turns out to be an anti-bot challenge page, the engine retries once through a disguised headless CDP browser that clicks the Argon2 PoW button itself (~4–6s; measured 8/8 while plain HTTP stayed 429). Note: DuckDuckGo's page-level HTTP 202 anomaly CAPTCHA ("select all squares containing a duck") is human verification and is intentionally not bypassed — it is reported asbot_challenge; Brave's challenge is computational (Argon2 PoW) and is solved by the page's own JavaScript, not by us. Real URL resolution for Sogou redirect links, BaiduLocation, and Bing Base64 links.📄 High-Purity Markdown Article Extraction & Dedicated Site Purifiers
Combines Mozilla Readability with intelligent chrome noise stripping. Features dedicated purification rules for WeChat Official Accounts (mp.weixin.qq.com), StackOverflow questions & accepted answers, and transparent internal routing for CSDN & Juejin. Removes<nav>,<aside>,<footer>, and breadcrumbs while faithfully preserving article titles, code blocks, and GFM tables. Supports seamless pagination (startIndex), plus three token-saving modes: BM25 relevance-rankedfocus, CSS-selectorextractto JSON with no LLM, and numberedcitations.📚 Unified Context7 Library Docs Lookup (
queryDocs)
Directly query official documentation with natural package names (e.g."next.js","vue") or Context7 IDs without needing a separate resolve step. Includes automatic candidate ranking, blind-guess 404 self-healing, and related alternatives discovery.🐙 Extended GitHub / Gitee Resource Fetcher
fetchGithubContent(formerlyfetchGithubReadme) supports both GitHub and Gitee: repository READMEs, any source file (/blob/..., withlines/#L120-L180slicing), directory trees (/tree/...), complete issue / PR discussions, commits (/commit/<sha>), compare views (/compare/a...b), and releases (/releases,/releases/tag/<tag>).🌏 Zero-Config Network Detection & Fake-IP Ready
Auto-detects OS system proxies (Windows Registry / macOS scutil / Linux env). Automatically routes overseas engines through proxy while keeping domestic engines on fast direct lines. Pre-whitelists198.18.0.0/15for TUN / Fake-IP compatibility while enforcing strict SSRF security bounds. No proxy is the supported default: outside mainland China, leavePROXY_URLunset and every engine is queried directly — you don't need Clash/v2ray to use this server.🔌 Versatile Deployment Modes
Works seamlessly across MCP STDIO (Claude Desktop/Cursor), Streamable HTTP / SSE (Cherry Studio/remote clients), CLI one-shot commands, and a Local HTTP Daemon with connection pool reuse and Prometheus/metricsmonitoring.
Related MCP server: web-mcp
🌍 16 Search Engines
Engine | Mainland China Connectivity | API Key | Core Technology | Best For |
| 🇨🇳 Direct | None | Chrome TLS impersonation + Base64 real URL resolution + stealth fallback | General technical search, mixed EN/ZH queries |
| 🌐 Proxy in CN | None | Bing News RSS + | Breaking news, last-24h updates |
| 🇨🇳 Direct | None | Concurrent | Chinese news, documentation, domestic forums |
| 🇨🇳 Direct | None | WAF cookie jar persistence + empty response auto-retry | Chinese error logs, debugging tutorials |
| 🇨🇳 Direct | None | Direct official API querying for clean long-form articles | Modern frontend/backend technical blogs |
| 🇨🇳 Direct | None | Desktop & mobile dual parsing + redirect link decoding + ad removal | WeChat ecosystem links & long-tail queries |
| 🌐 Proxy in CN | None | wreq Chrome-profile GET of the HTML endpoint (202 anomaly CAPTCHA only on the axios fallback); | English technical search & open-source discussions |
| 🌐 Proxy in CN | None | Multi-worker Anubis SHA-256 PoW solver (difficulty ≤ 6) + cookie session (no browser needed) | Google-backed high-privacy results |
| 🌐 Proxy in CN | None | SSR HTML + homepage cookie warm-up + | Stable Bing-index English fallback |
| 🌐 Proxy in CN | None | curl HTTP/1.1 (Windows Schannel; tested 7/10 before 429; axios 0/10; wreq HTTP/2 429) + strict sponsored ad stripping; compatible with Windows' older curl builds (no | Independent English index & technical blogs |
| 🌐 Proxy preferred | Optional Free Key | Official semantic search API (skipped if key is unset); direct works too, but proxy measured ~0.77s vs ~1.17s direct | Semantic similarity, AI research & papers |
| 🌐 Direct API | None | Official GitHub Search REST API + exact owner/repo fast path (issues & repositories) | GitHub issues, bug reports, repositories, and exact repo lookup |
| 🌐 Direct API | None | Official StackExchange 2.3 API with HTML entity decoding | Developer troubleshooting, stack traces & answers |
| 🌐 Direct API | None | Algolia HN Search API with points, comments & author metadata | Tech discussions, startup launches & engineering news |
| 🌐 Direct API | None | Official arXiv Atom XML API parsing titles, authors & abstracts | Scientific papers, machine learning & AI research |
| 🌐 Proxy in CN | None | grep.app API search across global GitHub code with repo, path & exact line numbers | Source code implementations, API usage patterns |
Version note: this README tracks the current source tree (1.5.1). 1.5.1 is a search-quality release: diversity-aware early return (usable results + ≥2 engines or ≥3 domains), host cap (≤2 per domain, ≤3 for official docs), Chinese-tech routing to Bing/Baidu/Sogou, entity/title relevance guards, official-intent anchor injection (MCP/Vitest/Node.js/Next.js/React/Vue/TypeScript/Docker/PostgreSQL/Zustand) plus an official LTS page for Node.js version queries, Baidu real-URL extraction, version-aware ranking for date-based specs, a 3.5 s cascade cap / 3.5 s CSDN timeout,
apiKeys.giteesupport, fast-failcodeSymbolwithoutGITHUB_TOKEN, and annpm run eval:quality10-scenario benchmark. 1.5.0 unified the test suite (all legacy scripts migrated to Vitest, single runner in CI), centralized everyprocess.envread behindsrc/config/env.ts(enforced by ESLint), splits the four largest modules, adds a 180 s browser-only window after a Brave 429, lazy-loads the server stack for a ~0.32 s CLI cold start, addsdoctor --networkcache-hit-rate / circuit-remaining / P50-P95 metrics, upgrades core dependencies (MCP SDK 1.32, axios 1.21, cheerio 1.2, express 5.3, zod 4), and requires Node.js ≥22 (Vitest 5 and the optionalunpdfPDF parser both require it;node:sqliteis used when available, with a sharded-JSON fallback) plus a Windows CI job on Node 22. Earlier: 1.4.2 added Brave 429 challenge detection (captchavsquota) and the disguised CDP browser fallback; 1.4.1 fixed Windows curl 8.4 compatibility; 1.4.0 addedcodeSymbol, CDP device profiles and DDG 202 fallback. npm latest may lag until the package is published.
🛠️ 5 Core MCP Tools (Minimal & Token-Efficient)
💡 Token Budget & Slimming: All tool descriptions are strictly kept within 250 characters (total description characters ≤ 1200), while detailed routing guidance is delegated to
McpServer({ instructions }), significantly reducing LLM context consumption.
Tool Name | Purpose | Key Highlights |
| Federated web & code search (16 engines) | Supports parallel queries, |
| Web page extraction, Markdown & transparent PDF parsing | All-in-one content extractor: Built-in disk-level Markdown cache (Node 22 |
| Comprehensive GitHub / Gitee content reader | Fetches README, one source file ( |
| All-in-one official library documentation | Resolves library names or Context7 IDs; optional |
| Remote GitHub symbol lookup | GitHub code search + raw/CDN files; optional Tree-sitter AST (grammar downloaded/cached in background, scanner fallback, one parse for definitions + call sites); multi-line signatures, Python decorators, lexical brace matching, filtered call sites, re-export + monorepo tsconfig |
💡 Backward Compatibility: Legacy
fetchCsdnArticleandfetchJuejinArticleare automatically routed underfetchWebContent; legacyresolveLibraryIdis merged intoqueryDocs(which accepts alibraryname or the legacylibraryIdalias). The separate legacy tools were removed — theREGISTER_LEGACY_ARTICLE_TOOLSswitch is gone, because keeping three unregistered handlers around still cost tool-description budget for nothing.
🧩 MCP Prompts:
deep-research(multi-source research with citations) andcode-lookup(official docs + source + release notes) are registered as server-side prompt templates; clients that support MCP Prompts can invoke them directly.
🗂️ MCP Resources: read-only
websearch://cache/pages/websearch://cache/docsindexes plus per-entry Markdown resources expose already-cached content without another tool call. Resources never trigger network fetches.
🚀 Quick Start
1. Run with NPX (Zero Configuration)
No installation required:
npx -y my-websearch@latest💡 Automated Proxy Discovery: The server automatically detects your operating system's proxy settings. If a local proxy environment is active, overseas engines automatically use the proxy while domestic engines stay on fast direct lines.
198.18.0.0/15is whitelisted out of the box for Fake-IP environments.
2. Configure in AI Clients
🔹 Automated setup (recommended)
init detects installed MCP clients, writes the server entry into their config files, and keeps a .bak backup:
npx -y my-websearch@latest init # auto-detect and write
npx -y my-websearch@latest init --dry-run # preview the diff, write nothing
npx -y my-websearch@latest init --client cursor,code # target specific clientsSupported out of the box: Claude Desktop, Cursor, and VS Code (workspace .vscode/mcp.json). An existing entry is never overwritten unless you pass --force, corrupt config files are left untouched, and on Windows it writes cmd /c npx for you. Restart the client, then verify with my-websearch doctor.
🔹 Manual configuration
Claude Desktop / Cursor / Windsurf / Cline (STDIO Mode)
Add to your MCP settings file:
{
"mcpServers": {
"my-websearch": {
"command": "npx",
"args": ["-y", "my-websearch@latest"],
"env": {
"MODE": "stdio"
}
}
}
}(For Windows: init handles this automatically. When configuring manually, if npx is not in the system path, set command to cmd and args to ["/c", "npx", "-y", "my-websearch@latest"])
🔹 Cherry Studio (Streamable HTTP Mode)
Start the server in your terminal with npx my-websearch@latest (default port 3211), then configure:
{
"mcpServers": {
"web-search": {
"name": "MyWebSearch",
"type": "streamableHttp",
"baseUrl": "http://localhost:3211/mcp"
}
}
}💻 CLI & Local Daemon
my-websearch can be used directly from your terminal as a standalone CLI tool or persistent daemon. Use --json in automation and keep the daemon on 127.0.0.1 unless you explicitly need remote access:
# Global install
npm install -g my-websearch
# Detect installed MCP clients and write their config (--dry-run to preview)
my-websearch init
# Start persistent local HTTP daemon (port 3210 by default)
my-websearch serve
# One-shot terminal search (reuses running daemon automatically)
my-websearch search "Model Context Protocol best practices" --limit 5 --min-results 5 --json
# Only results published within the last week (day|week|month|year)
my-websearch search "typescript 5.9 release" --time-range week --json
# Extract web page as clean Markdown
my-websearch fetch-web "https://blog.vuejs.org/posts/vue-3-5" --max-chars 15000 --json
# View daemon status and Prometheus metrics (/health, /status, /metrics)
my-websearch status --json
# Clear in-memory search cache
my-websearch cache-clear🩺 Diagnostics
Use the built-in doctor command before troubleshooting a proxy, browser, or engine:
# Local configuration and browser checks (path only; use --browser to verify launch)
my-websearch doctor
# Verify the Chromium-based browser can actually launch (auto-detected, CHROME_PATH, or PLAYWRIGHT_CDP_ENDPOINT)
my-websearch doctor --browser
# Real proxy/engine probes (may contact upstream services)
my-websearch doctor --network
# Limit network probes to selected engines
my-websearch doctor --network --engine bing,duckduckgo --json
# Reset and clear cached engine circuit breaker / health state
my-websearch doctor --reset-healthThe report separates configuration, proxy TCP reachability, HTTP reachability, application parsing, browser launch, and challenge verification status. For Brave, a 429 is additionally classified as captcha (human verification — retrying usually stays challenged) or quota (rate-limit window) in both the text report (challenge=...) and --json (challengeKind). Network mode also summarizes cache hit rates (search cache and fetch page cache), remaining circuit cooldowns per engine, and P50/P95 engine latency when persisted samples exist. Network mode performs real requests and should not be used as a high-frequency health check.
🌐 Proxy Troubleshooting & Upstream Rate Limiting
When configuring or diagnosing proxy connectivity, refer to these common edge cases:
Startup log shows
🌐 Proxy: disabledeven thoughproxy.urlis set?Check
useProxyflag: In~/.my-websearch/config.json, setting"useProxy": falsetakes precedence and explicitly overrides and disablesurl(settingPROXY_URL='false'). Change"useProxy": trueor simply omit the field (providing a validurlautomatically enables the proxy).System proxy is auto-detected: when no explicit proxy is configured, the OS system proxy (Windows registry / macOS
scutil/HTTPS_PROXY) is used for overseas engines and generic fetches — you do not needPROXY_URLjust because a proxy client is running. If detection fails or you want to pin a specific proxy, setPROXY_URL(e.g.http://127.0.0.1:7897).Prefer
~/.my-websearch/config.jsonover clientenv: putproxy.urland API keys in the config file. Some MCP clients (e.g. WorkBuddy) fingerprint the server bycommand + args + env key names + cwd, so adding aPROXY_URLenv entry changes the trust fingerprint and the server may silently drop out of the session. Config-file settings are read at startup and avoid that.Where to put the proxy (the error message used to say only "MCP client config", which was ambiguous):
Location
Use it when
~/.my-websearch/config.json→"proxy": { "url": "http://127.0.0.1:7897" }Recommended. Applies to every client (CLI, daemon, MCP) and does not change client trust fingerprints.
MCP client
env→PROXY_URLPer-client override. Restart the client; some clients include env keys in their trust fingerprint.
Proxy client's "system proxy" switch
No config at all. Since 1.3.8 it is auto-detected for overseas engines and generic fetches.
Proxy works, but an overseas engine still fails or times out? (Per-engine diagnosis, not a config error)
DuckDuckGo returns HTTP 202 / challenge: the 202 body is DDG's anomaly CAPTCHA (human verification), not a plain per-IP rate limit; the
d.jsarithmetic solver does not bypass it. The engine first requests the HTML endpoint with a wreq Chrome-profile GET, which normally returns 200 even when the axios POST path is challenged with 202 — so a 202 now only appears if that path fails and the axios fallback is used. If you still see 202, confirm your build includes the wreq GET path (1.3.8 and earlier do not), then retry or check that the proxy is attached. DDG remains best-effort: the same exit can still be challenged intermittently, so do not treat it as an SLA; for stability use Bing/Startpage or configureBRAVE_API_KEY/EXA_API_KEY. Note:SEARCH_MODE=playwright:duckduckgois not supported (DDG has no browser-rendering path), so it falls back to HTTP.Bing returns HTTP 200 but 0 results: the wreq impersonation path now falls back to the axios SERP path automatically when it parses an empty page. If both paths stay empty, the query/IP is likely rate-limited.
Brave returns HTTP 429 Too Many Requests: after a limited number of requests the CloudFront edge starts serving an anti-bot challenge page (HTTP 429) to the exit IP, while the first requests often still depend on the client TLS/HTTP stack. Fresh-node measurements: axios (Node OpenSSL, HTTP/1.1) → 0/10, all 429; curl (Schannel, HTTP/1.1) → 7/10 then 3/3 429; wreq (HTTP/2 Chrome) → 429; .NET Schannel HTTP/2 → 200/429 mixed. After curl hit 429, retrying .NET Schannel HTTP/2 on the same URL was 0/3, and on fresh URLs 0/3 — once the IP quota is exhausted, changing the client stack does not help. The Brave web path always uses curl (Windows Schannel; no axios fallback) and trips a rolling 60-second window of 6 requests — the 7th request is challenged (measured on a clean exit at 1 request / 10 s: 6 × 200, then 429). The 429 body is not a plain quota page: it is Brave's SPA challenge (captcha + Argon2
challengeSet), so it is reported asbot_challengerather thanrate_limited. Recovery follows the window, not a fixed timer: requests pass again once the oldest request of the burst ages out of 60 s (measured 62.9 s after a 6-request burst; ~15 s when the same 6 requests were spread over 52 s). Retrying inside the window adds requests and extends it — probing every 15 s / 30 s kept the IP blocked for 255 s / 6.5 min respectively. The engine therefore trips the Brave circuit for 60 s (then 120 → 240 → 300 s) and retries once through a disguised desktop headless CDP browser that clicks the PoW验证button automatically (8/8 success, ~4–6 s per query on a hard-blocked IP). That fallback only serves that search and does not clear the HTTP window: after a successful browser pass, the next HTTP attempt succeeded once and then hit 429 again. Client-stack swaps do not help either (axios 0/10; wreq HTTP/2 429; curl Schannel HTTP/1.1 7/10 before the window trips). The official API (BRAVE_API_KEY) remains the stable path.BRAVE_BROWSER_FALLBACK=0disables both the retry and the window. After a 429 the engine enters a 180 s browser-only window (long-running MCP/daemon processes only; one-shot CLI invocations start fresh): HTTP is skipped entirely and every Brave call goes straight to the disguised browser (no wasted curl request that would only extend the server-side window); browser-only engines stay eligible but are ranked last by auto routing and cascade, so they do not slow down normal searches; if the browser fails too, the circuit opens (60 → 120 → 240 → 300 s) and routing/cascade substitute other engines.Startpage times out or Anubis PoW difficulty spikes: difficulty 6 is now solved inline by up to 4 worker threads (~3–12s); >6 is skipped. Pass-challenge socket errors / transient 5xx are retried automatically.
💡 Resolution: these are upstream anti-bot/rate-limit behaviours, not a broken MCP configuration. Handle them per engine: DDG 202 → upgrade/retry (the wreq GET path; in
automode Yahoo also replaces an unhealthy DDG from the fallback pool); Brave 429 → the disguised-browser fallback usually rescues it (see above); otherwise wait out the window, rotate the exit IP, or setBRAVE_API_KEY; Bing 0 results → retry or adjust the query; Startpage PoW → retry. Switch exit IP only after the engine-specific step fails. As a last resort useengines: ["bing", "baidu"], or configure an official key (BRAVE_API_KEY/EXA_API_KEY) for an SLA path.
🔐 Security Model (read before exposing the server)
The defaults are safe as a package, and every one of them matters:
Default | What it protects |
Bind to | Nothing outside the machine can reach the port |
No authentication | Acceptable only because of loopback binding |
Host header validation | Blocks DNS-rebinding attacks from a browser on the same machine |
CORS disabled | No cross-origin browser access |
Relaxing one without the others silently removes the protection:
HOST=0.0.0.0(Docker) → the Host allowlist follows the bind address automatically; if you also use a public domain, add it viaOPEN_WEBSEARCH_ALLOWED_HOSTS(comma-separated).Exposing to the public internet → put it behind a reverse proxy with authentication (or an SSH tunnel). The daemon prints a warning when bound to a non-loopback address.
MCP session IDs are identifiers, not credentials — anyone who can reach the port can resume a session.
FETCH_WEB_INSECURE_TLS=truedisables certificate verification for user-supplied URLs; only enable it for a trusted network.The app copies API keys it finds in
~/.gemini/config/mcp_config.json/~/.workbuddy/mcp.jsoninto~/.my-websearch/config.json(mode0600) on first run. Runmy-websearch doctorto see where each key came from; setMYWEBSEARCH_NO_AUTO_BOOTSTRAP=1to disable the copy.
⚙️ Configuration & Environment Variables
Global Configuration (~/.my-websearch/config.json)
To share settings across all MCP clients without duplicate configuration:
{
"apiKeys": {
"context7": "ctx7sk-...",
"exa": "...",
"github": "ghp_...",
"gitee": "...",
"brave": "..."
},
"proxy": {
"url": "http://127.0.0.1:7897",
"useProxy": true,
"engines": ["duckduckgo", "exa", "brave", "startpage"]
}
}⚠️ Note on
useProxy: When"url"is configured, proxy is enabled automatically. Setting"useProxy": falsewill explicitly override and disableurl. If you want proxy enabled, set"useProxy": trueor omit the field entirely. Keys & restart: entries underapiKeysare mapped to env vars at startup (github→GITHUB_TOKEN,gitee→GITEE_TOKEN,context7→CONTEXT7_API_KEY, …) and only fill variables that are not already set in the client env. Restart the MCP server after editing the file;my-websearch doctorshows which keys are currently visible.
📋 Exhaustive Environment Variables Reference
Can be set via the global configuration file (~/.my-websearch/config.json), local .env, or system environment variables:
Precedence (highest first): process environment → project .env → project .my-websearch.json → ~/.my-websearch/config.json → built-in defaults.
0. 🖥️ Server & Runtime
Variable | Default | Allowed Values / Description |
|
|
|
|
| MCP HTTP bind address. Binding to a non-loopback address also registers that host in the Host allowlist and prints a no-auth warning. |
|
| MCP HTTP port |
| empty | Extra Host-header allowlist entries (comma-separated), needed when serving through a public domain/reverse proxy |
|
| Local REST daemon bind address (no auth: keep it on loopback) |
|
| Enables in-process counters and the daemon |
|
| Hard deadline for one |
|
| Prefer jsDelivr over |
|
| Total time budget for one |
|
| Legacy name retained for compatibility: budget for the Bing browser fallback (now backed by raw CDP) before HTTP results are kept |
| unset | Set to |
1. 🔍 Search & Routing Orchestration
Variable | Default | Allowed Values / Description |
|
| Default engine: |
| all | Comma-separated whitelist of allowed search engines (e.g. |
|
| Base URL for Bing queries. Overseas users or reverse proxy nodes can set to |
|
| Cascade threshold: triggers fallback engines when usable results are fewer than this value |
|
| Default result limit when not explicitly specified in tool calls (max 50) |
|
| Global hard deadline for multi-engine search execution (milliseconds) |
|
| Result fusion mode: |
|
| RRF constant k; lower values amplify rank differences. Only used when |
|
| First circuit-breaker cooldown for an engine (429 / 202 / bot_challenge). Repeated failures back off exponentially (30s → 60s → 120s…). Brave overrides the first step to 60s because its 429 is a rolling 60-second window of 6 requests (the 7th is challenged) and recovery only starts once the oldest request ages out; setting this variable explicitly disables the per-engine override. |
|
| Upper bound for the exponential circuit-breaker backoff (default 5 minutes). |
| empty | Additional authority domain whitelist for result re-ranking (comma-separated, appended to built-in list) |
|
| Search mode and browser policy. Supports single mode, multi-engine per mode, and compound multi-mode combinations:• Global mode: |
|
| When the keyless Brave web path returns HTTP 429 / an anti-bot challenge, retry once through a disguised desktop headless CDP browser that auto-triggers the Argon2 PoW |
|
| Comma-separated engines for default English auto-routing |
|
| Comma-separated engines for default Chinese auto-routing |
2. 🌐 Proxy & Network Security
Variable | Default | Allowed Values / Description |
| empty (off) | Consolidated upstream proxy configuration (merges previous |
|
| Whitelisted CIDRs for Fake-IP / TUN mode to avoid SSRF false-positive blocking |
|
| Comma-separated DNS servers used by the internal c-ares resolver (helps when the system DNS is polluted). Overrides the built-in list entirely |
|
|
|
|
| Operating system platform for fingerprint impersonation ( |
|
| Toggle Chrome TLS impersonation for Bing HTTP mode (auto-trips circuit breaker after consecutive empty results) |
|
| Maximum difficulty threshold for Startpage Anubis PoW solver; skips/falls back when difficulty exceeds this |
|
| Timeout in ms for direct-first connection probes before proxy fallback |
|
| Disable TLS certificate verification for |
|
| When |
Custom proxy engine lists: if you set
proxy.enginesin~/.my-websearch/config.jsonor use the;engine1,engine2suffix ofPROXY_URL,yahooandbing-newsare added automatically because they cannot be reached directly from CN (Yahoo times out in 15–20s; Bing News returns a 302/HTML page). Removing the list entirely is still recommended: defaults include them and keep the direct API engines (GitHub / Stack Overflow / Hacker News / arXiv) off the proxy.
3. 🔐 Credentials & API Keys
Variable | Default | Allowed Values / Description |
| empty | Context7 API Key (strongly recommended for proxy/TUN users to avoid shared IP quotas) |
| empty | Exa Free API Key (only required if using the |
| empty | Brave Search API Key (optional; uses official high-reliability API when set) |
| empty | GitHub Personal Access Token (optional; raises rate limit to 5,000 req/hr) |
| empty | Gitee Personal Access Token (optional; raises Gitee API rate limits and enables private repos). Also configurable as piKeys.gitee in ~/.my-websearch/config.json. |
| empty | Stack Apps Key (optional; raises quota to 10,000 req/day) |
4. 🖥️ Server Modes, Concurrency & Session Reaper
Variable | Default | Allowed Values / Description |
|
| MCP transport mode: |
|
| MCP HTTP/SSE listen port (local persistent daemon uses |
|
| Max concurrent searches in daemon mode ( |
|
| Max retained MCP HTTP sessions before returning HTTP 429 Too Many Requests |
|
| Idle session expiration duration before reaping (default 30 min) |
|
| Session reaper scan interval (default 5 min) |
|
| Log level: |
|
| Enable SSRF blocking and host header security audit logging |
| loopback | Host header whitelist for HTTP server (DNS Rebinding protection, comma-separated) |
5. 🧭 CDP Browser & Stealth Controls (legacy Playwright env names kept)
The browser fallback usesraw Chrome DevTools Protocol via chrome-remote-interface, so it requires a Chromium-based browser: Chrome, Edge, Chromium, Brave, Vivaldi or Opera (Firefox/WebKit are not supported). Standard install paths for all of these are auto-detected; override with CHROME_PATH / BROWSER_PATH / PLAYWRIGHT_EXECUTABLE_PATH, or connect a remote browser via PLAYWRIGHT_CDP_ENDPOINT (no local install needed). my-websearch doctor --browser reports which browser was found and how to fix a missing one. The PLAYWRIGHT_* env names and SEARCH_MODE=playwright value are kept for backward compatibility only. Browser fallback behavior is governed by SEARCH_MODE.
Two independent knobs — don't mix them up:
BROWSER_DRIVERpicks the driver library:cdp(default, built in viachrome-remote-interface) orplaywright(legacy escape hatch that requires you to installplaywright-core).PLAYWRIGHT_CDP_ENDPOINT/CHROME_PATH/BROWSER_PATH/PLAYWRIGHT_EXECUTABLE_PATHdecide where the browser comes from: an already-running local/remote CDP endpoint, an explicit executable, or auto-detection.
Every browser path — Bing fallback, Startpage, DuckDuckGo, Brave, fetchWebContent, CSDN article fetch, and doctor --browser — goes through the same entry point, so these settings apply to all of them.
Variable | Default | Allowed Values / Description |
|
| Browser backend. |
|
| CDP device profile: |
|
| When DuckDuckGo's HTTP paths return 202/CAPTCHA, retry the HTML endpoint through the CDP browser. Uses |
| empty | Remote Chrome/Edge CDP endpoint URL (e.g. |
| auto | Absolute path to a Chromium-based browser executable. Auto-detection covers Chrome / Edge / Chromium / Brave / Vivaldi / Opera in standard locations; |
|
| Run headless (on Windows, an isolated hidden desktop window is used); legacy name |
|
| Hard page navigation timeout (milliseconds); legacy name, applied to CDP navigation |
6. 🛠️ Application Directory & Tool Customization
Variable | Default | Allowed Values / Description |
|
| Root directory for configuration and persistent cookie files |
|
| Maximum character limit returned to LLMs to prevent prompt context explosion |
|
| Custom registration name for the search tool in MCP |
|
| Custom registration name for the content extraction tool |
|
| Custom registration name for the GitHub/Gitee content tool (set to |
💡 Zero-Config Upgrades (Redundant Variables Removed):
CORS Switches (
ENABLE_CORS/CORS_ORIGIN): CORS is disabled by default; enable it explicitly and restrict origins when browser clients need cross-origin access.Browser Backend: raw CDP via
chrome-remote-interface;playwright-coreis no longer bundled.PLAYWRIGHT_*names andSEARCH_MODE=playwrightremain as compatibility aliases.Emoji Switch (
OPEN_WEBSEARCH_NO_EMOJI): Automatic console code page detection smoothly falls back to ASCII tags on legacy terminals.Fragmented Debug Flag (
OPEN_WEBSEARCH_DEBUG): Unified into standardLOG_LEVEL=debug.Cookie Persistence TTL (
MYWEBSEARCH_COOKIE_TTL_DAYS): Solidified to 30 days with automated sliding renewal.
🤝 Contributing & Tests
Pull requests and issues are welcome! To run the test suite locally:
npm install
npm run build
npm test # Run the Vitest suite (`npm test` prints the current file/test counts)
npm run check:docs # Verify README env tables match docs/env-schema.json (edit the schema, then `npm run docs:env`)
npm run test:live # Run bounded-concurrency live integration tests
npm run eval:live # Run 23-case live STDIO JSON-RPC evaluation suiteAcknowledgements
Author: wtznicy
This project evolved from Open-WebSearch (originally created by Aas-ee) — special thanks to the original author. Thanks also to:
wreq-js for native Chrome TLS/HTTP2 fingerprint impersonation
Context7 for powering official documentation retrieval
Mozilla Readability & Turndown for content extraction and GFM Markdown formatting
Available Tools
5 toolscodeSymbolARead-onlyIdempotent
Find a symbol declaration and references in a remote GitHub repo. Uses GitHub code search + raw files; returns signature, code block, and reference lines. Requires GITHUB_TOKEN. Example: repo="fastapi/fastapi", symbol="Depends".
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Optional branch/tag/commit for raw file fetch | |
| kind | No | Optional declaration kind filter | |
| repo | Yes | GitHub repository: owner/repo or https://github.com/owner/repo | |
| symbol | Yes | Exact symbol name (class/function/interface/type/const) | |
| maxResults | No | Max definitions to return (1-10, default 5) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld/non-destructive, so the description's marginal value is in disclosing the auth requirement (GITHUB_TOKEN) and the retrieval mechanism (code search plus raw file fetch). It also discloses the return payload (signature, code block, reference lines), which matters since there is no output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core purpose, then the mechanism, then the prerequisite and example. No redundant or filler wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully summarizes what is returned and states the auth requirement, and the schema fully covers inputs. Minor gaps remain around rate limits or code-search coverage limits, which would matter for an openWorld remote-search tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all five parameters (including ref, kind, maxResults) are already documented in the schema. The description's example illustrates repo and symbol formats but adds no semantics beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Find a symbol declaration and references') scoped to 'a remote GitHub repo', and the method note (GitHub code search + raw files) distinguishes it from siblings like fetchGithubContent and search. An agent can tell what this returns without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a concrete usage example (repo="fastapi/fastapi", symbol="Depends") and a prerequisite (GITHUB_TOKEN), which gives clear context for invocation. It stops short of naming when-not-to-use or pointing to the sibling tools for whole-file retrieval, so it doesn't reach 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchGithubContentARead-onlyIdempotent
Fetch GitHub/Gitee by URL: README, source file (/blob/, with lines), tree, issue, PR, commit, compare, or releases (releaseLimit for recent N). Example: https://github.com/o/r/blob/main/src/index.ts — ref can be branch/tag/commit; not a repo clone.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| lines | No | Optional source-file line range, 1-based inclusive (e.g. "120-180"). Only valid for /blob/ URLs; URL hash #L120-L180 also works. | |
| releaseLimit | No | Optional number of releases to return for /releases URLs (1-30, default 10). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, and non-destructive, so the safety profile is covered. The description adds real context: which URL path shapes are recognized, that ref may be branch/tag/commit, and that lines apply only to /blob/ URLs. It stays silent on authentication needs for private repos, rate limits, and failure behavior (e.g., what happens on a non-matching URL pattern).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded — the artifact list comes first, then the example, then the two caveats. Every clause carries information; the only mild cost is that cramming the URL-type list, example, ref semantics, and clone exclusion into one sentence makes it slightly hard to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description carries the burden of explaining what comes back, and it only implies it ("with lines" suggests line-numbered source). For a multi-mode reader spanning eight different URL types, it should say more about the shape of results and about private-repo authentication before an agent can call it confidently in all modes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, and the undocumented parameter is url itself. The description compensates well: it gives a concrete example URL, enumerates the path patterns the tool accepts, and explains that ref can be a branch, tag, or commit. It also reinforces the /blob/-only constraint on lines, adding cross-parameter meaning the schema only states in isolation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb (fetch) and resource (GitHub/Gitee URL content) and enumerates the supported content kinds: README, source file at /blob/, tree, issue, PR, commit, compare, releases. It also rules out one adjacent capability ("not a repo clone"), though it never names the sibling fetchWebContent or search to sharpen the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: you supply a GitHub/Gitee URL and the tool resolves the corresponding artifact. The one explicit exclusion ("not a repo clone") helps, but there is no guidance on when to prefer fetchWebContent, search, or queryDocs, and no mention of prerequisites such as private-repo auth.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchWebContentARead-onlyIdempotent
Fetch readable web content/docs/PDFs from public URLs. Auto-cleans ads, keeps code/tables. Batch urls (1-3), CSS selector scoping, bm25 focus, startIndex paging, cache, schema extract to JSON, citations, link retention.
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | Return the raw response body (HTML/plain text) without extraction | |
| url | No | ||
| urls | No | Batch URLs (1~3 URLs run concurrently, splitting maxChars budget; ideal for comparing search results) | |
| cache | No | Cache mode: 'default' uses disk Markdown cache; 'reload' forces re-fetching upstream | |
| focus | No | Keyword or section name to target (e.g. 'Auth', 'Config'); returns targeted window (~5000 chars) instead of burning tokens on full document | |
| format | No | Content format (default: 'markdown' when raw is false): 'markdown' preserves headings, lists, fenced code blocks (with language) and GFM tables — better for technical docs; use 'text' for plain text | |
| extract | No | Extract structured JSON instead of markdown, using CSS selectors — no LLM needed. fields: [{name, selector, type: text|html|markdown|attribute|list, attribute?, fields?, all?}]; selector '@attr' reads an attribute of the current node | |
| maxChars | No | ||
| selector | No | CSS selector: return only the matched subtree, keeping code blocks and tables. Use when auto-extraction grabs the wrong container. Errors instead of silently returning the whole page when nothing matches | |
| citations | No | Rewrite inline links as numbered citations and append a '## References' list, so the answer can cite sources | |
| focusMode | No | How focus selects text: 'bm25' (default) ranks paragraphs by relevance and keeps the best ones; 'window' keeps the old ±2500-char window around the first match | |
| startIndex | No | Character offset to start reading from (for paging through long content) | |
| readability | No | ||
| retainLinks | No | Inline link retention: 'all' (default) keeps [label](url); 'text' keeps only the label (strips URLs, big win on index/list pages); 'none' removes the link entirely. Ignored inside code blocks | |
| includeLinks | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (read-only, idempotent, open-world, non-destructive), and the description still adds real behavior: ad stripping, disk Markdown caching, batch URLs splitting the maxChars budget, and selector failing loudly rather than silently returning the whole page. Missing are failure modes for inaccessible URLs, rate limiting, or what the default response looks like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening sentence is well front-loaded and earns its place. The follow-on feature list is telegraphic ('bm25 focus, startIndex paging, cache, schema extract to JSON') and largely restates schema parameters, adding tokens without adding meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter tool with a nested extract object and no output schema, the description covers the main modes but omits that at least one of url/urls must be supplied (zero required params), and never characterizes the default return shape. It is minimally sufficient but leaves real gaps for a tool this complex.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 73%, so most parameters are already documented in the schema itself. The description's second sentence names capabilities (bm25 focus, startIndex paging, cache, citations, link retention) but adds no syntax or defaults beyond what the parameter descriptions already state, so it earns only the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch readable web content/docs/PDFs from public URLs') plus the transformation applied ('Auto-cleans ads, keeps code/tables'). The 'public URLs' scope implicitly separates it from fetchGithubContent and queryDocs, but siblings are never named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Several in-line cues imply usage: batch urls are 'ideal for comparing search results', selector is for 'when auto-extraction grabs the wrong container', focus avoids 'burning tokens on full document'. However there is no explicit when-to-use versus fetchGithubContent/queryDocs/search, and no stated preconditions such as needing at least one of url or urls.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryDocsBRead-onlyIdempotent
Get official docs, API references, and code snippets for libraries/packages (e.g. 'react', 'fastapi', 'next.js'). Queries Context7 with fallback to npm/PyPI registry and GitHub README. Direct, no proxy needed.
| Name | Required | Description | Default |
|---|---|---|---|
| cache | No | Use the 24h local docs cache (default) or bypass/refresh it with 'reload' | |
| limit | No | Maximum number of code snippets to return (default 5) | |
| query | No | The question, feature, or task to get relevant documentation for (optional; defaults to an overview when omitted, e.g. 'how to set up middleware with auth') | |
| strict | No | If true, rejects low-confidence library name matches with an error instead of returning loosely matched candidate docs (default false) | |
| library | No | Library or package name (e.g. 'next.js', 'react', 'fastapi') or exact Context7 library ID (e.g. '/vercel/next.js', version-pinnable like '/vercel/next.js@v15.1.8') | |
| version | No | Optional explicit version (e.g. '14.2.0' or 'v15.1.8'); overrides an @version embedded in library | |
| libraryId | No | Legacy alias for library (e.g. /vercel/next.js) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and openWorld, so the safety profile is covered. The description adds genuinely new behavior: a prioritized source chain (Context7 primary, then npm/PyPI registry, then GitHub README) and that it connects directly with no proxy. With annotations carrying most of the burden, this extra sourcing context earns a 3 rather than a 4, since it says nothing about latency, rate limits, or what a fallback result looks like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core purpose before the source/fallback detail. Minor waste: 'Direct, no proxy needed' is vague and an agent cannot act on it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With seven optional parameters and no output schema, the description does at least say what kind of content comes back (docs, API references, code snippets). But it leaves the fallback behavior undefined from the caller's perspective, does not explain how multiple sources are merged or ordered in the response, and gives no steer on typical parameter combinations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and every parameter (cache, limit, query, strict, library, version, libraryId) is documented at the field level. The description only echoes the library examples already in the schema and adds no meaning for cache, strict, limit, or the libraryId alias, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get official docs, API references, and code snippets') scoped to libraries/packages with concrete examples ('react', 'fastapi', 'next.js'). It implicitly separates itself from fetchWebContent and fetchGithubContent by naming its own source chain (Context7, npm/PyPI registry, GitHub README), though it never names the siblings outright.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: the agent can infer this is for library/package documentation lookups, and the source-fallback chain hints at why it may succeed where a plain web fetch fails. There is no explicit when-to-use vs when-not-to-use guidance relative to search, fetchWebContent, fetchGithubContent, or codeSymbol.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchBRead-onlyIdempotent
Search the web across multiple engines (Chinese/English/Code). Supports multi-query fan-out ('queries'), timeRange filtering, site limits, fetchTop article content, and no API keys required.
| Name | Required | Description | Default |
|---|---|---|---|
| site | No | Restrict search to specific site domain(s) (e.g. ['github.com', 'developer.mozilla.org']) | |
| limit | No | Target total results after merging all engines (default: server DEFAULT_SEARCH_LIMIT, usually 10). Not a per-engine quota: once enough results are collected, slower engines may be cancelled (see cancelledEngines). Pass explicit `engines` to disable early cancellation. | |
| query | No | Single search query (required if 'queries' is omitted; use 'queries' for multi-intent fan-out) | |
| engines | No | Search engines to use (default: auto-routes by query language; explicit engines do not cascade unless minResults is specified) | |
| queries | No | 1-4 queries run concurrently and merged (required if 'query' is omitted) | |
| verbose | No | Include verbose diagnostic fields (engineMetrics, full failure hints, and redundant source hostnames); default false to save tokens | |
| fetchTop | No | Two-in-one search & fetch: automatically fetch full readable Markdown content for top N results (0~3) in a single turn | |
| timeRange | No | Publication time range filter ('day', 'week', 'month', 'year') | |
| minResults | No | Auto-run additional engines when fewer usable results come back (default: 5 when engines omitted, 0 when engines explicitly specified) | |
| searchMode | No | Execution mode: 'request' (HTTP only), 'auto' (HTTP with browser fallback), or 'playwright' (headless browser only) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and open-world semantics, so the description only needs to add context. It contributes 'no API keys required' (a real auth disclosure) and the multi-engine fan-out model, but omits rate-limit behavior, latency expectations, and that early engine cancellation can drop results — that last point lives only in the schema's 'limit' description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One front-loaded sentence naming the core action, followed by a capability list — efficient and scannable. The trailing 'no API keys required' is slightly tacked on but genuinely informative, so little is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a 10-parameter, multi-engine tool with no output schema, so the description should explain the shape of results (merged ranked results with source metadata, cancelledEngines diagnostics on early termination). It instead lists inputs already documented in the schema, leaving the agent to guess what comes back and how results are merged/ranked.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema description coverage, the baseline is 3. The description name-drops 'queries', 'timeRange', 'site', and 'fetchTop', which helps an agent notice key capabilities, but adds no syntax or semantics beyond what the schema already spells out (e.g. the fetchTop 0-3 range and its Markdown return are schema-only).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource: 'Search the web across multiple engines', with qualifiers (Chinese/English/Code corpora). This distinguishes it from the sibling fetchWebContent, which retrieves content from a known URL rather than discovering URLs. It does not name any sibling explicitly, so an agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the features listed (multi-query fan-out, timeRange, site restriction, fetchTop), but the description never says when to pick this tool over fetchWebContent or queryDocs, nor when to prefer 'query' versus 'queries'. The when-not guidance is absent entirely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v1.5.1- Changed
codeSymbol1 field changed- changed
Input schema / additionalPropertiesPrevious value: -trueNew value: +{}
- Changed
fetchGithubContent1 field changed- changed
Input schema / additionalPropertiesPrevious value: -trueNew value: +{}
- Changed
fetchWebContent4 fields changed- changed
Input schema / additionalPropertiesPrevious value: -trueNew value: +{} - changed
Input schema / properties / extract / additionalPropertiesPrevious value: -trueNew value: +{} - changed
Input schema / properties / extract / properties / fields / items / additionalPropertiesPrevious value: -trueNew value: +{} - added
Input schema / properties / startIndex / maximumAdded value: +9007199254740991
- Changed
queryDocs1 field changed- changed
Input schema / additionalPropertiesPrevious value: -trueNew value: +{}
- Changed
search1 field changed- changed
Input schema / additionalPropertiesPrevious value: -trueNew value: +{}
1 tool update
v1.4.2- Added
codeSymbol
1 tool update
v1.3.12- Changed
fetchGithubContent1 field changed- added
Input schema / properties / releaseLimitAdded value: +{ + "description": "Optional number of releases to return for /releases URLs (1-30, default 10).", + "maximum": 30, + "minimum": 1, + "type": "integer" +}
5 tool updates
v1.3.10- Added
fetchGithubContent - Removed
fetchGithubReadme - Changed
fetchWebContent7 fields changed- changed
Input schema / additionalPropertiesPrevious value: -falseNew value: +true - added
Input schema / properties / cacheAdded value: +{ + "description": "Cache mode: 'default' uses disk Markdown cache; 'reload' forces re-fetching upstream", + "enum": [ + "default", + "reload" + ], + "type": "string" +} - added
Input schema / properties / citationsAdded value: +{ + "description": "Rewrite inline links as numbered citations and append a '## References' list, so the answer can cite sources", + "type": "boolean" +} - added
Input schema / properties / extractAdded value: +{ + "additionalProperties": true, + "description": "Extract structured JSON instead of markdown, using CSS selectors — no LLM needed. fields: [{name, selector, type: text|html|markdown|attribute|list, attribute?, fields?, all?}]; selector '@attr' reads an attribute of the current node", + "properties": { + "baseSelector": { + "minLength": 1, + "type": "string" + }, + "fields": { + "items": { + "additionalProperties": true, + "properties": { + "all": { + "type": "boolean" + }, + "attribute": { + "minLength": 1, + "type": "string" + }, + "fields": { + "items": {}, + "type": "array" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "selector": { + "minLength": 1, + "type": "string" + }, + "type": { + "enum": [ + "text", + "html", + "markdown", + "attribute", + "list" + ], + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "maxItems": 50, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "fields" + ], + "type": "object" +} - added
Input schema / properties / focusModeAdded value: +{ + "description": "How focus selects text: 'bm25' (default) ranks paragraphs by relevance and keeps the best ones; 'window' keeps the old ±2500-char window around the first match", + "enum": [ + "bm25", + "window" + ], + "type": "string" +} - added
Input schema / properties / retainLinksAdded value: +{ + "description": "Inline link retention: 'all' (default) keeps [label](url); 'text' keeps only the label (strips URLs, big win on index/list pages); 'none' removes the link entirely. Ignored inside code blocks", + "enum": [ + "all", + "none", + "text" + ], + "type": "string" +} - added
Input schema / properties / selectorAdded value: +{ + "description": "CSS selector: return only the matched subtree, keeping code blocks and tables. Use when auto-extraction grabs the wrong container. Errors instead of silently returning the whole page when nothing matches", + "minLength": 1, + "type": "string" +}
- Changed
queryDocs4 fields changed- changed
Input schema / additionalPropertiesPrevious value: -falseNew value: +true - added
Input schema / properties / cacheAdded value: +{ + "description": "Use the 24h local docs cache (default) or bypass/refresh it with 'reload'", + "enum": [ + "default", + "reload" + ], + "type": "string" +} - added
Input schema / properties / strictAdded value: +{ + "description": "If true, rejects low-confidence library name matches with an error instead of returning loosely matched candidate docs (default false)", + "type": "boolean" +} - added
Input schema / properties / versionAdded value: +{ + "description": "Optional explicit version (e.g. '14.2.0' or 'v15.1.8'); overrides an @version embedded in library", + "minLength": 1, + "type": "string" +}
- Changed
search10 fields changed- changed
Input schema / additionalPropertiesPrevious value: -falseNew value: +true - changed
Input schema / properties / engines / descriptionPrevious value: -"Search engines to use (default: server default, which may auto-route by query language)"New value: +"Search engines to use (default: auto-routes by query language; explicit engines do not cascade unless minResults is specified)" - changed
Input schema / properties / engines / items / descriptionPrevious value: -"Search engine name. Valid values (normalize alias to canonical name): baidu, bing, csdn, duckduckgo, exa, brave, juejin, startpage, sogou, github, stackoverflow, hackernews, arxiv"New value: +"Search engine name. Valid values (normalize alias to canonical name): baidu, bing, bing-news, csdn, duckduckgo, exa, brave, juejin, startpage, yahoo, sogou, github, stackoverflow, hackernews, arxiv, code" - changed
Input schema / properties / fetchTop / descriptionPrevious value: -"Two-in-one search & fetch: automatically fetch full readable Markdown content for top N results (0~3) in a single turn (up to 4000 chars per article)"New value: +"Two-in-one search & fetch: automatically fetch full readable Markdown content for top N results (0~3) in a single turn" - changed
Input schema / properties / limit / descriptionPrevious value: -"Max results (default: server DEFAULT_SEARCH_LIMIT, usually 10)"New value: +"Target total results after merging all engines (default: server DEFAULT_SEARCH_LIMIT, usually 10). Not a per-engine quota: once enough results are collected, slower engines may be cancelled (see cancelledEngines). Pass explicit `engines` to disable early cancellation." - changed
Input schema / properties / minResults / descriptionPrevious value: -"Auto-run additional engines when fewer than this many USABLE results (relevant, non-entry-page) come back (default: server DEFAULT_MIN_RESULTS, usually 5; max 50 — larger values only burn the search deadline)"New value: +"Auto-run additional engines when fewer usable results come back (default: 5 when engines omitted, 0 when engines explicitly specified)" - changed
Input schema / properties / queries / descriptionPrevious value: -"1-4 queries run concurrently and merged (required if 'query' is omitted) — for covering multiple intents in one call (e.g. official docs / Chinese community / English). Response includes 'cached: true' only when all sub-queries hit the TTL cache."New value: +"1-4 queries run concurrently and merged (required if 'query' is omitted)" - added
Input schema / properties / searchMode / descriptionAdded value: +"Execution mode: 'request' (HTTP only), 'auto' (HTTP with browser fallback), or 'playwright' (headless browser only)" - added
Input schema / properties / siteAdded value: +{ + "description": "Restrict search to specific site domain(s) (e.g. ['github.com', 'developer.mozilla.org'])", + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / timeRangeAdded value: +{ + "description": "Publication time range filter ('day', 'week', 'month', 'year')", + "enum": [ + "day", + "week", + "month", + "year" + ], + "type": "string" +}
3 tool updates
v1.3.1- Removed
fetchCsdnArticle - Removed
fetchJuejinArticle - Changed
queryDocs1 field changed- removed
Input schema / requiredRemoved value: -[ - "library" -]
4 tool updates
v1.2.1- Changed
fetchWebContent3 fields changed- added
Input schema / properties / focusAdded value: +{ + "description": "Keyword or section name to target (e.g. 'Auth', 'Config'); returns targeted window (~5000 chars) instead of burning tokens on full document", + "minLength": 1, + "type": "string" +} - added
Input schema / properties / urlsAdded value: +{ + "description": "Batch URLs (1~3 URLs run concurrently, splitting maxChars budget; ideal for comparing search results)", + "items": { + "format": "uri", + "type": "string" + }, + "maxItems": 3, + "minItems": 1, + "type": "array" +} - removed
Input schema / requiredRemoved value: -[ - "url" -]
- Changed
queryDocs4 fields changed- added
Input schema / properties / libraryAdded value: +{ + "description": "Library or package name (e.g. 'next.js', 'react', 'fastapi') or exact Context7 library ID (e.g. '/vercel/next.js', version-pinnable like '/vercel/next.js@v15.1.8')", + "minLength": 1, + "type": "string" +} - changed
Input schema / properties / libraryId / descriptionPrevious value: -"Exact Context7-compatible library ID (e.g. /vercel/next.js, /packages/express; optional version like /vercel/next.js@v15.1.8)"New value: +"Legacy alias for library (e.g. /vercel/next.js)" - changed
Input schema / properties / query / descriptionPrevious value: -"The question or task to get relevant documentation for (optional; defaults to an overview when omitted, e.g. 'how to set up middleware with auth')"New value: +"The question, feature, or task to get relevant documentation for (optional; defaults to an overview when omitted, e.g. 'how to set up middleware with auth')" - changed
Input schema / requiredPrevious value: -[ - "libraryId" -]New value: +[ + "library" +]
- Removed
resolveLibraryId - Changed
search3 fields changed- changed
Input schema / properties / engines / items / descriptionPrevious value: -"Search engine name. Valid values (normalize alias to canonical name): baidu, bing, csdn, duckduckgo, exa, brave, juejin, startpage, sogou"New value: +"Search engine name. Valid values (normalize alias to canonical name): baidu, bing, csdn, duckduckgo, exa, brave, juejin, startpage, sogou, github, stackoverflow, hackernews, arxiv" - added
Input schema / properties / fetchTopAdded value: +{ + "description": "Two-in-one search & fetch: automatically fetch full readable Markdown content for top N results (0~3) in a single turn (up to 4000 chars per article)", + "maximum": 3, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / limit / typePrevious value: -"number"New value: +"integer"
2 tool updates
v1.0.25- Changed
fetchWebContent1 field changed- changed
Input schema / properties / format / descriptionPrevious value: -"Content format: 'markdown' preserves fenced code blocks (with language) and GFM tables — better for technical docs"New value: +"Content format (default: 'markdown' when raw is false): 'markdown' preserves headings, lists, fenced code blocks (with language) and GFM tables — better for technical docs; use 'text' for plain text"
- Changed
search2 fields changed- changed
Input schema / properties / queries / descriptionPrevious value: -"1-4 queries run concurrently and merged (required if 'query' is omitted) — for covering multiple intents in one call (e.g. official docs / Chinese community / English)"New value: +"1-4 queries run concurrently and merged (required if 'query' is omitted) — for covering multiple intents in one call (e.g. official docs / Chinese community / English). Response includes 'cached: true' only when all sub-queries hit the TTL cache." - added
Input schema / properties / verboseAdded value: +{ + "description": "Include verbose diagnostic fields (engineMetrics, full failure hints, and redundant source hostnames); default false to save tokens", + "type": "boolean" +}
1 tool update
v1.0.21- Changed
search2 fields changed- changed
Input schema / properties / queries / descriptionPrevious value: -"1-4 queries run concurrently and merged (dedup by URL) — for covering multiple intents in one call (e.g. official docs / Chinese community / English)"New value: +"1-4 queries run concurrently and merged (required if 'query' is omitted) — for covering multiple intents in one call (e.g. official docs / Chinese community / English)" - changed
Input schema / properties / query / descriptionPrevious value: -"Single search query (use queries for multi-intent fan-out)"New value: +"Single search query (required if 'queries' is omitted; use 'queries' for multi-intent fan-out)"
2 tool updates
v1.0.17- Changed
fetchJuejinArticle1 field changed- added
Input schema / properties / formatAdded value: +{ + "description": "Output format (default: text). 'markdown' keeps fenced code blocks (with language) and GFM tables — recommended for technical posts", + "enum": [ + "text", + "markdown" + ], + "type": "string" +}
- Changed
search2 fields changed- changed
Input schema / properties / minResults / descriptionPrevious value: -"Auto-run additional engines when fewer than this many USABLE results (relevant, non-entry-page) come back (default: server DEFAULT_MIN_RESULTS, usually 5)"New value: +"Auto-run additional engines when fewer than this many USABLE results (relevant, non-entry-page) come back (default: server DEFAULT_MIN_RESULTS, usually 5; max 50 — larger values only burn the search deadline)" - added
Input schema / properties / minResults / maximumAdded value: +50
2 tool updates
v1.0.15- Changed
fetchWebContent1 field changed- added
Input schema / properties / formatAdded value: +{ + "description": "Content format: 'markdown' preserves fenced code blocks (with language) and GFM tables — better for technical docs", + "enum": [ + "text", + "markdown" + ], + "type": "string" +}
- Changed
search8 fields changed- removed
Input schema / properties / engines / defaultRemoved value: -[ - "bing" -] - added
Input schema / properties / engines / descriptionAdded value: +"Search engines to use (default: server default, which may auto-route by query language)" - removed
Input schema / properties / limit / defaultRemoved value: -10 - added
Input schema / properties / limit / descriptionAdded value: +"Max results (default: server DEFAULT_SEARCH_LIMIT, usually 10)" - changed
Input schema / properties / minResults / descriptionPrevious value: -"Auto-run additional engines when fewer than this many results come back (default: disabled)"New value: +"Auto-run additional engines when fewer than this many USABLE results (relevant, non-entry-page) come back (default: server DEFAULT_MIN_RESULTS, usually 5)" - added
Input schema / properties / queriesAdded value: +{ + "description": "1-4 queries run concurrently and merged (dedup by URL) — for covering multiple intents in one call (e.g. official docs / Chinese community / English)", + "items": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "maxItems": 4, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / query / descriptionAdded value: +"Single search query (use queries for multi-intent fan-out)" - removed
Input schema / requiredRemoved value: -[ - "query" -]
7 tool updates
v1.0.11- First observed
fetchCsdnArticle - First observed
fetchGithubReadme - First observed
fetchJuejinArticle - First observed
fetchWebContent - First observed
queryDocs - First observed
resolveLibraryId - First observed
search
TDQS
Scored across 5 tools
Each tool targets a reasonably distinct need: general web search, generic URL/PDF extraction, GitHub-specific artifacts, library documentation lookup, and symbol-level code search. The main friction is between fetchWebContent and fetchGithubContent (both fetch content, and the boundary is only implied by URL type) and between queryDocs and search (docs lookup can fall back to GitHub READMEs). Descriptions largely resolve these, but an agent must read carefully to pick correctly for a github.com URL.
Four of five tools use camelCase verb_noun-ish names (fetchWebContent, fetchGithubContent, queryDocs, codeSymbol), giving a predictable pattern. The lone 'search' breaks the pattern by being a bare verb, a minor deviation rather than a systemic inconsistency.
Five tools is well-scoped for a web search/fetch server; each covers a genuinely different retrieval target (search, generic page, GitHub artifact, docs, symbol). No tool appears redundant or thrown in as filler.
The surface covers the core retrieval lifecycle: discovery (search, queryDocs), retrieval (fetchWebContent, fetchGithubContent), and code navigation (codeSymbol), with pagination and caching handled inside fetchWebContent. Minor gaps exist — no dedicated repo-wide listing/browsing, no explicit cache management or multi-page crawl tool — but agents can work around these via search and URL paging.
Maintenance
Related MCP Connectors
Web search API and MCP server for developers building AI agents.
Docs: https://docs.keenable.ai/mcp-server Keenable is a free, remote MCP server that gives agents access to the web index. Search the web with ranked results and date/site filters, then fetch any indexed page as clean markdown. Works out of the box with no account or API key.
Free web search for AI agents. No API key required. Hosted MCP in active development.
One MCP server for all web data. Scrape, search, crawl, and AI research in one install.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceMCP server providing web search, news search, and X/Twitter search capabilities via HTTP or stdio.-
- AlicenseNot gradedqualityCmaintenanceMCP server for multi-engine web search and web page fetching, supporting parallel search, content extraction, and optional LLM-powered search summarization and deep search.5MIT
- AlicenseAqualityCmaintenanceA self-hosted MCP server for web search with multi-engine support, combining search, URL fetching, and deep research along with proxy routing and deduplication.3MIT
- AlicenseNot gradedqualityAmaintenanceEnables LLMs to run web and academic searches across multiple engines (Baidu, Bing, DuckDuckGo, Tavily, Exa, Doubao and 9 scholarly sources) with local relevance scoring, deduplication and caching, plus SSRF-protected page fetching and PDF parsing, and works without any API key. It plugs into MCP clients like Claude Code, Cursor and Qwen Code, or can be embedded as a Go module.29MIT