mcp-retrieval

Servidor MCP en Go con tres herramientas de solo lectura: búsqueda web, búsqueda de imágenes y extracción de páginas a Markdown. No requiere claves API ni cuentas: la búsqueda se realiza a través de DuckDuckGo Lite, la búsqueda de imágenes a través de Bing Images, y las páginas se extraen con un analizador de legibilidad. Transportes stdio y HTTP, con licencia MIT.

Documentación

mcp-retrieval

Un servidor MCP que ofrece a un LLM tres herramientas web: búsqueda, búsqueda de imágenes y extracción de páginas — sin necesidad de claves API.

License MIT Go MCP Transport DuckDuckGo Bing Images uTLS

Herramientas · Inicio rápido · Configuración · Motor de recuperación · Arquitectura · Contribuciones


Qué es

mcp-retrieval es un servidor de Model Context Protocol escrito en Go. Expone capacidades de recuperación web a cualquier cliente compatible con MCP (Claude Desktop, agentes de IDE, aplicaciones LLM personalizadas) como tres herramientas de solo lectura. Internamente utiliza la biblioteca retrieval-go para buscar en la web y obtener páginas, devolviendo resultados como Markdown limpio listo para entregar a un modelo.

La biblioteca no requiere claves API: la búsqueda web pasa por DuckDuckGo Lite, la búsqueda de imágenes por Bing Images, y la obtención de páginas ejecuta el HTML a través de un extractor de legibilidad antes de convertirlo a Markdown. Para mantenerse fiable contra la protección anti-bots, suplanta a navegadores reales a nivel TLS y puede rotar tanto huellas de navegador como proxies — consulte Motor de recuperación.

Ambos transportes que soporta el SDK de MCP están disponibles y exponen el mismo conjunto de herramientas:

  • stdio — el cliente lanza el binario y se comunica por stdin/stdout (el predeterminado, ideal para clientes de escritorio).
  • http — un servidor HTTP de streaming de larga duración (útil para despliegues remotos/compartidos).

Herramientas

HerramientaDescripción
web_searchEjecuta una o más consultas en paralelo y devuelve fragmentos deduplicados y reordenados por relevancia con enlaces.
web_search_imagesEjecuta una o más consultas de imágenes en paralelo y devuelve resultados de imágenes deduplicados por consulta.
web_scrapeDescarga una o más páginas en paralelo y devuelve el texto principal del artículo como Markdown.

Las tres están anotadas como solo lectura. Cada herramienta devuelve una carga JSON estructurada que coincide con su esquema de salida; el SDK refleja el mismo JSON en el bloque de contenido de texto para clientes que no leen structuredContent.

web_search

ParámetroTipoPredeterminadoNotas
queries[]stringRequerido. Se ejecuta en paralelo.
max_resultsint5Fragmentos por consulta, limitado a max_results de configuración (20).
timeout_msint645000Tiempo de espera de toda la llamada; limitado a [min, max] de configuración.
datestringFiltro de frescura: d (día), w (semana), m (mes), y (año).

web_search_images

ParámetroTipoPredeterminadoNotas
queries[]stringRequerido. Se ejecuta en paralelo.
max_imagesint5Imágenes por consulta, limitado a max_images de configuración (10).
timeout_msint645000Tiempo de espera de toda la llamada; limitado a [min, max] de configuración.
datestringFiltro de frescura: d / w / m / y.

web_scrape

ParámetroTipoPredeterminadoNotas
urls[]stringRequerido. Se descarga en paralelo.
robots_txtboolfalseRespeta el robots.txt de la página.
timeout_msint645000Tiempo de espera de toda la llamada; limitado a [min, max] de configuración.
remove_linksboolfalseElimina los enlaces Markdown del texto.
max_charsint20000Trunca el texto de la página a N caracteres, limitado a max_document_chars de configuración (20000).

Tanto las listas queries/urls están limitadas a max_queries (10) elementos por llamada. Las consultas deben tener ≤ 512 caracteres; las URLs ≤ 2048 caracteres y solo http/https.

Resultados y recuentos

Cada llamada se distribuye en la lista de entrada y devuelve una entrada por consulta/URL, cada una con su propio statussuccess, failed o timeout — de modo que un fallo parcial aún devuelve los elementos que sí funcionaron.

count es el número de elementos realmente devueltos, y puede ser menor que el max_results / max_images solicitado: los duplicados dentro de los resultados de una sola consulta se eliminan antes de aplicar el límite, y el proveedor upstream puede simplemente tener menos elementos para ofrecer. Un count más pequeño es un resultado normal, no un error.

La deduplicación es por consulta, no entre consultas. Cada entrada se deduplica por sí sola, por lo que un enlace encontrado por dos de las consultas en la misma llamada aparece en ambas entradas — deduplique la unión usted mismo si lo necesita.

Errores

Los fallos a nivel de solicitud se devuelven como resultado de herramienta con isError: true y un mensaje de texto plano, no como un error JSON-RPC — el modelo lee el mensaje y puede corregir la llamada por sí mismo. Los fallos por elemento nunca hacen esto; permanecen dentro de la carga como status: "failed" / "timeout".

Una llamada falla por completo solo cuando la entrada se rechaza antes de que comience cualquier trabajo, o cuando todos los elementos de la misma fallan:

MensajeSignificado
invalid requestLos argumentos no pasaron la validación.
too many queries / too many urlsLa lista excede MAX_QUERIES.
query must not be emptyUna consulta vacía, o una lista queries vacía.
query is too longUna consulta excede 512 caracteres.
invalid urlUna URL está mal formada, supera 2048 caracteres, o no es http/https.
robots.txt deniedrobots_txt: true y la página no permite la obtención.
upstream service unavailableEl proveedor upstream respondió con un código de estado inesperado.
every url failed to be scraped; the pages may be unreachable or hold no extractable textTodas las URLs fallaron. Las causas individuales se registran en stderr, no se devuelven.
every query failed; the search upstream may be unreachableTodas las consultas fallaron.
internal server errorCualquier cosa no clasificada.

Los mensajes de fallo total deliberadamente no distinguen los tiempos de espera de otras causas: un lote mixto puede fallar por varias razones a la vez, y el status por elemento ya lleva ese detalle siempre que al menos un elemento sobreviva.

Limitaciones conocidas

  • web_scrape maneja solo HTML. Las páginas pasan por un extractor de legibilidad, que necesita marcado de artículo, por lo que las respuestas text/plain no producen nada y vuelven como status: "failed". Los hosts de archivos sin procesar son el caso común: raw.githubusercontent.com, github.com/.../raw/..., cdn.jsdelivr.net. Extraiga la página renderizada en lugar del archivo sin procesar.
  • La relevancia de web_search_images no está garantizada. Para algunas consultas, Bing Images sirve una página que no es un conjunto de resultados, y se analiza como si lo fuera — la herramienta entonces devuelve imágenes no relacionadas con status: "success". Trate los resultados de imágenes como de mejor esfuerzo y verifíquelos antes de mostrarlos a un usuario.
  • Sin JavaScript. Las páginas se obtienen tal cual; el contenido renderizado en el lado del cliente es invisible para el extractor.

Inicio rápido

Instalación

Elija la que mejor se adapte — todas dan el mismo servidor.

Contenedor (no se necesita cadena de herramientas Go):

docker pull ghcr.io/role1776/mcp-retrieval:latest

Binario precompilado — obtenga el archivo para su plataforma desde la última versión, descomprímalo y ponga mcp-retrieval en su PATH.

Paquete MCP — para clientes que instalan archivos .mcpb, descargue mcp-retrieval_<version>_<os>_<arch>.mcpb desde la última versión y ábralo con su cliente. El paquete lleva el binario compilado, por lo que no necesita ni Docker ni Go. Elija el archivo que coincida con su sistema operativo y arquitectura de CPU: un paquete contiene un solo binario nativo.

Desde el código fuente:

go install github.com/Role1776/mcp-retrieval/app/cmd/mcp-retrieval@latest   # needs Go 1.25.5+

O construya el binario en el lugar (el módulo Go vive en app/):

make build          # -> bin/mcp-retrieval

Ejecutar

# defaults: stdio transport, no configuration needed
./bin/mcp-retrieval

# with an explicit env file
./bin/mcp-retrieval -env /absolute/path/to/.env

La única bandera es opcional:

BanderaSignificado
-envRuta a un archivo .env. Si se omite — o si el archivo no existe — el servidor se inicia con los valores predeterminados y lo que ya esté en el entorno. No hay búsqueda implícita: bajo stdio el directorio de trabajo lo elige el cliente MCP, por lo que un valor relativo predeterminado sería impredecible.

Conectar un cliente MCP (stdio)

Apunte su cliente al binario compilado. Ejemplo de configuración de Claude Desktop:

{
  "mcpServers": {
    "retrieval": {
      "command": "/absolute/path/to/mcp-retrieval",
      "env": {
        "MAX_RESULTS": "20"
      }
    }
  }
}

El bloque env es opcional — "command" solo es suficiente.

Conectar un cliente MCP (contenedor)

Ejecute la imagen en stdio. La configuración sigue viajando a través del bloque env, pero Docker necesita cada variable nombrada en la línea de comandos con -e para que llegue al proceso:

{
  "mcpServers": {
    "retrieval": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MAX_RESULTS",
        "-e", "DEFAULT_TIMEOUT_MS",
        "ghcr.io/role1776/mcp-retrieval:latest"
      ],
      "env": {
        "MAX_RESULTS": "20",
        "DEFAULT_TIMEOUT_MS": "5000"
      }
    }
  }
}

-i es requerido — sin él el contenedor no recibe stdin y el cliente ve morir el servidor inmediatamente. Los clientes que instalan desde el Registro MCP construyen esta invocación ellos mismos y solicitan las variables declaradas en server.json.

Ejecutar sobre HTTP

Establezca MCP_TRANSPORT=http y el servidor escucha en SERVER_PORT en MCP_PATH (predeterminado http://localhost:8080/mcp).


Configuración

Todo se configura a través de variables de entorno, y cada valor se valida antes del inicio: un valor no numérico o no positivo es un error de inicio. Las relaciones entre límites no se verifican al inicio — consulte Límites. Las variables ya presentes en el entorno ganan sobre un archivo .env, por lo que el bloque env de un cliente MCP siempre tiene efecto. Cada campo tiene un valor predeterminado sensato, por lo que el servidor se ejecuta sin configuración alguna (transporte stdio).

Consulte .env.example para la lista completa con sus valores predeterminados, lista para copiar a .env.

Servidor MCP

VariablePredeterminadoNotas
MCP_TRANSPORTstdiostdio o http.
MCP_NAMEmcp-retrievalNombre del servidor anunciado a los clientes.
MCP_PATH/mcpRuta HTTP (solo transporte http).

La versión anunciada a los clientes no es configurable: se imprime en el binario en tiempo de compilación desde la etiqueta git.

Servidor HTTP (solo transporte http)

VariablePredeterminado
SERVER_PORT8080
SERVER_READ_TIMEOUT60s
SERVER_WRITE_TIMEOUT60s

Cliente HTTP y proxy

VariablePredeterminadoNotas
MAX_IDLE_CONNS_PER_HOST100Agrupación de conexiones HTTP.
PROXY_HOSTOpcional. Si se establece, las solicitudes se enrutan a través de un proxy de sesión rotativa.
PROXY_PORTRequerido cuando PROXY_HOST está establecido.
PROXY_SCHEMERequerido cuando PROXY_HOST está establecido.
PROXY_LOGINRequerido cuando PROXY_HOST está establecido.
PROXY_PASSWORDRequerido cuando PROXY_HOST está establecido.

Cuando se configura un proxy, cada solicitud saliente recibe un id de sesión único añadido al inicio de sesión, por lo que el proveedor upstream rota la IP de salida por solicitud.

Límites

EnvPredeterminado
MAX_QUERIES10
DEFAULT_RESULTS5
MAX_RESULTS20
DEFAULT_TIMEOUT_MS5000
MAX_TIMEOUT_MS10000
MIN_TIMEOUT_MS1000
DEFAULT_IMAGES5
MAX_IMAGES10
DEFAULT_DOCUMENT_CHARS20000
MAX_DOCUMENT_CHARS20000

Cada valor se verifica de forma independiente — debe ser mayor que cero — pero los tríos DEFAULT_*, MIN_* y MAX_* no se comparan entre sí al iniciar. Un conjunto inconsistente no detiene el servidor; se reconcilia por solicitud en su lugar:

  • un valor que el llamador omite, o pasa como cero o negativo, se revierte al DEFAULT_* correspondiente;
  • el resultado se limita luego a [MIN_*, MAX_*], de modo que un DEFAULT_* mayor que su MAX_* simplemente produce MAX_*;
  • si MIN_* excede MAX_*, gana el máximo.

Por lo tanto, el límite efectivo siempre está dentro del máximo configurado, y una configuración incorrecta degrada a un servidor funcional en lugar de un inicio fallido. La desventaja es que degrada silenciosamente: un error tipográfico como MAX_RESULTS=2 en lugar de 20 no genera ninguna advertencia, solo respuestas más pequeñas sin aviso. Vale la pena revisar estos valores cuando los resultados parecen truncados.

Registro (Logging)

EnvPredeterminadoNotas
LOG_MODElocallocal → manejador de texto en nivel de depuración; prod → manejador JSON en nivel de información. Los registros van a stderr.

Arquitectura

El proyecto sigue una estructura limpia y en capas. Las dependencias apuntan hacia adentro, hacia el dominio, y cada capa se comunica con la siguiente a través de interfaces.

app/                       the Go module: sources plus its build files
                           (Dockerfile, .dockerignore, .goreleaser.yaml)

cmd/mcp-retrieval/main.go  entry point: parse flags, load config, run app

internal/
  app/                     wiring + lifecycle (build server, run, graceful shutdown)
  config/                  config loading (.env → env vars → validate)
  domain/                  core types (Query, Link, Document, Snippet, Image) and errors
  dto/web/                 request/response shapes for the MCP tools
  transport/mcp/           MCP layer
    router/                registers every tool group on the MCP server
    web/                   tool handlers
    utils/                 schema helpers and error → tool-result mapping
  usecase/web/             business logic: validation, parallelism, timeouts, dedupe/limit/rerank
  adapter/web/             retrieval-go client wiring (search, images, scrape, proxy)
  pkg/                     reusable building blocks (mcpserver, server, logger, validator)

Flujo de solicitud para una llamada de herramienta:

MCP client → transport/mcp/web (handler) → usecase/web → adapter/web → retrieval-go → the web
                     ↑ maps errors               ↑ validates, fans out, limits results

La búsqueda y el raspado (scraping) se distribuyen en paralelo a través de la lista de entrada y agregan resultados por elemento, cada uno con su propio estado (success, failed, timeout). Una llamada solo falla por completo cuando todos los elementos fallan.


Motor de recuperación

Todo el trabajo de red se delega a retrieval-go, configurado en app/internal/adapter/web. Vale la pena saber:

  • Fuentes. La búsqueda web utiliza DuckDuckGo Lite; la búsqueda de imágenes utiliza Bing Images; la obtención de páginas ejecuta el HTML sin procesar a través de un extractor de legibilidad y convierte el artículo principal a Markdown (tablas incluidas). No se requieren claves de API de motores de búsqueda.
  • Suplantación de navegador. El adaptador habilita WithBrowserRotation(), por lo que cada solicitud se envía desde uno de ~11 perfiles de navegador reales elegidos al azar. Cada perfil combina una huella TLS/JA3 genuina (a través de uTLS) con un User-Agent coincidente y encabezados de sugerencia de cliente — Chrome 133/131/120 (Windows/macOS/Linux), Edge 131, Firefox 120 (Windows/macOS), Safari 18.4 (macOS) y Safari 18.4 de iOS. Esto hace que el tráfico parezca de navegadores comunes en lugar de un cliente HTTP de Go, que es lo que mantiene accesibles las fuentes gratuitas.
  • Rotación de proxy. Cuando PROXY_HOST está configurado, el adaptador instala una fábrica de proxies que agrega un session-<id> único al nombre de usuario del proxy en cada solicitud. Con un proveedor de proxy residencial/rotatorio basado en sesiones, eso produce una IP de salida nueva por solicitud, distribuyendo la carga y evitando límites de velocidad. Sin proxy, las solicitudes salen directamente.
  • Manejo de respuestas. Las respuestas se descomprimen de forma transparente (gzip, br, zstd, deflate), y el mantenimiento de conexión está deshabilitado (WithDisableKeepAlive()) para que las conexiones agrupadas no fijen una sola huella/IP entre solicitudes.

Nada de esto necesita configuración para funcionar — los valores predeterminados anteriores se aplican automáticamente. Solo las credenciales de proxy son extras opcionales.

Desarrollo

Todo lo relacionado con Go vive en app/, así que usa el makefile desde la raíz del repositorio o pasa -C app a la cadena de herramientas:

make build          # compile the binary
make test           # run tests

go -C app build ./...      # compile everything
go -C app test ./...       # run tests
go -C app vet ./...        # static checks

Consulta CONTRIBUTING.md para las pautas de solicitudes de extracción (pull requests).

Licencia

Publicado bajo la Licencia MIT.