keyless-web-search-mcp
keyless-web-search-mcp
Автономный MCP stdio-сервер: два инструмента без ключей — web_search поверх собственного пула движков (Bing, 360, Baidu, Google, Naver, Yandex, DuckDuckGo) и web_fetch (анонимный чтение HTTP(S)-страниц). Каждый поиск использует не более двух рабочих движков, направляет приоритеты по умолчанию в зависимости от языка запроса, добирает неудачные или нерелевантные движки в рамках ограниченного бюджета попыток, ранжирует результаты по релевантности, объединяет их и дедуплицирует по каноническому URL. Самодостаточный каталог, без шага сборки, не является частью пакетной системы harness — перемещайте куда угодно.
Зачем это нужно
Встроенный в harness web_search (провайдер DeepSeek) требует DEEPSEEK_API_KEY и отправляет каждый запрос в облако DeepSeek. Этот сервер — альтернатива, удобная для локальных моделей: без ключей, без вендорского поискового API, работает в сетях материкового Китая и Гонконга (Bing/360/Baidu с материковых линий, Bing/360/Naver/Yandex с протестированной гонконгской линии; DuckDuckGo недоступен ни с одной из них).
web_fetch существует по той же причине на стороне чтения: веб-профиль подключает поисковый провайдер, но не подключает fetch-провайдера, поэтому встроенный инструмент web_fetch (даже если пресет его включает) завершается ошибкой WEB_PROVIDER_UNAVAILABLE при каждом вызове. Этот инструмент вместо этого читает полное содержимое страницы через тот же сервер без ключей.
Related MCP server: web-search-mcp
Запуск
npm install --cache ./.npm-cache # deps: @modelcontextprotocol/sdk, zod
node index.js # speaks MCP over stdio
npm test # run deterministic ranking/fallback testsБыстрая проверка без клиента:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0.0.1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node index.jsУстановка в Claude Code
Установите его для текущего пользователя, чтобы каждый проект Claude Code мог его использовать. При регистрации сервера укажите оба пути в абсолютном виде; это избавит от зависимости от рабочей директории оболочки или PATH в дальнейшем:
claude mcp add --transport stdio --scope user web-search-self -- "$(command -v node)" "/absolute/path/to/keyless-web-search-mcp/index.js"
claude mcp get web-search-selfИспользуйте --scope local, чтобы ограничить его текущим проектом, или --scope project, чтобы записать общий .mcp.json. Claude Code хранит пользовательские и локальные регистрации в ~/.claude.json; определения MCP-серверов не должны находиться в settings.json.
После регистрации claude mcp list должен показать web-search-self как подключённый. Инструменты появятся как mcp__web-search-self__web_search и mcp__web-search-self__web_fetch.
Инструменты
web_search
web_search({ query, count?, maxSources?, engines? })
Параметр | По умолчанию | Значение |
| — | Поисковый запрос, на любом языке |
|
| Максимум объединённых результатов (1–20) |
|
| Максимум используемых движков на один поиск (жёсткий предел 2; попытки запасных вариантов ограничены) |
| зависит от языка запроса | Необязательный явный пул в порядке приоритета; если не указан, для запросов на китайском/корейском/японском/русском используются приоритеты, соответствующие региону |
Политика двух источников. Когда engines не указан, пул выбирается по языку запроса: для китайского сначала идут 360/Baidu/Bing; для английского — Bing/Google/Naver/360, чтобы бюджет из четырёх попыток сохранял рабочие запасные варианты в протестированных сетях материкового Китая и Гонконга. Явный список engines остаётся приоритетным. Кандидаты выполняются ограниченными параллельными раундами; движок занимает слот источника только после того, как вернёт разбираемые результаты, прошедшие консервативную лексическую фильтрацию релевантности. Неудачные, заблокированные, пустые или нерелевантные движки добираются в пределах бюджета попыток и бюджета сборщика, а диагностика выводится в строке «Engine note». Результаты ранжируются по пересечению с запросом с небольшой поправкой на качество источника: очевидные признаки репостов и контент-ферм понижаются, а сигналы официальных сайтов, документации, образования и GitHub получают преимущество, при этом обычные сайты не блокируются жёстко. Объединение остаётся круговым чередованием (1-й от движка A, 1-й от B, 2-й от A, …), а дедупликация по каноническому URL удаляет фрагменты, распространённые отслеживающие параметры и безопасные различия www..
Результат: нумерованный список title [engine] / реальный URL назначения / сниппет. Очистка ссылок по движкам: ссылки-трекеры Bing декодируются локально (параметр u в base64); 360 читает атрибут data-mdurl; Baidu читает атрибут mu блока (прямой URL, с одним best-effort redirect GET только для устаревших обёрток link?url=); у Naver и Yandex заголовки содержат прямой URL в якоре; обёртки Google /url?q= разворачиваются; трекеры DuckDuckGo /l/?uddg= разворачиваются. Бюджеты проб на движок: 10 с (Bing, Baidu), 8 с (360, Naver, Yandex), 5 с (Google, DuckDuckGo) — все выполняются параллельно, поэтому раунд занимает столько времени, сколько его самый медленный участник.
web_fetch
web_fetch({ url, maxChars? })
Параметр | По умолчанию | Значение |
| — | Абсолютный |
|
| Максимум возвращаемых символов содержимого (1000–100000) |
Анонимное чтение публичного веба, без учётных данных: браузерный User-Agent, не более пяти редиректов, 20 секунд реального времени на заголовки и тело, тело ответа обрезается ровно на 5 МБ. Каждая начальная и промежуточная цель проверяется после разрешения DNS; loopback-адреса, частные, link-local, операторский NAT (CGNAT), зарезервированные, multicast и локальные имена хостов отклоняются для предотвращения SSRF в машину или облачные metadata-сервисы. HTML преобразуется в видимый текст (script/style/noscript/svg/head/iframe/canvas/form отбрасываются, границы блоков становятся переводами строк, <title> переносится в заголовок); текстовые, JSON и XML медиатипы проходят с декодированием сущностей с использованием объявленной кодировки, а бинарные медиатипы отклоняются. На выходе — статусный заголовок: status, конечный url (после редиректов), content-type, признак усечения — затем содержимое. Ответы с кодом не из диапазона 2xx возвращают начало тела плюс примечание (403/429 интерпретируются как бот-проверка или пейволл), никогда не сфабрикованную страницу.
Движки и их статус
Проверено в 2026-07 с трёх линий (более ранней линии материкового Китая, линии Shanghai Telecom и линии дата-центра Hong Kong Zenlayer):
Поисковик | Эндпоинт | Статус с этих сетей |
|
| ✅ ~10 органических блоков на обеих линиях. В зависимости от линии |
|
| ✅ чистый 200; органические блоки содержат реальный URL в |
|
| Доступен (200), но этот клиент/IP не является доверенным для обычных HTML-выдач: тело — это стена meta-refresh |
|
| ✅ на шанхайской и гонконгской линиях: система контроля рисков блокирует запросы, похожие на скриптовые (302 только по UA на картинку-капчу |
|
| ✅ на гонконгской линии (200, ~10 органических блоков |
|
| ✅ на гонконгской линии: тот же урок про сигнатуру навигации, что и у Baidu — запросы, похожие на скриптовые, получают чекбокс SmartCaptcha «not a robot», но полный набор заголовков навигации документа плюс куки-сессия с главной страницы (yandexuid и др.) получает обычную HTML-выдачу (~50 блоков |
|
| ❌ TCP-недоступен на всех трёх протестированных линиях (включая HK); запасной движок для сетей, где он работает |
Сознательно не включены в пул: Sogou — делает 302 на sogou.com/antispider/, тот же класс IP-стены, что покрывает Baidu. Также просканированы и отклонены с гонконгской линии: Mojeek (отдаёт страницу с капчей), Ecosia (403 "Ecosia Firewall"), MetaGer (редиректит на страницу без результатов), а также Yahoo/Brave/Qwant/Startpage/goo.ne.jp (TCP-недоступны и с материковых, и с гонконгской линий).
Стена Google: что было испробовано
Ответ 200 с этого IP — не блокировка, а промежуточная страница JS-челленджа (~90 КБ обфусцированного/зашифрованного JavaScript; путь без JS — это meta-refresh в тупик). Стена состоит из двух слоёв:
JS-челлендж (вычислительный) — после декодирования он вычисляет proof-значение и устанавливает куки
SG_SS(срок действия 5 минут), затем перезагружается. Этот слой решаем вне браузера: запуск скриптов страницы в обычном Node.js с лёгким DOM-шимом (хранилище куки,navigator,Image,document) завершил вычисление, и полученная кукиSG_SSбыла один раз принята — Google ответил 200 и выдал доверенные кукиNID/AEC, которые он иначе никогда не отправляет на этот IP.Слой паттернов сессии (поведенческий) — последующие обычные HTTP-запросы, включая точную копию собственного потока перезагрузки скрипта (
emsg=SG_REL+ соответствующийsei, куки, объединённые в jar, браузерные заголовки), эскалируют до стены аномалийgoogle.com/sorry429. Этот слой оценивает всю сессию (TLS/HTTP2-отпечаток, темп запросов, смесь методов), чему стек Node.js на OpenSSL/undici не соответствует.
Клиентские рычаги, которые были протестированы и не изменили ответ слоя 1: параметр gbv=1, прогрев куки, полные заголовки браузерного отпечатка, поток retry/enablejs, вход через /m, UA текстовых браузеров/кнопочных телефонов/IE6/старых Android, поддельный UA Googlebot (Google проверяет его), куки CONSENT, POST-отправка (405), альтернативные TLD и ссылка «click here» emsg=SG_REL без куки. Альтернативы через Google-прокси также были недоступны или заблокированы из этой сети: Startpage и Qwant не отвечают по таймауту, Mojeek отдаёт страницу с капчей, Ecosia отдаёт 403.
Вывод: слой 1 решаем в Node.js (продемонстрировано); слой 2 — нет, из не-браузерного сетевого стека. Единственные надёжные пути к Google с этой машины — прокси с чистым IP или реальный браузерный контекст (headless Chromium); оба находятся в том же слое IP/паттернов трафика, который скрейпер не может честно обойти, а повторные проверки рискуют расширить окно аномалии IP — поэтому этот сервер не пытается их использовать. Google остаётся запасным движком, который активируется в сетях, где он отдаёт обычную HTML-выдачу.
Подключение к DeepSeek Harness
Добавьте в ваш cordis.yml (MCP-мост горячо перезагружает эту запись):
- id: mcp-search
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: search
transport: stdio
command: node
args: ['/absolute/path/to/keyless-web-search-mcp/index.js']Затем модель видит инструменты как mcp__search__web_search и mcp__search__web_fetch.
Ограничения
Качество поиска эвристическое: лексическая релевантность обрабатывает явно не по теме результаты и китайские n-граммы; внутренние страницы поисковых систем исключаются; сигналы контент-ферм/репостов только корректируют порядок, а не являются универсальной оценкой доверия. Релевантный результат не является доказательством правильности его утверждений — выполняйте fetch и перекрёстно проверяйте важные факты.
Скрейпинг, а не API: изменения вёрстки могут сломать парсер; сбой громкий («no parseable organic results»), никогда не фабрикуется. Парсеры Google и DuckDuckGo написаны по документированной структуре SERP и не могут быть проверены вживую из этой сети — настраивайте их при первом реальном ответе их движков.
Контроль частоты запросов: оба публичных движка терпят нерегулярное использование; интенсивные запросы привлекают бот-челленджи.
Запрос покидает машину и уходит к используемым движкам (это цена поиска без ключей); при ограничении на два источника максимум два из них видят запрос за один поиск.
Fetch — это ридер публичных страниц без ключей, а не браузер или интранет-клиент: JS-рендеренный контент для него невидим (тот же класс ограничения, что и у поисковых парсеров); страницы, которые отдают 403/429 анонимным клиентам, сообщаются, а не обходятся. Локальные/частные/зарезервированные сетевые адреса намеренно блокируются, включая адреса редиректов.
Available Tools
2 toolsweb_fetchWeb Fetch (keyless anonymous HTTP(S) reader)A
Fetch one HTTP(S) URL anonymously and return the page as readable text: HTML is stripped to visible text with block structure preserved; text/markdown/JSON/XML pass through decoded. No credentials: browser User-Agent, up to five validated redirects, bounded output. Local, private, link-local, and reserved network targets are blocked, including after DNS resolution and redirects. The result is a status header (status code, final URL, content-type, truncation) followed by the content. Use it to read full page content for a URL, e.g. one found by web_search.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute http(s) URL to fetch. | |
| maxChars | No | Maximum content characters to return (default 20000, hard cap 100000). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility. It discloses anonymous access, no credentials, browser User-Agent, redirect limits, target blocking, and truncation behavior, which is strong transparency for a network tool.
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?
Three purposeful sentences: main behavior, safety boundaries, and output/usage context. No filler, no redundant schema echoing, and key constraints are front-loaded.
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?
With no output schema or annotations, the description still explains output format, status header components, redirects, target restrictions, and content conversion. This is an unusually complete one-paragraph contract for a fetch tool.
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?
The schema already clearly documents both parameters at 100% coverage, with maxChars including a default and limits. The description affirms bounded output but does not add substantial meaning about parameter formats beyond the schema.
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, resource, and output: fetches one HTTP(S) URL and returns readable text. It also references web_search, making the distinction between search and fetch explicit without ambiguity.
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?
Description explicitly says to use it to read page content for a URL and gives a web_search output as an example, naming the sibling tool. It does not explicitly state when not to use it, so it stops short of a full when/when-not map.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
web_searchWeb Search (self pool: Bing / 360 / Baidu / Google / Naver / Yandex / DuckDuckGo)A
Search the web without any API key from a self-operated pool: Bing, 360, Baidu, Google, Naver, Yandex, and DuckDuckGo. Each search keeps at most two usable engines — the pool is routed by query language when no explicit pool is given, failed or irrelevant engines are back-filled within a bounded attempt budget, and results are relevance-ranked, quality-adjusted, and deduplicated by canonical URL. Each result includes a title, the real destination URL, a snippet, and its engine. Engines unreachable from the local network, or walled by captcha/JS/bot checks, are skipped and reported in an engine note; the remaining engines still answer.
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Maximum merged results to return (default 8). | |
| query | Yes | Search query in any language. | |
| engines | No | Optional explicit engine pool in priority order; omitted uses query-language routing. | |
| maxSources | No | Maximum usable engines per search (default 2, hard cap 2; fallback attempts are bounded). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description thoroughly discloses behavioral details: no API key needed, engine pool self-operated, at most two usable engines, back-fill on failures, relevance/quality ranking, deduplication, skipping unreachable or walled engines, and reporting an engine note. This exceeds the baseline and compensates for the absence of annotations. No contradiction with annotations since none are provided.
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?
The description is concise and front-loaded with the primary purpose (search the web without API key) and then provides relevant behavioral details in a structured manner. Each sentence adds value; it is not overly verbose. Slight redundancy with the engines list in the title but overall well-structured.
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?
The description covers key aspects: purpose, engine selection, failure handling, result format, and edge cases (captcha/JS/bot walls). No output schema exists, but the description mentions what each result includes (title, URL, snippet, engine), which partially compensates. The absence of annotations is mitigated by the detailed description. Minor gaps remain about the return structure of the full response, but the information is sufficient for an agent to invoke the tool correctly.
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?
The input schema already has complete (100% coverage) descriptions for all four parameters: count, query, engines, and maxSources. The description adds contextual insight on routing by query language and back-fill behavior but does not add substantial new parameter semantics beyond what the schema provides. Per the guideline, high schema coverage means baseline 3 is appropriate.
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?
The description clearly states the tool performs a web search using a self-operated engine pool, listing the supported engines and the result characteristics (title, URL, snippet, engine). It does not explicitly contrast with the sibling tool web_fetch, but the purpose is distinct and clear.
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?
The description explains how the engine pool is selected (query-language routing when no explicit pool), how failures are handled (back-filling, bounded attempts), and how results are ranked and deduplicated. It does not explicitly state 'when to use' versus 'when not to use' but provides contextual routing guidance. The sibling tool web_fetch is not referenced, so no alternative routing is mentioned.
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
v1.1.0- First observed
web_fetch - First observed
web_search
TDQS
Scored across 2 tools
The two tools are completely distinct: web_search performs web searches across multiple engines, while web_fetch retrieves the content of a specific URL. There is no overlap or ambiguity between them.
Both tool names follow the same verb_noun pattern: web_search and web_fetch. The naming is clear, consistent, and predictable.
The server has only two tools, which is slightly below the typical well-scoped range of 3-15. However, each tool is essential and covers the core functionality of keyless web search and fetching, so the count is reasonable for the narrow scope.
The tool surface fully covers the stated purpose of keyless web search: web_search finds relevant results and web_fetch retrieves full page content. There are no obvious missing operations for this domain.
Maintenance
Related MCP Connectors
Google, Bing, DuckDuckGo and Google Maps search results, free without an API key.
LLM-ready web search + instant answers + URL-to-clean-text fetch for agents and RAG.
Keyless web, GitHub, YouTube and Reddit search and read. Delegated shop, booking and signup.
Web search for AI agents — one tool across 6 engines, routed to the cheapest + cached.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables web search without API keys using DuckDuckGo and Bing search engines, and retrieves webpage content. Supports multiple search engines simultaneously with privacy protection and asynchronous processing.263 PyPI10MIT
- FlicenseNot gradedqualityDmaintenanceProvides free web search, content fetching, image search, and deep research via SearXNG, no API keys required.-
- AlicenseAqualityCmaintenanceEnables keyless multi-engine web, news, image, and video metasearch plus full-page markdown extraction for AI agents over MCP, with resilient fallback across DuckDuckGo, Bing, Brave, Google, and other backends.6MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to perform free web search and read web pages as markdown without paid APIs, Docker, or a background service, by querying multiple free search engines in parallel with optional fallbacks and extracting clean page content. Also supports fetching a site's llms.txt documentation files.MIT