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.
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
| Herramienta | Descripción |
|---|---|
web_search | Ejecuta una o más consultas en paralelo y devuelve fragmentos deduplicados y reordenados por relevancia con enlaces. |
web_search_images | Ejecuta una o más consultas de imágenes en paralelo y devuelve resultados de imágenes deduplicados por consulta. |
web_scrape | Descarga 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ámetro | Tipo | Predeterminado | Notas |
|---|---|---|---|
queries | []string | — | Requerido. Se ejecuta en paralelo. |
max_results | int | 5 | Fragmentos por consulta, limitado a max_results de configuración (20). |
timeout_ms | int64 | 5000 | Tiempo de espera de toda la llamada; limitado a [min, max] de configuración. |
date | string | — | Filtro de frescura: d (día), w (semana), m (mes), y (año). |
web_search_images
| Parámetro | Tipo | Predeterminado | Notas |
|---|---|---|---|
queries | []string | — | Requerido. Se ejecuta en paralelo. |
max_images | int | 5 | Imágenes por consulta, limitado a max_images de configuración (10). |
timeout_ms | int64 | 5000 | Tiempo de espera de toda la llamada; limitado a [min, max] de configuración. |
date | string | — | Filtro de frescura: d / w / m / y. |
web_scrape
| Parámetro | Tipo | Predeterminado | Notas |
|---|---|---|---|
urls | []string | — | Requerido. Se descarga en paralelo. |
robots_txt | bool | false | Respeta el robots.txt de la página. |
timeout_ms | int64 | 5000 | Tiempo de espera de toda la llamada; limitado a [min, max] de configuración. |
remove_links | bool | false | Elimina los enlaces Markdown del texto. |
max_chars | int | 20000 | Trunca el texto de la página a N caracteres, limitado a max_document_chars de configuración (20000). |
Tanto las listas
queries/urlsestán limitadas amax_queries(10) elementos por llamada. Las consultas deben tener ≤ 512 caracteres; las URLs ≤ 2048 caracteres y solohttp/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 status — success, 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:
| Mensaje | Significado |
|---|---|
invalid request | Los argumentos no pasaron la validación. |
too many queries / too many urls | La lista excede MAX_QUERIES. |
query must not be empty | Una consulta vacía, o una lista queries vacía. |
query is too long | Una consulta excede 512 caracteres. |
invalid url | Una URL está mal formada, supera 2048 caracteres, o no es http/https. |
robots.txt denied | robots_txt: true y la página no permite la obtención. |
upstream service unavailable | El 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 text | Todas las URLs fallaron. Las causas individuales se registran en stderr, no se devuelven. |
every query failed; the search upstream may be unreachable | Todas las consultas fallaron. |
internal server error | Cualquier 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_scrapemaneja solo HTML. Las páginas pasan por un extractor de legibilidad, que necesita marcado de artículo, por lo que las respuestastext/plainno producen nada y vuelven comostatus: "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_imagesno 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 constatus: "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:
| Bandera | Significado |
|---|---|
-env | Ruta 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
| Variable | Predeterminado | Notas |
|---|---|---|
MCP_TRANSPORT | stdio | stdio o http. |
MCP_NAME | mcp-retrieval | Nombre del servidor anunciado a los clientes. |
MCP_PATH | /mcp | Ruta 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)
| Variable | Predeterminado |
|---|---|
SERVER_PORT | 8080 |
SERVER_READ_TIMEOUT | 60s |
SERVER_WRITE_TIMEOUT | 60s |
Cliente HTTP y proxy
| Variable | Predeterminado | Notas |
|---|---|---|
MAX_IDLE_CONNS_PER_HOST | 100 | Agrupación de conexiones HTTP. |
PROXY_HOST | — | Opcional. Si se establece, las solicitudes se enrutan a través de un proxy de sesión rotativa. |
PROXY_PORT | — | Requerido cuando PROXY_HOST está establecido. |
PROXY_SCHEME | — | Requerido cuando PROXY_HOST está establecido. |
PROXY_LOGIN | — | Requerido cuando PROXY_HOST está establecido. |
PROXY_PASSWORD | — | Requerido 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
| Env | Predeterminado |
|---|---|
MAX_QUERIES | 10 |
DEFAULT_RESULTS | 5 |
MAX_RESULTS | 20 |
DEFAULT_TIMEOUT_MS | 5000 |
MAX_TIMEOUT_MS | 10000 |
MIN_TIMEOUT_MS | 1000 |
DEFAULT_IMAGES | 5 |
MAX_IMAGES | 10 |
DEFAULT_DOCUMENT_CHARS | 20000 |
MAX_DOCUMENT_CHARS | 20000 |
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 unDEFAULT_*mayor que suMAX_*simplemente produceMAX_*; - si
MIN_*excedeMAX_*, 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)
| Env | Predeterminado | Notas |
|---|---|---|
LOG_MODE | local | local → 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 unUser-Agentcoincidente 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_HOSTestá configurado, el adaptador instala una fábrica de proxies que agrega unsession-<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.