free-search-mcp
free-search-mcp is a local-first, no-API-key MCP server that gives LLMs web search, content fetching, and document reading capabilities.
search— Multi-engine web search (DuckDuckGo, Mojeek, Startpage, SearX, etc.) with RRF-merged, deduplicated results; supportsfreshness,include_domains,exclude_domains,category(news/pdf/github/paper/forum/blog),include_text, andexclude_textfiltersresearch— One-shot tool that searches, fetches top N results, and returns a citable Markdown brief with source index in a single callfetch— Retrieve a single URL as reader-mode Markdown (with author, publish date, sitename); uses HTTP fast path with Playwright browser fallbackfetch_batch— Concurrently fetch multiple URLs in parallel with per-URL error handlingread_doc— Parse local or remote PDF, DOCX, HTML, TXT, or Markdown files into Markdown, with character-offset paginationextract_structured— Pull structured data (JSON-LD, OpenGraph, Twitter cards, microdata) from web pagescache_search— Full-text search (SQLite FTS5) over previously fetched pages, supporting AND/OR/NOT and phrase queriesengines— List all available search engine names for dynamic selection
Key properties: No API keys required; LLM-optimized Markdown output with token estimates and smart truncation; anti-detection via curl_cffi Chrome fingerprints and Playwright stealth; integrates with Claude Desktop, Cursor, Continue, and other MCP clients.
Included in the 'paper' category filter, restricting search results to arXiv (along with other academic domains).
Allows web search using Baidu as an opt-in engine, but may face intermittent challenges for headless clients.
Allows web search using Brave Search as an opt-in engine, though may require browser fallback to handle captchas.
Allows web search using DuckDuckGo, one of the default multi-engine search sources without API key.
Allows web search using Mojeek, one of the default multi-engine search sources without API key.
Allows web search using Startpage, one of the default multi-engine search sources without API key.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@free-search-mcpresearch Quantum computing breakthroughs, depth=2"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
free-search-mcp
A local-first, no-API-key Model Context Protocol server that gives any LLM (Claude, GPT, local Ollama, …) the ability to search the web, fetch and clean up pages, and read documents — without you signing up for a single search API.
It bundles together the best ideas from a handful of open-source MCPs into one Python package, and adds the LLM-ergonomics and reliability work they were each missing.
research("how does reciprocal rank fusion work", depth=3)
↓
# Research brief: how does reciprocal rank fusion work
_engines: duckduckgo, mojeek, searx · sources: 3 · ~3,400 tokens_
## Sources
- [1] Reciprocal rank fusion | Elasticsearch Reference — <https://…>
- [2] Hybrid Search Scoring (RRF) | Microsoft Learn — <https://…>
- [3] RRF explained in 4 mins — Medium — <https://…>
## Documents
…full Markdown bodies of each page, ready for the LLM to read…One tool call. Three sources. No API key. No OPENAI_API_KEY-but-for-search
shakedown.
Why this exists
Existing search MCPs each do one thing well, but you usually want all of it:
Multi-engine | No API key | Smart fallback | PDF/DOCX | FTS5 cache | Filters | Trafilatura | LLM-tuned | |
| ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ~ |
| ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ~ |
| ✓ | ✓ | ~ | ✗ | ✗ | ✗ | ✗ | ~ |
| ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ~ |
free-search-mcp | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
"LLM-tuned" here means: Markdown-first output, token estimates, smart
truncation at paragraph boundaries, "Best for / Not for / Returns / Common
mistakes" docstrings the model uses to pick the right tool, actionable
error hints, MCP prompts and resource templates, and a one-shot
research() that collapses search→fetch→fetch→fetch into a single turn.
"Trafilatura" means we extract main content using
trafilatura — winner of the
Bevendorff 2023 ROUGE benchmark (~0.85 vs ~0.55 for naive boilerplate
stripping). Each fetched page also returns author, published_date, and
sitename for free.
"Filters" means search/research accept freshness, include_domains,
exclude_domains, category (news/pdf/github/paper/forum/blog),
include_text, exclude_text.
Anti-detection & resilience
HTTP fast path uses
curl_cffiwith a real Chrome 131 JA3/JA4 + HTTP/2 fingerprint, fixing the DDG "anomaly 202" rate-limit response that vanilla httpx triggered.Playwright fallback uses
launch_persistent_context(cookies survive restarts on disk), prefers a real installed Chrome (channel="chrome"), drops the--no-sandboxfingerprint marker on macOS, and randomizes the viewport per session.Result dedup is title-fuzzy + host-canonical (rapidfuzz
token_set_ratio >= 92, host normalized forwww./m./amp.and country-TLD collapse), catchingbbc.co.ukvsbbc.comduplicates that URL-only dedup misses.searchincludes an honest extractivelead_snippet— picks the top-3 result whose snippet contains ≥2 query terms and is ≥80 chars; rendered as> **Lead:** According to {host}: …. No LLM call. Returns nothing if no snippet qualifies (no fake answer).
⚠️ We deliberately do not attempt to defeat proof-of-work captchas on Bing or Brave — that crosses the ToS line. When those engines challenge us, we fall back to other engines instead.
Tools (9)
Tool | Description |
| Parallel multi-engine search, RRF-merged, title-fuzzy + host-canonical deduped, with optional extractive |
| One-shot: search + fetch top N + return Markdown brief |
| Concurrent fetch of 2-5 URLs, side-by-side excerpts keyed by question |
| Fetch a page, return reader-mode Markdown (trafilatura, with author/date/sitename) |
| Concurrent multi-URL fetch |
| Parse PDF / DOCX / HTML / TXT / MD with pagination |
| Pull JSON-LD / OpenGraph / Twitter cards / microdata via extruct |
| FTS5 search across previously fetched pages |
| List engine names available to |
Plus 4 MCP prompts (Research thoroughly, Fact-check claim,
Compare sources, News brief) and 2 resource templates
(cache://page/{url}, cache://search/{query_hash}).
Filters (search / research)
Param | Values | Effect |
|
| Only results from the last N |
|
| Restrict to these domains |
|
| Remove these |
|
| Content-type shortcut (paper = arxiv/acm/ieee/…, forum = reddit/HN/SE, etc.) |
|
| Substring required in title/snippet |
|
| Substring forbidden |
|
| Override the 7-day default cache TTL on this call |
All tools default to format="markdown" — readable, ~40% fewer tokens than
JSON, with provenance and a token-budget header. Pass format="json" for
structured access.
Tool annotations
Every tool ships correct readOnlyHint, idempotentHint, and
openWorldHint annotations so MCP clients can label them and gate
elevated actions.
Engines
Default set (all-HTTP, no browser, ~2x faster than the old default
that included Startpage):
duckduckgo, mojeek, googlenews.
Opt-in:
startpage— browser-rendered (~5-10s/query); good for hard-to-reach results that the HTTP defaults miss.brave,bing,baidu— intermittent challenges to headless clients.searx— meta-search proxy via public SearXNG instances; included for completeness but most public instances are slow/unreliable in 2026.
Brave/Bing/Baidu all gate headless browsers after a handful of calls (PoW CAPTCHAs, "something went wrong" pages, redirect wrappers). Pass
engines=["brave"]etc. only when the defaults can't find what you need.
Sparse-result diagnostics
When filters drop results so aggressively that ≤3 are returned, the
response includes filter_diagnostics so the LLM knows which knob to
relax. Example for category="forum" + exclude_text="beginner":
⚠️ **Filter diagnostics** (results were sparse)
Raw results: 20 across 3 engines → 0 after filters.
Top drops: category_forum (20).
Hint: Filters dropped 20 of 20 raw results. Most were excluded by
category=forum. Try widening or removing one filter.Install
git clone https://github.com/ymylive/free-search-mcp.git
cd free-search-mcp
uv sync
uv run playwright install chromiumRun as a stand-alone server (stdio transport):
uv run search-mcpRun live tests (hits the real web — set the env var):
SEARCH_MCP_TEST_NETWORK=1 uv run pytest -vOffline tests run by default and don't touch the network.
Wire into Claude Desktop
Add this to ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS) or the equivalent on your platform:
{
"mcpServers": {
"search": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/free-search-mcp", "run", "search-mcp"]
}
}
}Restart Claude Desktop. The seven tools above will appear in the tool drawer.
Wire into other clients
The server speaks plain MCP over stdio. Anything that supports MCP works:
Claude Code (
claude mcp add search uv --directory /…/free-search-mcp run search-mcp)Cursor / Continue / Cline (use the JSON snippet above)
Custom Python / TypeScript clients via the official MCP SDK
Configuration
All settings can be overridden by environment variables prefixed with
SEARCH_MCP_:
Var | Default | Meaning |
|
| JSON list |
|
| |
|
| per engine |
|
| shared |
|
| |
|
| 7 days |
|
|
|
|
| |
|
| concurrent pages |
|
| per result truncation |
Architecture
┌─────────────────────────────────────────────────────┐
│ FastMCP server (stdio) │
│ tools: search / research / fetch / fetch_batch / │
│ read_doc / cache_search / engines │
└────────────┬────────────────────────────────────────┘
│
┌────────────▼────────────┐ ┌────────────────────────┐
│ aggregator │ │ fetcher │
│ - parallel engines │ │ - httpx fast path │
│ - reciprocal rank │ │ - playwright fallback │
│ fusion │ │ - markdownify │
│ - search cache (FTS5) │ │ - page cache (FTS5) │
└────┬────────────────────┘ └────────────┬───────────┘
│ │
┌────▼─────────────────┐ ┌──────────────▼─────────────┐
│ engines/ │ │ browser pool │
│ duckduckgo.py │ │ - persistent context │
│ mojeek.py │ │ - stealth init script │
│ searx.py │ │ - shared cookies │
│ startpage.py (opt) │ │ - semaphore-bounded pages│
│ brave.py (opt) │ └────────────────────────────┘
│ bing.py (opt) │
│ baidu.py (opt) │
└──────────────────────┘
┌────────────────────────────┐ ┌──────────────────┐
│ documents/ │ │ ratelimit │
│ pypdf, python-docx, │ │ token bucket │
│ markdownify │ │ per engine │
└────────────────────────────┘ └──────────────────┘
┌────────────────────────────┐ ┌──────────────────┐
│ formatting │ │ research │
│ token estimate │ │ composed │
│ smart truncation │ │ workflow │
│ markdown renderers │ │ │
└────────────────────────────┘ └──────────────────┘Engine adapter pattern
Each engine in src/search_mcp/engines/ implements:
class Engine:
name: str
needs_browser: bool # Force Playwright?
wait_selector: str | None # CSS to wait for in browser mode
def build_url(self, query: str, max_results: int) -> str: ...
def parse(self, html: str) -> list[SearchResult]: ...The base class handles transport (httpx → Playwright fallback), rate limiting, and the case where HTTP returns a captcha shell instead of results (auto-retries via the browser).
Credits
This project stands on the shoulders of:
mrkrsl/web-search-mcp— smart httpx-then-Playwright fetch strategy, multi-engine fallback chainAas-ee/open-webSearch— multi-engine breadth (Bing/DDG/Baidu/Brave/Startpage)VincentKaufmann/noapi-google-search-mcp— anti-detection patterns (navigator.webdriver, UA, cookies), SQLite FTS5 cache idea, multi-formatread_documentnickclyde/duckduckgo-mcp-server— per-engine rate limiting, LLM-friendly content cleanupMojeek — independent search index that doesn't gate on User-Agent
License
MIT — see LICENSE.
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ymylive/free-search-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server