@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_idouselector;near: {"text":"..."}para localizar o controle logo depois de um texto,within: {"row_containing":"..."}para limitar por trecho ewithin: {"row_containing_exact":"..."}para exigir um elemento com o texto exato na linha, sem casar com15287210ao procurar1528721;within: {"role":"dialog"}semnamepara usar o único diálogo visível; o MCP ignora diálogos visualmente ocultos, inclusive os que ficaram no DOM comopacity: 0;name: ""comindexnã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;selectorcom CSS ou XPath como último recurso. O MCP identifica essa escolha emwarnings; não é permitido enviar JavaScript nem coordenadas;timeout_secondsopcional em cada etapa, limitado pela configuração central;typecommode: "keys"para digitar sequencialmente em campos com máscara,blur: truepara desfocar o campo esensitive: falsepara permitir que o valor apareça nas evidências. Nesse modo,\nenvia Enter e\tenvia Tab;wait_for_text,wait_for_value,wait_for_enabledewait_for_condition(network_idle,hidden,text_hiddenouangular_idle).waitrecebemspara uma pausa curta e limitada.angular_idleaguarda requisições$httpe digest do AngularJS.wait_for_textaceitafail_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_idleaceitaurl_containspara aguardar só as requisições correspondentes;press(o alias legadopress_keytambém é aceito) comPageDown,PageUp,Home,End, setas,Enter,Escape,TabouShift+Tab. Sem alvo, envia a tecla ao elemento focado; com alvo, usa o localizador informado;assert_text,assert_value,assert_visible,assert_hiddeneassert_enabled;assert_textpode receber apenasrolequando a região, comoalert, não tem nome acessível;upload_filepara input rotulado, botão que abre o seletor de arquivo ou dropzone;audit_accessibilitycom axe-core para WCAG 2.1 A/AA;like_commenteunlike_comment, que localizam uma linha pelo autor e texto, não repetem uma reação já no estado pedido e distinguemCurtirdeDescurtir.
As proteções e evidências por etapa usam estes campos:
confirm_dialogrecebeexpected_textebutton. Em diálogos HTML, o MCP confere o texto antes de localizar e clicar no botão. Também trata diálogos nativosalert/confirm: precisa haver uma etapaconfirm_dialoglogo após o clique que os abre, o texto deve corresponder e um diálogo inesperado é fechado sem aceitar;mutating: truemarca etapas que gravam dados ou disparam efeitos externos. O MCP também classifica botões conhecidos comoSalvar,Emitir,CancelarouSimpelo nome e pelo controle resolvido, inclusive quando o plano usa um seletor CSS.options.dry_run: trueexecuta até a primeira etapa mutável e para antes dela; retornadry_run_stopped_before_stepe umconfirmation_tokentemporário, de uso único e vinculado ao fluxo e à página. Reenvie a mesma chamada comoptions.confirmation_tokenpara autorizar essa etapa. O MCP pausa novamente antes de cada outra etapa mutável. Semdry_run, o primeiro pedido de ação mutável também retornastatus: "confirmation_required"e token; nenhuma etapa mutável roda sem essa autorização. O token expira após dez minutos por padrão;duration_msaparece em cada etapa concluída ou falha.mutating_stepslista etapas mutáveis que foram executadas ou falharam;screenshot: truesalva uma captura depois da etapa e inclui seu caminho na evidência da etapa;options.report_pathgrava um resumo.mdou JUnit.xml. O caminho deve ser absoluto e estar dentro deJEV_BROWSER_ARTIFACT_DIR(ou do diretório de artefatos configurado). O relatório resume versão/status, duração, alvo eresolved_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:
harnessusa Chrome headless e contexto isolado, adequado a execuções do harness e CI; o estado de autenticação é descartado ao final da chamada.computerabre o Chrome ou Edge instalado em modo visível e usa o diretório persistentebrowser.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.
