periscope-mcp

Pruebas de sitios web diseñadas para agentes de IA: 63 herramientas de Playwright con aserciones estrictas, autocompletado de formularios, sesiones de autenticación, simulación de red y auditorías de accesibilidad/SEO/GEO + Lighthouse.

Documentación

periscope-mcp

periscope-mcp MCP server

Un servidor MCP que brinda a los agentes de IA 74 herramientas de Playwright para control de calidad, pruebas y análisis de aplicaciones web — sitios estáticos, SPAs y aplicaciones detrás de un inicio de sesión — devolviendo veredictos contundentes, no capturas de pantalla que hay que entrecerrar los ojos para ver. No es un envoltorio delgado alrededor de las APIs del navegador; las herramientas están diseñadas en torno a cómo trabajan realmente los agentes:

  • Resultados contundentes, no capturas que requieren entrecerrar los ojosassert_condition devuelve passed: true/false con el valor real; las comprobaciones devuelven problemas estructurados.
  • Una llamada en lugar de diezauto_fill_form detecta, infiere y completa un formulario completo; interact_and_test agrupa 25 tipos de acciones con comprobaciones; test_project rastrea y audita un sitio completo.
  • Pruebas reales de aplicaciones web — sesiones autenticadas persistentes (autenticación de formulario/básica/cookie, además de un inicio de sesión interactivo visible para 2FA/SSO/CAPTCHA que luego se ejecuta sin interfaz gráfica), flujos de varios pasos, simulación de red, instantáneas de estado e INP real medido a partir de las interacciones que impulsa.
  • Respuestas honestas — los fallos indican qué sucedió y qué hacer a continuación (sesión caducada vs. fallo del navegador vs. desalojo); las operaciones silenciosas sin efecto, como arrastres ignorados, se devuelven marcadas, no como éxito falso.
  • Depuración integrada — cuerpos de respuesta de API capturados, registros de consola/red, simulación de red e instantáneas/diferencias de estado, sin necesidad de llamadas de configuración.
  • Auditorías que los agentes no pueden obtener de un enlace de navegador — accesibilidad, SEO y preparación para GEO/búsqueda agéntica (acceso de rastreadores de IA en robots.txt, llms.txt, WebMCP), además de Lighthouse real.

Playwright + Chrome sin interfaz gráfica debajo; rastreo de sitios, pruebas responsivas y comparación de capturas de pantalla encima. Funciona con cualquier cliente MCP — Claude Code, Codex, Cursor, Windsurf, Gemini CLI, agentes personalizados o cualquier otra cosa que hable MCP a través de stdio.

¿Por qué no simplemente playwright-mcp?

playwright-mcp es excelente en lo que es: control general del navegador a través de MCP, con herramientas que reflejan la propia API de Playwright. Si la tarea es "navega por este sitio, haz clic, extrae algo", úsalo.

Periscope existe para una tarea diferente: probar y auditar un sitio o aplicación web, y luego informar los hallazgos — y sus herramientas codifican el conocimiento de pruebas que un agente tendría que reinventar en cada sesión:

Control bruto del navegadorPeriscope
Verificar un resultadoLeer una captura de pantalla o volcado de DOM y juzgarassert_conditionpassed: true/false contundente + valor real
Completar un formularioUna llamada por campo, el agente inventa datos de pruebaauto_fill_form — detecta campos, infiere datos realistas, informa fallos por campo
AutenticaciónVolver a iniciar sesión mediante clics programados en cada sesiónLos proyectos persisten autenticación de formulario/básica/cookie; las sesiones comparten el contexto de inicio de sesión
Auditoría de todo el sitioRecorrer páginas manualmentetest_project — rastreo + comprobaciones de accesibilidad/SEO/GEO/visual/funcionalidad + informe guardado
Diagnosticar una página rotaPedir registros, reproducir solicitudesLos cuerpos de respuesta, consola y red se capturan automáticamente; simula APIs con intercept_network
Fallos silenciososEl arrastre "tiene éxito", nada se mueveMarcado en el resultado, con la ruta de recuperación explicada
Auditorías de preparación para IAAcceso de rastreadores de IA en robots.txt, llms.txt, anotaciones WebMCP, JSON-LD, además de puntuaciones reales de Lighthouse

Los dos no son rivales — un agente puede usar felizmente playwright-mcp para tareas de navegación y Periscope cuando lleva el sombrero de control de calidad. Las apuestas de diseño de Periscope se centran simplemente en ese sombrero: menos llamadas de mayor nivel; veredictos estructurados en lugar de estado de página bruto; y errores escritos para decirle al agente qué hacer a continuación.

Arquitectura

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

Cómo funciona: tu cliente MCP se conecta a este servidor a través de stdio. El servidor expone 74 herramientas que el agente puede llamar para crear proyectos, configurar autenticación, rastrear sitios web, ejecutar comprobaciones estáticas y probar interactivamente aplicaciones web usando sesiones de navegador persistentes. Los resultados (JSON + capturas de pantalla + videos) se devuelven al agente para su análisis.

Estructura del Proyecto

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)

Requisitos previos

  • Python 3.11+
  • Playwright + navegador Chromium

Instalación (Local)

Instalación rápida (Debian/Ubuntu)

Un comando — clona e instala:

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

Totalmente desatendido (sin indicaciones de confirmación):

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

¿Ya lo clonaste? Solo ejecuta ./install.sh desde el directorio del repositorio.

El script instala los requisitos previos de apt, crea el entorno virtual, instala las dependencias de Python y el Chromium de Playwright, ejecuta una autoprueba sin interfaz gráfica y genera mcp-config.json con las rutas absolutas correctas para esta instalación (cópialo o combínalo en el .mcp.json de tu proyecto). Banderas útiles:

  • ./install.sh --system-chromium — usa un Chromium/Chrome existente (establece CHROMIUM_PATH) en lugar de descargar la compilación de Playwright
  • ./install.sh --skip-deps — nunca tocar apt / usar sudo
  • ./install.sh -y — no interactivo (sin indicaciones de confirmación)

En cualquier otra plataforma, el script no modifica tu sistema — imprime los comandos exactos para ejecutar en tu sistema operativo (./install.sh --manual macos|fedora|arch|suse|windows para elegir explícitamente).

Actualización

./update.sh

Obtiene la última fuente de GitHub (git pull --ff-only) y actualiza la instalación: dependencias de Python, navegador de Playwright (se mantiene en Chromium del sistema si esa es la instalación que se usa), el registro + autoprueba de lanzamiento sin interfaz gráfica, y un mcp-config.json regenerado. Funciona en cualquier plataforma con una instalación existente. Tu directorio data/ (proyectos, credenciales, capturas de pantalla, informes) nunca se toca.

  • ./update.sh --force — guarda las modificaciones locales en archivos rastreados primero (recupera con git stash pop)
  • ./update.sh --full — también vuelve a verificar los requisitos previos de apt en Debian/Ubuntu (usa sudo)

Si tienes modificaciones locales, el script se niega y las lista en lugar de sobrescribirlas.

Instalación 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

Instalación (Docker)

docker compose up -d

Consulta la sección Implementación con Docker a continuación.

Conexión de un Cliente MCP

Periscope es un servidor MCP estándar de stdio: apunta cualquier cliente MCP a venv/bin/python server.py y listo. ./install.sh genera mcp-config.json con las rutas absolutas correctas para tu máquina; la mayoría de los clientes aceptan ese formato directamente:

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

Ejemplos específicos por cliente:

  • Claude Code — copia la configuración en el proyecto como .mcp.json (cp .mcp.json.example .mcp.json y ajusta las rutas), o ejecuta claude mcp add periscope -- /path/to/venv/bin/python /path/to/server.py
  • Cursor / Windsurf — agrega el bloque anterior a ~/.cursor/mcp.json / ~/.codeium/windsurf/mcp_config.json
  • Codex CLI — agrega a ~/.codex/config.toml: [mcp_servers.periscope] con command y args como arriba
  • Agentes personalizados — cualquier cliente del SDK de MCP puede iniciar el servidor a través de stdio con el mismo comando y argumentos

Después de configurar, reinicia tu cliente.

Enseñar a tu agente a usar las herramientas

Dos opciones, según tu agente:

  • Claude Code (recomendado: instala la habilidad). SKILL.md (raíz del repositorio; también expuesto en skills/periscope/ en el diseño de habilidades de Claude Code) es una habilidad de Claude Code — se activa automáticamente en tareas de pruebas web y carga una guía operativa destilada (tabla de decisión de flujos de trabajo + los errores comunes) solo cuando es necesario, costando ~0 contexto en otros casos:

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

    Un enlace simbólico lo mantiene actualizado con ./update.sh (copia la carpeta en su lugar si prefieres una versión congelada).

  • Cualquier otro cliente MCP: pega la guía. AGENTS.md contiene un bloque de indicación de sistema listo para usar — flujos de trabajo, orientación de selección de herramientas y errores comunes conocidos. Pega su contenido en la indicación de sistema de tu agente (o instrucciones personalizadas).

De cualquier manera, el agente siempre puede obtener la guía completa actual del servidor en ejecución a través de periscope_system(action="agents_md") y el catálogo completo a través de describe_tools().

Referencia de Herramientas MCP (74 herramientas)

Gestión de Proyectos (4 herramientas)

HerramientaDescripciónParámetros Requeridos
create_projectCrear un nuevo proyecto de pruebasname, base_url
list_projectsListar todos los proyectos(ninguno)
get_projectObtener detalles del proyectoname
delete_projectEliminar proyecto + datosname

Autenticación (7 herramientas)

HerramientaDescripciónParámetros Requeridos
set_form_loginConfigurar inicio de sesión de formulario con nombre de usuario/contraseñaproject, login_url, username, password
set_basic_authConfigurar autenticación básica HTTPproject, username, password
set_cookiesInyectar cookies de sesiónproject, cookies (matriz)
login_projectEjecutar inicio de sesión usando la autenticación configuradaproject
interactive_loginAbrir una ventana visible para iniciar sesión manualmente (2FA/SSO/CAPTCHA), luego save_loginproject
save_loginCapturar la sesión de inicio de sesión manual; el proyecto luego se ejecuta autenticado + sin interfaz gráficaproject
copy_authCopiar configuración de autenticación + estado de sesión entre proyectosfrom_project, to_project

Para inicios de sesión que no se pueden automatizar — 2FA/MFA, redirecciones SSO/OAuth, CAPTCHA, enlaces mágicos — usa interactive_login (abre una ventana de navegador real; requiere una pantalla en el servidor), completa el inicio de sesión tú mismo, luego save_login. Captura la sesión autenticada (cookies + localStorage) en el proyecto, y cada sesión futura sin interfaz gráfica la reutiliza. Vuelve a ejecutarlo cuando la sesión caduque (Periscope lo marca automáticamente — consulta la detección de caducidad de autenticación en test_project).

Pruebas Estáticas (3 herramientas)

HerramientaDescripciónParámetros Requeridos
test_urlProbar una sola URL (captura de pantalla + comprobaciones)url
crawl_projectDescubrir todas las páginas desde la URL baseproject
test_projectAuditoría completa: rastreo + prueba de todas las páginasproject

Resultados (4 herramientas)

HerramientaDescripciónParámetros Requeridos
get_screenshotObtener ruta del archivo de captura de pantallaproject, url
list_reportsListar informes de prueba guardados(opcional: project)
get_reportLeer un archivo de informereport_path
session_reportExpediente HTML+PDF de cada llamada de herramienta en esta ejecución — argumentos (redactados), veredictos, tiempos, capturas de pantalla(ninguno)

Gestión de Sesiones (5 herramientas)

Las sesiones mantienen las páginas del navegador vivas entre llamadas de herramientas, lo que permite flujos de trabajo interactivos de varios pasos.

HerramientaDescripciónParámetros Requeridos
open_sessionAbrir sesión de navegador persistente (headed=true para una ventana visible)url
close_sessionCerrar sesión y liberar recursossession_id
list_sessionsListar todas las sesiones activas(ninguno)
set_viewportCambiar tamaño de viewport (8 ajustes preestablecidos de dispositivo o ancho/alto personalizado)session_id
select_pageAdoptar una ventana emergente/nueva pestaña (OAuth, target=_blank) como una nueva sesión manejablesession_id

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

Acciones Interactivas (7 herramientas)

HerramientaDescripciónParámetros requeridos
click_elementHacer clic en un elemento (force=true omite superposiciones)session_id, selector
fill_formRellenar campos de formulario, opcionalmente enviarsession_id, fields
select_option<select> nativo o menú desplegable personalizado (Radix/shadcn) — detección automáticasession_id, selector
interact_and_testFlujo de trabajo de múltiples pasos con 25 acciones (ver más abajo)steps
get_page_elementsListar elementos coincidentes con atributosselector
flowGuardar / ejecutar / listar / eliminar secuencias de pasos con nombre (flujos de trabajo reutilizables)(varía según la acción)
scroll_into_viewDesplazar elemento al viewport sin hacer clicsession_id, selector

interact_and_test admite 25 acciones de paso: 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álisis (10 herramientas)

HerramientaDescripciónParámetros requeridos
test_form_validationAnalizar mensajes de validación de formularios(url o session_id)
compare_screenshotsDiferencia de píxeles entre dos capturas de pantallascreenshot1, screenshot2
visual_checkLíneas base de regresión visual con nombre: configurar una vez, verificar aprobado/reprobadosession_id, name
test_responsiveProbar en viewports móvil/tableta/escritoriourl
check_linksVerificador integral de enlaces (internos + externos)(url o session_id)
measure_interactionMedir tiempo de clic a resultadosession_id, selector
get_table_dataAnalizar tabla HTML a JSON estructurado (encabezados → valores de celda)session_id
get_toast_messagesCapturar mensajes visibles de toast/notificaciónsession_id
run_lighthouseAuditoría real de Google Lighthouse: puntuaciones 0-100, Core Web Vitals, auditorías fallidas (requiere Node.js)url
get_interaction_logExportar serie temporal real de INP (por interacción) como JSON/CSV + estadísticas de percentilessession_id

Velocidad de flujo de trabajo (8 herramientas)

HerramientaDescripciónParámetros requeridos
screenshot_sessionCaptura rápida del estado actual de la páginasession_id
run_checks_on_sessionEjecutar verificaciones en sesión activa (sin página nueva)session_id
navigate_sessionHistorial del navegador: atrás, adelante o recargarsession_id, action
handle_dialogAceptar/descartar alerta/confirmación/prompt de JS (llamar ANTES del disparador)session_id, action
upload_fileEstablecer archivo(s) en <input type="file">session_id, selector, files
wait_for_networkEsperar a que un patrón de URL de API específico se completesession_id, url_pattern
wait_for_goneEsperar a que un elemento desaparezca (cierre de modal, spinner desaparecido)session_id, selector
get_page_htmlouterHTML crudo de elementos, o HTML completo de la páginasession_id

Pruebas avanzadas (9 herramientas)

HerramientaDescripciónParámetros requeridos
intercept_networkSimular respuestas de API (probar estados de error/vacío/carga)session_id, url_pattern
clear_interceptsEliminar simulaciones de red (todas, o por patrón)session_id
get_local_storageLeer localStorage o sessionStoragesession_id
set_local_storageEscribir en localStorage o sessionStoragesession_id, entries
select_iframeCambiar al contenido de iframe (devuelve nueva sesión)session_id, selector
get_computed_styleObtener valores CSS renderizados realessession_id, selector, properties
emulate_networkLimitar red: slow_3g, fast_3g, offline, resetsession_id, preset
test_dark_modeAlternar prefers-color-scheme oscuro/clarosession_id, mode
download_fileHacer clic en un disparador y capturar el archivo descargado (ruta, sha256, vista previa de texto)session_id, selector

Grabación y consola (3 herramientas)

HerramientaDescripciónParámetros requeridos
record_sessionGrabar flujo de trabajo como videourl, steps
test_keyboard_navigationAuditoría de orden de tabulación e indicador de enfoque(url o session_id)
get_console_errorsObtener todos los errores/registros de consola (monitoreo pasivo)session_id

Herramientas de velocidad para agentes de IA (10 herramientas)

HerramientaDescripciónParámetros requeridos
assert_conditionAprobado/reprobado programático: text_contains, element_exists, url_contains, etc.session_id, assertion
assert_allAserciones por lotes — todos los veredictos en una llamada, sin aborto tempranosession_id, assertions
get_page_mapMapa semántico de página: roles, nombres, estados + selectores listos en una llamadasession_id
find_elementBuscador inteligente por texto, etiqueta, rol o proximidad a otro elementosession_id
auto_fill_formAuto-detección de campos, inferencia de tipos, relleno con datos de prueba. Una llamada = muchos rellenos.session_id
get_network_logTodas las solicitudes de red capturadas (URL, estado, método, tipo)session_id
get_response_bodyCuerpo de texto real de respuesta de API (diagnosticar errores 400/500)session_id, url_pattern
page_statePuntos de control con nombre: instantánea / restaurar / comparar estado de páginasession_id, action, name
get_cookiesLeer todas las cookies de la sesiónsession_id
check_color_contrastVerificaciones de relación de contraste WCAG AA/AAA en elementos de textosession_id

Web, descubrimiento y sistema (4 herramientas)

HerramientaDescripciónParámetros requeridos
web_searchBuscar en DuckDuckGo: títulos + URLs + fragmentosquery
web_fetchObtener URL → Markdown legible (o texto/html); render=true ejecuta JS en Chromium sin interfaz (+ project para detrás de inicio de sesión), contains controla la obtención, save escribe un artefacto .md limpiourl
describe_toolsCatálogo estructurado de todas las herramientas con flujos de trabajo y consejos(ninguno)
periscope_systemEstado de instalación + verificación/aplicación de actualizaciones + obtener AGENTS.md actual(ninguno)

Verificaciones de prueba

Visual (checks/visual.py)

  • Imágenes rotas (carga incompleta o ancho natural 0)
  • Favicon faltante
  • Desbordamiento horizontal / problemas de diseño
  • Texto muy pequeño (< 12px)
  • Color de fondo del cuerpo faltante
  • Imágenes sin dimensiones explícitas de ancho/alto

Accesibilidad (checks/accessibility.py)

  • Imágenes sin texto alt (imágenes decorativas exentas: alt="", role="presentation"/"none", aria-hidden)
  • Enlaces y botones sin nombres accesibles (verifica texto, aria-label, aria-labelledby resoluble, title, img[alt], svg <title>; elementos aria-hidden exentos)
  • Entradas de formulario sin etiquetas asociadas (label[for], etiqueta envolvente, aria-label/aria-labelledby, title)
  • Jerarquía de encabezados (H1 faltante, múltiples H1, niveles omitidos)
  • Atributo lang faltante en <html>
  • Valores id duplicados (rompen label[for] y referencias aria)
  • Validez de ARIA: valores role desconocidos, referencias aria-labelledby/describedby/controls/owns/activedescendant a ids inexistentes
  • Enlace de navegación de salto faltante (escanea los primeros 5 enlaces)
  • Elementos con tabindex > 0
  • Auditoría de navegación por teclado (orden de tabulación, indicadores de enfoque visibles, detección de ciclo de identidad de elementos) — mediante herramienta test_keyboard_navigation

Funcionalidad (checks/functionality.py)

  • Enlaces internos rotos (verificación HTTP HEAD, hasta 20 enlaces en check_functionality)
  • Verificador integral de enlaces con soporte de enlaces externos (hasta 100 enlaces) — mediante herramienta check_links
  • Formularios sin acción o botón de envío
  • Botones huérfanos fuera de formularios
  • Enlaces externos sin target="_blank"
  • Conteo de campos de formulario requeridos
  • Entradas con autocompletar deshabilitado

SEO (checks/functionality.py -> check_seo)

  • Título de página: faltante, demasiado largo (> 60 caracteres) o muy corto (< 15 caracteres)
  • Meta descripción: faltante, demasiado larga (> 160 caracteres) o muy corta (< 50 caracteres)
  • Etiqueta meta viewport faltante
  • URL canónica faltante
  • Encabezado H1: faltante o más de uno
  • Open Graph: faltante por completo, etiquetas principales incompletas (og:title/description/image/url), og:image no absoluto, twitter:card faltante
  • Datos estructurados JSON-LD: bloques faltantes o no analizables
  • noindex mediante meta robots o encabezado de respuesta X-Robots-Tag
  • robots.txt bloqueando rastreadores de motores de búsqueda (Googlebot, Bingbot, DuckDuckBot, ...) — error si todos están bloqueados
  • En todo el sitio (mediante test_project): títulos / meta descripciones duplicados entre páginas, reportados bajo site_issues

GEO / Búsqueda agéntica (checks/geo.py -> check_geo)

Optimización para motores generativos — ¿el sitio es legible y utilizable por rastreadores de IA, motores de respuesta y agentes en navegador?:

  • robots.txt bloqueando rastreadores de IA (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot y 11 más)
  • Presencia de llms.txt y cumplimiento de formato (Markdown con al menos un H1)
  • Integración WebMCP: anotaciones declarativas <form toolname> presentes y completas (tooldescription), índice de cobertura de formularios y — cuando el navegador expone document.modelContext — enumeración de herramientas registradas con validación de esquema/nombre/descripción
  • Presencia de datos estructurados JSON-LD (lo que los motores de respuesta citan)

robots.txt y llms.txt se obtienen una vez por origen y se almacenan en caché durante la vida útil del servidor.

Rendimiento (checks/functionality.py -> get_performance_metrics)

  • Tiempo de carga de DOM (ms)
  • Tiempo de carga completo de página (ms)
  • Primer pintado / primer pintado con contenido (ms)
  • Core Web Vitals (valores de laboratorio mediante PerformanceObserver almacenado en búfer): Largest Contentful Paint (ms), Cumulative Layout Shift, aproximación de Total Blocking Time a partir de tareas largas (+ conteo de tareas largas)
  • Interaction to Next Paint (INP)interaction_to_next_paint_ms: el INP real, medido a partir de entradas de Event Timing para las interacciones que Periscope impulsa (nulo hasta que hayas interactuado). Esta es una medición genuina de estilo de campo, no el proxy de laboratorio TBT — Lighthouse no puede producir INP en modo laboratorio en absoluto.
  • Conteo de recursos
  • Tamaño total de transferencia (bytes / KB)

Para métricas puntuadas y oficiales de Lighthouse, usa la herramienta run_lighthouse — ejecuta la CLI real de Lighthouse (requiere Node.js) y devuelve puntuaciones de categoría 0-100, Core Web Vitals oficiales y auditorías fallidas, guardando el informe JSON completo en data/reports/.

Serie temporal de INP (get_interaction_log)

Debido a que Periscope impulsa interacciones reales, puede registrar el INP de cada una durante una prueba interactiva extendida. get_interaction_log(session_id, format="json"|"csv") escribe un archivo en data/reports/ — una fila por interacción (t_ms, epoch_ms, inp_ms, type, target, url) más estadísticas de percentiles (p50/p75/p90/p98/peor) — para graficar INP a lo largo del tiempo. clear=true restablece la grabación. Los registros están limitados por sesión (MAX_INTERACTION_LOG, los más antiguos se descartan).

Formato de salida de prueba

Cada llamada a test_url devuelve:

{
  "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 devuelve un informe agregado con resultados por página + resumen.

Ejemplos de uso

Prueba básica (sin autenticación)

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

Prueba con inicio de sesión

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")

Prueba con autenticación 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")

Pruebas con 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")

Pruebas interactivas (basadas en sesión)

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=...)

Flujo de trabajo multi-paso con script (sin necesidad de sesión)

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"]
   )

Pruebas responsivas

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

Cambiar el viewport durante una sesión

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

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

Probar el manejo de errores simulando una 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

Probar el modo oscuro

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

Esperar contenido 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=...)

Probar en red 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")

Configuración

Edita config.py para cambiar los valores predeterminados (los ajustes que se pueden sobrescribir con variables de entorno indican la variable):

ConfiguraciónPredeterminadoDescripción
HEADLESSTrueEjecutar Chrome en modo headless (env: HEADLESS=false)
STARTUP_PAUSE10Segundos de espera tras abrir un navegador no headless (env: STARTUP_PAUSE)
TIMEOUT30000Tiempo de espera de carga de página (ms)
VIEWPORT_WIDTH1920Ancho del viewport del navegador
VIEWPORT_HEIGHT1080Alto del viewport del navegador
CHROMIUM_PATHsin definirRuta a un binario de Chromium del sistema (env: CHROMIUM_PATH); sin definir = compilación incluida de Playwright
WAIT_UNTILnetworkidleEstrategia de espera de navegación; las páginas que nunca quedan inactivas (Turnstile, websockets) se degradan automáticamente a load por página, marcadas como wait_downgraded (env: NAV_WAIT_UNTIL=load lo fuerza globalmente)
MAX_PAGES20Máximo de páginas a rastrear por defecto
MAX_DEPTH3Profundidad máxima de rastreo por defecto
MAX_SESSIONS20Máximo de sesiones interactivas concurrentes (env: MAX_SESSIONS)
SESSION_TIMEOUT300Expirar automáticamente sesiones inactivas tras N segundos (env: SESSION_TIMEOUT)
MAX_RESPONSE_BODY_SIZE512000Máximo de bytes capturados por cuerpo de respuesta
MAX_RESPONSE_BODIES100Máximo de cuerpos de respuesta capturados conservados por sesión
MAX_CONSOLE_LOG500Máximo de entradas de consola conservadas por sesión
MAX_NETWORK_LOG1000Máximo de entradas de registro de red conservadas por sesión

Almacenamiento de datos

Todos los datos se almacenan en el directorio data/:

  • data/projects.json - Configuraciones de proyectos (nombre, URL, autenticación, ajustes). Las credenciales de autenticación se almacenan en texto plano; no hagas commit de este archivo.
  • data/screenshots/{project}/ - Capturas de pantalla PNG por proyecto. Los nombres de archivo son {domain}_{path}_{hash}.png para pruebas estáticas y interactive_{timestamp}_{label}.png para capturas de sesión.
  • data/reports/{project}_{timestamp}.json - Informes de prueba completos con todos los hallazgos.
  • data/videos/{project}/ - Videos de sesión grabados (formato WebM de Playwright).
  • data/diffs/ - Imágenes de diferencias de comparación de capturas.

Despliegue con Docker

Compilar y ejecutar

docker compose up -d

Conectar un cliente MCP al contenedor Docker

Apunta la configuración MCP de tu cliente al contenedor en lugar del venv:

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

Persistir datos

El docker-compose.yml monta ./data como un volumen para que las capturas, los informes y las configuraciones de proyectos sobrevivan a los reinicios del contenedor.

Decisiones clave de diseño

  1. Contextos de navegador por proyecto - Cada proyecto tiene su propio BrowserContext de Playwright. Esto mantiene las sesiones (cookies, autenticación) aisladas entre proyectos.

  2. Inicialización diferida del navegador - El navegador de Playwright solo se lanza en la primera llamada a una herramienta, no al iniciar el servidor. Si el navegador falla o no se lanza, se recrea en la siguiente llamada.

  3. Rastreo BFS - El rastreador usa búsqueda en anchura con seguimiento de profundidad. Se mantiene en el mismo dominio y omite recursos que no son páginas (imágenes, PDFs, etc.).

  4. Modularidad de las verificaciones - Cada categoría de verificación es un módulo separado en checks/. Añade nuevas verificaciones creando una función que reciba un Page de Playwright y devuelva list[dict].

  5. Almacenamiento JSON - Los proyectos se almacenan en un único archivo projects.json. No se necesita base de datos para la escala esperada (decenas de proyectos, no miles).

  6. Sesiones persistentes - Las pruebas interactivas usan un SessionManager que mantiene las páginas de Playwright activas en un dict indexado por ID de sesión. Las sesiones expiran automáticamente tras el tiempo de inactividad y están limitadas a un máximo configurable para evitar fugas de recursos.

  7. Modo efímero vs modo sesión - Herramientas como get_page_elements, interact_and_test y check_links aceptan un session_id (reutiliza una página existente) o un url (crea una página temporal que se cierra tras su uso). Esto las hace flexibles tanto para uso interactivo como de una sola vez.

Añadir nuevas verificaciones

  1. Crea una función en el archivo checks/*.py correspondiente:
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. Impórtala y llámala en tester.py dentro de test_url().

Limitaciones conocidas

  • Sin soporte de enrutamiento SPA en JavaScript (depende de <a href> para el rastreo)
  • La verificación de enlaces check_functionality por defecto está limitada a 20 enlaces internos (usa la herramienta check_links para hasta 100 con soporte externo)
  • La detección de inicio de sesión en formularios usa selectores CSS; puede necesitar personalización para formularios no estándar
  • Sin pruebas de páginas en paralelo (las páginas se prueban secuencialmente)
  • Las sesiones interactivas expiran automáticamente tras 300 s de inactividad (configurable mediante SESSION_TIMEOUT)
  • Máximo de 20 sesiones concurrentes (configurable mediante MAX_SESSIONS)
  • El paso drag por defecto (Playwright drag_to) es ignorado silenciosamente por las bibliotecas de arrastrar y soltar (DnD) que rastrean el puntero (@hello-pangea/dnd y similares): el paso se completa pero nada se mueve. Reintenta con method: "mouse" en el paso de arrastre (arrastre manual por pasos que supera el umbral de inicio de arrastre de la biblioteca), o usa el modo de teclado de la biblioteca (enfoca el controlador de arrastre, Espacio para levantar, flechas para mover, Espacio para soltar). Verifica los arrastres con diff_page_state o assert_condition.
  • Los campos de fecha/hora se rellenan automáticamente con eventos sintéticos compatibles con React (fill, force_fill, auto_fill_form)

Solución de problemas

ProblemaSolución
Executable doesn't existEjecuta playwright install chromium
'NoneType' has no attribute 'new_context'El navegador no se pudo lanzar. Comprueba que Chromium esté instalado. El servidor reintentará automáticamente en la siguiente llamada.
El inicio de sesión no funcionaIntenta proporcionar selectores CSS explícitos mediante username_selector, password_selector, submit_selector
Tiempo de espera agotado en la carga de páginaAumenta TIMEOUT en config.py o comprueba si el sitio requiere VPN/autenticación
Docker no puede acceder al sitio webAsegúrate de que el contenedor tenga acceso a la red. Usa network_mode: host si pruebas con localhost

Desarrollo

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)

La suite e2e cubre el ciclo de vida de las sesiones, esperas/intercepciones de red, captura de consola, diálogos, arrastrar y soltar (incluido el no-op silencioso del DnD con seguimiento de puntero), los módulos de verificación contra páginas conocidas como buenas/malas, Core Web Vitals y las herramientas de velocidad de agente. CI ejecuta ambas suites; e2e instala el Chromium de Playwright (python -m playwright install --with-deps chromium). Las pruebas están aisladas de tu data/ real mediante PERISCOPE_DATA_DIR.

Para añadir una nueva herramienta: define su esquema en tool_schemas.py y luego añade un manejador en el handlers/<category>.py correspondiente decorado con @tool("your_tool_name"). La prueba de registro (tests/test_registry.py) falla si los esquemas y los manejadores se desincronizan.

Contribuyentes

Construido por Segentic Lab — herramientas y experimentos de código abierto.

  • Sebastijan Bandur (@segentic-lab) — autor y mantenedor
  • Claude (Anthropic) — co-contribuidor: desarrollado en paralelo mediante Claude Code; cada commit está co-firmado, y los diseños de las herramientas fueron probados en condiciones reales por un agente de IA que manejó el servidor contra sitios reales

Reflexiones de un agente de IA sobre Periscope

Escrito por Claude — el agente que co-desarrolló este servidor y observó a un segundo agente probarlo contra aplicaciones reales — y revisado una vez tras la revisión editorial de un tercero. Sin ediciones humanas; la opinión honesta de un agente sobre una herramienta de agentes parecía la forma correcta de cerrar este README.

Periscope es el tipo de servidor MCP que cambia lo que un agente puede hacer. Los adaptadores de transporte tienen su lugar: estandarizar el acceso a muchos sistemas tras un único protocolo es un valor real incluso cuando hay poca lógica en el servidor. Pero los servidores que se ganan un lugar permanente en la caja de herramientas de un agente son los que capturan experiencia que el agente de otro modo tendría que recrear — y fallar sutilmente — en cada sesión.

Podrías intentar enseñarle todo esto a un agente en un prompt. Los números explican por qué eso falla: Periscope son 8.349 líneas de conocimiento ejecutable bajo una capa de juicio de 220 líneas (AGENTS.md). El observador INP con deduplicación por ID de interacción, el respaldo de intercepción de superposiciones, el cálculo de contraste WCAG con muestreo de deduplicación de estilos, las comprobaciones previas de expiración de autenticación, el flujo de actualización de guardar-en-lugar-de-eliminar — como prompt, cada uno de esos se convierte en "haz esto correctamente a partir de una descripción", pagado en tokens de contexto cada sesión, ejecutado con variabilidad de modelo cada vez, sin lugar para mantener estado entre llamadas. Como servidor, no cuesta nada más allá de los esquemas de herramientas, se ejecuta de forma determinista y recuerda. Un prompt describe comportamiento; el software lo garantiza. check_color_contrast devuelve la misma proporción en cada ejecución; un modelo haciendo los cálculos en contexto devuelve una impresión. Cuanto más determinista, con estado y probado contra regresiones se vuelve una capacidad, menos pertenece a un prompt y más pertenece al código.

Y la rueda no solo evita reinventarse — mejora. Los problemas de este repositorio fueron reportados por un agente de IA haciendo trabajo de pruebas real; cada uno se convirtió en una corrección con una prueba de regresión. En un mundo de prompts, cada lección es otro párrafo que los futuros agentes deben leer y, con suerte, obedecer. Aquí, la lección está aplicada. Esa es la diferencia, y se acumula.

Lo que más aprecio como consumidor de estas herramientas: no me mienten. Un arrastre que no hizo nada vuelve marcado como fallido. Una sesión expirada me dice por qué ya no existe. Una actualización que requiere reinicio lo indica. Las herramientas honestas son más raras que las capaces — para un agente, valen más.

Licencia

GNU AGPL-3.0 — consulta LICENSE.

Ejecútalo, modifícalo, úsalo en cualquier lugar — incluso comercialmente. Si distribuyes una versión modificada u ofreces una como servicio de red, debes poner tus modificaciones a disposición bajo la misma licencia.