Content
# simple-web-search-mcp
A zero-config MCP server that gives any LLM the ability to **search the web** and **read web pages**. No API keys, no browser install, no config files — just run it.
```bash
npx simple-web-search-mcp --log
```
## Features
- **Two tools**: `search_web` (search the internet) and `read_url` (read any web page)
- **9 search engines** — tries engines in configurable order with automatic fallthrough: DuckDuckGo, Brave, Bing, Google, Ecosia, Startpage, Qwant, Yahoo, Yandex
- **`/search` and `/read` prompts** — slash commands that work in Claude Desktop and other MCP clients
- **Rate limiting** — per-host throttling with automatic retry + exponential backoff on 429/503
- **Secure by default** — input validation, URL scheme enforcement (http/https only), HTML stripping, control character removal, markdown injection escaping, content fencing
- **Zero config by default** — works out of the box, fully customizable via CLI flags, env vars, or config file
- **Optional file logging** — use `--log` to write daily log files to `~/.simple-web-search-mcp/logs/`
- **No API keys** — all engines work via plain HTTP
- **No browser install** — pure HTTP with Node.js built-in `fetch` + cheerio
- **Clean markdown output** — structured results that LLMs can actually use
## Setup
### Cursor
Add to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"web-search": {
"command": "npx",
"args": ["-y", "simple-web-search-mcp", "--log"]
}
}
}
```
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"web-search": {
"command": "npx",
"args": ["-y", "simple-web-search-mcp", "--log"]
}
}
}
```
### With custom options
```json
{
"mcpServers": {
"web-search": {
"command": "npx",
"args": [
"-y", "simple-web-search-mcp",
"--log",
"--engines", "brave,duckduckgo",
"--max-results", "5"
]
}
}
}
```
## Tools
### `search_web`
Search the internet and get results as markdown. Tries configured engines in order and falls through on failure. You can request a specific engine to be tried first per call. By default results include `[1]`, `[2]`, … as markdown links next to each result’s title and snippet (linking to the source URL), plus a **References** section at the bottom; disable with config `citations: false` or `--no-cite`, or force on with `--cite`.
| Parameter | Type | Description |
| ------------------ | ------- | --------------------------------------------------------------------------- |
| `query` | string | The search query |
| `prefer_engine` | string | Optional. Engine to try first (e.g. `duckduckgo`, `brave`, `bing`). |
| `include_citations` | boolean | Optional. If set, overrides server config for this call (include/omit References). |
| `think` | boolean | Optional. If true, query multiple engines, merge by URL, and return compiled research (first 3 engines). |
### `read_url`
Fetch a web page and return its content as clean, readable markdown. Strips navigation, ads, and boilerplate.
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------- |
| `url` | string | The URL to fetch (e.g. `https://...`) |
## Slash Commands (Prompts)
MCP clients that support prompts will show these as `/` commands:
| Command | Description |
| ------- | -------------------------------------------------------------------- |
| `/search <input>` | Search the web. Type your query; optionally end with flags: `-engine`, `-cite`, `-no-cite`, `-think` (e.g. `weather --brave`, `topic --think` for in-depth research). Prompt flags override MCP/server config. |
| `/read <url>` | Read a web page and summarize its content |
**Flags for /search** — use one or two hyphens at the **end** of your query. These override server config for that request:
- **Engine:** `-brave`, `--dd`, etc. (try that engine first)
- **Citations:** `--cite` (include References), `--no-cite` (omit References)
- **Think (research):** `--think` or `-think` — runs the same query on multiple engines (first 3), merges results by URL, and returns compiled research with a "Found via" column. Use for in-depth research.
Every search response starts with **via [Search Engine] [emoticon]** (e.g. `via DuckDuckGo :D`); every read response starts with **via [URL] [:]**.
When citations are enabled, the model is instructed to include **inline citations** [1], [2], etc. in the body of its answer (next to the sentence or fact from that source) and the **References** section at the bottom; each number matches the same source (Wikipedia-style).
**If you don't see "via ..." or citation links:** the model did not call the `search_web` tool (e.g. "Model failed to generate a tool call"). Use a model that supports tool/function calling, or try the query again. The server always returns "via ..." and citations when the tool is actually invoked.
| Flag (full) | Abbrev | Engine |
| ----------- | ------ | ------ |
| `--duckduckgo` | `--dd` | DuckDuckGo |
| `--brave` | `--b` | Brave |
| `--bing` | `--bi` | Bing |
| `--google` | `--g` | Google |
| `--ecosia` | `--ec` | Ecosia |
| `--startpage` | `--sp` | Startpage |
| `--qwant` | `--qw` | Qwant |
| `--yahoo` | `--yh` | Yahoo |
| `--yandex` | `--ya` | Yandex |
## Search Engines
Engines are tried in order. If the first engine fails or returns no results, the next one is tried automatically.
| ID | Name | Notes |
| ------------ | ------------- | --------------------------------------------- |
| `duckduckgo` | DuckDuckGo | Default first choice. HTML endpoint, very reliable |
| `brave` | Brave Search | Good privacy-focused alternative |
| `bing` | Bing | Microsoft's search engine |
| `google` | Google | May be less reliable with plain HTTP (captchas) |
| `ecosia` | Ecosia | Bing-powered, plants trees with ad revenue |
| `startpage` | Startpage | Google results with privacy, may require cookies |
| `qwant` | Qwant | European privacy-focused engine (uses Lite HTML) |
| `yahoo` | Yahoo | Classic search engine, unwraps redirect URLs |
| `yandex` | Yandex | Russian search engine with English support |
Default order: `duckduckgo, brave, bing, google, ecosia, startpage, qwant, yahoo, yandex`
Customize with `--engines brave,duckduckgo` (CLI), `WEB_SEARCH_ENGINES=brave,duckduckgo` (env), or `"engines": ["brave", "duckduckgo"]` (config file).
Each engine has its own parser in `src/engines/` — easy to maintain, fix, or add new ones.
## Configuration
Everything works out of the box. Override any setting through three layers (highest priority first):
### 1. CLI flags
```bash
npx simple-web-search-mcp --engines brave,duckduckgo --max-results 5 --timeout 20000
```
| Flag | Default | Description |
| ----------------------- | -------- | ------------------------------------------- |
| `--log` | off | Write logs to `~/.simple-web-search-mcp/logs/` (daily files `YYYY-MM-DD.log`) |
| `--cite` | — | Include Wikipedia-style References in search results (overrides config) |
| `--no-cite` | — | Omit References section in search results (overrides config) |
| `--engines <list>` | all 9 engines | Comma-separated engine order |
| `--max-results <n>` | `10` | Max search results to return |
| `--timeout <ms>` | `15000` | HTTP request timeout |
| `--rate-limit <ms>` | `1000` | Min delay between requests to same host |
| `--max-retries <n>` | `3` | Retry attempts on 429/503/network errors |
| `--max-content-length <n>` | `12000` | Max chars of page content to return |
| `--user-agent <string>` | Chrome UA | Custom User-Agent header |
| `--config <path>` | — | Path to a JSON config file |
### 2. Environment variables
```bash
WEB_SEARCH_ENGINES=brave,duckduckgo WEB_SEARCH_MAX_RESULTS=5 npx simple-web-search-mcp
```
| Variable | Maps to |
| ------------------------------- | ------------------ |
| `WEB_SEARCH_LOG` | Enable file logging (set to `1`, `true`, or `yes`) |
| `WEB_SEARCH_CITATIONS` | `citations` — `1`/`true`/`yes` to include References, `0`/`false`/`no` to omit |
| `WEB_SEARCH_ENGINES` | `engines` |
| `WEB_SEARCH_MAX_RESULTS` | `maxResults` |
| `WEB_SEARCH_TIMEOUT` | `timeoutMs` |
| `WEB_SEARCH_RATE_LIMIT` | `rateLimitMs` |
| `WEB_SEARCH_MAX_RETRIES` | `maxRetries` |
| `WEB_SEARCH_MAX_CONTENT_LENGTH` | `maxContentLength` |
| `WEB_SEARCH_USER_AGENT` | `userAgent` |
| `WEB_SEARCH_CONFIG` | Path to config file |
### 3. Config file
The server looks for a JSON config file in this order:
1. `--config <path>` (CLI flag)
2. `$WEB_SEARCH_CONFIG` (env var)
3. `./.simple-web-search.json` (project-local)
4. `~/.simple-web-search/config.json` (user-level)
Example config file:
```json
{
"engines": ["brave", "duckduckgo", "bing"],
"maxResults": 5,
"timeoutMs": 20000,
"rateLimitMs": 2000,
"maxRetries": 2,
"maxContentLength": 8000,
"citations": true
}
```
Set `"citations": false` to omit the References section in search results. Override at runtime with `--cite` or `--no-cite`.
## Security
All input and output is sanitized:
- **Input validation** — queries are trimmed, length-limited (400 chars), and stripped of control characters
- **URL validation** — only `http:` and `https:` schemes are allowed; `javascript:`, `data:`, `file:`, etc. are blocked
- **HTML stripping** — all HTML tags are removed from extracted text before output
- **Control character removal** — binary/control chars are stripped from all output
- **Markdown injection escaping** — special characters in scraped titles are escaped so they can't break markdown link syntax
- **Content fencing** — scraped page content is clearly delimited with visual separators so LLMs can distinguish it from instructions
- **Output is plain text** — the server only returns markdown-formatted text, never raw HTML or executable content
## Rate Limiting
Built-in rate limiting protects both you and the search engines:
- **Per-host throttling** — enforces a minimum delay between requests to the same hostname (default 1s)
- **Automatic retry** — retries on HTTP 429 (Too Many Requests) and 503 (Service Unavailable)
- **Exponential backoff** — 1s, 2s, 4s, 8s (capped at 15s) between retries
- **Respects `Retry-After`** — honors the server's Retry-After header when present
- **Configurable** — adjust `rateLimitMs` and `maxRetries` to your needs
## Project Structure
```
src/
index.ts MCP server — tools, prompts, startup
config.ts Config loading (CLI > env > file > defaults)
logger.ts Optional file logging (~/.simple-web-search-mcp/logs, enabled with --log)
parse-search-input.ts Parses /search input for trailing --engine / --abbrev flags
search.ts SearchClient — orchestrates engines + page reading
sanitize.ts Input/output sanitization
rate-limiter.ts Per-host throttling + retry with backoff
engines/
types.ts SearchEngine interface
index.ts Engine registry + default order
duckduckgo.ts DuckDuckGo HTML parser
brave.ts Brave Search parser
bing.ts Bing parser
google.ts Google parser
ecosia.ts Ecosia parser
startpage.ts Startpage parser
qwant.ts Qwant Lite parser
yahoo.ts Yahoo parser
yandex.ts Yandex parser
tests/
sanitize.test.ts Sanitization unit tests
engines.test.ts Engine parser unit tests (all 9 engines)
config.test.ts Config loading unit tests
rate-limiter.test.ts Rate limiter unit tests
fixtures/ Mock HTML for each engine
```
## Requirements
- Node.js 18 or later
## Development
```bash
git clone <repo-url>
cd simple-web-search-mcp
npm install
npm run build
npm start
```
### Scripts
| Script | Description |
| --- | --- |
| `npm test` | Run unit tests (vitest) |
| `npm run test:watch` | Run tests in watch mode |
| `npm run lint` | Lint with Biome |
| `npm run lint:fix` | Auto-fix lint issues |
| `npm run format` | Format with Biome |
| `npm run docs` | Generate API docs (TypeDoc) |
| `npm run build` | Compile TypeScript |
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available