keyless-web-mcp
This server lets MCP clients search the web without an API key and fetch readable content from web pages.
Run keyless web searches across configurable engines such as Bing, DuckDuckGo, Google News RSS, and Wikipedia, with 1–20 results.
Receive search results as JSON with title, URL, snippet, engine, and optional source/date fields for news hits, deduplicated by URL.
Fetch a single http(s) URL and extract its readable main text, or pretty-print JSON endpoints.
Truncate fetched content with max_chars and optionally list up to 30 page links for follow-up reading.
Get failures back as actionable error[KEYLESS_...] text with hints instead of exceptions.
Provides web search through DuckDuckGo's HTML endpoint, returning merged search results with titles, URLs, snippets, and engine attribution. DuckDuckGo is the default first search engine.
Provides news search through Google News RSS, returning news hits with titles, URLs, snippets, source names, and publication dates. It is the default second search engine.
Provides encyclopedic search through Wikipedia's public API, returning stable baseline results with titles, URLs, and snippets. It is the default third search engine.
Click on "Deploy 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., "@keyless-web-mcp search the web for recent news about AI regulation"
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.
keyless-web-mcp
Web search and page reading for MCP clients — no API key, no account, no configuration.
keyless-web-mcp is a small Model Context Protocol server that
gives an agent two tools:
Tool | What it does |
| Queries several keyless backends in parallel (Google, Firecrawl, DuckDuckGo and Google News by default), interleaves the hits and returns JSON |
| Fetches one URL and returns its readable main text (or pretty-printed JSON) |
Both run over stdio, start in well under a second, and work the moment the process starts. There is nothing to sign up for and no key to paste.
The google_web engine renders google.com/search in an installed Chrome or Edge, so that one
engine needs a browser on the machine. Every other engine, and web_fetch, is pure HTTP: with no
browser present google_web simply reports itself unavailable and the rest keep working.
Why this exists
Most search servers for MCP require either a paid search API key (Brave, Tavily, Serper, Bing…), a vendor account, or a hosted endpoint your machine may not be able to reach. That is a poor fit when you just want an agent to look something up:
Onboarding is the whole feature. A key means a signup, a credit card and a config edit before the first useful call — for a workflow that is often throwaway.
Some networks block the popular search backends while plain HTML endpoints still work. This server tries a direct connection first and can fall back to an explicit proxy without changing any tool call.
The answers are ordinary web pages. No proprietary index, no ranking you cannot inspect: the scraper is a normal HTTP client, and every result keeps its source URL so it can be cited.
Related MCP server: codex-search-mcp
Install
# run without installing (recommended)
uvx keyless-web-mcp
# or install it as a tool
pipx install keyless-web-mcp
# or into a project environment
pip install keyless-web-mcpRequires Python 3.10+. The console script keyless-web-mcp and python -m keyless_web are
equivalent.
Configure your client
DeepSeek Harness (DSH)
DSH loads MCP servers from a Cordis profile patch. Add an entry to
~/.dsh/profiles/<profile>/cordis.patch.yml (or pass it with dsh web --patch <file>):
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- insert:
- id: keyless-web
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: web
transport: stdio
command: uvx
args: ['keyless-web-mcp']
env:
MCP_WEB_PROXY: ''
toolCallTimeoutMs: 120000On Windows, if the client does not resolve uvx through PATH, point command at the absolute
executable path (...\uv.exe / ...\uvx.exe).
Claude Desktop
Edit claude_desktop_config.json (%APPDATA%\Claude\ on Windows, ~/Library/Application Support/Claude/
on macOS) and restart the app:
{
"mcpServers": {
"web": {
"command": "uvx",
"args": ["keyless-web-mcp"],
"env": {
"MCP_WEB_PROXY": ""
}
}
}
}Any other stdio-capable MCP client works the same way: run uvx keyless-web-mcp, pipx run keyless-web-mcp or python -m keyless_web as the server command.
Tools
web_search
Argument | Type | Default | Notes |
|
| required | Search terms |
|
|
| 1–20 |
|
| configured default | Any of |
Returns a JSON array (a string with [] when nothing matched):
[
{
"title": "NCCL: Scalable Multi-GPU Communication",
"url": "https://developer.nvidia.com/blog/...",
"snippet": "Collective communication primitives for multi-GPU...",
"engine": "duckduckgo"
},
{
"title": "NVIDIA NCCL 2.27 release notes",
"url": "https://news.google.com/rss/articles/...",
"snippet": "...",
"engine": "google",
"source": "NVIDIA Developer",
"date": "Tue, 04 Nov 2026 09:12:00 GMT"
}
]source and date are present only for news hits (engine: "google"). Engines are queried in the
order you give them; hits are merged and de-duplicated by URL.
web_fetch
Argument | Type | Default | Notes |
|
| required | Absolute |
|
|
| 500–100000, truncates the returned text |
|
|
| Also list up to 30 links found on the page |
Returns plain text that starts with Title: / URL: followed by the extracted main content.
JSON endpoints are pretty-printed instead of parsed as HTML.
Both tools report failures as text rather than raising, so the model can react:
error[KEYLESS_BAD_URL]: only http:// and https:// URLs are supported (got 'file:///etc/passwd').
Hint: Pass an absolute http:// or https:// URL, for example https://docs.python.org/3/.Engines
Engine | Endpoint | Default | Notes |
|
| ✅ first | The real Google web index, rendered in a headless Chrome/Edge. A plain HTTP client only receives a JavaScript shell, and the stock |
|
| ✅ second | A JSON search API rather than a scraped SERP: no key and no account, but rate limited per client address. Snippets are page excerpts, cut at 4000 characters; a 5xx or an empty |
|
| ✅ third | No-JS HTML endpoint, no key and no cookie; redirect links are unwrapped from |
|
| ✅ fourth | Google News RSS, not the web index: news and tech coverage. Item URLs are Google News article links, which open in a browser but do not return article text to |
|
| ❌ opt-in | Organic results over plain HTTP; click-tracker links ( |
|
| ❌ opt-in | Encyclopedic baseline; stable public API. Sends its own descriptive User-Agent, because Wikimedia answers HTTP 403 to browser-impersonating clients |
Default order: google_web, firecrawl, duckduckgo, google. Every engine runs concurrently and the
hits are interleaved round-robin, so one answer can cite several independent sources without any
backend disappearing behind a longer list. A single engine failing never fails the call; if every
engine fails or finds nothing, web_search returns [].
A backend that answers a different query
A consumer search endpoint may answer a scripted client with something other than its own results: Bing hands back a decoy SERP built from other queries, DuckDuckGo an "anomaly" page. Both look legitimate — same blocks, same result count, same echoed query in the title — so the only in-process evidence is that the hits have nothing to do with the query.
Each engine's hits are therefore checked against the query's terms (substring, case-insensitive, Latin words of two characters or more, CJK runs as-is). A group where no hit shares a term is discarded whole, before the merge, and the reason is recorded. Only whole groups are judged: a relevant hit can share no term with the query at all ("NCCL timeout" → a fault-tolerance article that never names it), so individual hits are never dropped. If every non-empty group would be discarded, the gate switches itself off for that call rather than turning "some data" into "no data".
web_search still returns the same JSON array; the per-engine record is available to code that asks
for it (keyless_web.engines.search_with_report, and the engines member of the JSON-lines worker's
search result):
{"name": "bing", "returned": 10, "matched": 0, "dropped": "no hit shared a term with the query"}The gate is a safety net, not a ranking: it cannot rescue a backend that answers with plausible but
wrong hits that happen to mention a query term, and it cannot tell NCCL the library from NCCL the
catechetical-leaders conference.
Configuration
Everything is read from the environment, once, at start-up.
Variable | Default | Meaning |
| (unset) | Fallback proxy, |
|
| Default engine list (comma separated, unknown names dropped) |
|
| Minimum seconds between two requests to the same host; |
|
| Search result cache TTL in seconds; |
|
| Fetched page cache TTL in seconds; |
| a desktop Chrome UA |
|
|
| Per-request timeout in seconds |
| auto-detected | Browser executable used by |
|
| Wall-clock budget for one rendered page, in seconds |
|
| Virtual time budget handed to the browser, in milliseconds |
Example:
MCP_WEB_PROXY=http://127.0.0.1:7890 KEYLESS_WEB_MIN_INTERVAL=2 keyless-web-mcpKEYLESS_WEB_USER_AGENT applies to the HTML scrapers and to web_fetch. The Wikipedia engine always
identifies itself as keyless-web-mcp/<version>, because Wikimedia's
robot policy requires a descriptive agent and
answers HTTP 403 to browser-impersonating clients.
Proxy fallback and TLS
Each request is attempted in order:
Direct — a normal client that honours
HTTP_PROXY/HTTPS_PROXYfrom the environment.Explicit proxy — only when
MCP_WEB_PROXYis set (for examplehttp://127.0.0.1:7890, or10.20.30.40:8080which is normalized tohttp://10.20.30.40:8080). Plainhost:portis accepted, and an IP works even when local DNS is broken because the proxy resolves hostnames.
Only transport failures (timeout, connect/DNS/TLS error) move to the next backend. Once a response
arrives, its HTTP status is final — 4xx/5xx is reported, not retried through another path. When
every backend fails, all per-backend errors are collected into one KEYLESS_NETWORK message, so you
can see which route failed and why.
TLS verification stays on. On start-up the server loads the operating system certificate store
via truststore, so TLS-interception proxies and
desktop antivirus products that inject their own root CA work without --insecure flags or
REQUESTS_CA_BUNDLE settings.
Politeness: rate limiting and caching
Per-host minimum interval (
KEYLESS_WEB_MIN_INTERVAL, default 1 request/second). The limiter keys on host, so hammering one site is throttled while other hosts proceed independently.In-process TTL cache: search results 300 s, fetched pages 900 s, keyed on engine, query and every output-affecting argument. Re-reading the page an agent just found costs nothing.
Both are deliberately conservative: this is a single-user tool, not a crawler. Raise the interval if you script many queries, and see the compliance note below.
Errors
Failures are reported to the model as error[CODE]: message. Hint: ....
Code | When | Typical fix |
| URL is not absolute | Pass a full |
| Empty query, unknown engine name, out-of-range argument | Fix the argument (the hint lists valid engines) |
| No backend reached the host, or it answered 4xx/5xx | Check connectivity, retry, or set |
| HTTP 429, or the site refuses scripted access (401/403) | Wait, raise |
| Response arrived but could not be parsed | The site changed its markup or served a block page; retry later |
|
| Install a browser or point |
Compliance
This project fetches public web pages with an ordinary HTTP client and no API agreement with the sites it reads. By using it you agree to:
respect each site's Terms of Service,
robots.txtand rate limits — keepKEYLESS_WEB_MIN_INTERVALat a polite value;not use it for bulk crawling, scraping behind logins, or republishing copyrighted content;
treat results as pointers, not as licensed data: cite and link the original page.
Scraping HTML endpoints is inherently unstable: an upstream markup change can break an engine
without notice. No availability or correctness guarantee is offered. Engines may stop working,
be blocked from your network, or return degraded results; that is a property of the endpoints, not a
bug report target. Two of those degradations look exactly like success, so they are worth naming:
DuckDuckGo's HTML endpoints answer HTTP 202 with an "anomaly" page (no results, no error), and Bing
answers a scripted client with a decoy SERP whose hits belong to other queries while the page title
still echoes the requested one. Prefer a backend that reports its own results (google_web, or the
firecrawl JSON API), and treat a result set you cannot match to your query as untrustworthy.
MIT licensed, provided "as is".
Development
git clone https://github.com/ai-v5/keyless-web-mcp
cd keyless-web-mcp
python -m venv .venv
.venv/Scripts/activate # Windows; use .venv/bin/activate elsewhere
pip install -e ".[dev]"
ruff check .
ruff format --check .
mypy src
pytest -qThe architecture keeps policy out of the parsers:
src/keyless_web/
├── config.py # environment -> frozen Settings
├── errors.py # KEYLESS_* codes, actionable hints
├── cache.py # TTL cache + cache_key()
├── browser.py # headless Chrome/Edge -> rendered DOM (--dump-dom)
├── net/transport.py # direct -> proxy fallback, rate limiting, truststore
├── engines/ # one module per engine, each just (client, query, n)
│ ├── base.py # SearchResult (pydantic) + Engine protocol
│ ├── duckduckgo.py bing.py google_news.py wikipedia.py firecrawl.py
│ ├── google_web.py # parses the rendered Google page, recovers /goto URLs
│ └── __init__.py # ENGINES registry + parallel run + round-robin merge
├── fetch/extract.py # trafilatura/BeautifulSoup extraction, JSON passthrough
└── server.py # MCP tool shell (declarations + argument validation only)Engines never build their own client: they receive an httpx.Client, which is why every engine can
be unit-tested with httpx.MockTransport and zero network access. The one exception is google_web,
which takes its DOM from browser.dump_dom() and only uses the client to resolve redirect links --
so its parser is still exercised offline against a fixture.
Roadmap
Optional next steps, not commitments:
result scoring (dedupe near-duplicates, rank by title/query overlap) to shrink the JSON the model has to read;
additional keyless engines behind the same registry;
optional
robots.txtpre-check forweb_fetch;reuse one browser process across searches instead of one launch per query (the launch dominates the ~4–8 s the
google_webengine costs).
License
MIT — see LICENSE. Security policy: SECURITY.md. Contributing: CONTRIBUTING.md.
中文文档见 README.zh-CN.md。
Available Tools
2 toolsweb_fetchA
Fetch one http(s) URL and return its readable main text for reading or citation; JSON endpoints are pretty-printed instead. Only http:// and https:// are accepted. include_links=true also lists up to 30 links found on the page (useful to discover follow-up URLs). max_chars caps the returned text and must be 500-100000. On failure it returns a text starting with 'error[KEYLESS_...]:' plus a Hint instead of raising.
Inputs: url (required), max_chars, include_links. Returns: result
web_searchA
Search the web without an API key and return a JSON array of results, each shaped {"title": str, "url": str, "snippet": str, "engine": str} plus optional "source" and "date" fields for news hits. Engines are tried in the given order and results are merged and de-duplicated by URL. engines: any of "bing", "duckduckgo", "google", "wikipedia"; omitted uses the server default (duckduckgo, then google, then wikipedia). Note that "google" is Google News RSS (news and tech coverage, no general web index) and that "bing" is opt-in only. max_results must be 1-20. Always valid JSON: an empty array [] means no engine produced results (retry with different terms or another engine); on failure it returns a text starting with 'error[KEYLESS_...]:' plus a Hint.
Inputs: query (required), engines, max_results. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 2 tools
The two tools split cleanly into discovery (web_search) and retrieval (web_fetch) of a single URL, with no overlapping behavior or ambiguous inputs. An agent can decide which to call without hesitation.
Both names follow the same snake_case verb_noun pattern (web_search, web_fetch) with a consistent 'web_' prefix. Parameter conventions (max_results vs max_chars) are also parallel.
Two tools is thin, though search+fetch is the minimal viable pair for a web-access server. There is no tool for batch fetching or content extraction beyond readable text, leaving the surface at the borderline of under-scoped.
Search plus fetch covers the core read-only lifecycle of web access, and the search tool's engine selection, dedup, and JSON contract make it self-sufficient. Minor gaps remain: no multi-URL or batch fetch, and no structured extraction beyond links and pretty-printed JSON.
Maintenance
Related MCP Connectors
Scrape, crawl and search the web for AI agents via MCP.
Web MCP: scrape/crawl sites, web search, brand assets, app stores, YouTube, Reddit, Hacker News.
Web search API and MCP server for developers building AI agents.
Web and URL utilities over MCP: shorten URLs, screenshot pages, read page metadata, encode URLs.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server that provides web search scraping from DuckDuckGo (with Mojeek fallback) and URL content fetching as markdown/text or raw HTML.1ISC
- AlicenseBqualityBmaintenanceEnables MCP clients to run live web searches, fetch and navigate readable web pages, search images, capture PDF screenshots, and retrieve finance, weather, sports, and world-time data through a zero-configuration authenticated backend.118 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to search the web and fetch individual pages through two stdio tools, returning ranked results or extracted page content, titles and outbound links. It wraps a hosted, API-key-authenticated service (or a local daemon) with zero runtime dependencies.13 npmMIT
- AlicenseNot gradedqualityCmaintenanceExposes metasearch across many engines and a page-to-Markdown fetcher as two MCP tools, web_search and web_fetch, so any MCP client can run searches and pull clean article content on demand. It also supports news-focused queries with category and time-range filters, plus optional inline page content.AGPL 3.0