Para dar búsqueda web a un agente LLM autohospedado, conéctalo a un servidor MCP que devuelva resultados estructurados y deja que el modelo lo invoque como herramienta. Tienes dos opciones reales. Autohospedar SearXNG junto a un navegador headless y cargar tú mismo con toda la capa anti-bot: fingerprints TLS, retos gestionados de Cloudflare, resolución de CAPTCHA y extracción de HTML a texto. O apuntar el agente a una API de búsqueda alojada por MCP y recibir JSON ya parseado sin mantener un pool de proxies. Este tutorial monta la segunda vía en unos diez minutos usando el servidor MCP remoto de Scavio en https://mcp.scavio.dev/mcp, y muestra un fallback en HTTP plano para harnesses que aún no hablan MCP. SearXNG funciona de sobra cuando tus objetivos cooperan y no te importa el mantenimiento; pero en cuanto Google limita la tasa de tu IP de datacenter o Cloudflare lanza un reto Turnstile, acabas manteniendo infraestructura en vez de construir tu agente. Un endpoint gestionado saca esa superficie de fallo de tu máquina: Google SERP, Reddit, YouTube, Amazon, Walmart y TikTok vuelven como JSON tipado desde una sola clave. Sin Puppeteer, sin pasada por markitdown, sin cola de CAPTCHA.
Requisitos previos
- Una clave de API de Scavio (50 créditos gratis al registrarte, sin tarjeta) desde scavio.dev
- Un host de agente compatible con MCP: Claude Code, Cursor, VS Code, Windsurf, opencode o un harness propio como Pi.dev
- Python 3.10+ o Node 18+ para la vía de fallback por HTTP
- Familiaridad básica con la configuración de llamadas a herramientas de tu agente
Guia paso a paso
Paso 1: Decide: SearXNG autohospedado o un MCP de búsqueda alojado
SearXNG te da un meta-buscador privado que controlas al 100 %, pero heredas todos los problemas anti-bot: las IP de datacenter reciben 403, los retos de Cloudflare exigen renderizar en un navegador completo y tendrás que escribir tu propia extracción de HTML a texto. Un MCP de búsqueda alojado devuelve JSON limpio y absorbe la pelea del fingerprinting y los límites de tasa. Si necesitas algo totalmente local sin llamadas a la nube, monta SearXNG. Si quieres el agente funcionando hoy, usa el MCP alojado y dedica tu tiempo a la lógica del agente en lugar de a rotar proxies.
Paso 2: Registra el servidor MCP de Scavio en tu host de agente
El servidor MCP remoto vive en https://mcp.scavio.dev/mcp y se autentica con la cabecera x-api-key (ojo: la API REST usa Authorization: Bearer, pero el transporte MCP usa x-api-key). Añádelo a la configuración de tu cliente MCP. En Claude Code, Cursor o cualquier cliente que lea un .mcp.json, el bloque de abajo registra todas las herramientas de Scavio, incluidas Google SERP, la búsqueda en Reddit y los endpoints de TikTok.
{
"mcpServers": {
"scavio": {
"type": "http",
"url": "https://mcp.scavio.dev/mcp",
"headers": {
"x-api-key": "${SCAVIO_API_KEY}"
}
}
}
}Paso 3: Confirma que las herramientas quedan expuestas al modelo
Reinicia el host del agente para que haga el handshake con el servidor MCP. El modelo debería ver ahora herramientas de búsqueda que puede invocar por su cuenta; una búsqueda en Google devuelve resultados orgánicos, y poner light_request en false devuelve además el knowledge graph, el people-also-ask y las búsquedas relacionadas en la misma llamada. Esa llamada más rica cuesta 2 créditos en vez de 1, lo cual importa si tu agente abre una pregunta en decenas de búsquedas.
Paso 4: Añade un fallback en HTTP plano para harnesses sin MCP
Si tu harness (un bucle propio en Pi.dev, un script con function-calling de OpenAI, un Ollama pelado) no habla MCP, registra el endpoint como una herramienta normal. Todos los endpoints REST usan Authorization: Bearer. El endpoint de Google reemplaza la cadena SearXNG + navegador + extracción por un único POST que devuelve JSON parseado, así que no hay HTML que limpiar ni nada que pasar por markitdown.
Paso 5: Gestiona los modos de fallo que te preocupaban
Los retos de Cloudflare, los CAPTCHA y los límites de tasa de los buscadores se manejan del lado del servidor, así que tu agente nunca ve una página de Turnstile. Los objetivos públicos e indexados (Google SERP, hilos de Reddit, listados de YouTube, productos de Amazon y Walmart) vuelven estructurados. Lo que una API de búsqueda no puede hacer: páginas tras un login, o un sitio concreto que solo se renderiza tras mucho JavaScript y no está en ningún índice. Para eso sigues necesitando un navegador real. Enruta el subconjunto público e indexado por la API y reserva tu navegador headless para el puñado de objetivos que de verdad lo requieren.
Ejemplo en Python
import os, requests
HEADERS = {
"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}",
"Content-Type": "application/json",
}
def web_search(query: str, rich: bool = False) -> dict:
"""Tool the agent can call. rich=True also returns
knowledge_graph + people_also_ask (2 credits vs 1)."""
r = requests.post(
"https://api.scavio.dev/api/v1/google",
headers=HEADERS,
json={"query": query, "light_request": not rich},
timeout=30,
)
r.raise_for_status()
data = r.json()
# Feed only the useful text to the LLM, no HTML cleanup needed
return {
"organic": [
{"title": o["title"], "link": o["link"], "snippet": o.get("snippet", "")}
for o in data.get("organic", [])[:8]
],
"answer_box": data.get("knowledge_graph") if rich else None,
}
if __name__ == "__main__":
print(web_search("self-hosted vector database comparison", rich=True))Ejemplo en JavaScript
const HEADERS = {
Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`,
"Content-Type": "application/json",
};
async function webSearch(query, rich = false) {
const res = await fetch("https://api.scavio.dev/api/v1/google", {
method: "POST",
headers: HEADERS,
body: JSON.stringify({ query, light_request: !rich }),
});
if (!res.ok) throw new Error(`Scavio ${res.status}`);
const data = await res.json();
return {
organic: (data.organic || []).slice(0, 8).map((o) => ({
title: o.title,
link: o.link,
snippet: o.snippet ?? "",
})),
answerBox: rich ? data.knowledge_graph ?? null : null,
};
}
webSearch("self-hosted vector database comparison", true).then(console.log);Salida esperada
Un objeto JSON que tu agente puede meter directo en su ventana de contexto: un array `organic` de hasta 8 resultados, cada uno con `title`, `link` y `snippet`, más un `answer_box` opcional poblado desde el knowledge graph cuando pasas rich=True. Sin HTML, sin quitar boilerplate, sin página de CAPTCHA. Una llamada básica a Google cuesta 1 crédito (0,005 $ en pago por uso); la llamada rica con knowledge graph y people-also-ask cuesta 2 créditos. Los 50 créditos gratis del registro bastan para dejar esto montado y correr unas 25 búsquedas ricas antes de decidir si conservas SearXNG para los casos totalmente locales.