keyless-web-mcp
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-mcpsearch 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 DuckDuckGo, Google News and Wikipedia, merges the hits, 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.
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 | No-JS HTML endpoint, most reliable default |
|
| ✅ second | 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 |
|
| ✅ third | Encyclopedic baseline; stable public API. Sends its own descriptive User-Agent, because Wikimedia answers HTTP 403 to browser-impersonating clients |
|
| ❌ opt-in | Bing needs cookies to serve organic results and often answers with spam from datacenter IPs, so it is excluded from the default order. Still available via |
Default order: duckduckgo, google, wikipedia (bing never runs unless requested).
A single engine failing never fails the call: the remaining engines still answer. If every engine
fails or finds nothing, web_search returns [].
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 |
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 |
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. 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()
├── 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 google_news.py wikipedia.py bing.py
│ └── __init__.py # ENGINES registry + multi-engine search()
├── 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.
Roadmap
Optional next steps, not commitments:
a DSH provider plugin so
web_search/web_fetchare first-class DSH tools rather than MCP tools;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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| max_chars | No | ||
| include_links | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well: it discloses the http/https-only restriction, JSON pretty-printing, the 30-link cap, the max_chars bound, and — importantly — that failures return text prefixed 'error[KEYLESS_...]:' plus a Hint rather than raising. Not covered: redirect policy, JS-rendered pages, rate limits, or how truncation is signalled in the returned text.
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 dense paragraph with zero filler, front-loaded with the primary purpose before the scheme restriction, optional-parameter behavior, and failure mode. Every clause adds an actionable fact.
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?
An output schema exists, so return values need not be re-explained, yet the description usefully covers the error-return convention that callers must parse. For a fetch tool the remaining gaps are environmental (redirects, JS-rendered content, truncation indicators), which are minor but non-trivial for a web-reading agent.
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 0%, so the description must compensate, and it does: url is constrained to http/https, include_links is given real semantics ('up to 30 links found on the page ... to discover follow-up URLs'), and max_chars is bounded. The max_chars range largely restates the schema's minimum/maximum, which keeps this just short of a 5.
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 one http(s) URL') plus the concrete output ('readable main text for reading or citation') and the JSON special case. An agent can immediately tell this apart from its sibling web_search, which discovers URLs rather than retrieving a known one.
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 only implied: 'for reading or citation' and the note that include_links helps 'discover follow-up URLs' hint at the workflow, but the description never says when to choose this over web_search or whether a prior search is required. No exclusions or prerequisites are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| engines | No | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses keyless operation, engine ordering, merge/de-duplication by URL, that 'google' is News RSS rather than a web index, that 'bing' is opt-in, and the exact failure shape ('error[KEYLESS_...]:' plus a Hint). Rate limits, latency across chained engines, and truncation behavior are not mentioned, which keeps it short of a 5.
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?
Front-loaded with purpose and result shape, then engine semantics, then edge cases. Dense but nearly every clause carries information; the sentence enumerating the inline result object is slightly verbose given an output schema exists.
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?
Despite an output schema being present, the definition covers the result fields, engine behavior, default ordering, result-count bounds, and the empty-array and error-code cases. An agent has everything needed to call it and interpret both success and failure responses.
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 0%, so the description must compensate, and it mostly does: it enumerates the engine values, explains that order matters and the list is merged/de-duplicated, and defines the default. The max_results 1-20 range merely repeats the schema's minimum/maximum, and 'query' syntax (phrasing, quoting, operators) is left unexplained.
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?
Opens with a specific verb+resource ('Search the web without an API key') and immediately characterizes the return value, so the tool is clearly distinguishable from web_fetch (which retrieves a known URL). It never names the sibling explicitly, so differentiation is implied rather than stated.
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?
Gives concrete selection guidance: engine order controls priority, omitting engines falls back to the server default (duckduckgo, then google, then wikipedia), and an empty array means 'retry with different terms or another engine'. What's missing is guidance on when to prefer web_fetch over searching, or when to include wikipedia/news engines for a given intent.
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.
2 tool updates
v0.1.0- First observed
web_fetch - First observed
web_search
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.1123 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables 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