fast-web-search-mcp
v0.1.0
Published
A fast, lightweight, local MCP server for free web search and webpage extraction.
Maintainers
Readme
Why fast-web-search-mcp?
Most MCP search servers require paid API keys, Docker containers, or headless browsers. fast-web-search-mcp provides free, fast, local web search with zero external dependencies beyond Node.js.
| Capability | fast-web-search-mcp | Paid APIs | Headless Browser | |------------|:-------------------:|:---------:|:----------------:| | Free to use | ✅ | ❌ | ✅ | | No API key | ✅ | ❌ | ✅ | | No Docker | ✅ | ✅ | ❌ | | Sub-second response | ✅ | ✅ | ❌ | | SSRF protection | ✅ | N/A | ❌ | | Result caching | ✅ | ❌ | ❌ | | Multiple providers | ✅ | ❌ | ❌ | | Works on Windows | ✅ | ✅ | ⚠️ |
Features
- Web Search — DuckDuckGo primary with Bing fallback and Wikipedia reference lookup
- News Search — Date-sensitive news results with publication metadata
- Page Fetching — Readability extraction with Markdown or plain text output
- Combined Research — Search, rank, and fetch top results in one call
- Intelligent Caching — In-memory LRU cache with configurable TTL
- Request Coalescing — Deduplicates concurrent identical searches
- Rate Limiting — Per-provider throttling with exponential backoff
- Result Deduplication — URL canonicalization and title similarity matching
- SSRF Protection — Blocks private IPs, localhost, cloud metadata endpoints
- Provider Fallback — Automatic failover when a provider is unavailable
- Zero Runtime Dependencies — No Python, no Docker, no browser engine
Quick Start
Install
# Global install (recommended)
npm install -g fast-web-search-mcp
# Or run directly without installing
npx -y fast-web-search-mcpRequirements
| Dependency | Version | |------------|---------| | Node.js | ≥ 22.0 | | npm | ≥ 10.0 |
Verify Installation
# Confirm the binary is available
fast-web-search-mcp --version
# Or run directly
npx fast-web-search-mcp --versionIntegration
Add fast-web-search-mcp to your AI coding assistant. Select your platform below:
Project scope — add to .claude/settings.json in your project root:
{
"mcpServers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"env": {
"FWSMCP_LOG_LEVEL": "warn",
"FWSMCP_REDACT_QUERIES": "true"
}
}
}
}User scope — add to ~/.claude/settings.json for global availability:
{
"mcpServers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"env": {
"FWSMCP_DEFAULT_REGION": "us-en",
"FWSMCP_DEFAULT_LANGUAGE": "en",
"FWSMCP_REDACT_QUERIES": "true"
}
}
}
}Or use the CLI:
claude mcp add fast-web-search -- fast-web-search-mcpWindows users: Wrap the command with
cmd /c:claude mcp add fast-web-search -- cmd /c fast-web-search-mcp
Add to your configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"env": {
"FWSMCP_LOG_LEVEL": "warn",
"FWSMCP_DEFAULT_REGION": "us-en",
"FWSMCP_DEFAULT_LANGUAGE": "en"
}
}
}
}Restart Claude Desktop after saving.
Add to ~/.codex/config.toml (global) or .codex/config.toml (project):
[mcp_servers.fast-web-search]
command = "fast-web-search-mcp"
env = { FWSMCP_LOG_LEVEL = "warn", FWSMCP_REDACT_QUERIES = "true" }Or use the CLI:
codex mcp add fast-web-search -- fast-web-search-mcpAdd to ~/.config/opencode/opencode.json:
{
"mcp": {
"fast-web-search": {
"type": "local",
"command": ["fast-web-search-mcp"],
"enabled": true,
"environment": {
"FWSMCP_DEFAULT_REGION": "us-en",
"FWSMCP_DEFAULT_LANGUAGE": "en",
"FWSMCP_REDACT_QUERIES": "true",
"FWSMCP_SAFE_SEARCH": "moderate"
}
}
}
}Antigravity IDE:
- Click ... in the agent side panel → MCP Servers → Manage MCP Servers → View raw config
- Add the server to the opened
mcp_config.jsonfile
Antigravity CLI:
Type /mcp in the prompt panel to open the interactive MCP Manager.
Configuration file:
- Global:
~/.gemini/config/mcp_config.json - Project:
.agents/mcp_config.json
{
"mcpServers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"args": [],
"env": {
"FWSMCP_LOG_LEVEL": "warn",
"FWSMCP_REDACT_QUERIES": "true"
}
}
}
}Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"env": {
"FWSMCP_LOG_LEVEL": "warn"
}
}
}
}Add to .vscode/mcp.json in your workspace:
{
"servers": {
"fast-web-search": {
"command": "fast-web-search-mcp",
"env": {
"FWSMCP_LOG_LEVEL": "warn"
}
}
}
}MCP Tools
| Tool | Description | Use Case |
|------|-------------|----------|
| web_search | Search the general web via DuckDuckGo + Bing fallback | General research, fact-finding |
| news_search | Search for recent news articles with publication dates | Current events, monitoring |
| fetch_url | Fetch a URL and extract clean Markdown or plain text | Article reading, documentation |
| search_and_fetch | Combined search + fetch workflow with character budget | Deep research, analysis |
| diagnostics | Return non-sensitive server diagnostics | Troubleshooting (disabled by default) |
Tool Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| query | string | required | Search query (1-500 characters) |
| max_results | integer | 10 | Maximum results to return (1-25) |
| strategy | string | "fallback" | Provider strategy: fallback or merge |
| region | string | "us-en" | Region code for localized results |
| language | string | "en" | Language code |
| time_range | string | "any" | Time filter: day, week, month, year, any |
| safe_search | string | "moderate" | Content filter: off, moderate, strict |
| include_domains | array | — | Restrict results to these domains |
| exclude_domains | array | — | Exclude results from these domains |
| deduplicate | boolean | true | Remove duplicate results by URL |
| timeout_ms | integer | — | Operation timeout (1000-60000 ms) |
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| query | string | required | News search query (1-500 characters) |
| max_results | integer | 10 | Maximum results (1-25) |
| time_range | string | "week" | Time filter: day, week, month |
| region | string | "us-en" | Region code |
| language | string | "en" | Language code |
| include_domains | array | — | Restrict to these domains |
| exclude_domains | array | — | Exclude these domains |
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| url | string | required | HTTP/HTTPS URL to fetch |
| output | string | "markdown" | Output format: markdown or text |
| max_characters | integer | 20000 | Maximum characters (100-100000) |
| include_metadata | boolean | true | Include page metadata |
| include_links | boolean | false | Include extracted links |
| timeout_ms | integer | — | Fetch timeout (1000-60000 ms) |
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| query | string | required | Search query (1-500 characters) |
| search_results | integer | 8 | Number of results to consider (1-20) |
| fetch_results | integer | 3 | Number of pages to fetch (1-5) |
| max_characters_per_page | integer | 20000 | Character limit per page |
| total_character_budget | integer | 100000 | Total character budget |
| timeout_ms | integer | — | Total timeout (5000-120000 ms) |
Configuration
All configuration is environment-based. No config files required.
| Variable | Default | Description |
|----------|---------|-------------|
| FWSMCP_LOG_LEVEL | warn | Log verbosity: debug, info, warn, error |
| FWSMCP_LOG_FORMAT | text | Log format: text or json |
| FWSMCP_REDACT_QUERIES | true | Redact search queries in logs |
| FWSMCP_DEFAULT_REGION | us-en | Default region for searches |
| FWSMCP_DEFAULT_LANGUAGE | en | Default language for searches |
| FWSMCP_SAFE_SEARCH | moderate | Safe search level: off, moderate, strict |
| FWSMCP_PROVIDERS | duckduckgo,bing,wikipedia | Comma-separated provider list |
| FWSMCP_PROVIDER_STRATEGY | fallback | Strategy: fallback (sequential) or merge (parallel) |
| FWSMCP_SEARCH_TIMEOUT_MS | 20000 | Search operation timeout (ms) |
| FWSMCP_PROVIDER_TIMEOUT_MS | 10000 | Per-provider timeout (ms) |
| FWSMCP_FETCH_TIMEOUT_MS | 15000 | Page fetch timeout (ms) |
| FWSMCP_RESEARCH_TIMEOUT_MS | 45000 | search_and_fetch total timeout (ms) |
| FWSMCP_MAX_SEARCH_RESULTS | 25 | Maximum results per search |
| FWSMCP_MAX_FETCH_CHARACTERS | 100000 | Maximum characters per fetch |
| FWSMCP_CACHE_ENABLED | true | Enable in-memory result cache |
| FWSMCP_CACHE_MAX_ENTRIES | 250 | Maximum cache entries |
| FWSMCP_SEARCH_CACHE_TTL_MS | 900000 | Search cache TTL (15 min) |
| FWSMCP_NEWS_CACHE_TTL_MS | 300000 | News cache TTL (5 min) |
| FWSMCP_PAGE_CACHE_TTL_MS | 1800000 | Page cache TTL (30 min) |
| FWSMCP_SEARXNG_URL | — | Optional SearXNG instance URL |
| FWSMCP_DIAGNOSTICS_ENABLED | false | Enable diagnostics tool |
| FWSMCP_GLOBAL_EXCLUDE_DOMAINS | — | Comma-separated domains to always exclude |
| FWSMCP_CONFIG_FILE | — | Path to optional JSON config file |
Optional JSON Configuration
For complex setups, point FWSMCP_CONFIG_FILE to a JSON file:
{
"defaults": {
"region": "us-en",
"language": "en",
"safeSearch": "moderate"
},
"providers": {
"order": ["duckduckgo", "bing", "wikipedia"],
"strategy": "fallback"
},
"cache": {
"enabled": true,
"maxEntries": 250,
"searchTtlMs": 900000
},
"limits": {
"maxSearchResults": 25,
"maxFetchCharacters": 100000
},
"privacy": {
"redactQueriesInLogs": true
}
}Priority: Environment variables > JSON config file > defaults.
Architecture
┌─────────────────────────────────────────────────────────┐
│ MCP Client │
│ (Claude Code / Codex / OpenCode) │
└─────────────────────┬───────────────────────────────────┘
│ stdio (JSON-RPC)
▼
┌─────────────────────────────────────────────────────────┐
│ fast-web-search-mcp │
│ │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌────────┐ │
│ │ Tools │ │ Cache │ │ Coalescer │ │ Limiter│ │
│ │ 5 tools │ │ LRU+TTL │ │ Dedup │ │ Per- │ │
│ │ │ │ │ │ inflight │ │ provider│ │
│ └────┬─────┘ └──────────┘ └───────────┘ └────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Orchestrator Pipeline │ │
│ │ Validate → Cache → Coalesce → Select → Execute │ │
│ │ → Normalize → Filter → Dedup → Rank → Return │ │
│ └─────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
│ │ DuckDuck │ │ Bing │ │ Wikipedia │ │
│ │ Go │ │ │ │ REST API │ │
│ │ (HTML) │ │ (HTML) │ │ │ │
│ └─────────┘ └──────────┘ └────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ HTTP Client (Node fetch) │ │
│ │ Timeout · Body Limit · Redirect · SSRF Block │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘Security
Protections
| Threat | Mitigation |
|--------|------------|
| SSRF / private network access | Blocks localhost, RFC1918, link-local, cloud metadata IPs |
| Credential leakage | Rejects URLs with embedded username/password |
| Query exposure | Logs redact queries by default (FWSMCP_REDACT_QUERIES=true) |
| Prompt injection via fetched content | Includes untrusted content warning in all fetch results |
| Uncontrolled resource consumption | Body size limits, timeout enforcement, character budgets |
| Process isolation | stdio transport only — no network listener, no HTTP endpoint |
Disabled by Default
- Diagnostics tool — must be explicitly enabled via
FWSMCP_DIAGNOSTICS_ENABLED=true - SearXNG integration — requires explicit
FWSMCP_SEARXNG_URLconfiguration
See docs/SECURITY.md for the full threat model.
Development
git clone https://github.com/your-org/fast-web-search-mcp.git
cd fast-web-search-mcp
npm ci
npm run build
npm testAvailable Scripts
| Script | Description |
|--------|-------------|
| npm run build | Production build with tsup |
| npm run dev | Development mode with tsx |
| npm test | Run full test suite (177 tests) |
| npm run test:unit | Unit tests only |
| npm run test:security | Security tests only |
| npm run typecheck | TypeScript type checking |
| npm run lint | ESLint |
| npm run format | Prettier formatting |
Project Structure
fast-web-search-mcp/
├── src/
│ ├── cache/ # LRU cache, cache key generation
│ ├── config/ # Defaults, env parsing, config loader
│ ├── fetch/ # URL validation, content extraction, Markdown
│ ├── http/ # HTTP client, errors, headers
│ ├── observability/ # Structured stderr logger
│ ├── providers/ # DuckDuckGo, Bing, Wikipedia, SearXNG
│ ├── schemas/ # Zod validation schemas
│ ├── search/ # Orchestrator, coalescer, rate limiter, dedup
│ ├── server/ # MCP server factory, tool registration
│ ├── tools/ # 5 MCP tool implementations
│ ├── utils/ # Domain, text, date, async utilities
│ └── index.ts # Entry point
├── test/
│ ├── fixtures/ # HTML fixtures for parser tests
│ └── unit/ # Unit tests (177 tests)
├── docs/ # Documentation and ADRs
└── examples/ # Config examplesDocumentation
| Document | Description | |----------|-------------| | Configuration Reference | All environment variables and options | | Provider Guide | Provider details, strategy, and SearXNG setup | | Security Model | Threat model and protections | | Troubleshooting | Common issues and debugging | | OpenCode Integration | OpenCode-specific setup guide | | Changelog | Version history |
Roadmap
- [ ] SQLite persistent cache
- [ ] Brave Search provider
- [ ] Image search support
- [ ] Streamable HTTP transport
- [ ] Result scoring customization
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'feat: add amazing feature') - Push to branch (
git push origin feature/amazing) - Open a Pull Request
License
MIT License. See LICENSE for details.
