periscope-mcp

Testes de site construídos para agentes de IA: 63 ferramentas Playwright com asserções rígidas, preenchimento automático de formulários, sessões de autenticação, simulação de rede e auditorias de acessibilidade/SEO/GEO + Lighthouse.

Documentação

periscope-mcp

periscope-mcp MCP server

Um servidor MCP que dá a agentes de IA 74 ferramentas Playwright para QA, testar e analisar aplicativos web — sites estáticos, SPAs e aplicativos atrás de login — retornando veredictos concretos, não capturas de tela para você ficar analisando. Não é um wrapper fino em torno das APIs do navegador; as ferramentas são moldadas em torno de como os agentes realmente trabalham:

  • Resultados concretos, não ficar olhando capturas de telaassert_condition retorna passed: true/false com o valor real; as verificações retornam problemas estruturados.
  • Uma chamada em vez de dezauto_fill_form detecta, infere e preenche um formulário inteiro; interact_and_test agrupa 25 tipos de ação com verificações; test_project rastreia e audita um site inteiro.
  • Teste real de aplicativos web — sessões autenticadas persistentes (auth por formulário/básica/cookie, além de um login interativo visível para 2FA/SSO/CAPTCHA que depois roda em modo headless), fluxos de várias etapas, mock de rede, snapshots de estado e INP real medido a partir das interações que ele conduz.
  • Respostas honestas — falhas dizem o que aconteceu e o que fazer em seguida (sessão expirada vs. falha do navegador vs. despejo de memória); no-ops silenciosos como arrastos ignorados voltam sinalizados, não como sucesso falso.
  • Depuração integrada — corpos de resposta de API capturados, logs de console/rede, mock de rede e snapshots/diffs de estado, sem necessidade de chamadas de configuração.
  • Auditorias que agentes não conseguem de um binding de navegador — acessibilidade, SEO e prontidão para GEO/busca agêntica (acesso de rastreadores de IA no robots.txt, llms.txt, WebMCP), além de Lighthouse real.

Playwright + Chrome headless por baixo; rastreamento de sites, testes responsivos e diff de capturas de tela por cima. Funciona com qualquer cliente MCP — Claude Code, Codex, Cursor, Windsurf, Gemini CLI, agentes personalizados ou qualquer outra coisa que fale MCP via stdio.

Por que não apenas playwright-mcp?

playwright-mcp é excelente no que faz: controle geral do navegador via MCP, com ferramentas que espelham a própria API do Playwright. Se o trabalho é "navegue por este site, clique por aí, extraia algo", use ele.

O Periscope existe para um trabalho diferente: testar e auditar um site ou aplicativo web e depois relatar os resultados — e suas ferramentas codificam o conhecimento de teste que um agente teria que reinventar a cada sessão:

Controle bruto do navegadorPeriscope
Verificar um resultadoLer uma captura de tela ou dump do DOM e julgarassert_conditionpassed: true/false concreto + valor real
Preencher um formulárioUma chamada por campo, agente inventa dados de testeauto_fill_form — detecta campos, infere dados realistas, relata falhas por campo
AutenticaçãoRefazer login roteirizando cliques a cada sessãoProjetos persistem auth por formulário/básica/cookie; sessões compartilham o contexto logado
Auditoria do site inteiroPercorrer páginas manualmentetest_project — rastreamento + verificações de acessibilidade/SEO/GEO/visual/funcionalidade + relatório salvo
Diagnosticar página quebradaPedir logs, repetir requisiçõesCorpos de resposta, console e rede são capturados automaticamente; simule APIs com intercept_network
Falhas silenciosasArrastar "funcionou", nada se moveuSinalizado no resultado, com o caminho de recuperação explicado
Auditorias de prontidão para IAAcesso de rastreadores de IA no robots.txt, llms.txt, anotações WebMCP, JSON-LD, além de pontuações reais do Lighthouse

Os dois não são rivais — um agente pode usar tranquilamente o playwright-mcp para tarefas de navegação e o Periscope quando estiver com o chapéu de QA. As apostas de design do Periscope são simplesmente sobre esse chapéu: menos chamadas e de nível mais alto; veredictos estruturados em vez de estado bruto da página; e erros escritos para dizer ao agente o que fazer em seguida.

Arquitetura

MCP client (AI agent)  -->  MCP Server (stdio)  -->  Playwright (Headless Chrome)
                                 |                         |
                                 +-- Projects (JSON)       +-- Persistent Sessions
                                 +-- Screenshots (PNG)     +-- Network Interception
                                 +-- Reports (JSON)        +-- Device Emulation
                                 +-- Videos (WebM)

Como funciona: seu cliente MCP conecta a este servidor via stdio. O servidor expõe 74 ferramentas que o agente pode chamar para criar projetos, configurar autenticação, rastrear sites, executar verificações estáticas e testar interativamente aplicativos web usando sessões de navegador persistentes. Os resultados (JSON + capturas de tela + vídeos) são retornados ao agente para análise.

Estrutura do Projeto

periscope-mcp/
├── server.py              # MCP server entry point (stdio wiring + dispatch)
├── tool_schemas.py        # All 74 MCP tool definitions (schemas)
├── runtime.py             # Shared singletons (project store, sessions, browser)
├── coercion.py            # Argument coercion for MCP clients with stale schemas
├── handlers/              # Tool handlers, grouped by category
│   ├── registry.py        # @tool(name) decorator + HANDLERS registry
│   ├── projects.py        # create/list/get/delete project
│   ├── auth.py            # form login, basic auth, cookies, copy_auth
│   ├── static_testing.py  # test_url, crawl, test_project, reports, responsive
│   ├── session_tools.py   # open/close/list sessions, viewport, history
│   ├── interactive.py     # click, fill, steps, element queries, dialogs
│   ├── analysis.py        # forms, links, keyboard nav, tables, toasts, contrast
│   ├── advanced.py        # network mocking, storage, iframes, emulation, recording
│   ├── agent_speed.py     # assertions, smart find, auto-fill, snapshots
│   ├── web.py             # web_search, web_fetch
│   ├── discovery.py       # describe_tools catalog
│   └── system.py          # periscope_system: status, self-update, agents_md
├── tester.py              # Playwright browser control + test orchestration
├── crawler.py             # Page discovery (BFS crawl, same-domain only)
├── projects.py            # Project CRUD + auth config storage
├── auth.py                # Authentication handlers (form, basic, cookies)
├── sessions.py            # SessionManager + PageSession — persistent page lifecycle
├── interactions.py        # Interaction primitives (click, fill, execute_steps)
├── utils.py               # Screenshot comparison (Pillow pixel diff)
├── config.py              # Global settings (timeouts, paths, session limits)
├── checks/
│   ├── visual.py          # Broken images, favicon, overflow, small text
│   ├── accessibility.py   # Alt text, labels, headings, lang, ARIA, keyboard nav
│   ├── functionality.py   # Broken links, forms, SEO, performance, link checker
│   └── geo.py             # GEO/agentic search: robots.txt AI crawlers, llms.txt, WebMCP, JSON-LD
├── tests/                 # Unit tests (no browser) + tests/e2e/ (real browser + fixture pages)
├── data/                  # Created at runtime (gitignored — contains credentials)
├── Dockerfile
├── docker-compose.yml
└── .mcp.json.example      # MCP registration template (copy to .mcp.json)

Pré-requisitos

  • Python 3.11+
  • Playwright + navegador Chromium

Instalação (Local)

Instalação rápida (Debian/Ubuntu)

Um comando — clona e instala:

git clone https://github.com/segentic-lab/periscope-mcp.git && cd periscope-mcp && ./install.sh

Totalmente desassistido (sem prompts de confirmação):

git clone https://github.com/segentic-lab/periscope-mcp.git && cd periscope-mcp && ./install.sh -y

Já clonou? Basta executar ./install.sh a partir do diretório do repositório.

O script instala os pré-requisitos apt, cria o venv, instala as dependências Python e o Chromium do Playwright, executa um autoteste headless e gera mcp-config.json com os caminhos absolutos corretos para esta instalação (copie ou mescle no .mcp.json do seu projeto). Flags úteis:

  • ./install.sh --system-chromium — usar um Chromium/Chrome existente (define CHROMIUM_PATH) em vez de baixar a versão do Playwright
  • ./install.sh --skip-deps — nunca mexer em apt / usar sudo
  • ./install.sh -y — não interativo (sem prompts de confirmação)

Em qualquer outra plataforma, o script não modifica seu sistema — ele imprime os comandos exatos para seu SO (./install.sh --manual macos|fedora|arch|suse|windows para escolher explicitamente).

Atualização

./update.sh

Busca o código-fonte mais recente do GitHub (git pull --ff-only) e atualiza a instalação: dependências Python, navegador Playwright (mantido no Chromium do sistema se for o que a instalação usa), o registro + autoteste de inicialização headless e um mcp-config.json regenerado. Funciona em qualquer plataforma com instalação existente. Seu diretório data/ (projetos, credenciais, capturas de tela, relatórios) nunca é tocado.

  • ./update.sh --force — guarde primeiro as modificações locais em arquivos rastreados (recupere com git stash pop)
  • ./update.sh --full — também reverifica os pré-requisitos apt no Debian/Ubuntu (usa sudo)

Se você tiver modificações locais, o script se recusa e as lista em vez de sobrescrever.

Instalação manual

# Clone the repo
cd periscope-mcp

# Create virtual environment
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Install Chromium for Playwright
playwright install chromium

Instalação (Docker)

docker compose up -d

Veja a seção Implantação Docker abaixo.

Conectando um Cliente MCP

O Periscope é um servidor MCP stdio padrão: aponte qualquer cliente MCP para venv/bin/python server.py e pronto. ./install.sh gera mcp-config.json com os caminhos absolutos corretos para sua máquina; a maioria dos clientes aceita esse formato diretamente:

{
  "mcpServers": {
    "periscope": {
      "command": "/path/to/periscope-mcp/venv/bin/python",
      "args": ["/path/to/periscope-mcp/server.py"]
    }
  }
}

Exemplos específicos por cliente:

  • Claude Code — copie a configuração para o projeto como .mcp.json (cp .mcp.json.example .mcp.json e ajuste os caminhos), ou execute claude mcp add periscope -- /path/to/venv/bin/python /path/to/server.py
  • Cursor / Windsurf — adicione o bloco acima em ~/.cursor/mcp.json / ~/.codeium/windsurf/mcp_config.json
  • Codex CLI — adicione em ~/.codex/config.toml: [mcp_servers.periscope] com command e args como acima
  • Agentes personalizados — qualquer cliente do SDK MCP pode iniciar o servidor via stdio com o mesmo comando e argumentos

Após configurar, reinicie seu cliente.

Ensinando seu agente a usar as ferramentas

Duas opções, dependendo do seu agente:

  • Claude Code (recomendado: instale a skill). SKILL.md (raiz do repositório; também exposta em skills/periscope/ no layout de skills do Claude Code) é uma skill do Claude Code — ela dispara automaticamente em tarefas de teste web e carrega um guia operacional condensado (tabela de decisão de fluxos de trabalho + armadilhas) somente quando necessário, custando ~0 de contexto no restante do tempo:

    ln -s "$(pwd)/skills/periscope" ~/.claude/skills/periscope
    

    Um symlink a mantém atualizada com ./update.sh (copie a pasta se preferir uma versão congelada).

  • Qualquer outro cliente MCP: cole o guia. AGENTS.md contém um bloco de system prompt pronto — fluxos de trabalho, orientação de seleção de ferramentas e armadilhas conhecidas. Cole o conteúdo no system prompt do seu agente (ou nas instruções personalizadas).

De qualquer forma, o agente sempre pode buscar o guia completo atual do servidor em execução via periscope_system(action="agents_md") e o catálogo completo via describe_tools().

Referência de Ferramentas MCP (74 ferramentas)

Gerenciamento de Projetos (4 ferramentas)

FerramentaDescriçãoParâmetros obrigatórios
create_projectCriar um novo projeto de testename, base_url
list_projectsListar todos os projetos(nenhum)
get_projectObter detalhes do projetoname
delete_projectExcluir projeto + dadosname

Autenticação (7 ferramentas)

FerramentaDescriçãoParâmetros obrigatórios
set_form_loginConfigurar login por formulário com usuário/senhaproject, login_url, username, password
set_basic_authConfigurar HTTP Basic Authproject, username, password
set_cookiesInjetar cookies de sessãoproject, cookies (array)
login_projectExecutar login usando a auth configuradaproject
interactive_loginAbrir uma janela visível para login manual (2FA/SSO/CAPTCHA) e depois save_loginproject
save_loginCapturar a sessão do login manual; o projeto então roda autenticado e headlessproject
copy_authCopiar configuração de auth + estado da sessão entre projetosfrom_project, to_project

Para logins que não podem ser automatizados — 2FA/MFA, redirecionamentos SSO/OAuth, CAPTCHA, magic links — use interactive_login (abre uma janela real do navegador; requer display no servidor), complete o login você mesmo e então save_login. Ele captura a sessão autenticada (cookies + localStorage) no projeto, e toda sessão headless futura a reutiliza. Execute novamente quando a sessão expirar (o Periscope sinaliza isso automaticamente — veja a detecção de expiração de auth em test_project).

Testes Estáticos (3 ferramentas)

FerramentaDescriçãoParâmetros obrigatórios
test_urlTestar uma única URL (captura de tela + verificações)url
crawl_projectDescobrir todas as páginas a partir da URL baseproject
test_projectAuditoria completa: rastrear + testar todas as páginasproject

Resultados (4 ferramentas)

FerramentaDescriçãoParâmetros obrigatórios
get_screenshotObter o caminho do arquivo da captura de telaproject, url
list_reportsListar relatórios de teste salvos(opcional: project)
get_reportLer um arquivo de relatórioreport_path
session_reportDossiê em HTML+PDF de toda chamada de ferramenta desta execução — argumentos (redigidos), veredictos, tempos, capturas de tela(nenhum)

Gerenciamento de Sessões (5 ferramentas)

As sessões mantêm páginas do navegador vivas entre chamadas de ferramentas, permitindo fluxos de trabalho interativos de várias etapas.

FerramentaDescriçãoParâmetros obrigatórios
open_sessionAbrir sessão persistente do navegador (headed=true para uma janela visível)url
close_sessionFechar sessão e liberar recursossession_id
list_sessionsListar todas as sessões ativas(nenhum)
set_viewportAlterar o tamanho do viewport (8 presets de dispositivo ou largura/altura personalizada)session_id
select_pageAdotar um popup/nova aba (OAuth, target=_blank) como uma nova sessão dirigívelsession_id

Presets de set_viewport: mobile_sm (320x568), mobile (375x812), mobile_lg (428x926), tablet (768x1024), tablet_lg (1024x1366), laptop (1366x768), desktop (1920x1080), desktop_lg (2560x1440)

Ações Interativas (7 ferramentas)

Tradução: (Portuguese Brasil) - Markdown chunk 2/3

FerramentaDescriçãoParâmetros Obrigatórios
click_elementClique no elemento (force=true ignora sobreposições)session_id, selector
fill_formPreencher campos de formulário, com envio opcionalsession_id, fields
select_option<select> nativo ou dropdown personalizado (Radix/shadcn) — detecção automáticasession_id, selector
interact_and_testFluxo de trabalho em várias etapas com 25 ações (veja abaixo)steps
get_page_elementsListar elementos correspondentes com atributosselector
flowSalvar / executar / listar / excluir sequências de etapas nomeadas (fluxos reutilizáveis)(varia conforme a ação)
scroll_into_viewRolar elemento para a viewport sem clicarsession_id, selector

interact_and_test suporta 25 ações de etapa: click, force_click, fill, force_fill, type, select, select_option, wait, wait_for, wait_for_text, screenshot, navigate, hover, press_key, check, uncheck, scroll_to, scroll_within, evaluate_js, drag, right_click, go_back, go_forward, upload_file, wait_for_network

Análise (10 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
test_form_validationAnalisar mensagens de validação de formulário(url ou session_id)
compare_screenshotsDiferença de pixels entre duas capturas de telascreenshot1, screenshot2
visual_checkBaselines nomeados de regressão visual: defina uma vez, verifique aprovação/reprovaçãosession_id, name
test_responsiveTestar em viewports mobile/tablet/desktopurl
check_linksVerificador abrangente de links (internos + externos)(url ou session_id)
measure_interactionMedir tempo de clique até o resultadosession_id, selector
get_table_dataAnalisar tabela HTML em JSON estruturado (cabeçalhos → valores de células)session_id
get_toast_messagesCapturar mensagens de toast/notificação visíveissession_id
run_lighthouseAuditoria real do Google Lighthouse: pontuações 0-100, Core Web Vitals, auditorias falhas (requer Node.js)url
get_interaction_logExportar série temporal real de INP (por interação) como JSON/CSV + estatísticas de percentilsession_id

Velocidade de Workflow (8 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
screenshot_sessionCaptura de tela rápida do estado atual da páginasession_id
run_checks_on_sessionExecutar verificações na sessão ativa (sem nova página)session_id
navigate_sessionHistórico do navegador: voltar, avançar ou recarregarsession_id, action
handle_dialogAceitar/ignorar alerta/confirm/prompt JS (chamar ANTES do gatilho)session_id, action
upload_fileDefinir arquivo(s) em <input type="file">session_id, selector, files
wait_for_networkAguardar padrão específico de URL de API concluirsession_id, url_pattern
wait_for_goneAguardar elemento desaparecer (fechamento de modal, spinner sumido)session_id, selector
get_page_htmlouterHTML bruto de elementos, ou HTML completo da páginasession_id

Testes Avançados (9 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
intercept_networkSimular respostas de API (testar estados de erro/vazio/carregamento)session_id, url_pattern
clear_interceptsRemover simulações de rede (todas, ou por padrão)session_id
get_local_storageLer localStorage ou sessionStoragesession_id
set_local_storageEscrever em localStorage ou sessionStoragesession_id, entries
select_iframeEntrar no conteúdo de iframe (retorna nova sessão)session_id, selector
get_computed_styleObter valores reais de CSS renderizadosession_id, selector, properties
emulate_networkLimitador de rede: slow_3g, fast_3g, offline, resetsession_id, preset
test_dark_modeAlternar prefers-color-scheme escuro/clarosession_id, mode
download_fileClicar em um gatilho e capturar o arquivo baixado (caminho, sha256, prévia de texto)session_id, selector

Gravação e Console (3 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
record_sessionGravar fluxo de trabalho como vídeourl, steps
test_keyboard_navigationAuditoria de ordem de tabulação e indicador de foco(url ou session_id)
get_console_errorsObter todos os erros/logs do console (monitoramento passivo)session_id

Ferramentas de Velocidade para Agentes de IA (10 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
assert_conditionAprovação/reprovação programática: text_contains, element_exists, url_contains, etc.session_id, assertion
assert_allAsserções em lote — todos os veredictos em uma chamada, sem abortamento antecipadosession_id, assertions
get_page_mapMapa semântico da página: papéis, nomes, estados + seletores prontos em uma chamadasession_id
find_elementLocalizador inteligente por texto, tag, papel ou proximidade de outro elementosession_id
auto_fill_formDetectar automaticamente campos, inferir tipos, preencher com dados de teste. Uma chamada = muitos preenchimentos.session_id
get_network_logTodas as solicitações de rede capturadas (URL, status, método, tipo)session_id
get_response_bodyCorpo real da resposta de API (diagnosticar erros 400/500)session_id, url_pattern
page_stateCheckpoints nomeados: snapshot / restaurar / comparar estado da páginasession_id, action, name
get_cookiesLer todos os cookies da sessãosession_id
check_color_contrastVerificação de contraste WCAG AA/AAA em elementos de textosession_id

Web, Descoberta e Sistema (4 ferramentas)

FerramentaDescriçãoParâmetros Obrigatórios
web_searchPesquisar no DuckDuckGo: títulos + URLs + trechosquery
web_fetchBuscar URL → Markdown legível (ou texto/html); render=true executa JS em Chromium headless (+ project para atrás de login), contains controla a busca, save grava um artefato .md limpourl
describe_toolsCatálogo estruturado de todas as ferramentas com fluxos e dicas(nenhum)
periscope_systemStatus de instalação + verificação/aplicação de atualização + obter AGENTS.md atual(nenhum)

Verificações de Teste

Visual (checks/visual.py)

  • Imagens quebradas (carregamento incompleto ou largura natural 0)
  • Favicon ausente
  • Estouro horizontal / problemas de layout
  • Texto muito pequeno (< 12px)
  • Cor de fundo do corpo ausente
  • Imagens sem dimensões explícitas de largura/altura

Acessibilidade (checks/accessibility.py)

  • Imagens sem texto alt (imagens decorativas isentas: alt="", role="presentation"/"none", aria-hidden)
  • Links e botões sem nomes acessíveis (verifica texto, aria-label, aria-labelledby resolvível, title, img[alt], svg <title>; elementos aria-hidden isentos)
  • Entradas de formulário sem rótulos associados (label[for], rótulo de envolvimento, aria-label/aria-labelledby, title)
  • Hierarquia de cabeçalhos (H1 ausente, múltiplos H1, níveis pulados)
  • Atributo lang ausente em <html>
  • Valores duplicados de id (quebram label[for] e referências aria)
  • Validade ARIA: valores desconhecidos de role, referências aria-labelledby/describedby/controls/owns/activedescendant a ids inexistentes
  • Link de navegação de pulo ausente (escaneia os primeiros 5 links)
  • Elementos com tabindex > 0
  • Auditoria de navegação por teclado (ordem de tabulação, indicadores de foco visíveis, detecção de ciclo de identidade de elemento) — via ferramenta test_keyboard_navigation

Funcionalidade (checks/functionality.py)

  • Links internos quebrados (verificação HTTP HEAD, até 20 links em check_functionality)
  • Verificador abrangente de links com suporte a links externos (até 100 links) — via ferramenta check_links
  • Formulários sem ação ou botão de envio
  • Botões órfãos fora de formulários
  • Links externos sem target="_blank"
  • Contagem de campos obrigatórios do formulário
  • Entradas com autocomplete desabilitado

SEO (checks/functionality.py -> check_seo)

  • Título da página: ausente, muito longo (> 60 caracteres) ou muito curto (< 15 caracteres)
  • Meta descrição: ausente, muito longa (> 160 caracteres) ou muito curta (< 50 caracteres)
  • Meta tag viewport ausente
  • URL canônica ausente
  • Cabeçalho H1: ausente ou mais de um
  • Open Graph: ausente por completo, tags essenciais incompletas (og:title/description/image/url), og:image não absoluto, twitter:card ausente
  • Dados estruturados JSON-LD: blocos ausentes ou não analisáveis
  • noindex via meta robots ou cabeçalho de resposta X-Robots-Tag
  • robots.txt bloqueando rastreadores de mecanismos de busca (Googlebot, Bingbot, DuckDuckBot, ...) — erro se todos estiverem bloqueados
  • Em todo o site (via test_project): títulos / meta descrições duplicados entre páginas, reportados em site_issues

GEO / Busca Agêntica (checks/geo.py -> check_geo)

Otimização para Mecanismos Generativos — o site é legível e utilizável por rastreadores de IA, mecanismos de resposta e agentes no navegador:

  • robots.txt bloqueando rastreadores de IA (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot, e mais 11)
  • Presença e conformidade de formato de llms.txt (Markdown com pelo menos um H1)
  • Integração WebMCP: anotações declarativas <form toolname> presentes e completas (tooldescription), índice de cobertura de formulários e, quando o navegador expõe document.modelContext — enumeração de ferramentas registradas com validação de schema/nome/descrição
  • Presença de dados estruturados JSON-LD (do que os mecanismos de resposta citam)

robots.txt e llms.txt são buscados uma vez por origem e armazenados em cache pela vida útil do servidor.

Performance (checks/functionality.py -> get_performance_metrics)

  • Tempo de carregamento do DOM (ms)
  • Tempo total de carregamento da página (ms)
  • Primeira pintura / primeira pintura com conteúdo (ms)
  • Core Web Vitals (valores de laboratório via PerformanceObserver armazenado em buffer): Maior Pintura com Conteúdo (ms), Mudança Cumulativa de Layout, Aproximação de Tempo Total de Bloqueio a partir de tarefas longas (+ contagem de tarefas longas)
  • Interação até a Próxima Pintura (INP)interaction_to_next_paint_ms: o INP real, medido a partir de entradas de Event Timing para as interações que o Periscope aciona (nulo até que você interaja). Esta é uma medição genuína de estilo de campo, não o proxy de laboratório TBT — o Lighthouse não consegue produzir INP em modo de laboratório.
  • Contagem de recursos
  • Tamanho total de transferência (bytes / KB)

Para métricas pontuadas e oficiais do Lighthouse, use a ferramenta run_lighthouse — ela executa o CLI real do Lighthouse (requer Node.js) e retorna pontuações de categorias de 0-100, Core Web Vitals oficiais e auditorias falhas, salvando o relatório JSON completo em data/reports/.

Série temporal de INP (get_interaction_log)

Como o Periscope aciona interações reais, ele pode registrar o INP de cada uma durante um teste interativo estendido. get_interaction_log(session_id, format="json"|"csv") grava um arquivo em data/reports/ — uma linha por interação (t_ms, epoch_ms, inp_ms, type, target, url) além de estatísticas de percentil (p50/p75/p90/p98/pior) — para gráficos de INP ao longo do tempo. clear=true redefine a gravação. Os registros são limitados por sessão (MAX_INTERACTION_LOG, os mais antigos descartados).

Formato de Saída de Teste

Cada chamada de test_url retorna:

{
  "url": "https://example.com",
  "status": "success",
  "status_code": 200,
  "title": "Page Title",
  "screenshot_path": "/path/to/screenshot.png",
  "load_time_ms": 1500,
  "issues": [
    {
      "type": "accessibility",
      "severity": "error",
      "message": "3 images missing alt text",
      "details": ["img1.png", "img2.png", "img3.png"]
    }
  ],
  "issue_count": 5,
  "issues_by_severity": {"error": 1, "warning": 2, "info": 2},
  "issues_by_type": {"accessibility": 2, "seo": 2, "visual": 1},
  "performance": {
    "dom_content_loaded_ms": 120,
    "load_complete_ms": 1500,
    "first_paint_ms": 140,
    "first_contentful_paint_ms": 140,
    "resource_count": 25,
    "total_size_bytes": 512000,
    "total_size_kb": 500
  },
  "console_errors": []
}

test_project retorna um relatório agregado com resultados por página + resumo.

Exemplos de Uso

Teste básico (sem autenticação)

User: "Test https://example.com for issues"

The agent calls:
1. create_project(name="example", base_url="https://example.com")
2. test_project(project="example")
3. Analyzes results and reports findings

Teste com login

User: "Test https://myapp.com, login is admin/password123"

The agent calls:
1. create_project(name="myapp", base_url="https://myapp.com")
2. set_form_login(project="myapp", login_url="https://myapp.com/login",
                  username="admin", password="password123")
3. login_project(project="myapp")
4. test_project(project="myapp")

Teste com Autenticação Básica

User: "Test https://staging.example.com, it uses basic auth admin/secret"

The agent calls:
1. create_project(name="staging", base_url="https://staging.example.com")
2. set_basic_auth(project="staging", username="admin", password="secret")
3. login_project(project="staging")
4. test_project(project="staging")

Teste com cookies

User: "Test myapp using this session cookie: session=abc123"

The agent calls:
1. set_cookies(project="myapp", cookies=[
     {"name": "session", "value": "abc123", "domain": "myapp.com"}
   ])
2. test_project(project="myapp")

Teste interativo (baseado em sessão)

User: "Go to myapp.com, click the login button, fill in the form, and check what happens"

The agent calls:
1. open_session(url="https://myapp.com") → session_id
2. get_page_elements(session_id=..., selector="button, a") → see clickable elements
3. click_element(session_id=..., selector="#login-btn") → screenshot after click
4. fill_form(session_id=..., fields=[
     {"selector": "#email", "value": "user@test.com"},
     {"selector": "#password", "value": "test123"}
   ], submit_selector="button[type='submit']")
5. Analyzes screenshot to see result
6. close_session(session_id=...)

Fluxo de trabalho multi-etapas com script (sem necessidade de sessão)

User: "Test the checkout flow on myshop.com"

The agent calls:
1. interact_and_test(
     url="https://myshop.com/products/1",
     steps=[
       {"action": "click", "selector": "#add-to-cart"},
       {"action": "wait", "timeout": 1000},
       {"action": "click", "selector": "#checkout-btn"},
       {"action": "fill", "selector": "#email", "value": "test@test.com"},
       {"action": "screenshot", "label": "checkout_form"},
       {"action": "click", "selector": "#submit-order"}
     ],
     run_checks=["visual", "accessibility"]
   )

Teste responsivo

User: "Check how example.com looks on mobile, tablet, and desktop"

The agent calls:
1. test_responsive(url="https://example.com", run_checks=["visual"])
→ Returns screenshots at 375x812, 768x1024, and 1920x1080

Alternar viewport durante uma sessão

User: "Show me how this page looks on mobile"

The agent calls:
1. set_viewport(session_id=..., device="mobile")
→ Returns screenshot at 375x812

Testar tratamento de erros simulando uma API

User: "What happens when the API returns a 500 error?"

The agent calls:
1. intercept_network(session_id=..., url_pattern="/api/tasks", status=500,
     body='{"error": "Internal server error"}')
2. navigate_session(session_id=..., action="reload")
3. screenshot_session(session_id=...)
→ Shows how the app handles the error state

Testar modo escuro

User: "Does this site support dark mode?"

The agent calls:
1. open_session(url="https://example.com") → session_id
2. test_dark_mode(session_id=..., mode="dark")
→ Screenshot shows the page with prefers-color-scheme: dark

Aguardar conteúdo dinâmico

User: "Submit this form and wait for the success message"

The agent calls:
1. fill_form(session_id=..., fields=[...], submit_selector="#submit")
2. wait_for_network(session_id=..., url_pattern="/api/submit")
3. screenshot_session(session_id=...)

Testar em rede lenta

User: "How does this page load on a slow connection?"

The agent calls:
1. emulate_network(session_id=..., preset="slow_3g")
2. navigate_session(session_id=..., action="reload")
3. screenshot_session(session_id=...)
4. emulate_network(session_id=..., preset="reset")

Configuração

Edite config.py para alterar os padrões (configurações substituíveis por env observam a variável):

ConfiguraçãoPadrãoDescrição
HEADLESSTrueExecutar o Chrome em modo headless (env: HEADLESS=false)
STARTUP_PAUSE10Segundos para aguardar após a abertura de um navegador não headless (env: STARTUP_PAUSE)
TIMEOUT30000Tempo limite de carregamento da página (ms)
VIEWPORT_WIDTH1920Largura do viewport do navegador
VIEWPORT_HEIGHT1080Altura do viewport do navegador
CHROMIUM_PATHunsetCaminho para um binário Chromium do sistema (env: CHROMIUM_PATH); não definido = build empacotado do Playwright
WAIT_UNTILnetworkidleEstratégia de espera de navegação; páginas que nunca ficam ociosas (Turnstile, websockets) fazem downgrade automático para load por página, sinalizadas como wait_downgraded (env: NAV_WAIT_UNTIL=load força isso globalmente)
MAX_PAGES20Máximo padrão de páginas para rastrear
MAX_DEPTH3Profundidade máxima padrão de rastreamento
MAX_SESSIONS20Máximo de sessões interativas simultâneas (env: MAX_SESSIONS)
SESSION_TIMEOUT300Expirar automaticamente sessões ociosas após N segundos (env: SESSION_TIMEOUT)
MAX_RESPONSE_BODY_SIZE512000Máximo de bytes capturados por corpo de resposta
MAX_RESPONSE_BODIES100Máximo de corpos de resposta capturados mantidos por sessão
MAX_CONSOLE_LOG500Máximo de entradas de console mantidas por sessão
MAX_NETWORK_LOG1000Máximo de entradas de log de rede mantidas por sessão

Armazenamento de Dados

Todos os dados são armazenados no diretório data/:

  • data/projects.json - Configurações do projeto (nome, URL, autenticação, configurações). As credenciais de autenticação são armazenadas em texto simples - não faça commit deste arquivo.
  • data/screenshots/{project}/ - Capturas de tela PNG por projeto. Os nomes de arquivo são {domain}_{path}_{hash}.png para testes estáticos, interactive_{timestamp}_{label}.png para capturas de tela de sessão.
  • data/reports/{project}_{timestamp}.json - Relatórios de teste completos com todas as descobertas.
  • data/videos/{project}/ - Vídeos de sessão gravados (formato WebM do Playwright).
  • data/diffs/ - Imagens de diff de comparação de capturas de tela.

Implantação Docker

Construir e executar

docker compose up -d

Conectar um cliente MCP ao contêiner Docker

Aponte a configuração MCP do seu cliente para o contêiner em vez do venv:

{
  "mcpServers": {
    "periscope": {
      "command": "docker",
      "args": ["exec", "-i", "periscope", "python", "/app/server.py"]
    }
  }
}

Persistir dados

O docker-compose.yml monta ./data como um volume para que capturas de tela, relatórios e configurações de projeto sobrevivam a reinicializações do contêiner.

Principais Decisões de Design

  1. Contextos de navegador por projeto - Cada projeto recebe seu próprio BrowserContext do Playwright. Isso mantém as sessões (cookies, autenticação) isoladas entre projetos.

  2. Inicialização preguiçosa do navegador - O navegador do Playwright só é iniciado na primeira chamada de ferramenta, não na inicialização do servidor. Se o navegador travar ou falhar ao iniciar, ele é recriado na próxima chamada.

  3. Rastreamento BFS - O rastreador usa busca em largura com rastreamento de profundidade. Ele permanece no mesmo domínio e ignora recursos que não são páginas (imagens, PDFs, etc.).

  4. Modularidade de verificação - Cada categoria de verificação é um módulo separado em checks/. Adicione novas verificações criando uma função que recebe um Page do Playwright e retorna list[dict].

  5. Armazenamento JSON - Os projetos são armazenados em um único arquivo projects.json. Nenhum banco de dados é necessário para a escala esperada (dezenas de projetos, não milhares).

  6. Sessões persistentes - O teste interativo usa um SessionManager que mantém as páginas do Playwright ativas em um dict chaveado por ID de sessão. As sessões expiram automaticamente após o tempo limite de ociosidade e são limitadas a um máximo configurável para evitar vazamentos de recursos.

  7. Modo efêmero vs sessão - Ferramentas como get_page_elements, interact_and_test e check_links aceitam um session_id (reutiliza uma página existente) ou um url (cria uma página temporária que é fechada após o uso). Isso as torna flexíveis para uso interativo e de uso único.

Adicionando Novas Verificações

  1. Crie uma função no arquivo checks/*.py apropriado:
async def check_something(page: Page) -> list[dict]:
    # Run your check
    result = await page.evaluate("() => { ... }")

    issues = []
    if result:
        issues.append({
            "type": "your_category",   # visual, accessibility, seo, etc.
            "severity": "error",       # error, warning, info
            "message": "Description",
            "details": []              # optional
        })
    return issues
  1. Importe e chame-a em tester.py dentro de test_url().

Limitações Conhecidas

  • Sem suporte a roteamento SPA em JavaScript (depende de <a href> para rastreamento)
  • Verificação de links check_functionality padrão limitada a 20 links internos (use a ferramenta check_links para até 100 com suporte externo)
  • A detecção de login por formulário usa seletores CSS, pode precisar de personalização para formulários não padrão
  • Sem teste paralelo de páginas (as páginas são testadas sequencialmente)
  • Sessões interativas expiram automaticamente após 300s de ociosidade (configurável via SESSION_TIMEOUT)
  • Máximo de 20 sessões simultâneas (configurável via MAX_SESSIONS)
  • A etapa padrão drag (Playwright drag_to) é silenciosamente ignorada por bibliotecas de arrastar e soltar que rastreiam o ponteiro (@hello-pangea/dnd e similares) — a etapa é bem-sucedida, mas nada se move. Tente novamente com method: "mouse" na etapa de arrastar (arrasto manual em etapas que cruza o limite de início de arrasto da biblioteca) ou use o modo de teclado da biblioteca (foco na alça de arrasto, Espaço para levantar, setas para mover, Espaço para soltar). Verifique os arrastos com diff_page_state ou assert_condition.
  • Entradas de data/hora são preenchidas automaticamente com eventos sintéticos compatíveis com React (fill, force_fill, auto_fill_form)

Solução de Problemas

ProblemaSolução
Executable doesn't existExecute playwright install chromium
'NoneType' has no attribute 'new_context'Falha ao iniciar o navegador. Verifique se o Chromium está instalado. O servidor tentará novamente automaticamente na próxima chamada.
Login não está funcionandoTente fornecer seletores CSS explícitos via username_selector, password_selector, submit_selector
Tempo limite no carregamento da páginaAumente TIMEOUT em config.py ou verifique se o site exige VPN/autenticação
Docker não consegue acessar o siteGaranta que o contêiner tenha acesso à rede. Use network_mode: host se estiver testando localhost

Desenvolvimento

pip install -r requirements-dev.txt
pytest --ignore=tests/e2e   # unit tests, no browser required
pytest tests/e2e            # behavioral tests: real headless Chromium against
                            # fixture pages in tests/e2e/fixtures/ (~30s)

A suíte e2e cobre o ciclo de vida da sessão, esperas/interceptações de rede, captura de console, diálogos, arrastar e soltar (incluindo o no-op silencioso de DnD com rastreamento de ponteiro), os módulos de verificação em páginas conhecidas como boas/ruins, Core Web Vitals e as ferramentas de velocidade de agente. O CI executa ambas as suítes; o e2e instala o Chromium do Playwright (python -m playwright install --with-deps chromium). Os testes são isolados do seu data/ real via PERISCOPE_DATA_DIR.

Adicionando uma nova ferramenta: defina seu esquema em tool_schemas.py, depois adicione um manipulador no handlers/<category>.py correspondente decorado com @tool("your_tool_name"). O teste de registro (tests/test_registry.py) falha se esquemas e manipuladores divergirem.

Contribuidores

Construído por Segentic Lab — ferramentas e experimentos de código aberto.

  • Sebastijan Bandur (@segentic-lab) — autor e mantenedor
  • Claude (Anthropic) — co-contribuidor: desenvolvido em conjunto via Claude Code; cada commit é coautorado, e os designs das ferramentas foram testados em batalha por um agente de IA dirigindo o servidor contra sites reais

Pensamentos de um agente de IA sobre o Periscope

Escrito por Claude — o agente que co-desenvolveu este servidor e observou um segundo agente testá-lo em aplicativos reais — e revisado uma vez após revisão editorial por um terceiro. Sem edições humanas; a visão honesta de um agente sobre uma ferramenta de agente parecia a maneira certa de encerrar este README.

Periscope é o tipo de servidor MCP que muda o que um agente pode fazer. Adaptadores de transporte têm seu lugar — padronizar o acesso a muitos sistemas atrás de um protocolo é valor real mesmo quando pouca lógica vive no servidor. Mas os servidores que ganham um lugar permanente na caixa de ferramentas de um agente são aqueles que capturam conhecimento que o agente teria que recriar — e errar sutilmente — a cada sessão.

Você poderia tentar ensinar tudo isso a um agente em um prompt. Os números mostram por que isso falha: Periscope é 8,349 linhas de conhecimento executável sob uma camada de julgamento de 220 linhas (AGENTS.md). O observador INP com deduplicação por ID de interação, o fallback de interceptação de overlay, a matemática de contraste WCAG com amostragem de deduplicação de estilo, preflights de expiração de autenticação, o fluxo de atualização stash-não-delete — como prompt, cada um deles se torna "por favor, faça isso corretamente a partir de uma descrição", pago em tokens de contexto a cada sessão, executado com variação de modelo a cada vez, sem lugar para manter estado entre chamadas. Como servidor, custa nada além de esquemas de ferramentas, executa deterministicamente e lembra. Um prompt descreve comportamento; software garante isso. check_color_contrast retorna a mesma proporção a cada execução; um modelo fazendo a matemática em contexto retorna uma vibração. Quanto mais determinística, com estado e testada por regressão uma capacidade se torna, menos ela pertence a um prompt e mais pertence ao código.

E a roda não apenas evita ser reinventada — ela fica melhor. Os problemas neste repositório foram relatados por um agente de IA fazendo trabalho de teste real; cada um se tornou uma correção com um teste de regressão. Em um mundo de prompts, cada lição é outro parágrafo que futuros agentes devem ler e, esperançosamente, obedecer. Aqui, a lição é aplicada. Essa é a diferença, e ela se acumula.

O que mais aprecio como consumidor dessas ferramentas: elas não mentem para mim. Um arrasto que não fez nada volta sinalizado. Uma sessão expirada me diz por que ela se foi. Uma atualização que precisa de reinicialização diz isso. Ferramentas honestas são mais raras do que capazes — para um agente, elas valem mais.

Licença

GNU AGPL-3.0 — veja Licença.

Execute-o, modifique-o, use-o em qualquer lugar — inclusive comercialmente. Se você distribuir uma versão modificada ou oferecê-la como um serviço de rede, deve disponibilizar suas modificações sob a mesma licença.