Skip to main content
Glama

Playwright MCP en Render

Implementa Playwright MCP en Render con un clic. Obtén un servidor de MCP alojado con Chromium headless que tus herramientas de IA puedan usar a través de HTTP: sin instalar un navegador local ni npx en cada máquina.

Deploy to Render

https://github.com/user-attachments/assets/0f31c279-f2e0-431f-a854-50677bd800c5

Qué hace

Playwright MCP es un servidor del Model Context Protocol que permite a un LLM abrir un navegador real, navegar, hacer clic, escribir y leer páginas mediante instantáneas de accesibilidad estructuradas (no capturas de pantalla). Normalmente se ejecuta localmente con npx @playwright/mcp. Esta plantilla lo ejecuta como un único servicio web de Render, de modo que cualquier cliente MCP pueda conectarse a una URL HTTPS compartida.

Es un envoltorio fino sobre la imagen oficial mcr.microsoft.com/playwright/mcp (Chromium headless ya incluida), sin cambios en el código fuente. El envoltorio añade los flags que Render necesita (--headless, --no-sandbox, además de --port y --allowed-hosts desde el entorno) y un pequeño control de acceso bearer token delante del servidor, porque Playwright MCP no tiene autenticación propia.

Autenticado por defecto. Playwright MCP no tiene autenticación en modo HTTP e incluye una herramienta equivalente a RCE; por eso esta plantilla no lo publica directamente: las peticiones deben llevar Authorization: Bearer $MCP_TOKEN, y Render genera ese token por ti en el momento del despliegue. Lee Seguridad para saber qué protege y qué no.

Para ver la lista completa de herramientas, opciones de configuración y configuración de clientes, consulta el README upstream.

Related MCP server: WebControl

Arquitectura

Un único servicio web de Render ejecuta la imagen oficial de Playwright MCP detrás de un controlo de acceso bearer token. Un cliente MCP se comunica por Streamable HTTP con /mcp a través del borde TLS de Render; el controlo de acceso verifica el token y reenvía el resto al servidor MCP en loopback, que maneja un Chromium headless en el mismo contenedor y devuelve instantáneas de accesibilidad.

┌─────────────┐   HTTPS /mcp    ┌────────────────────────────────────────────────┐
│  MCP client │ ──────────────► │ Render web service  (Docker, standard plan)    │
│ (Claude,    │  + Bearer       │                                                │
│  Cursor, …) │    token        │  render-entrypoint.sh                          │
│             │                 │    │ reads PORT, allowed hosts                 │
│             │  Streamable     │    ▼                                           │
│             │ ◄────────────── │  render-auth-proxy.mjs   :$PORT  (public)      │
└─────────────┘   snapshots     │    │ 401 unless the Bearer token matches       │
                                │    ▼                                           │
                                │  node /app/cli.js  127.0.0.1:8931  (loopback)  │
                                │    │                                           │
                                │    ▼                                           │
                                │  headless Chromium  (baked into base image)    │
                                └────────────────────────────────────────────────┘

Cómo se ensambla un despliegue:

Archivo

Rol

render.yaml

Blueprint. Declara el único servicio web de Docker, su plan/región, la variable de entorno PORT y el MCP_TOKEN generado. Es lo que lee el botón Deploy.

Dockerfile.render

Envoltorio fino sobre mcr.microsoft.com/playwright/mcp (Chromium headless ya incorporado). Solo añade el entrypoint y el controlo de acceso: sin descargar el navegador, sin compilar fuentes.

render-entrypoint.sh

Lee PORT, resuelve el valor de allowed-hosts y hace exec del control de acceso con el servidor MCP (enlazado a loopback) como argumento.

render-auth-proxy.mjs

PID 1. Node de pura librería estándar, sin dependencias: rechaza cualquier petición sin Authorization: Bearer $MCP_TOKEN y limita los intentos repetidos, reenvía el resto a 127.0.0.1:8931, supervisa el servidor MCP, mantiene el puerto público cerrado hasta que ese servidor acepte y reenvía TERMECH.

render-auth-limiter.mjs

El límite de tamaño del control de acceso, mantenido separado porque es la única pieza con estado que vale la pena comprobar por sí sola: tope de ventana fija para los intentos de autenticación fallidos, contados en todos los clientes. Lo importa el proxy, por lo que Dockerfile.render también debe copiarlo.

render-smoke-test.sh

Compila la imagen y prueba el envoltorio de extremo a extremo: falla cerrado sin MCP_TOKEN, devuelve 401 sin la cabellera, 429 después de varios intentos malos, un handshake y una llamada reala al navegador con el token correcto, y limpio SIGTERM. CI lo ejecuta en cada PR.

.env.example

Documenta los mismos parámetros para ejecutar el contenedor localmente.

Propiedades clave:

  • Envolatorio fino, sin fork de la herramienta. La versión de Playwright MCP está fijada por la etiqueta de la imagen base en Dockerfile.render; las actualizaciones son tocar esa etiqueta en una línea (consulta Actualización de Playwright MCP).

  • Sin base de datos, sin disco que llenar. El único secreto, MCP_TOKEN, lo generó Render en el despliegue. El navegador mantiene por defecto el perfil persistente de upstream, por lo que los inicios de sesión se mantienen entre peticiones durante toda la vida de la instancia; útil para un servidor tuyo solo, como asume está plantilla (consulta Configuración).

  • Autenticada y con cierre ante faltas. El controlador de acceso lo añade esta plantilla, no upstream. Si no se establece MCP_TOKEN, el contenedor se niega a iniciarse, así que no hay configuración en la que el servidor quede expuesto sin autorización (consulta Seguridad).

Requisitos previos

Para desplegar, necesitas:

  • Una cuenta de Render — crear una es gratis; el propio servicio se ejecuta en el tipo standard de pago (consulta Despliegue).

  • Una cuenta de GitHub, para hacer un fork de este repositorio (el botón Deploy lee render.yaml de un repositorio que sea tuyo).

No se necesitan claves de API ni cuentas de terceros. El único secreto, MCP_TOKEN, se genera automáticamente al desplegar: solo tienes que copiarlo en tu cliente MCP (consulta Despliegue).

Para ejecutarlo también en local (opcional — consulta Ejecución local):

  • Docker con BuildKit (Docker Desktop 4.6 o superior, o compatible con 4. or Docker Engine 23+). Dockerfile.render usa una directiva # syntax= y COPY --chmod, ambas funciones de BuildKit.

  • Un cliente MCP al que apuntarlo (p. ej., Claude Code, Cursor), o simplemente curl.

No necesita Node.js ni Playwright ni un Chromium local: la imagen base ya incluye todo eso.

Despliegue

  1. Haz clic en Deploy to Render arriba (o bifurca este repositorio y crea un nuevo Blueprint a partir de él).

  2. Render lee la información del blueprint de permitir Render y aprovisiona un único servicio web Docker (playwright-mcp) en el plan standard.

  3. Espera a que el despliegue pase a live (en línea). Tu servidor estará en https://<tu-servicio>.onrender.com/mcp.

  4. Copia MCP_TOKEN de la página Environment del servicio en el Dashboard de Render; Render lo generó por ti. Tu cliente MCP debe enviarlo como Authorization: Bearer <token> (consulta Usar la aplicación); sin él, cada petición tendrá un 401.

  5. Antes de apuntar algo sensible, lee Seguridad. The token is the only thing between the Internet and the code execution in this container, so treat it like the password — and further tighten it if possible.

Sizing del plan: Chromium headless con memoria (OOM) en el nivel gratuito / starter (512 MB), así que el blueprint usa por defecto standard (2 GB). Cambia a un plan inferior solo si has confirmado que tu carga funciona con menos.

Usando la aplicación

Apunte cualquier cliente MCP al endpoint /mcp de tu servicio y envía MCP_TOKEN como token saltador. Por ejemplo, con Claude Code:

claude mcp add --transport http playwright https://<your-service>.onrender.com/mcp \
  --header "Authorization: Bearer <your-MCP_TOKEN>"

O el ciente de la configuración:

{
  "mcpServers": {
    "playwright": {
      "url": "https://<your-service>.onrender.com/mcp",
      "headers": {
        "Authorization": "Bearer <your-MCP_TOKEN>"
      }
    }
  }
}

Si el cliente devuelve un 401, la cabecera falta o el token no coincide con el valor actual del Dashboard.

Puedes pedirle a tu asistente que navegue… por ejemplo: "Abre example.com y dame el título de la página y el encabezado principal."… que combine a las herramientas de Playwright MCP contra tu navegador alojado y devuelva ese resultado.

Ejecutar (o) el proyecto local

Opcional, el camino de despliegue anterior no necesita nada de esto. Útil si quieres modificar render-entrypoint.sh y ver el efecto antes de enviar.

git clone https://github.com/render-examples/playwright-mcp-render.git
cd playwright-mcp-render
cp .env.example .env          # then set MCP_TOKEN, e.g. to `openssl rand -hex 32`
docker build -f Dockerfile.render -t playwright-mcp-render .
docker run --rm --env-file .env -p 10000:10000 playwright-mcp-render

Cuando esté listo, el contenedor imprime el Listening on … de upstream y luego el [auth] Bearer-token gate listening … del controlador, **ese orden es clave porque el controlador mantiene el puerto público cerrado hasta que el servidor detrás acepta, ** y que el último line es el that indica que el servicio eshttps://usable. (La línea [startup] de arriba imprime una URL https:// — ese esquema es para el despliegue; localmente usa http://.) Verifica el estado con un handshake de MCP: exporta el mismo token que pusiste en .env primero, para que la cabecera siguiente se resuelva:

curl -sS -X POST http://localhost:10000/mcp \
  -H "Authorization: Bearer $MCP_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

Deberías obtener un bloque serverInfo que nombre de CLI Playwright, o si el Authorization salta, 401 Unauthorized. Entonces, apunta un cliente a http://localhost:10000/mcp igual que lo harías con la dirección del despliegue.

MCP_TOKEN es la única variable de entorno que debes suministrar; el contenedor se cae sin ella sin más. El resto de .env es comodidad: fuera de Render no hay RENDER_EXTERNAL_HOSTNAME, así que la entrada usa PORT=10000 y --allowed-hosts * como valor por defecto.

¿Prefieres no ejecutar Docker? Upstream sirve el mismo servidor directamente: npx @playwright/mcp@latest --port 10000 — ten en cuenta that this is whatever npm publishes, esnoor esa, la etiqueta que fija la plantilla, y además no incluye el control de acceso (que es aportación de esta plantilla). Ese camino tampoco es lo que Render despliega, así que verifica tus cambios dentro del contenedor antes de hacer el push.

Configuración

Todo se define en render.yaml y el archivo de entorno .env.example documenta los mismos parámetros para ejecutar en localmente.

Variable

Default

Para qué

PORT

10000

El puerto en el que se expone el gate por el que Render enruta.

MCP_TOKEN

generado por Render

Obligatorio. Token para pasarlas en cada petición a través de la cabecera Authorization: Bearer. Sin servidor no se monta. Cambia el valor (pulsando en Generate) en el Dashboard y updating tus clientes.

UPSTREAM_PORT

8931

Puerto loopback del servidor MCP detrás del control el. Enhanced only if there is a collision with something inside the container.

.env.example también lista PLAYWRIGHT_MCP_HOST, PLAYWRIGHT_MCP_HEADLESS y PLAYWRIGHT_MCP_NO_SANDBOX. Estos parámetros solo importan cuando ejecutas el servidor directamente (fuera de esta imagen) — en Render, render-entrypoint.sh siempre pasa las banderas CLI equivalentes, y las banderas CLI tienen prioridad, así que configurarlos en el panel de Render no tiene ningún efecto. La única excepción es PLAYWRIGHT_MCP_ALLOWED_HOSTS, que el entrypoint sí respeta.

El entrypoint limita la comprobación de host del servidor al nombre de host onrender.com de tu propio servicio automáticamente mediante RENDER_EXTERNAL_HOSTNAME de Render. Si añades un dominio personalizado, las solicitudes dirigidas a él serán rechazadas por la comprobación de host hasta que definas PLAYWRIGHT_MCP_ALLOWED_HOSTS (separado por comas, por ejemplo, myapp.com,myapp.onrender.com; * desactiva la comprobación).

Estado del perfil del navegador

El entrypoint no pasa ninguna bandera de perfil, por lo que obtienes el valor predeterminado de Playwright MCP: un perfil persistente en el sistema de archivos del contenedor (~/.cache/ms-playwright/mcp-*). Merece la pena conocer dos consecuencias:

  • Los inicios de sesión sobreviven entre llamadas — normalmente es lo que quieres. Autentícate una vez a través del navegador alojado y las sesiones posteriores reutilizarán la sesión. Cada cliente que apunte a la URL comparte ese único perfil, así que esto asume que el servicio es tuyo (consulta Seguridad). No se adjunta ningún disco, por lo que el perfil se borra al reiniciar o volver a implementar.

  • Clientes concurrentes pueden entrar en conflicto. El proyecto de origen señala que un perfil persistente "solo puede ser usado por una instancia del navegador a la vez, por lo que los clientes MCP concurrentes que compartan el mismo espacio de trabajo entrarán en conflicto" — así que dos editores apuntando a una misma URL pueden colisionar. Escalar a más de una instancia también le da a cada instancia su propio perfil.

Para modificar cualquiera de estos comportamientos, edita render-entrypoint.sh:

Quieres

Añade

Un perfil nuevo en memoria por sesión, descartado al final

--isolated

Un perfil que sobrevive a redespliegues (sesiones guardadas)

un Render Disk junto con --user-device-dir <mount-path>

Seguridad

Lo que estás ejecutando realmente

Playwright MCP expone browser_run_code_unsafe, descrito en el proyecto de origen como: "Ejecuta un snippet de código de Playwright. No seguro: ejecuta JavaScript procesado y arbitrario en el proceso del servidor de Playwright y es equivalente a RCE." En la versión fija, está enumerado bajo Core automation, no entre las capacidades opcionales, y --caps solo habilita capacidades adicionales (vision, pdf, devtools) — quindi no puede eliminar este tool, y ninguna bandera descarta herramientas individuales. Cualquier persona que pueda llamar a este servidor puede ejecutar código en tu contenedor como el usuario del contenedor, y llegar a tus otros servicios de Render a través de la red privada.

El proyecto de origen no incluye el inicio de autenticating en modo HTTP, lo que es defendible cuando el servidor es npx en el loopback de tu portátil. En Render its afford a public URL, so this template add the missing door.

Qué hace la plantilla al respecto

render-auth-proxy.mjs es lo único que escucha en $PORT. Responde 401 a cualquier solicitud sin Authorization: Bearer $MCP_TOKEN y reenvía el resto al servidor MCP, que se une a 127.0.0.1 y nobody se publica. Específicamente:

  • Falla de forma segura (cierre por defecto). Sin MCP_TOKEN no hay inicio: no hay deliberadamente ninguna bandera de para desactivar la comprobación, por lo que ninguna mala configuración deja al servidor anónimo.

  • Secreto generado, no predeterminado. render.yaml usa generateValue: true, de modo que cada servicio recibe su propio token, generado una vez cuando se crea el servicio y estable a través de los redespliegues, por lo que los clientes siguen funcionando: además hay no que no hay credencial común en este repo que pueda filtrarse.

  • Comparación de tiempo constante de los resúmenes SHA-256, así que la comprobación no filtra el token byte a byte, y un prefijo del token no se acepta.

  • Nunca registra el token. Los rechazos solo loguean el método, la ruta y el código 401 — nada de lo que presenta el cliente.

  • Limita él la adivinanza. Los intentos fallidos comparten un presupuesto global: diez en un minuto y el resto de la ventana obtiene 429 con Retry-After, más una sola línea de registro en lugar de una po cada intento. El presupuesto solo se gasta con solicitudes que iban a ser rechazadas de todos modos, así que un token válido no está sujeto a ello nunca — el bloqueo no puede apuntarte a ti, y siempre puedes volver a entrar mientras un atacante está bloqueado. No es deliberadamente por cliente: esta puerta da servicio a un único secreto compartido, por lo que lo que vale la pena garantizar es un techo total a la velocidad de adivinanza, y un límite por dirección no proporciona esto — el enemigo que va rotando direcciones cada vez consigue una nueva presupuesto.

  • Detiene el token en la puerta. El encabezado Authorization no se reenvía al MCP, junto con los encabezados hop-by-hop que un proxy está obligado a eliminar. Las actualizaciones de conexión (WebSocket y similares) se contestan con 501 en vez de proxyse, ya que el transporte MCP nunca necesita una.

Lo que no hace

Esto es una puerta, no defensa en profundidad. No aísla el RCE, no limita lo que puede hacer una persona autenticada, ni te da identidad por cliente: cualquiera que con el token es el mismo principal, y el límite de tasa anterior frena la adivinanza, no el mal uso de un token que ya se filtró. Si puedes, añade una capa más fuerte encima:

  • Limita quién puede llegar a este servicio. Las reglas de IP entrante restringidas a tus direcciones conocidas (planes Scale y Enterprise) significan que un token robado no se puede usar desde cualquier otro lugar.

  • No lo expongas al público. Si su cliente MCP es otro servicio de Render, cambie type: webee a type: pserv en render.yaml para que se transforme en un servicio privado — sin URL pública, esencialmente más seguro, y el token sigue aplicando. Tenga en cuenta que no recibe RENDER_EXTERNAL_HOSTNAME, así que la comprobación de host vuelve a * y el banner de inicio imprime una URL localhost; alcáncelo a través de la red privada en la que reside.

  • Trata el token como una contraseña. Manténgalo fuera de configuraciones y rastreadores de incidencias; roundí celo en el panel de control si se pudiera haber filtrado (un despliegue revise el nuevo valor) y suspenda o elimine el servicio. Después de usar, no lo utilice más.

  • Cuídalo. Los logs del servicio y las métricas son cómo se darías cuenta de un uso que no iniciaron; las líneas 401 significan que alguien está llamando.

Una consecuencia de ese presupuesto compartido que se debe conocer antes de depurar: mientras el atacante gasta el presupuesto, una solicitud con el token incorrecto obtiene 429 en vez de 401. Cualquiera puede mantenerlo vacío indefinidamente: con diez solicitudes rechazadas por minuto ¿Y si alguien? Quien; así que esto puede ser a un servicio que recibe escaneos de fondo, este puede ser un estado normal en lugar de inusual. Si es una prueba con un token que parece obsoleto (por ejemplo, rotado en el Dashboard, o copiado de un servicio antiguo), el código de estado no te dirá eso, y la solución es revisar el token y no aguardar el Retry-After. La petición con el token correct no se ve afectada en ningún caso, lo que hace que aceptar este compromiso sea razonable: cualquiera puede agotar el presupuesto, pero el bloqueo que produce no puede apuntarse contra ti.

La verificación de host que el entrypoint configura (ver Configuración) es una comprobación del encabezado Host contra el rebote de captura de DNS (DNS rebinding) — no control de acceso. Esta no impide que una petición directa con el token válido llegue.

Preparación de la versión de Playwright MCP

La versión queda fijada en un solo lugar: la etiqueta de la imagen base en Dockerfile.render. Para subirla, cambia etiqueta y actualiza la imagen base, ejecuta commit y vuelve a implementar (las imágenes runtime: docker no se despliegan cuando se mueve; un nuevo despliegue tira de la nueva imagen base).

Luego vuelve a comprobar las dos afirmaciones en Seguridad contra el README del proyecto original con la nueva etiqueta: si browser_run_code_unsafe sigue siendo una herramienta de automatización básica no opcional, y si --caps solo sigue agregando capacidades. Actualiza igualmente el enlace permanente de esa sección a la nueva etiqueta, ya es la única otra parte donde aparece una versión literal, y el enlace permanente ha de llevar una.

Después vuelve a verificar la puerta de enlace, ya que depende de la superfície HTTP del projecto. npm run test:wrapper cubre la lógica de la propia puerta en unos segundos sin Docker (el presupuesto de errores, el proxy que responde con 401/429/200, con un upstream espejo), y ./render-smoke-test.sh compila la imagen nueva y la comprueba de extremo a extremo (401 sin el encabezado, 429 tras repetidos intentos con tokens defectuosos, una conexión con el token correcto, una llamada real a la herramienta mediante el tránsito SSE, cierre limpio). El CI ejecuta lo dos en cada PR, así que que que tú abrعامi una PR es equivalente. Si el proyecto finalmente publica ayuda autenticación de verdad, prueba a usarla y borra render-auth-proxy.mjs; este archivo solo existe para llenar ese vacío.

No es una versión que debas mantener sincronizada: el campo version en package.json. Este repo es un fork de microsoft/playwright-mcp, por lo que ese campo es el propio letrero de la versión de npm del proyecto — — y el despliegue nunca lo ve (usa, Dockerfile.render solo copia render-entrypoint.sh, render-auth-proxy.mjs y render-auth-limiter.mjs). Espera que se difiera de la etiqueta de la imagen; déjalo como esta.


Basado en icrosos/playwright-mcp · Se despliega en render.

A
license - permissive license
B
quality
C
maintenance

Maintenance

0Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.

  • Live browser debugging for AI assistants — DOM, console, network via MCP.

  • Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.

  • Reliable web access for AI agents: smart HTTP, rotating proxies, and full-browser rendering.

View all MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to automate web tasks such as browsing, clicking, typing, and taking screenshots via the Model Context Protocol.
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Headless browser automation for LLM agents via REST API or MCP tools. Enables navigating pages, reading structured content, clicking elements, filling forms, and executing JavaScript.

View all related MCP servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/erlanov2023e/playwright-mcp-render-mt8x2ig6'

If you have feedback or need assistance with the MCP directory API, please join our Discord server