npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@diegosouzacdv/jev-browser-mcp

v0.6.1

Published

Portable MCP server for bounded Playwright screen flows selected by Jev

Readme

Automação de tela com Jev e Playwright

Instalar em outros harnesses com npm/npx

O repositório também contém um pacote Node independente do harness. Ele fala MCP por stdio, executa o Playwright no mesmo processo e pode ser iniciado por qualquer harness que aceite command e args para um servidor MCP. O pacote usa o mesmo config/ui-testing.json deste repositório; não precisa instalar o Python do orquestrador. As opções específicas do pacote ficam isoladas no bloco jev_browser_mcp para preservar o contrato do servidor Python.

Instale a versão publicada do npm diretamente no harness. Para fixar uma versão em produção, use o número explícito no argumento do pacote:

{
  "mcpServers": {
    "jev-browser": {
      "command": "npx",
      "args": ["--yes", "@diegosouzacdv/[email protected]"],
      "env": {
        "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
        "JEV_BROWSER_MODE": "computer"
      }
    }
  }
}

O formato de interpolação de variáveis varia por harness. Injete a chave por um secret manager ou pelo ambiente do processo; não grave a chave no arquivo de configuração. Para instalar no projeto Node do próprio harness:

npm install @diegosouzacdv/[email protected]

Se o harness executa o MCP repetidamente nesta máquina, instale uma versão fixa globalmente para evitar a resolução e o download feitos pelo npx em cada inicialização:

npm install --global @diegosouzacdv/[email protected]

Depois configure o servidor MCP com command: "jev-browser-mcp" e args: []. Para atualizar, rode npm install --global @diegosouzacdv/jev-browser-mcp@<versão> e reinicie o processo do harness.

O padrão computer exige Chrome ou Edge instalado. Se optar por harness, configure JEV_BROWSER_MODE=harness antes de instalar o navegador gerenciado pelo Playwright com npx --yes @diegosouzacdv/[email protected] --install-browser.

O padrão é computer: o pacote abre o Chrome/Edge instalado e usa um perfil persistente exclusivo em browser.computer_user_data_dir. No modo harness, a instalação baixa uma vez o Chrome/Edge que o Playwright controlará. Personalize as escolhas com JEV_BROWSER_MODE, JEV_BROWSER_CHANNEL e JEV_BROWSER_PROFILE. O browser permanece aquecido enquanto o processo MCP estiver ativo e fecha quando o harness encerra o processo. JEV_PROVIDER_URL e JEV_MODEL podem substituir os valores centrais; a credencial continua no nome de variável declarado em jev.credential_env.

Uma aplicação Node também pode importar createJevBrowserServer por @diegosouzacdv/jev-browser-mcp/server e conectar o servidor ao transporte MCP que ela já utiliza.

O pacote é montado em uma pasta temporária isolada: o publicador usa o campo files do package.json para copiar somente o código Node, a configuração compartilhada e esta documentação, que também vira o README.md da raiz do pacote. Assim, o npm não inclui o README geral do orquestrador na página do MCP. Para gerar e conferir o pacote antes de publicar, execute npm run pack:jev-browser-mcp; para publicar uma versão já autenticada no npm, execute npm run publish:jev-browser-mcp. A instalação por npm é a recomendada; use a referência GitHub somente quando precisar experimentar uma revisão ainda não publicada.

Contrato do pacote

O executável oferece browser_health, choose_next_action e run_browser_flow, mantém uma sessão do browser por processo e reutiliza essa sessão entre chamadas. O plano passado ao Jev continua declarativo: clique, preenchimento, seleção nativa, hover, espera, teclas aprovadas, asserções por elemento, upload restrito a uma raiz local configurada e reações idempotentes a um comentário único. Também aceita um nome de iframe para as ações que ocorrem dentro dele. Não aceita JavaScript enviado pelo harness nem coordenadas. Além de papel/nome acessível, aceita label, placeholder, title, text, test_id e selector; CSS/XPath são o último recurso e geram um aviso no resultado. Ele usa a biblioteca Playwright diretamente, sem iniciar um segundo servidor MCP do Playwright. O transporte MCP usa stdio; toda saída de diagnóstico vai para stderr para não misturar com JSON-RPC.

O modo computer grava cookies no perfil exclusivo configurado, e conteúdo da página pode conter instruções maliciosas. O Jev recebe a captura acessível com uma instrução para tratar esse conteúdo como dado não confiável; não inclua segredos no fluxo, no resultado esperado ou nas descrições dos planos. Antes de enviar contexto ao provedor, o cliente mascara valores de campos e padrões detectados de CPF/CNPJ, email, telefone, nome de cliente e valores monetários. Isso reduz exposição acidental, mas não substitui o cuidado com os dados que o harness escolhe incluir no fluxo. Use local_only: true quando nenhuma chamada externa ao Jev puder ocorrer; esse modo recusa fluxos que precisam escolher entre vários planos.

Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de aceitação no projeto e chama jev_browser.run_browser_flow com planos candidatos declarativos. O MCP abre uma única sessão do Playwright, navega para a página inicial, captura o snapshot acessível e pede ao Jev que escolha um plano. Em seguida, executa o plano inteiro na mesma sessão e confere o resultado esperado na tela.

Cada plano pode usar:

  • click, type, hover, select_option, press, assert_* e upload com papel/nome acessível ou um localizador: label, placeholder, title, text, test_id ou selector;
  • near: {"text":"..."} para localizar o controle logo depois de um texto, within: {"row_containing":"..."} para limitar por trecho e within: {"row_containing_exact":"..."} para exigir um elemento com o texto exato na linha, sem casar com 15287210 ao procurar 1528721;
  • within: {"role":"dialog"} sem name para usar o único diálogo visível; o MCP ignora diálogos visualmente ocultos, inclusive os que ficaram no DOM com opacity: 0;
  • name: "" com index não negativo para controles sem nome. O resultado inclui um aviso porque a posição pode mudar entre execuções. O índice é zero-based para o mesmo papel e inclui nomes vazios ou compostos só por espaços/glyphs de uso privado, como ícones Font Awesome; controles desabilitados não aparecem no diagnóstico, mas continuam contando para que o índice aponte ao controle correto;
  • selector com CSS ou XPath como último recurso. O MCP identifica essa escolha em warnings; não é permitido enviar JavaScript nem coordenadas;
  • timeout_seconds opcional em cada etapa, limitado pela configuração central;
  • type com mode: "keys" para digitar sequencialmente em campos com máscara, blur: true para desfocar o campo e sensitive: false para permitir que o valor apareça nas evidências. Nesse modo, \n envia Enter e \t envia Tab;
  • wait_for_text, wait_for_value, wait_for_enabled e wait_for_condition (network_idle, hidden, text_hidden ou angular_idle). wait recebe ms para uma pausa curta e limitada. angular_idle aguarda requisições $http e digest do AngularJS. wait_for_text aceita fail_on: {"role":"alert"} para interromper a espera assim que um alerta visível aparecer e incluir seu texto no erro. Se o alerta contiver o texto esperado, a etapa passa; caso contrário, o alerta interrompe a espera. network_idle aceita url_contains para aguardar só as requisições correspondentes;
  • press (o alias legado press_key também é aceito) com PageDown, PageUp, Home, End, setas, Enter, Escape, Tab ou Shift+Tab. Sem alvo, envia a tecla ao elemento focado; com alvo, usa o localizador informado;
  • assert_text, assert_value, assert_visible, assert_hidden e assert_enabled; assert_text pode receber apenas role quando a região, como alert, não tem nome acessível;
  • upload_file para input rotulado, botão que abre o seletor de arquivo ou dropzone;
  • audit_accessibility com axe-core para WCAG 2.1 A/AA;
  • like_comment e unlike_comment, que localizam uma linha pelo autor e texto, não repetem uma reação já no estado pedido e distinguem Curtir de Descurtir.

As proteções e evidências por etapa usam estes campos:

  • confirm_dialog recebe expected_text e button. Em diálogos HTML, o MCP confere o texto antes de localizar e clicar no botão. Também trata diálogos nativos alert/confirm: precisa haver uma etapa confirm_dialog logo após o clique que os abre, o texto deve corresponder e um diálogo inesperado é fechado sem aceitar;
  • mutating: true marca etapas que gravam dados ou disparam efeitos externos. O MCP também classifica botões conhecidos como Salvar, Emitir, Cancelar ou Sim pelo nome e pelo controle resolvido, inclusive quando o plano usa um seletor CSS. options.dry_run: true executa até a primeira etapa mutável e para antes dela; retorna dry_run_stopped_before_step e um confirmation_token temporário, de uso único e vinculado ao fluxo e à página. Reenvie a mesma chamada com options.confirmation_token para autorizar essa etapa. O MCP pausa novamente antes de cada outra etapa mutável. Sem dry_run, o primeiro pedido de ação mutável também retorna status: "confirmation_required" e token; nenhuma etapa mutável roda sem essa autorização. O token expira após dez minutos por padrão;
  • duration_ms aparece em cada etapa concluída ou falha. mutating_steps lista etapas mutáveis que foram executadas ou falharam;
  • screenshot: true salva uma captura depois da etapa e inclui seu caminho na evidência da etapa;
  • options.report_path grava um resumo .md ou JUnit .xml. O caminho deve ser absoluto e estar dentro de JEV_BROWSER_ARTIFACT_DIR (ou do diretório de artefatos configurado). O relatório resume versão/status, duração, alvo e resolved_target, asserções de rede, falhas de rede e caminhos das capturas.

run_browser_flow também aceita params como mapa de texto, números e booleanos. Use {pedido} em flow, expected_outcome e nos campos textuais do plano para reutilizar um valor sem editar o roteiro em vários lugares. A ação extract lê text (padrão), value ou attribute de um elemento e salva o resultado na variável indicada por as; etapas seguintes podem usar {documento}. O valor extraído é tratado como sensível e fica oculto no resultado por padrão; use sensitive: false só quando for apropriado exibi-lo. resolved_target.match_strategy informa quando um rótulo foi associado por proximidade (label-proximity) em vez de um label[for] direto.

A ação assert_network verifica respostas observadas depois da etapa anterior, incluindo respostas 2xx ou erros esperados como 404. Exemplo:

{
  "action": "assert_network",
  "url_contains": "/documentoFinanceiro/atualizar",
  "method": "PUT",
  "status": 404,
  "message_contains": "Boleto não encontrado"
}

Essa asserção aparece na evidência como response_status, separado do campo status da etapa (passed ou failed). message_contains lê somente resposta da mesma origem e respeita o limite configurado para captura de corpos.

Exemplo de parâmetro e extração: passe params: {"pedido":"1528721"}, filtre com within: {"row_containing_exact":"{pedido}"}, e use uma etapa {"action":"extract","role":"cell","name":"{pedido}","within":{"row_containing_exact":"{pedido}"},"as":"documento"}. A etapa seguinte pode localizar o mesmo documento com name: "{documento}".

Em wait_for_condition com condition: "hidden", use qualquer um desses localizadores, near ou within; text_hidden recebe o texto direto. Para aguardar o fim de uma chamada específica, use condition: "network_idle" com url_contains; o MCP precisa observar a requisição depois da etapa anterior e esperar que ela termine.

Quando vários controles têm o mesmo papel e nome, within limita a busca a um container acessível único, como uma linha, card ou diálogo. index escolhe uma ocorrência zero-based dentro desse escopo; sem index, o MCP exige exatamente um alvo. Para uma página com rótulo for quebrado, label também procura o controle próximo ao texto visível do rótulo.

within pode combinar um container e uma linha. Use, por exemplo, {"role":"cell","name":"Documento A","within":{"role":"table","row_containing_word":"1528721"}}. row_containing_word usa limites de palavra para não confundir 1528721 com 15287210; row_containing_exact continua disponível para texto de célula exato. check e uncheck alteram checkboxes, e select_option aceita rótulo exato (option), valor (value) ou rótulo parcial único (label_contains). navigate_menu percorre uma lista de rótulos exatos e abre #open_btn quando o primeiro item ainda está oculto. Se vários itens visíveis tiverem o mesmo rótulo, seleciona a última ocorrência, útil quando o submenu recém-aberto repete o nome da categoria pai.

{
  "action": "click",
  "role": "button",
  "name": "Add to cart",
  "within": {"role": "group", "name": "Sauce Labs Backpack"}
}
{"action":"click","role":"button","name":"Add to cart","index":0}

Exemplos para controles legados sem nome acessível:

{"action":"type","role":"textbox","label":"Vencimento","text":"05/10/2026"}
{"action":"type","role":"textbox","target_text":"Vencimento","text":"05/10/2026"}
{"action":"click","role":"link","title":"Editar documento","within":{"row_containing":"1528721"}}
{"action":"press","key":"Enter","role":"textbox","placeholder":"Busca rápida"}
{"action":"click","role":"button","name":"","index":0}
{"action":"click","selector":"#save-document"}

O reconhecimento retorna unnamed_controls_initial e unnamed_controls_final, cada um com papel, índice Playwright por papel, rótulo mais próximo e um trecho HTML sanitizado dos controles visíveis e habilitados sem nome. Nomes vazios ou compostos apenas por espaços e glyphs da área Unicode de uso privado (como ícones Font Awesome) entram nessa lista e podem ser selecionados com name: "" e o mesmo índice. unnamed_controls continua disponível como alias da lista final. O placeholder conta como nome acessível. O snapshot também resume campos de formulário com id, name, valor, estado desabilitado, rótulo e índice zero-based entre campos de mesmo nome. Valores sensíveis, como senha, token e cartão, são ocultados. snapshot_include_hidden é false por padrão; defina true somente quando precisar inspecionar campos ocultos também.

Em uma etapa type, text contém o valor a digitar; use target_text para localizar pelo texto visível próximo ao campo. Esse localizador exige role textbox, searchbox ou combobox.

Por padrão, o MCP executa todos os passos antes de avaliar expected_outcome. Valores dentro de campos não contam como resultado visível. stop_on_expected: true habilita parada antecipada quando o texto esperado aparece fora dos campos; mantenha false para fluxos com várias etapas.

Os erros de validação apontam o campo inválido e os campos aceitos. Os limites de timeout_seconds informam o máximo configurado, em vez de exigir tentativa e erro.

Captura de downloads

Marque o clique que deve iniciar um download com expect_download: true. O Playwright começa a aguardar o evento antes do clique; isso também captura downloads gerados por URL.createObjectURL (por exemplo, PDFs Blob). Após a transferência terminar, o MCP verifica o tamanho, sanitiza o nome sugerido e copia o arquivo para JEV_BROWSER_ARTIFACT_DIR (ou para o diretório de artefatos configurado). O resultado inclui a lista downloaded_files, com nome, caminho local e bytes; cada passo também contém o arquivo capturado. Os limites contam arquivos e bytes por fluxo. A checagem de tamanho acontece depois da transferência do navegador, antes de copiar para a pasta de artefatos.

{"action":"click","role":"button","name":"Export report","expect_download":true}

jev_browser_mcp.browser.max_download_files, max_download_file_bytes, max_download_total_bytes e max_download_timeout_seconds definem os limites. O diretório de artefatos deve ser local e protegido: arquivos baixados podem conter dados da aplicação.

Auditoria automatizada de acessibilidade

Use audit_accessibility depois de colocar a página no estado que deseja verificar:

{"action":"audit_accessibility","standard":"wcag2aa"}

O MCP usa @axe-core/playwright com as tags WCAG 2.0 e 2.1 A/AA. O retorno resume as violações por regra, impacto e quantidade de nós, sem copiar HTML ou trechos da página. Qualquer violação reprova esse fluxo. A auditoria automática encontra problemas comuns, mas não comprova conformidade WCAG completa; combine-a com revisão manual e testes com usuários assistivos.

Para ações em iframe, acrescente "frame": "payment-iframe"; o valor precisa corresponder ao atributo name ou title do iframe. O snapshot inclui o conteúdo acessível dos iframes nomeados dentro do escopo, limitado pela configuração. Se um alvo estiver ausente, o erro inclui até três nomes acessíveis próximos quando o snapshot os encontrar. CSS/XPath são permitidos somente como localizadores explícitos de último recurso e geram aviso. O plano não aceita JavaScript enviado pelo harness nem coordenadas.

Os aliases value → text em type/wait_for_text e value → expected em asserções são aceitos. comment também é aceito em planos e passos, mas é ignorado e não é enviado ao Jev. Campos fora do contrato continuam sendo recusados.

Upload de arquivos

Defina JEV_BROWSER_UPLOAD_ROOT como uma pasta absoluta que contenha os arquivos de fixture. Cada caminho passado ao fluxo também precisa ser absoluto; o MCP resolve links simbólicos e recusa qualquer arquivo fora dessa raiz. Só aceita arquivos regulares, até 5 arquivos por passo, 10 MiB por arquivo e 25 MiB no total do passo, conforme jev_browser_mcp.browser em config/ui-testing.json. Os bytes são lidos e validados no processo local antes de serem entregues ao Playwright. O MCP não envia esses bytes ao Jev nem os inclui diretamente na resposta; texto que o próprio site exibir na interface ainda pode aparecer no snapshot devolvido ao harness.

{"action":"upload_file","target":"input","label":"Nota fiscal","file_path":"/fixtures/nota.pdf"}

Para uma área de arrastar, use o papel e o nome acessível da dropzone. Para um botão que abre a janela nativa, use target: "button"; sem target, a presença de label seleciona o input e role/name seleciona esse botão.

{"action":"upload_file","target":"dropzone","role":"region","name":"Anexos","file_paths":["/fixtures/a.pdf","/fixtures/b.png"]}

Se JEV_BROWSER_UPLOAD_ROOT não estiver definido, a ação recusa a execução. O limite evita que um plano transforme o MCP em leitor arbitrário de arquivos do computador.

Exemplo de chamada:

{
  "flow": "Adicionar o produto ao carrinho e confirmar o resumo",
  "initial_url": "http://127.0.0.1:4173/products/coffee",
  "expected_outcome": "Coffee added to cart",
  "options": {
    "fast_path": true,
    "snapshot_scope": "main",
    "capture_network_errors": true,
    "screenshot_on_failure": true
  },
  "candidate_plans": {
    "add_and_confirm": {
      "description": "Adicionar o produto visível ao carrinho e abrir o resumo",
      "steps": [
        {"action": "click", "role": "button", "name": "Add to cart"},
        {"action": "wait_for_text", "text": "Coffee added to cart"},
        {"action": "click", "role": "link", "name": "View cart"},
        {"action": "assert_text", "role": "heading", "name": "Order summary", "expected": "Coffee"}
      ]
    }
  }
}

O plano é montado pelo LLM do harness, mas o Jev escolhe qual plano fornecido deve executar usando o fluxo e o snapshot inicial. O Jev não cria ações, nomes de controles ou valores de formulário. Valores de type são usados localmente pelo Playwright e são removidos do texto enviado ao provedor e da evidência de retorno. Use valores de teste; autenticação deve ficar no perfil de navegador configurado.

O MCP também mantém choose_next_action para fluxos exploratórios em que o harness precisa inspecionar e decidir entre ações uma por vez. Esse caminho é mais lento porque exige uma nova decisão e uma nova chamada de ferramenta por ação; prefira run_browser_flow quando os passos esperados puderem ser descritos antes da execução.

Configuração

Edite config/ui-testing.json. URL, modelo do provedor, variável de credencial e limites compartilhados ficam nos blocos browser e jev. As opções próprias do pacote Node ficam no bloco opcional jev_browser_mcp, que não altera o contrato lido pelo servidor Python. Preserve a estrutura completa exigida pelos validadores.

browser.mode aceita harness ou computer; o padrão é computer:

  • harness usa Chrome headless e contexto isolado, adequado a execuções do harness e CI; o estado de autenticação é descartado ao final da chamada.
  • computer abre o Chrome ou Edge instalado em modo visível e usa o diretório persistente browser.computer_user_data_dir, separado por navegador. Não reutiliza o perfil pessoal já aberto. Faça login uma vez nesse perfil; os cookies permanecem nele entre chamadas.

browser.max_flow_steps limita a soma de passos declarados entre os planos e browser.max_text_entry_chars limita cada valor digitado. O resultado contém status, o plano escolhido, as ações executadas, a última captura acessível e se o critério esperado apareceu. Quando existem asserções explícitas, status também pode ser passed com todas elas satisfeitas; assertions_passed registra esse resultado e expected_outcome_visible continua descrevendo somente o texto global. incomplete significa que nenhum critério foi comprovado; confiança do Jev não substitui essa verificação. timings_ms.ready_ms mede a espera da SPA; warnings registra capturas vazias durante transições; e failed_step identifica índice, ação, localizador, timeout e erro resumido quando uma etapa falha. Cada etapa executada também registra a composição do localizador usado e, quando disponível, resolved_target com tag, id, papel, nome acessível, title casado e href sanitizado do elemento resolvido. Etapas type só incluem o valor final do campo quando sensitive: false.

Com capture_network_error_bodies: true, respostas JSON de erro da mesma origem são capturadas mesmo quando usam transferência chunked e não enviam Content-Length. Corpos acima do limite, com formato inválido ou que não podem ser lidos com segurança são omitidos e explicados em warnings.

jev_browser_mcp.browser.max_action_timeout_seconds limita esperas por ações, seletores e condições. JEV_BROWSER_UPLOAD_ROOT libera uploads somente dentro de uma pasta absoluta escolhida pelo operador. jev_browser_mcp.browser.max_upload_files, max_upload_path_chars, max_upload_file_bytes e max_upload_total_bytes limitam quantidade e tamanho. Não configure a raiz como o disco inteiro ou a pasta home: use uma pasta de fixtures dedicada.

Os limites de download ficam no mesmo bloco: max_download_files, max_download_file_bytes, max_download_total_bytes e max_download_timeout_seconds. jev_browser_mcp.jev.max_accessibility_violations limita quantas descrições de violações axe entram no resultado; a contagem total continua informada mesmo quando a lista é truncada.

O run_browser_flow aceita options com fast_path, snapshot_scope, block_trackers, block_fonts, local_only, busy_selectors, auto_angular_idle, login_url_contains, login_text, capture_console_errors, capture_network_errors, capture_network_error_bodies, ready_timeout_seconds, ready_network_idle, ready_stable_ms, ready_text, continue_from_current_page, reuse_page, step_timeout_seconds, max_flow_steps, return_snapshot, screenshot_on_failure, trace_on_failure, snapshot_include_hidden, stop_on_expected, dry_run, confirmation_token e report_path. Sem override, os padrões são lidos de jev_browser_mcp em config/ui-testing.json: um plano candidato pula a chamada Decisions; captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio de recursos ficam desligados; o plano roda até o fim e campos ocultos não são acrescentados ao snapshot. O browser espera a SPA renderizar uma captura acessível estável na navegação inicial. Depois, a continuidade fica ligada por padrão: continue_from_current_page: true mantém a página e seu estado entre chamadas na mesma origem, não repete page.goto e consulta um snapshot atual sem esperar novamente por load/networkidle. As etapas continuam aguardando seus próprios alvos e condições. reuse_page segue como alias legado; defina continue_from_current_page: false para iniciar cada chamada em initial_url. Se a página atual e initial_url tiverem origens diferentes, o MCP navega para initial_url. Se forem da mesma origem, continua a página atual e avisa quando as URLs completas forem diferentes. snapshot_scope aceita body, main ou dialog; se main não existir, o snapshot usa body.

block_trackers: true bloqueia analytics/trackers e os tipos de recurso configurados. Fontes ficam habilitadas por padrão para preservar ícones e glyphs; block_fonts: true bloqueia fontes separadamente. busy_selectors complementa aria-busy="true" ao aguardar overlays de carregamento. auto_angular_idle aguarda AngularJS depois de cliques e digitação quando a página expõe o injector. login_url_contains e login_text substituem a detecção padrão de autenticação. local_only: true recusa qualquer etapa que precisaria chamar o provedor Jev; use exatamente um plano com fast_path: true. step_timeout_seconds define o timeout padrão por etapa e cada etapa pode substituí-lo com timeout_seconds. max_flow_steps reduz o limite por chamada, sem superar o teto da configuração. return_snapshot aceita full, diff ou none; diff retorna linhas acessíveis novas desde o snapshot inicial.

Com capture_console_errors e capture_network_errors, o retorno traz console_errors e network_failures, limitados em quantidade e tamanho. Respostas HTTP 4xx/5xx e requisições que falham são listadas; query strings, fragmentos, valores de formulário e nomes de arquivo são removidos ou sanitizados. capture_network_error_bodies: true lê somente respostas JSON 4xx/5xx da mesma origem, limita o corpo e inclui apenas o campo textual message, também sanitizado. Em falhas, screenshot_on_failure salva screenshot local e retorna screenshot_path. trace_on_failure: true também grava um .zip compatível com o Trace Viewer do Playwright e retorna trace_path. O diretório padrão é ~/.cache/orquestrador/jev-browser-artifacts; JEV_BROWSER_ARTIFACT_DIR pode substituí-lo por uma pasta absoluta fora do pacote. Screenshots e traces podem conter dados visíveis da aplicação: mantenha o diretório local protegido e compartilhe os arquivos somente se o teste permitir.

Screenshots por etapa usam screenshot: true no próprio passo; o MCP os grava depois que a ação termina. options.report_path pode apontar para .md ou JUnit .xml dentro da raiz de artefatos. O relatório inclui a versão, tempos, localizadores resolvidos, respostas verificadas por assert_network, falhas de rede e caminhos dos screenshots, para anexar a um PR ou card.

Se o Chrome/Edge não iniciar porque o perfil persistente já está aberto, o erro identifica o PID que o mantém ocupado quando o sistema consegue associar o perfil ao processo. Em Windows, o MCP consulta a linha de comando dos processos Chrome/Edge; em outros sistemas, usa o SingletonLock do Chromium. Configure JEV_BROWSER_PROFILE com outro diretório absoluto para usar uma sessão isolada. Resultados MCP incluem server_version; erros também começam com a versão do servidor para facilitar a comparação entre instalações. A ferramenta browser_health informa se a sessão está ativa e tenta reconectar um browser que encerrou desde a chamada anterior. Passe {"restart":true} para fechar e reiniciar somente o navegador administrado por este processo MCP. Um fluxo pode ser repetido automaticamente uma vez após BROWSER_DISCONNECTED quando nenhuma etapa mutável foi executada.

harness_browser e computer_browser aceitam chrome ou msedge. computer_user_data_dir deve ficar fora do repositório e conter {browser}; o diretório persistente armazena dados de login e é resolvido sob a pasta home do usuário quando começa com ~. O Playwright MCP fica desabilitado até ORQUESTRADOR_MCP_PLAYWRIGHT_ENABLED=1 ser configurado no ambiente do harness.

Defina o valor secreto na variável indicada por jev.credential_env, usando o secret manager ou ambiente do processo que inicia o harness. Não grave a chave em config/ui-testing.json. provider_url é o endpoint HTTPS completo da API Decisions. O cliente não segue redirects e recusa URL com credencial, query string ou fragmento. O Jev fica indisponível quando a política de MCP está em modo offline.

Na primeira execução, aqueça uma vez o cache local do pacote declarado em browser.playwright_mcp_package com npx --yes <pacote> --help. O MCP inicia depois com --offline, evitando uma consulta ao registry npm em cada fluxo. Quando a versão configurada mudar, aqueça o novo pacote uma vez.

Desempenho e evidência

O pacote Node usa a decisão remota do Jev para escolher entre múltiplos planos. Com exatamente um plano e fast_path ligado (padrão), executa esse plano sem chamar a API Decisions; jev_decisions fica em zero. Inclua o fluxo completo em um plano candidato para evitar chamadas separadas ao harness; seleção, navegação, ações e asserções ficam em uma chamada MCP. O servidor Python mcp_servers/jev_browser_server.py mantém seu fluxo próprio e usa uma decisão remota do Jev por chamada. A sessão Playwright fecha antes de o MCP Python retornar. O perfil do modo computer preserva o login para chamadas seguintes; nesse servidor o processo e a janela não são reutilizados. O pacote Node mantém o browser aquecido até o harness encerrar o processo. O snapshot enviado ao Jev e devolvido ao harness remove o rodapé e, se ainda exceder o limite, mantém o início e o fim da captura com um marcador de truncamento. snapshot_scope pode limitar a captura a main ou dialog; iframes nomeados contidos nesse escopo também podem ser incluídos.

A chamada composta aquecida deve ficar dentro da meta de 20 segundos em páginas que respondem normalmente; a inicialização fria, páginas lentas, MFA e conteúdo sob demanda podem excedê-la. O MCP devolve total_ms, browser_session_ms, navigation_ms, initial_snapshot_ms, jev_decision_ms e browser_plan_ms para localizar o custo. O teto observado em um fluxo sintético local anterior foi 6,411 s; isso não mede o Instagram nem garante o mesmo tempo em outros sites.

O snapshot inicial, o fluxo, o resultado esperado e as descrições dos planos são enviados ao endpoint Decisions quando há mais de uma opção. Com fast-path, um plano não gera chamada remota. Os passos, valores digitados, valores esperados pelas asserções, caminhos e conteúdo dos arquivos não são enviados. Não inclua segredos em flow, expected_outcome ou nas descrições. A resposta retorna o plano escolhido quando houver decisão, custo/confiança do provedor quando disponíveis e o snapshot final sanitizado. O fluxo passa quando o texto esperado aparece no snapshot ou quando todas as asserções declaradas passam.

network_idle é uma espera limitada e opcional; páginas com polling ou conexões contínuas podem atingir o timeout. Prefira wait_for_text e asserções de elemento quando houver um sinal de interface específico.

Consulte a introdução do Jev e o tutorial da API Decisions no OpenRouter para os tipos de resposta e autenticação.