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ídamax_chars, polling de estabilização JSwait_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), sondamust_contain(CORRESPONDE/NÃO-CORRESPONDE + trechos, ~60 tokens),archive(ressurreição via Waybackauto/only) estitch(seguirrel=nextaté 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 eenrich(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 (
isErrornas 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
- Navegador CLI Browsh — O mecanismo de renderização.
- Firefox — Necessário como backend para o Browsh.
- Especificação do Protocolo de Contexto de Modelo (MCP) — O protocolo agente/servidor.
Como Funciona
- 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) oufetch_web_batch(até 10 URLs). - blowsh-mcp inicia o Browsh no modo servidor HTTP (no primeiro uso) e o reutiliza para todas as chamadas posteriores.
- 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; paracrawl_web, percorre sitemaps + fronteira via o mesmo singleton do Browsh + XML de sitemap via axios. - 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}. - 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); comsection: "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: truecom uma mensagemFetchErrorincluindo 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.ts—FetchError+ 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 emmain/v*..env— Substituições de configuração. Veja.env.examplepara 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.
- No Debian/Ubuntu, instale com:
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_PATHpermite personalizar o executável do Firefox usado pelo Browsh durante operação headless/HTTP.HTML2MARKDOWN_PATHpermite especificar um caminho personalizado para o binário html2markdown (padrão:html2markdownno PATH).CACHE_TTL_MS,BROWSH_REQUEST_TIMEOUT_MSeALLOW_PRIVATE_URLSajustam 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
| Arquivo | Público | Propósito |
|---|---|---|
AGENTS.md | Agentes | Regras operacionais, salvaguardas, ciclo de vida de tarefas |
DESIGN.md | Todos | Linguagem de design de resposta/saída MCP |
docs/architecture.md | Devs | Visão geral do sistema, conexão de componentes |
docs/data_model.md | Devs | Esquemas de entrada/saída de ferramentas e modelo de erros |
docs/conventions.md | Devs | Padrão de data/hora, diretrizes SOLID |
CHANGELOG.md | Todos | Histórico de versões (Keep a Changelog) |
tasks/ | Equipe | Arquivos 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
| Nome | Params | Caso 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). Comselector, 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: truee uma mensagemFetchErrorque 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ável | Padrão | Descrição |
|---|---|---|
BROWSH_FIREFOX_PATH | firefox | Binário do Firefox usado pelo Browsh (ex.: /usr/bin/firefox-esr). |
HTML2MARKDOWN_PATH | html2markdown | Caminho para o binário html2markdown. |
BROWSH_REQUEST_TIMEOUT_MS | 30000 | Timeout de solicitação por renderização (ms). |
PDF_MAX_BYTES | 20971520 | Tamanho máximo do arquivo PDF em bytes para fetch_web type: pdf. |
BROWSH_RECYCLE_REQUESTS | 100 | Número de solicitações após o qual o processo do navegador é reciclado. |
BROWSH_IDLE_TIMEOUT_MS | 600000 | Tempo ocioso em ms antes de o processo do navegador ser encerrado (10 min). |
CACHE_TTL_MS | 300000 | TTL do cache de renderização em memória (ms). |
ALLOW_PRIVATE_URLS | false | Defina true para desativar a proteção SSRF para alvos loopback/privados. |
MCP_TRANSPORT | stdio | Tipo de transporte (apenas stdio implementado). |
NODE_ENV | production | Ambiente 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. Useintentquando souber o domínio (código/artigo/notícia/entidade) equery_variantspara lembranças ambíguas; definadeadline_mspara limitar a latência. - Use
fetch_webpara páginas únicas:plainquando precisar de saída legível rápida para sumarização/classificação;htmlpara analisar elementos, links ou tabelas;markdownpara blocos de contexto amigáveis a LLM. Adicioneselector/max_chars/wait_mspara ser eficiente em tokens e obter conteúdo estabilizado e relevante. Novo:focusquando souber o tópico (corta tokens em 50-80%),toc→sectionpara duas chamadas baratas em páginas longas,must_containpara perguntas de verificação (MATCH + trechos, ~60 tokens),archive=autopara links mortos,stitchpara paginação. - Use
crawl_webpara sites: Ciente de sitemap, classificado por foco, com orçamentos. Comece commode: mappara inventário barato; depoismode: full, focus: "topic"para páginas relevantes. Retome com tokenresumese parar cedo. - Use
extract_linksantes de rastreamentos profundos: Siga a navegação de forma barata em vez de buscar DOMs completos. - Use
fetch_web_batchpara 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
- Instalar dependências:
npm install- Compilar o projeto:
npm run build- 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_batchrecusam URLs que resolvem para faixas de IP loopback, privadas, link-local ou reservadas (verificadas via DNS). DefinaALLOW_PRIVATE_URLS=truepara 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_PATHem.envpara 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