blowsh-mcp

Kit de ferramentas MCP com transporte Docker para busca web, fetch, crawl e extração de links com renderização JS via Browsh. Sem chaves de API, protegido contra SSRF, MIT.

Documentação

blowsh-mcp

Servidor de Protocolo de Contexto de Modelo para Navegação em Terminal com Suporte a JavaScript usando Browsh


O que é blowsh-mcp?

blowsh-mcp é um servidor de Protocolo de Contexto de Modelo (MCP) que expõe o poder do Browsh—um navegador de terminal totalmente compatível com JavaScript—para qualquer Agente de IA, agente de IDE ou cliente MCP. Este projeto permite que sua IA busque e renderize qualquer página web moderna, incluindo aquelas que exigem JavaScript, e receba o resultado como texto simples, HTML ou Markdown de fácil análise.

Mnemônico: "blowsh" = servidor MCP com tecnologia Browsh.


Principais Recursos

  • Ferramenta fetch_web: Ferramenta unificada para extração de texto simples legível, HTML ou Markdown (após renderização completa de JS). Suporta extração por CSS selector, limites de saída max_chars, polling de estabilização JS wait_ms, além de extras de paridade DonSeTch: focus (relevância BM25 — reduz tokens em 50-80%), toc/section (esboço econômico → seção direcionada), sonda must_contain (CORRESPONDE/NÃO-CORRESPONDE + trechos, ~60 tokens), archive (ressurreição via Wayback auto/only) e stitch (seguir rel=next até 6 partes, mesmo host).
  • Ferramenta search_web: Descubra páginas via 4 mecanismos renderizados (DuckDuckGo HTML, Bing, Brave, Mojeek) fundidos por consenso entre mecanismos — além de verticais de intenção (código→GitHub, artigo→arXiv, notícias→HN, entidade→Wikipedia). Suporta query_variants (formulações alternativas paralelas), intent (auto/web/código/artigo/notícias/entidade), deadline_ms (orçamento rígido), paginação e enrich (top-3 em markdown).
  • Ferramenta crawl_web: Rastreamento ciente de sitemap — duas fases (mapa + conteúdo), fronteira classificada por foco (BM25-lite), ritmo do Governor, robots.txt, tokens de retomada (30 min), delta since_last, globs (include/exclude), same_host, orçamentos (max_pages/max_total_chars/deadline_s).
  • Ferramenta extract_links: Liste hiperlinks (texto + URL absoluta) de qualquer página renderizada por JS para navegação posterior.
  • Ferramenta fetch_web_batch: Busque até 10 URLs em uma única chamada com isolamento de erros por URL.
  • Proteção SSRF: Recusa solicitações para endereços loopback, privados, link-local ou reservados (resolvidos por DNS), protegendo o navegador do lado do servidor.
  • Documentação de ferramentas otimizada para IA: Entradas, saídas e casos de uso ilustrados projetados para automação contínua de agentes. As ferramentas geram erros estruturados com códigos de status HTTP (isError nas respostas MCP).
  • Gerenciamento robusto do Browsh: Inicia o Browsh uma vez, mantém em execução, reutiliza um singleton leve em RAM/CPU, desligamento gracioso ao sair.
  • Cache de renderização em memória com TTL: Buscas repetidas são atendidas instantaneamente sem re-renderização.
  • Projetado para PaaS, Nuvem, ferramentas de IA locais e agentes de IDE.

Links


Como Funciona

  1. IA/Agente faz uma solicitação MCP: fetch_web (URL única, agora com foco/toc/seção/deve_conter/arquivo/emenda), search_web (consulta + variantes/intenção), crawl_web (semente + orçamentos), extract_links (URL) ou fetch_web_batch (até 10 URLs).
  2. blowsh-mcp inicia o Browsh no modo servidor HTTP (no primeiro uso) e o reutiliza para todas as chamadas posteriores.
  3. blowsh-mcp solicita a saída bruta do Browsh, usando X-Browsh-Raw-Mode: PLAIN (para texto), DOM (para HTML), ou busca HTML e depois converte para Markdown; para crawl_web, percorre sitemaps + fronteira via o mesmo singleton do Browsh + XML de sitemap via axios.
  4. A página (após execução completa de JS) é retornada como texto simples de terminal, DOM HTML rico ou Markdown limpo—IA/agentes escolhem o tipo de saída para corresponder ao processamento downstream; o rastreamento retorna {pages, map, queued, skipped, stop}.
  5. Os resultados são armazenados em cache na memória (TTL) para que buscas repetidas sejam instantâneas; toda solicitação é verificada por SSRF antes de chegar ao navegador.

Início Rápido (Docker — Imagem Pré-construída)

A imagem é publicada no Registro de Contêineres do GitHub e reconstruída automaticamente a cada push main via GitHub Actions — sem necessidade de Firefox/Browsh/html2markdown no lado do host:

docker pull ghcr.io/mokhtarabadi/blowsh-mcp:latest
docker run --rm -i ghcr.io/mokhtarabadi/blowsh-mcp:latest

A flag -i é obrigatória: o servidor MCP fala JSON-RPC via stdin/stdout. Mantenha-o interativo e envie solicitações via pipe, ou aponte seu cliente MCP para ele (veja Configuração do Cliente de IA abaixo).


Exemplo de Uso

Do Claude, Cursor ou qualquer 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}

A IA recebe:

  • Com type: plain: texto simples legível (tabelas, listas, conteúdo do corpo principal; ideal para NLP/sumarização ou ingestão de contexto de terminal).
  • Com type: html: a marcação HTML completa, após todo o JavaScript. Use para análise de elementos, construção de grafo de links, raspagens complexas, etc.
  • Com type: markdown: uma versão Markdown limpa—melhor para blocos de contexto de LLM, pipelines semânticos e consumo/fluxos de trabalho amigáveis à IA.
  • Com toc: true: apenas o esboço de cabeçalhos (# Table of Contents); com section: "Heading" o markdown dessa seção.
  • Com must_contain: veredito de sonda (MATCH/NO-MATCH) + ≤3 trechos.
  • Com archive: "auto": snapshot do Wayback rotulado com data quando a busca ao vivo falha.
  • Com stitch: true: artigo multi-página emendado com marcadores *(part N)*.
  • Erros são estruturados: respostas MCP definem isError: true com uma mensagem FetchError incluindo o status HTTP quando disponível.

Estrutura do Projeto

  • src/server.ts — Servidor MCP expondo ferramentas (5 ferramentas na v2.3.0).
  • src/browshManager.ts — Iniciar, monitorar, encerrar o Browsh.
  • src/tools/fetchWeb.ts — fetchWeb (texto simples/html/markdown/pdf; seletor/max_chars/wait_ms + foco/toc/seção/deve_conter/arquivo/emenda).
  • src/tools/searchWeb.ts — search_web (consenso DDG+Bing+Brave+Mojeek + verticais de intenção + variantes_de_consulta + prazo).
  • src/tools/crawlWeb.ts — crawl_web (descoberta de sitemap, fronteira BM25-lite, ritmo do Governor, tokens de retomada, desde_última).
  • src/tools/extractLinks.ts — extract_links (hiperlinks do DOM renderizado).
  • src/tools/fetchWebBatch.ts — fetch_web_batch (multi-URL, isolamento de erros por URL).
  • src/tools/html2markdownManager.ts — Wrapper para o CLI html2markdown.
  • src/ssrf.ts — Proteção SSRF (bloqueia alvos privados/loopback/reservados).
  • src/cache.ts — Cache de renderização TTL em memória.
  • src/extract.ts — Extração de conteúdo principal, auxiliares de seletor, truncamento, além de foco BM25, toc/seção, deve_conter, auxiliares de emenda.
  • src/errors.tsFetchError + formatação de mensagens.
  • README.md — Este arquivo.
  • Dockerfile — Contêiner multi-estágio (compila TS, empacota Firefox, Browsh, html2markdown).
  • .github/workflows/docker-publish.yml — CI/CD: compila e publica a imagem no ghcr.io em main/v*.
  • .env — Substituições de configuração. Veja .env.example para todas as opções.

Instalação

Requisitos:

  • Node.js >= 20.18
  • Firefox instalado e no PATH
  • CLI Browsh instalado e no PATH
  • CLI html2markdown instalado e no PATH
    • No Debian/Ubuntu, instale com:
      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
      
    • Ou use o binário pré-construído para seu sistema operacional na página de releases.

Prefere Docker? Pule as instalações no lado do host completamente — a imagem multi-estágio empacota Firefox, Browsh e html2markdown. O caminho mais rápido é a imagem publicada (ghcr.io/mokhtarabadi/blowsh-mcp:latest, veja Início Rápido); para compilá-la você mesmo:

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

Execute o servidor MCP

Após a compilação, inicie o servidor usando:

node dist/server.js

Substitua dist/server.js pelo caminho correto se a saída da sua compilação for diferente.

Crie um arquivo .env conforme necessário para configuração. Por exemplo:

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_PATH permite personalizar o executável do Firefox usado pelo Browsh durante operação headless/HTTP.
  • HTML2MARKDOWN_PATH permite especificar um caminho personalizado para o binário html2markdown (padrão: html2markdown no PATH).
  • CACHE_TTL_MS, BROWSH_REQUEST_TIMEOUT_MS e ALLOW_PRIVATE_URLS ajustam o cache de renderização, o tempo limite por solicitação e a proteção SSRF, respectivamente.
  • A porta/host HTTP do Browsh NÃO são configuráveis.

Documentação do Projeto

ArquivoPúblicoPropósito
AGENTS.mdAgentesRegras operacionais, salvaguardas, ciclo de vida de tarefas
DESIGN.mdTodosLinguagem de design de resposta/saída MCP
docs/architecture.mdDevsVisão geral do sistema, conexão de componentes
docs/data_model.mdDevsEsquemas de entrada/saída de ferramentas e modelo de erros
docs/conventions.mdDevsPadrão de data/hora, diretrizes SOLID
CHANGELOG.mdTodosHistórico de versões (Keep a Changelog)
tasks/EquipeArquivos de tarefas Kanban (backlog → arquivo)

Este README é o ponto de entrada voltado ao usuário; as regras voltadas ao agente estão em AGENTS.md e são leitura obrigatória antes de qualquer implementação.


API de Ferramentas

NomeParamsCaso de Uso/Descrição 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 }Busca uma página após renderização de JS como texto/HTML/Markdown. selector (CSS) extrai apenas o elemento correspondente; max_chars limita a saída; wait_ms faz polling até o JS estabilizar. focus filtra blocos relevantes por BM25 (50-80% mais curto); toc retorna um esboço, section retorna o conteúdo de um cabeçalho; must_contain sonda retorna MATCH/NO-MATCH + trechos; archive ressuscita links mortos via Wayback; stitch segue rel=next até 6 partes; deadline_ms orçamento rígido; tier auto/1/2; links/media alternâncias; since_last detecção de conteúdo inalterado; offset retomada. type: pdf baixa o PDF diretamente (máx. 20 MB) e extrai texto via pdftotext — selector/wait_ms/max_chars não se aplicam.
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 na web (DDG+Bing+Brave+Mojeek fundidos por consenso + verticais de intenção: GitHub/Wikipedia/arXiv/HN) e retorna [{title, url, snippet, fetched_at}]. query_variants busca formulações alternativas em paralelo (mescladas); intent seleciona verticais; deadline_ms limita a chamada com erro de prazo honesto (nunca trava); page 1–10; enrich: true substitui os 3 principais trechos por markdown buscado (orçamento de 45 s). fetched_at é o epoch UTC em ms. Alimente URLs para 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 }Rastreia um site a partir da semente: descoberta de sitemap em duas fases + fronteira classificada por foco (BM25-lite) + ritmo do Governor (variância de permanência + backoff). mode full=mapa+conteúdo, map=somente inventário, content=BFS a partir da semente. Orçamentos: focus classifica a fronteira, max_pages/max_total_chars/deadline_s limitam a execução; resume token (30 min com backup em disco) continua; since_last pula páginas inalteradas (impressão digital <24h). Retorna {seed, pages:[{url,title,kind,chars,quality}], map, queued, skipped, stop, elapsed_s, resume, crawl_delay}.
extract_links{ url: string, limit?: number }Retorna todos os hiperlinks ({text, url}, absolutos) presentes em uma página renderizada por JS, para seguir navegação sem despejos completos de DOM.
fetch_web_batch{ urls: string[], type: "plain"|"html"|"markdown", selector?, max_chars?, wait_ms? }Busca até 10 URLs em uma única chamada (ciente de cache). Retorna {url, ok, content|error} por URL — uma falha nunca mata o lote.

Retornos

  • type: plain: Texto legível estilo terminal, executado por JS (ou string de erro).
  • type: html: String de markup HTML pós-JS (ou string de erro). Com selector, apenas o HTML do elemento correspondente.
  • type: markdown: Conversão em Markdown do conteúdo principal ou elemento selecionado (ou string de erro). Links, cabeçalhos, listas e estrutura da página preservados para contexto amigável à IA.
  • type: pdf: texto simples extraído do documento PDF (via pdftotext, limite de 20 MB).
  • Erros são estruturados: uma resposta MCP com isError: true e uma mensagem FetchError que inclui o status HTTP quando conhecível (nunca uma string vazia silenciosa).

Variáveis de Ambiente

Defina estas via .env (carregado automaticamente) ou no ambiente:

VariávelPadrãoDescrição
BROWSH_FIREFOX_PATHfirefoxBinário do Firefox usado pelo Browsh (ex.: /usr/bin/firefox-esr).
HTML2MARKDOWN_PATHhtml2markdownCaminho para o binário html2markdown.
BROWSH_REQUEST_TIMEOUT_MS30000Timeout de solicitação por renderização (ms).
PDF_MAX_BYTES20971520Tamanho máximo do arquivo PDF em bytes para fetch_web type: pdf.
BROWSH_RECYCLE_REQUESTS100Número de solicitações após o qual o processo do navegador é reciclado.
BROWSH_IDLE_TIMEOUT_MS600000Tempo ocioso em ms antes de o processo do navegador ser encerrado (10 min).
CACHE_TTL_MS300000TTL do cache de renderização em memória (ms).
ALLOW_PRIVATE_URLSfalseDefina true para desativar a proteção SSRF para alvos loopback/privados.
MCP_TRANSPORTstdioTipo de transporte (apenas stdio implementado).
NODE_ENVproductionAmbiente do Node.

Seleção de Ferramentas Guiada por IA

  • Comece com search_web: Para descobrir páginas, execute uma consulta e escolha as melhores URLs de resultado; depois busque-as. Use intent quando souber o domínio (código/artigo/notícia/entidade) e query_variants para lembranças ambíguas; defina deadline_ms para limitar a latência.
  • Use fetch_web para páginas únicas: plain quando precisar de saída legível rápida para sumarização/classificação; html para analisar elementos, links ou tabelas; markdown para blocos de contexto amigáveis a LLM. Adicione selector/max_chars/wait_ms para ser eficiente em tokens e obter conteúdo estabilizado e relevante. Novo: focus quando souber o tópico (corta tokens em 50-80%), toc→section para duas chamadas baratas em páginas longas, must_contain para perguntas de verificação (MATCH + trechos, ~60 tokens), archive=auto para links mortos, stitch para paginação.
  • Use crawl_web para sites: Ciente de sitemap, classificado por foco, com orçamentos. Comece com mode: map para inventário barato; depois mode: full, focus: "topic" para páginas relevantes. Retome com token resume se parar cedo.
  • Use extract_links antes de rastreamentos profundos: Siga a navegação de forma barata em vez de buscar DOMs completos.
  • Use fetch_web_batch para múltiplas fontes: Uma chamada em vez de N idas e voltas; falhas são isoladas por URL.

Tratamento de erros: As ferramentas lançam FetchError e o MCP retorna isError: true com uma mensagem acionável — protocolos inválidos, bloqueios SSRF, seletores sem correspondência, códigos de status HTTP e falhas de renderização nunca são silenciosos.


Protocolo MCP: Configuração do Cliente de IA

Antes de configurar seu cliente de IA (Claude, Cursor, etc.), você deve

  1. Instalar dependências:    npm install
  2. Compilar o projeto:    npm run build
  3. Iniciar o servidor MCP a partir da saída compilada:    node dist/server.js

Exemplo de configuração para Claude Desktop ou Cursor:

{
  "mcpServers": {
    "blowsh": {
      "command": "node",
      "args": ["dist/server.js"],
      "env": {}
    }
  }
}

Exemplo de configuração para opencode (projeto 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" }
}

A forma Docker não precisa de binários no host; a imagem inclui Firefox, Browsh e html2markdown. Reinicie o opencode após salvar (a configuração é carregada uma vez na inicialização).


Desligamento Gracioso

blowsh-mcp captura SIGINT/SIGTERM e garante que o Browsh seja encerrado de forma limpa—sem navegadores órfãos.


Segurança e Considerações

  • O servidor executa o Browsh localmente e busca via HTTP localhost.
  • Proteção SSRF: Por padrão, fetch_web/search_web/extract_links/fetch_web_batch recusam URLs que resolvem para faixas de IP loopback, privadas, link-local ou reservadas (verificadas via DNS). Defina ALLOW_PRIVATE_URLS=true para desativar — não recomendado.
  • Sem exposição pública, a menos que o servidor MCP HTTP/streamable seja explicitamente configurado.
  • Nunca exponha portas à web aberta sem firewall.
  • Use variáveis de ambiente para segredos/configuração.

Extensão

Adicione novas ferramentas em src/tools/, exporte-as em src/server.ts e documente.
Os clientes de IA descobrirão docstrings automaticamente.


Solução de Problemas

  • Se fetchPlain retornar 404 ou falhar ao renderizar JS: verifique se Firefox e Browsh estão instalados e no PATH.
  • Se o Firefox não for encontrado ou falhar ao iniciar, defina BROWSH_FIREFOX_PATH em .env para especificar o caminho completo para sua instalação do Firefox.
  • A porta/host do Browsh são fixos—não há configuração de ambiente ou CLI para alterá-los.
  • Para máxima segurança, execute em um contêiner.

Licença

MIT


Autor: Mohammad Reza Mokhtarabadi mmokhtarabadi@gmail.com