MCP-WebSearch-SearXNG
Permite que tu modelo (o agente autónomo) navegue por la web, realice búsquedas en múltiples motores a la vez y muestre imágenes directamente en tu interfaz.
Documentación
WebSearch SearXNG + Servidor MCP
Un metabuscador SearXNG autoalojado junto con el servidor MCP mcp-searxng, diseñado para LLMs, agentes y aplicaciones que usan herramientas. Permite que tu modelo (o agente autónomo) navegue por la web, ejecute búsquedas en muchos motores a la vez y muestre imágenes directamente en tu interfaz. Se ejecuta completamente en tu propia máquina mediante Docker, se accede desde tu navegador y habla MCP sobre transporte HTTP Streamable, por lo que se conecta a cualquier cliente compatible con MCP (Claude Desktop, Cline, Open WebUI, LM Studio, frameworks de agentes personalizados, etc.). Mejorado con búsqueda de imágenes/videos/categorías, paginación por desplazamiento y redimensionamiento inteligente de imágenes en base64 para modelos con capacidades de visión. Ambos servicios permanecen en tu red local, por lo que nada sale de tu equipo a menos que lo solicites. Sin anuncios, sin telemetría, sin rastreadores de terceros, sin cuentas — la interfaz de SearXNG está diseñada para la privacidad y los parches MCP no añaden ningún tipo de comunicación no solicitada propia.
Instalación
Nota rápida sobre privacidad antes de comenzar. Este stack se ejecuta completamente en tu máquina y está diseñado para respetar la privacidad (sin telemetría, sin cuentas, sin rastreadores de terceros), pero los valores predeterminados están ajustados para "que funcione", no para máxima privacidad. Antes de exponerlo más allá de tu propia computadora, a tu LAN, a internet o a cualquier otra persona, lee ADVANCED.md
Requisitos previos
- Docker y Docker Compose deben estar instalados (Node.js no es necesario en el host: el servidor MCP se ejecuta dentro de su imagen Docker)
Paso 1: Clonar el repositorio
git clone https://github.com/hypersniper05/MCP-WebSearch-SearXNG.git
cd MCP-WebSearch-SearXNG
Esto te proporciona el docker-compose.yml, el settings.yml de SearXNG, los parches MCP y el Dockerfile personalizado ya organizados, por lo que el siguiente paso es directamente la configuración.
¿Prefieres configurar todo manualmente en lugar de clonar? Consulta Construcción desde cero (sin clonar) en ADVANCED.md.
Paso 2: Configurar los ajustes de SearXNG
El searxng/config/settings.yml incluido ya está preconfigurado para este stack: salida JSON habilitada, limitador de velocidad desactivado, un conjunto seleccionado de motores que responden a instancias autoalojadas y tiempos de suspensión de motores largos para que un motor bloqueado no se reintente en cada consulta. Solo contiene anulaciones: SearXNG lo fusiona sobre la configuración predeterminada dentro de la imagen (fijada). Lo único que necesitas configurar es la clave secreta.
¿Quieres cambiar qué motores están habilitados, el tiempo de espera de la solicitud o cualquier otro valor predeterminado de SearXNG? Consulta Modificación de la configuración predeterminada en ADVANCED.md.
Establecer el secret_key (o usar la variable de entorno)
SearXNG se iniciará sin una clave, por lo que para una instancia personal solo localhost puedes volver a esto más tarde. Pero si alguna vez te vinculas a 0.0.0.0, expones esto a tu LAN o lo compartes con alguien, establece una clave real; de lo contrario, cualquiera puede falsificar el HMAC que el endpoint /image_proxy de SearXNG verifica y usar tu instancia como un proxy de imágenes abierto.
Configúralo en un archivo .env junto a docker-compose.yml:
SEARXNG_SECRET=<paste your generated 64-char hex string here>
docker-compose.yml siempre pasa SEARXNG_SECRET al contenedor, y SearXNG permite que esa variable anule settings.yml incluso cuando está vacía. Por lo tanto, el marcador de posición secret_key: "CHANGE_ME_BEFORE_RUNNING" en settings.yml nunca se usa, y editarlo no tiene efecto: sin un valor .env la instancia se ejecuta con una clave vacía. Usa .env (está en gitignore, por lo que la clave nunca llega al repositorio).
Genera una clave con la herramienta que tengas instalada:
# PowerShell (Windows PowerShell 5.1 and PowerShell 7 — built in)
$b = New-Object byte[] 32; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); -join ($b | ForEach-Object { $_.ToString('x2') })
# Node.js (if you have it)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Docker (works anywhere this stack does)
docker run --rm alpine sh -c "apk add --no-cache openssl > /dev/null && openssl rand -hex 32"
# Git Bash / Linux / macOS
openssl rand -hex 32
# Python
python -c "import secrets; print(secrets.token_hex(32))"
Para el resto de las compensaciones de privacidad/endurecimiento (exposición LAN, autenticación MCP, enrutamiento Tor), consulta ADVANCED.md.
Paso 3: Construir e iniciar todo
docker compose build
docker compose up -d
Paso 4: Verificar los servicios
Verificación de salud:
curl -s http://localhost:3001/health
Esperado: {"status":"healthy","server":"ihor-sokoliuk/mcp-searxng","version":"2.4.0-enhanced","transport":"http"}
Búsqueda de prueba:
curl -s "http://localhost:8888/search?q=test&format=json" | head -c 200
Paso 5 (solo si accedes desde otra máquina): Abrir los enlaces a tu LAN / Tailscale
Por defecto, el docker-compose.yml incluido vincula SearXNG a 127.0.0.1 (solo esta máquina) y el servidor MCP a 0.0.0.0 (ya accesible desde tu LAN / tailnet de Tailscale). Ambos provienen de .env: SEARXNG_BIND (predeterminado 127.0.0.1) y MCP_BIND (predeterminado 0.0.0.0).
Diagnóstico rápido para detectar esto:
docker compose ps
Observa la columna PUERTOS. Si ves:
| Lo que ves | Lo que significa |
|---|---|
0.0.0.0:8888->8080/tcp | Accesible desde cualquier lugar de la red ✓ |
127.0.0.1:8888->8080/tcp | Solo localhost — el síntoma exacto que hace que el acceso externo "se agote" |
Si una fila muestra 127.0.0.1: y necesitas que sea accesible fuera del host, establece el enlace en .env (junto a docker-compose.yml). No edites docker-compose.yml en sí: .env está en gitignore, por lo que git pull nunca entra en conflicto con tu elección.
SEARXNG_BIND=0.0.0.0 # default 127.0.0.1
MCP_BIND=0.0.0.0 # already the default; 127.0.0.1 keeps the MCP endpoint on this machine
Luego recrea los contenedores (los cambios de puerto no se aplican con un simple restart):
docker compose down
docker compose up -d
docker compose ps
Las filas cambiadas ahora deberían mostrar 0.0.0.0:....
Si docker compose ps ya muestra 0.0.0.0 y el acceso externo aún falla, es el firewall del sistema operativo. En Windows, permite los puertos desde un PowerShell de administrador:
New-NetFirewallRule -DisplayName "MCP SearXNG (8888)" -Direction Inbound -Protocol TCP -LocalPort 8888 -Action Allow
New-NetFirewallRule -DisplayName "MCP Server (3001)" -Direction Inbound -Protocol TCP -LocalPort 3001 -Action Allow
Clientes MCP basados en navegador (cualquier cosa que envíe un encabezado Origin, como una interfaz web o el Inspector MCP) se rechazan con HTTP 403 a menos que su origen esté en la lista. Agrega los orígenes exactos, con esquema y puerto, a .env, por ejemplo MCP_ALLOWED_ORIGINS=http://localhost:3001,http://100.x.y.z:3001. La lista reemplaza el valor predeterminado de localhost, así que inclúyelos también si los necesitas. Los clientes que no envían Origin (Claude Code, clientes SDK, LM Studio, curl) no se ven afectados.
Nota de seguridad:
0.0.0.0expone un servicio a cualquiera que pueda alcanzar tu máquina en ese puerto, y el servidor MCP está vinculado a0.0.0.0por defecto (estableceMCP_BIND=127.0.0.1en.envpara mantenerlo local). El servidor MCP no tiene autenticación por defecto. Upstream puede requerir un token estático de portador en su modo endurecido, o tokens de acceso OAuth; si vas más allá de una red doméstica/Tailscale de confianza, lee ADVANCED.md → Nivel 2.
Resumen de endpoints
| Servicio | URL | Propósito |
|---|---|---|
| Interfaz SearXNG | http://localhost:8888 | Interfaz de búsqueda web |
| API SearXNG | http://localhost:8888/search | API de búsqueda JSON |
| Servidor MCP | http://localhost:3001/mcp | MCP Streamable HTTP |
| Salud MCP | http://localhost:3001/health | Endpoint de verificación de salud |
¿Necesitas TLS para un cliente solo HTTPS? El camino más limpio es Tailscale Serve si ambos extremos están en una tailnet (certificado real de confianza de Let's Encrypt, cero configuración por cliente), o un proxy inverso como Caddy / nginx con tu propio certificado en caso contrario. Cualquiera de los dos se agrega frente al endpoint HTTP MCP existente sin cambios en este stack.
Herramientas MCP
1. searxng_web_search
Búsqueda web con soporte de categoría, paginación y filtrado. Devuelve JSON estructurado: results (título, url, descripción, fecha de publicación, fuente engines, score), suggestions, corrections, answers, infoboxes, unresponsive_engines, engine_coverage (answered motores y failed_count) y pagination.
Separa consultas con | para ejecutar varias búsquedas en una sola llamada ("CFM LEAP engine | A320neo sharklet") — hasta 10, dos a la vez.
| Parámetro | Tipo | Descripción |
|---|---|---|
query | string | Consulta de búsqueda (obligatorio). Operadores: "exact phrase", site:, filetype:, +required -excluded, OR |
categories | string | general (predeterminado), images, videos, news, science, music, files (torrents), it (sitios de desarrolladores/TI), social media; separados por comas para varios |
max_results | number | Resultados por llamada, 1-50 (predeterminado: 20 general/noticias, 10 imágenes/videos) |
offset | number | Omitir N resultados para paginación (por ejemplo, 10 para resultados 11-20) |
pageno | number | Número de página de SearXNG (un nuevo lote de los motores) |
time_range | string | day, week, month, year |
language | string | Código de idioma (por ejemplo, en, fr); all = sin filtro (predeterminado) |
safesearch | number | 0 (ninguno, predeterminado), 1 (moderado), 2 (estricto) |
engines | string | Nombres de motores separados por comas para usar en lugar de los motores de la categoría (por ejemplo, bing,yahoo) |
min_score | number | Descartar resultados por debajo de esta puntuación de relevancia (0.0-1.0) |
Los argumentos opcionales pueden ser null o ""; se tratan como no establecidos.
2. web_url_read
Lee páginas web (como markdown), PDFs (hasta 16 MB) e imágenes. Las URLs de imágenes se detectan automáticamente y se devuelven como bloques de imagen base64 redimensionados.
| Parámetro | Tipo | Descripción |
|---|---|---|
url | string | URL a leer (obligatorio) |
detail | string | Nivel de redimensionado de imagen: low (448px, ~256 tokens), medium (768px, ~756 tokens, predeterminado), high (1280px, ~2048 tokens) |
startChar | number | Desplazamiento de caracteres para paginación de texto |
maxLength | number | Máximo de caracteres a devolver |
section | string | Extraer contenido bajo un encabezado |
paragraphRange | string | Rango de párrafos (p. ej., 1-5) |
readHeadings | boolean | Devolver solo encabezados |
web_url_read rechaza direcciones privadas — localhost, LAN (RFC1918), Tailscale (100.64.0.0/10), enlace local y nombres de host de Docker — en la primera solicitud y en cada redirección. Establece MCP_HTTP_ALLOW_PRIVATE_URLS=true en el servicio mcp-searxng solo si deseas que lea servicios internos.
3. searxng_instance_info y searxng_search_suggestions
Del upstream. searxng_instance_info enumera las categorías y motores de la instancia. searxng_search_suggestions no devuelve sugerencias de consulta aquí, porque el autocompletado está desactivado en settings.yml (la interfaz web, de lo contrario, enviaría cada pulsación de tecla a un servicio de sugerencias de terceros). Solo completa los prefijos !bang de SearXNG, p. ej., !bi → !bing.
Flujo de búsqueda de imágenes
searxng_web_search({query: "sunset", categories: "images"})-> devuelve URLs de imágenes + metadatos- Elige una imagen ->
web_url_read({url: "https://example.com/sunset.jpg", detail: "medium"})-> devuelve imagen base64 redimensionada - Para ver más resultados ->
searxng_web_search({query: "sunset", categories: "images", offset: 10})-> resultados 11-20
Las imágenes SVG se renderizan a PNG. Se rechazan imágenes de más de 100 megapíxeles y, como máximo, se ejecutan dos redimensionados de trama y dos renderizados SVG a la vez (las imágenes JPEG/PNG/GIF/WebP pequeñas que no necesitan redimensionado pasan directamente).
Archivos de parche personalizados
El servidor MCP es el upstream mcp-searxng 2.4.0 con algunos archivos montados de solo lectura sobre su carpeta dist/. Todos los demás archivos del upstream (incluida su seguridad de URL, seguridad HTTP y lectura de PDF) se ejecutan sin modificar.
| Archivo | Propósito |
|---|---|
Dockerfile | Upstream isokoliuk/mcp-searxng:2.4.0 + sharp (redimensionado de imágenes) + fuentes DejaVu (texto en SVGs) |
index.js | Archivo del upstream con pequeñas ediciones marcadas // ENHANCED: — enruta las lecturas de búsqueda e imágenes a los módulos siguientes |
version.js | Informa 2.4.0-enhanced |
enhanced-search.js | Salida JSON estructurada, ` |
image-reader.js | Obtención de imágenes (con la protección SSRF del upstream), redimensionado sharp con los 3 ajustes preestablecidos de detalle, renderizado SVG en un proceso hijo con límites de memoria y tiempo |
Actualización de SearXNG
SearXNG está fijado a una etiqueta con fecha en docker-compose.yml (actualmente 2026.9.23-3cd69d30e). settings.yml contiene solo anulaciones sobre la configuración predeterminada dentro de la imagen, por lo que una imagen nueva puede habilitar, deshabilitar o eliminar motores silenciosamente — por eso las actualizaciones son un aumento deliberado de etiqueta en lugar de un pull de :latest.
# 1. Edit docker-compose.yml: set the new tag from https://hub.docker.com/r/searxng/searxng/tags
docker compose up -d searxng # pulls the tag, recreates the container
docker compose logs searxng --tail 100 # look for "can't register engine" (unknown settings keys are mostly ignored silently)
curl.exe -s http://localhost:8888/config > config-new.json # compare categories/engines with the old version
curl.exe -s http://localhost:8888/stats/errors # after a few searches: which engines fail
También compara la lista plugins: en settings.yml con el /usr/local/searxng/searx/settings.yml propio de la imagen: la superposición reemplaza la lista completa de complementos, por lo que un nuevo complemento del upstream no se carga hasta que lo agregues. SearXNG no publica lanzamientos en GitHub; la parte después de la fecha en una etiqueta es un hash de commit, así que revisa los cambios entre las dos etiquetas en https://github.com/searxng/searxng/compare/3cd69d30e...<new-hash>. Para revertir, vuelve a establecer la etiqueta anterior.
Un punto medio razonable para mantenerse actualizado: programa un trabajo semanal que verifique en Docker Hub si hay una etiqueta más nueva y lo informe, pero que no la aplique.
Actualización del servidor MCP
mcp-searxng se construye localmente desde mcp-searxng-patches/Dockerfile, que está fijado a isokoliuk/mcp-searxng:2.4.0. Después de hacer pull de este repositorio en otra máquina, o después de cambiar el Dockerfile:
docker compose build mcp-searxng
docker compose up -d --force-recreate mcp-searxng
curl -s http://localhost:3001/health # version must be 2.4.0-enhanced
--force-recreate no es opcional. Un docker compose up -d simple reconstruye la imagen pero deja el contenedor existente ejecutándose en la imagen antigua, por lo que la actualización no hace nada silenciosamente. Confirma que el contenedor está en la imagen recién construida — estos dos ids deben coincidir:
docker inspect mcp-searxng --format '{{.Image}}' && docker images --no-trunc --format '{{.ID}}' mcp-searxng-enhanced:latest
Para pasar a una versión más nueva del upstream: aumenta la etiqueta FROM, copia los nuevos dist/index.js y dist/version.js de la imagen (docker run --rm --entrypoint cat isokoliuk/mcp-searxng:<tag> /app/dist/index.js), vuelve a aplicar las ediciones // ENHANCED: y reconstruye. enhanced-search.js y image-reader.js importan módulos del upstream, así que verifícalos contra exportaciones renombradas.
Probado con
- Qwen3.6 35B A3B — ejecuta tanto
searxng_web_search(incluida la multi-consulta y los filtros de categoría) comoweb_url_read(modos de texto e imagen) de manera confiable. La selección de herramientas, la inferencia de parámetros y la interpretación de resultados funcionan bien con este modelo.
Notas
- El servidor MCP usa transporte HTTP Streamable (no stdio ni solo SSE)
- SearXNG se conecta internamente mediante el nombre de host de Docker
searxng:8080 - No se necesitan claves de API externas. Las consultas van desde tu IP a los motores de búsqueda; un motor, findborg, es un proxy sin clave de terceros del índice de Brave (desactívalo en
settings.ymlsi no lo deseas). - Los parches personalizados tienen licencia MIT y no añaden telemetría. Su único acceso a procesos/archivos:
image-reader.jsinicia su renderizador SVG a través de/bin/sh -c 'ulimit -v … && exec node …'(con un entorno vacío) y escribe el/proc/<pid>/oom_score_adjde ese hijo. - Motores de búsqueda (web general): Bing, Yahoo, DuckDuckGo (a través de su endpoint web), Google (a través de un endpoint de Búsqueda personalizada sin clave), Yandex, Keenable, Swisscows, mwmbl y findborg, además de infoboxes de Wikipedia. Normalmente, 6-8 de ellos responden a cada consulta, y los resultados en los que coinciden varios motores se clasifican primero.
- Si los resultados parecen escasos, verifica primero tu IP de salida. Brave (HTTP 429), el endpoint html de DuckDuckGo, Google web, Qwant web/imágenes y GMX (CAPTCHA), y Yep (HTTP 403) rechazan instancias autoalojadas en direcciones compartidas o marcadas — las salidas de VPN, CGNAT y los rangos de centros de datos son las causas comunes. Fallan en la primera consulta desde un contenedor frío, por lo que no es un problema de limitación de velocidad que puedas ajustar, y están deshabilitados en la configuración incluida. Startpage también está desactivado: su desafío de prueba de trabajo se vuelve más difícil para tu IP después de una consulta. (Qwant News y Qwant Videos aún responden y permanecen habilitados.) Verifica lo que informa SearXNG:
Opciones, aproximadamente en orden de esfuerzo: buscar desde una IP residencial sin VPN, poner las solicitudes salientes detrás de proxies (curl -s "http://localhost:8888/search?q=test&format=json" | jq .unresponsive_engines curl -s http://localhost:8888/stats/errorsoutgoing.proxiesensearxng/config/settings.yml), o proporcionar una clave de API para un motor que venda una (braveapi). Habilitar más motores bloqueados no ayuda. - Algunos fallos son normales y autolimitados: Swisscows rechaza algunas consultas con HTTP 450 ("No disponible por razones legales"), Yahoo rechaza ráfagas de consultas por un corto tiempo, y el endpoint de Búsqueda personalizada de Google responde "tráfico inusual desde tu red" cuando tu IP de salida está marcada, a veces en la primera consulta. SearXNG suspende el motor (15 minutos para Google) y los demás continúan con la búsqueda.