blowsh-mcp
Kit de herramientas MCP con transporte Docker para búsqueda web, extracción, rastreo y extracción de enlaces con renderizado JS mediante Browsh. Sin claves API, protegido contra SSRF, MIT.
Documentación
blowsh-mcp
Servidor de Protocolo de Contexto Modelo (MCP) para Navegación de Terminal con Capacidad JavaScript usando Browsh
¿Qué es blowsh-mcp?
blowsh-mcp es un servidor de Protocolo de Contexto Modelo (MCP) que expone el poder de Browsh—un navegador de terminal con capacidad total de JavaScript—a cualquier Agente de IA, agente de IDE o cliente MCP. Este proyecto permite que tu IA obtenga y renderice cualquier página web moderna, incluidas aquellas que requieren JavaScript, y reciba el resultado como texto plano fácil de analizar, HTML o Markdown.
Mnemónico: "blowsh" = servidor MCP impulsado por Browsh.
Características Principales
- Herramienta fetch_web: Herramienta unificada para extracción de texto plano legible, HTML o Markdown (después del renderizado completo de JS). Soporta extracción CSS
selector, límites de salidamax_chars, sondeo de estabilización JSwait_ms, además de extras de paridad DonSeTch:focus(relevancia BM25 — reduce tokens 50-80%),toc/section(esquema económico → sección específica), sondamust_contain(COINCIDENCIA/NO-COINCIDENCIA + extractos, ~60 tokens),archive(resurrección de Waybackauto/only) ystitch(seguirrel=nexthasta 6 partes, mismo host). - Herramienta search_web: Descubre páginas mediante 4 motores renderizados (DuckDuckGo HTML, Bing, Brave, Mojeek) fusionados por consenso entre motores — además de verticales de intención (código→GitHub, paper→arXiv, noticias→HN, entidad→Wikipedia). Soporta
query_variants(formulaciones alternativas en paralelo),intent(auto/web/código/paper/noticias/entidad),deadline_ms(presupuesto estricto), paginación yenrich(markdown de los 3 principales). - Herramienta crawl_web: Rastreo consciente de sitemaps — dos fases (mapa + contenido), frontera clasificada por enfoque (BM25-lite), ritmo del Gobernador, robots.txt, tokens de reanudación (30 min), delta
since_last, globs (include/exclude),same_host, presupuestos (max_pages/max_total_chars/deadline_s). - Herramienta extract_links: Lista hipervínculos (texto + URL absoluta) de cualquier página renderizada con JS para seguir la navegación.
- Herramienta fetch_web_batch: Obtiene hasta 10 URLs en una sola llamada con aislamiento de errores por URL.
- Protección SSRF: Rechaza solicitudes a direcciones de bucle local, privadas, de enlace local o reservadas (resueltas por DNS), protegiendo el navegador del lado del servidor.
- Documentación de herramientas optimizada para IA: Entradas, salidas y casos de uso ilustrados diseñados para automatización fluida de agentes. Las herramientas lanzan errores estructurados con códigos de estado HTTP (
isErroren respuestas MCP). - Gestión robusta de Browsh: Lanza Browsh una vez, lo mantiene en ejecución, reutiliza un singleton ligero en RAM/CPU, apagado elegante al salir.
- Caché de renderizado en memoria con TTL: Las búsquedas repetidas se sirven al instante sin volver a renderizar.
- Diseñado para PaaS, Nube, herramientas de IA locales y agentes de IDE.
Enlaces
- Navegador CLI Browsh — El motor de renderizado.
- Firefox — Requerido como backend para Browsh.
- Especificación del Protocolo de Contexto Modelo (MCP) — El protocolo agente/servidor.
Cómo Funciona
- La IA/Agente realiza una solicitud MCP:
fetch_web(URL única, ahora con enfoque/toc/sección/debe_contener/archivo/unión),search_web(consulta + variantes/intención),crawl_web(semilla + presupuestos),extract_links(URL) ofetch_web_batch(hasta 10 URLs). - blowsh-mcp lanza Browsh en modo servidor HTTP (en el primer uso) y lo reutiliza para todas las llamadas posteriores.
- blowsh-mcp solicita la salida cruda de Browsh, usando
X-Browsh-Raw-Mode: PLAIN(para texto),DOM(para HTML), u obtiene HTML y luego lo convierte a Markdown; paracrawl_web, recorre sitemaps + frontera mediante el mismo singleton de Browsh + XML de sitemap sobre axios. - La página (después de la ejecución completa de JS) se devuelve como texto plano de terminal, DOM HTML enriquecido o Markdown limpio—la IA/agentes eligen el tipo de salida para coincidir con el procesamiento posterior; el rastreo devuelve
{pages, map, queued, skipped, stop}. - Los resultados se almacenan en caché en memoria (TTL) para que las búsquedas repetidas sean instantáneas; cada solicitud se verifica con SSRF antes de llegar al navegador.
Inicio Rápido (Docker — Imagen Preconstruida)
La imagen se publica en Registro de Contenedores de GitHub y se reconstruye automáticamente en
cada push de main mediante GitHub Actions — sin necesidad de Firefox/Browsh/html2markdown en el host:
docker pull ghcr.io/mokhtarabadi/blowsh-mcp:latest
docker run --rm -i ghcr.io/mokhtarabadi/blowsh-mcp:latest
El indicador
-ies obligatorio: el servidor MCP habla JSON-RPC sobre stdin/stdout. Mantenlo interactivo y canaliza solicitudes, o apunta tu cliente MCP hacia él (consulta Configuración de Cliente de IA a continuación).
Ejemplo de Uso
Desde Claude, Cursor o cualquier agente habilitado para MCP:
{
"tool": "search_web",
"params": { "query": "bitcoin price today", "max_results": 5 }
}
// → Ranked results with URLs + snippets → feed top URL to fetch_web
{
"tool": "fetch_web",
"params": { "url": "https://coindesk.com/price/bitcoin/", "type": "plain" }
}
// → Returns readable plain text (live price as text table, etc)
{
"tool": "fetch_web",
"params": { "url": "https://coindesk.com/price/bitcoin/", "type": "markdown", "selector": "main", "wait_ms": 3000 }
}
// → Markdown of <main> only, after JS settles ("# Bitcoin Price\n\n| Time | Price | ...")
{
"tool": "extract_links",
"params": { "url": "https://example.com", "limit": 20 }
}
// → [{"text": "Learn more", "url": "https://iana.org/domains/example"}, ...]
{
"tool": "fetch_web_batch",
"params": { "urls": ["https://a.com", "https://b.com"], "type": "markdown" }
}
// → Per-URL results; a failing page never fails the batch
{
"tool": "fetch_web",
"params": { "url": "https://example.com/long-docs", "type": "markdown", "focus": "authentication error handling", "toc": false }
}
// → Only BM25-relevant blocks (50-80% shorter)
{
"tool": "fetch_web",
"params": { "url": "https://example.com/article", "type": "markdown", "must_contain": "/CVE-2026-\\d+/" }
}
// → MATCH/NO-MATCH + 3 excerpts (~60 tokens) instead of full page
{
"tool": "crawl_web",
"params": { "url": "https://docs.example.com", "mode": "full", "focus": "authentication", "max_pages": 20 }
}
// → {pages:[{url, title, kind, markdown, chars, quality}], map, queued, stop, resume}
La IA recibe:
- Con
type: plain: texto plano legible puro (tablas, listas, contenido del cuerpo principal; ideal para NLP/resumen o ingesta de contexto de terminal). - Con
type: html: el marcado HTML completo, después de todo el JavaScript. Úsalo para análisis de elementos, construcción de grafos de enlaces, raspados complejos, etc. - Con
type: markdown: una versión Markdown limpia—mejor para fragmentos de contexto de LLM, pipelines semánticos y flujos de trabajo amigables con IA. - Con
toc: true: solo esquema de encabezados (# Table of Contents); consection: "Heading"el markdown de esa sección. - Con
must_contain: veredicto de sonda (MATCH/NO-MATCH) + ≤3 extractos. - Con
archive: "auto": instantánea de Wayback etiquetada con fecha cuando falla la búsqueda en vivo. - Con
stitch: true: artículo multipágina unido con marcadores*(part N)*. - Los errores son estructurados: las respuestas MCP establecen
isError: truecon un mensajeFetchErrorque incluye el estado HTTP cuando está disponible.
Estructura del Proyecto
src/server.ts— Servidor MCP que expone herramientas (5 herramientas en v2.3.0).src/browshManager.ts— Lanzar, monitorear, apagar Browsh.src/tools/fetchWeb.ts— fetchWeb (texto plano/html/markdown/pdf; selector/max_chars/wait_ms + enfoque/toc/sección/debe_contener/archivo/unión).src/tools/searchWeb.ts— search_web (consenso DDG+Bing+Brave+Mojeek + verticales de intención + variantes_de_consulta + fecha_límite).src/tools/crawlWeb.ts— crawl_web (descubrimiento de sitemap, frontera BM25-lite, ritmo del Gobernador, tokens de reanudación, desde_última).src/tools/extractLinks.ts— extract_links (hipervínculos del DOM renderizado).src/tools/fetchWebBatch.ts— fetch_web_batch (multi-URL, aislamiento de errores por URL).src/tools/html2markdownManager.ts— Envoltorio para CLI html2markdown.src/ssrf.ts— Protección SSRF (bloquea objetivos privados/bucle local/reservados).src/cache.ts— Caché de renderizado TTL en memoria.src/extract.ts— Extracción de contenido principal, ayudantes de selector, truncamiento, además de enfoque BM25, toc/sección, debe_contener, ayudantes de unión.src/errors.ts—FetchError+ formato de mensajes.README.md— Este archivo.Dockerfile— Contenedor multietapa (compila TS, agrupa Firefox, Browsh, html2markdown)..github/workflows/docker-publish.yml— CI/CD: compila y publica la imagen en ghcr.io enmain/v*..env— Anulaciones de configuración. Consulta.env.examplepara todas las opciones.
Instalación
Requisitos:
- Node.js >= 20.18
- Firefox instalado y en PATH
- CLI Browsh instalado y en PATH
- CLI html2markdown instalado y en PATH
- En Debian/Ubuntu, instala con:
wget -O /tmp/html2markdown.deb "https://github.com/JohannesKaufmann/html-to-markdown/releases/download/v2.5.2/html2markdown_2.5.2_linux_amd64.deb" sudo apt-get install -y /tmp/html2markdown.deb rm /tmp/html2markdown.deb - O usa el binario preconstruido para tu sistema operativo desde la página de versiones.
- En Debian/Ubuntu, instala con:
¿Prefieres Docker? Omite las instalaciones del lado del host por completo—la imagen multietapa incluye Firefox, Browsh y html2markdown. El camino más rápido es la imagen publicada (
ghcr.io/mokhtarabadi/blowsh-mcp:latest, consulta Inicio Rápido); para compilarla tú mismo:docker build -t blowsh-mcp:latest . docker run --rm -i blowsh-mcp:latest
git clone https://github.com/mokhtarabadi/blowsh-mcp.git
cd blowsh-mcp
npm install
npm run build
Ejecutar el servidor MCP
Después de compilar, inicia el servidor usando:
node dist/server.js
Reemplaza dist/server.js con la ruta correcta si tu salida de compilación difiere.
Crea un archivo .env según sea necesario para la configuración. Por ejemplo:
MCP_TRANSPORT=stdio
BROWSH_FIREFOX_PATH=/usr/bin/firefox-esr
HTML2MARKDOWN_PATH=html2markdown
CACHE_TTL_MS=300000
BROWSH_REQUEST_TIMEOUT_MS=30000
ALLOW_PRIVATE_URLS=false
NODE_ENV=production
BROWSH_FIREFOX_PATHte permite personalizar el ejecutable de Firefox usado por Browsh durante la operación headless/HTTP.HTML2MARKDOWN_PATHte permite especificar una ruta personalizada al binario html2markdown (predeterminado:html2markdownen PATH).CACHE_TTL_MS,BROWSH_REQUEST_TIMEOUT_MSyALLOW_PRIVATE_URLSajustan la caché de renderizado, el tiempo de espera por solicitud y la protección SSRF respectivamente.- El puerto/host HTTP de Browsh NO son configurables.
Documentación del Proyecto
| Archivo | Audiencia | Propósito |
|---|---|---|
AGENTS.md | Agentes | Reglas operativas, salvaguardas, ciclo de vida de tareas |
DESIGN.md | Todos | Lenguaje de diseño de respuestas/salidas MCP |
docs/architecture.md | Desarrolladores | Visión general del sistema, cableado de componentes |
docs/data_model.md | Desarrolladores | Esquemas de entrada/salida de herramientas y modelo de errores |
docs/conventions.md | Desarrolladores | Estándar de fecha/hora, pautas SOLID |
CHANGELOG.md | Todos | Historial de versiones (Mantener un Changelog) |
tasks/ | Equipo | Archivos de tareas Kanban (backlog → archivo) |
Este README es el punto de entrada orientado al usuario; las reglas orientadas a agentes viven en AGENTS.md y son lectura obligatoria antes de cualquier implementación.
API de Herramientas
| Nombre | Params | Caso de uso/Descripción para IA |
|---|---|---|
| fetch_web | { url, type: "plain"|"html"|"markdown"|"pdf", selector?, max_chars?, wait_ms?, focus?, toc?: boolean, section?, must_contain?, archive?: "auto"|"only"|"off", stitch?: boolean, deadline_ms?: number, tier?: "auto"|"1"|"2", links?: boolean, media?: boolean, since_last?: boolean, offset?: number } | Obtiene una página tras el renderizado de JS como texto/HTML/Markdown. selector (CSS) extrae solo el elemento coincidente; max_chars limita la salida; wait_ms sondea hasta que JS se estabiliza. focus filtra bloques relevantes por BM25 (50-80% más corto); toc devuelve el esquema, section devuelve el contenido de un encabezado; must_contain la sonda devuelve MATCH/NO-MATCH + extractos; archive resucita enlaces muertos vía Wayback; stitch sigue rel=next hasta 6 partes; deadline_ms presupuesto duro; tier auto/1/2; links/media alternadores; since_last detección de contenido sin cambios; offset reanudar. type: pdf descarga el PDF directamente (máx. 20 MB) y extrae texto vía pdftotext — selector/wait_ms/max_chars no aplican. |
| search_web | { query: string, max_results?: number, page?: number, enrich?: boolean, query_variants?: string[] (max2), intent?: "auto"|"web"|"code"|"paper"|"news"|"entity", deadline_ms?: number } | Busca en la web (DDG+Bing+Brave+Mojeek fusionados por consenso + verticales por intención: GitHub/Wikipedia/arXiv/HN) y devuelve [{title, url, snippet, fetched_at}]. query_variants busca formulaciones alternativas en paralelo (fusionadas); intent selecciona verticales; deadline_ms limita la llamada con error de plazo honesto (nunca se cuelga); page 1–10; enrich: true reemplaza los 3 primeros fragmentos con markdown obtenido (presupuesto de 45 s). fetched_at es época UTC en ms. Alimenta las URLs a fetch_web/extract_links. |
| crawl_web | { url: string, mode?: "full"|"map"|"content", focus?, max_pages?: number, max_depth?: number, max_total_chars?: number, per_page_max?: number, include_paths?: string[], exclude_paths?: string[], same_host?: boolean, respect_robots?: boolean, deadline_s?: number, resume?: string, since_last?: boolean } | Rastrea un sitio desde la semilla: descubrimiento de sitemap en dos fases + frontera clasificada por enfoque (BM25-lite) + ritmo del Gobernador (varianza de permanencia + retroceso). mode full=mapa+contenido, map=solo inventario, content=BFS desde la semilla. Presupuestos: focus clasifica la frontera, max_pages/max_total_chars/deadline_s limitan la ejecución; resume token (30 min respaldado en disco) continúa; since_last omite páginas sin cambios (huella <24h). Devuelve {seed, pages:[{url,title,kind,chars,quality}], map, queued, skipped, stop, elapsed_s, resume, crawl_delay}. |
| extract_links | { url: string, limit?: number } | Devuelve todos los hipervínculos ({text, url}, absolutos) presentes en una página renderizada con JS, para seguir la navegación sin volcados completos del DOM. |
| fetch_web_batch | { urls: string[], type: "plain"|"html"|"markdown", selector?, max_chars?, wait_ms? } | Obtiene hasta 10 URLs en una sola llamada (consciente de caché). Devuelve {url, ok, content|error} por URL — un fallo nunca mata el lote. |
Devoluciones
type: plain: Texto legible estilo terminal, ejecutado con JS (o cadena de error).type: html: Cadena de marcado HTML posterior a JS (o cadena de error). Conselector, solo el HTML del elemento coincidente.type: markdown: Conversión a Markdown del contenido principal o del elemento seleccionado (o cadena de error). Enlaces, encabezados, listas y estructura de página conservados para contexto amigable con IA.type: pdf: texto plano extraído del documento PDF (vía pdftotext, límite de 20 MB).- Los errores son estructurados: una respuesta MCP con
isError: truey un mensajeFetchErrorque incluye el estado HTTP cuando es conocible (nunca una cadena vacía silenciosa).
Variables de Entorno
Configúralas vía .env (cargado automáticamente) o el entorno:
| Variable | Predeterminado | Descripción |
|---|---|---|
BROWSH_FIREFOX_PATH | firefox | Binario de Firefox usado por Browsh (p. ej. /usr/bin/firefox-esr). |
HTML2MARKDOWN_PATH | html2markdown | Ruta al binario html2markdown. |
BROWSH_REQUEST_TIMEOUT_MS | 30000 | Tiempo de espera de solicitud por renderizado (ms). |
PDF_MAX_BYTES | 20971520 | Tamaño máximo de archivo PDF en bytes para fetch_web type: pdf. |
BROWSH_RECYCLE_REQUESTS | 100 | Número de solicitudes tras las cuales se recicla el proceso del navegador. |
BROWSH_IDLE_TIMEOUT_MS | 600000 | Tiempo de inactividad en ms antes de que se elimine el proceso del navegador (10 min). |
CACHE_TTL_MS | 300000 | TTL de caché de renderizado en memoria (ms). |
ALLOW_PRIVATE_URLS | false | Configura true para deshabilitar la protección SSRF para objetivos de bucle local/privados. |
MCP_TRANSPORT | stdio | Tipo de transporte (solo stdio implementado). |
NODE_ENV | production | Entorno de Node. |
Selección de Herramientas Guiada por IA
- Comienza con
search_web: Para descubrir páginas, ejecuta una consulta y elige las mejores URLs de resultados; luego obténlas. Usaintentcuando conozcas el dominio (código/artículo/noticia/entidad) yquery_variantspara recuerdos ambiguos; configuradeadline_mspara acotar la latencia. - Usa
fetch_webpara páginas individuales:plaincuando necesites salida legible rápida para resumir/clasificar;htmlpara analizar elementos, enlaces o tablas;markdownpara fragmentos de contexto amigables con LLM. Añadeselector/max_chars/wait_mspara ser eficiente en tokens y obtener contenido establecido y relevante. Nuevo:focuscuando conozcas el tema (reduce tokens 50-80%),toc→sectionpara dos llamadas baratas en páginas largas,must_containpara preguntas de verificación (MATCH + extractos, ~60 tokens),archive=autopara enlaces muertos,stitchpara paginación. - Usa
crawl_webpara sitios: Consciente de sitemap, clasificado por enfoque, con presupuestos. Comienza conmode: mappara inventario barato; luegomode: full, focus: "topic"para páginas relevantes. Reanuda con el tokenresumesi se detuvo temprano. - Usa
extract_linksantes de rastreos profundos: Sigue la navegación de forma económica en lugar de obtener DOMs completos. - Usa
fetch_web_batchpara múltiples fuentes: Una llamada en lugar de N idas y vueltas; los fallos se aíslan por URL.
Manejo de errores:
Las herramientas lanzan FetchError y MCP devuelve isError: true con un mensaje accionable — protocolos inválidos, bloqueos SSRF, selectores sin coincidencia, códigos de estado HTTP y fallos de renderizado nunca son silenciosos.
Protocolo MCP: Configuración del Cliente de IA
Antes de configurar tu cliente de IA (Claude, Cursor, etc.), debes
- Instalar dependencias:
npm install- Compilar el proyecto:
npm run build- Lanzar el servidor MCP desde la salida compilada:
node dist/server.js
Ejemplo de configuración para Claude Desktop o Cursor:
{
"mcpServers": {
"blowsh": {
"command": "node",
"args": ["dist/server.js"],
"env": {}
}
}
}
Ejemplo de configuración para opencode (proyecto opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"blowsh": {
"type": "local",
"command": ["docker", "run", "--rm", "-i", "ghcr.io/mokhtarabadi/blowsh-mcp:latest"],
"enabled": true,
"timeout": 120000
}
},
"permission": { "blowsh_*": "allow" }
}
La forma Docker no necesita binarios del lado del host; la imagen incluye Firefox, Browsh y html2markdown. Reinicia opencode después de guardar (la configuración se carga una vez al inicio).
Apagado Elegante
blowsh-mcp captura SIGINT/SIGTERM y asegura que Browsh se termine limpiamente—sin navegadores huérfanos.
Seguridad y Consideraciones
- El servidor ejecuta Browsh localmente y obtiene vía HTTP localhost.
- Protección SSRF: Por defecto,
fetch_web/search_web/extract_links/fetch_web_batchrechazan URLs que resuelven a rangos de IP de bucle local, privados, link-local o reservados (verificado vía DNS). ConfiguraALLOW_PRIVATE_URLS=truepara deshabilitar — no recomendado. - Sin exposición pública a menos que se configure explícitamente MCP HTTP/streamable.
- Nunca expongas puertos a la web abierta sin firewall.
- Usa variables de entorno para secretos/configuración.
Extensión
Añade nuevas herramientas en src/tools/, expórtalas en src/server.ts y documenta.
Los clientes de IA descubrirán automáticamente los docstrings.
Solución de Problemas
- Si fetchPlain devuelve 404 o falla al renderizar JS: verifica que Firefox y Browsh estén instalados y en PATH.
- Si Firefox no se encuentra o falla al iniciar, configura
BROWSH_FIREFOX_PATHen.envpara especificar la ruta completa a tu instalación de Firefox. - El puerto/host de Browsh son fijos—no hay configuración de entorno o CLI para cambiarlos.
- Para máxima seguridad, ejecuta en un contenedor.
Licencia
MIT
Autor: Mohammad Reza Mokhtarabadi mmokhtarabadi@gmail.com