Volver al Centro de conocimiento
Despliegue

Servidor MCP de Puppeteer con un build propio

Configura un servidor MCP basado en Puppeteer para lanzar un build concreto de Chromium con perfil y comprobar qué binario arrancó en realidad.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Despliegue?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

Un servidor MCP de Puppeteer controla el lanzamiento del navegador, así que la configuración que recibe decide contra qué binario de Chromium y qué perfil se ejecuta cada tarea del agente. LaunchOptions de Puppeteer tiene una forma distinta de la de Playwright, y una configuración copiada entre ambos pierde en silencio las partes que no existen en el otro lado. El fallo es discreto: el servidor arranca, las herramientas responden y el navegador acaba siendo el que el servidor habría usado de todos modos. Puppeteer documenta executablePath como una ruta que se usa en lugar del navegador incluido, junto con la advertencia de que solo garantiza soporte para su propio build, de modo que el ajuste es deliberado y no transparente. BotBrowser proporciona el arranque desde Puppeteer con executablePath más un argumento de perfil dentro de args, pero no puede ofrecer un servidor MCP propio, de modo que un servidor que nunca reenvía esos valores no se puede corregir desde el lado del navegador.

TL;DR

  • El objeto de lanzamiento de Puppeteer coloca executablePath, args, headless, userDataDir, ignoreDefaultArgs, pipe y browser en un mismo nivel. Playwright anida las mismas ideas bajo launchOptions, así que las dos formas no son intercambiables.
  • headless en Puppeteer acepta true, false o la cadena 'shell', y 'shell' selecciona la implementación headless antigua. Un booleano escrito al estilo de Playwright es válido en ese campo; una configuración de Playwright copiada entera no lo es.
  • executablePath sustituye al navegador incluido, y la propia documentación de Puppeteer advierte que solo el build incluido está garantizado. Una ruta propia es una configuración admitida pero no verificada, y se demuestra leyendo el proceso.
  • El perfil viaja en args como un elemento propio, exactamente igual que bajo Playwright, porque es un argumento del navegador y no una opción de Puppeteer.
  • ignoreDefaultArgs y pipe cambian lo que el servidor entrega al proceso. Un servidor que fija ignoreDefaultArgs: true descarta los flags que Puppeteer añadiría, así que el argv efectivo no es ni el propio ni el conjunto de valores por defecto de Puppeteer.

Un flujo de configuración muestra un servidor MCP de Puppeteer que lee executablePath, args y headless de un único objeto de lanzamiento plano y después arranca el binario configurado, con una columna comparativa que muestra que la forma de Playwright MCP anida los mismos valores bajo browser.launchOptions.

Contenido

Dónde vive el objeto de lanzamiento de Puppeteer

LaunchOptions de Puppeteer es un objeto plano que se pasa a puppeteer.launch(). executablePath es un miembro de primer nivel, junto a args, headless, userDataDir, ignoreDefaultArgs, pipe, browser, channel y debuggingPort. No existe un envoltorio launchOptions, porque el objeto completo es, en sí mismo, las opciones de lanzamiento.

{
  "mcpServers": {
    "puppeteer": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-puppeteer"],
      "env": {
        "PUPPETEER_EXECUTABLE_PATH": "/opt/botbrowser/chrome",
        "PUPPETEER_LAUNCH_OPTIONS": "{\"headless\":true,\"args\":[\"--bot-profile=/etc/botbrowser/profile.enc\"]}"
      }
    }
  }
}

El ejemplo anterior muestra las dos vías por las que un servidor puede recibir los valores: una variable de entorno dedicada al ejecutable y un bloque JSON para el resto. Ninguno de los dos nombres está estandarizado entre servidores basados en Puppeteer, y ese es el primer punto que se revisa: la documentación del propio servidor indica qué variable o flag consume exactamente, y después se confirma que el valor llega.

Miembro de PuppeteerTipoEfecto sobre un build propio
executablePathstringSustituye al navegador incluido; Puppeteer solo garantiza soporte para su propio build
argsstring[]Argumentos de línea de comandos adicionales, un elemento del array por argumento
headlessboolean o 'shell'true usa el modo headless nuevo, 'shell' el antiguo
userDataDirstringDirectorio de perfil persistente, distinto de un argumento de perfil de BotBrowser
ignoreDefaultArgsboolean o string[]Descarta los flags por defecto de Puppeteer, o filtra los que se nombran
pipebooleanTransporta el protocolo por stdio en lugar de por un WebSocket
browserSupportedBrowserSelecciona la familia de navegador que Puppeteer debe esperar
channelChromeReleaseChannelBusca un Chrome instalado en una ubicación conocida en lugar del binario incluido

Dos de esas filas merecen atención en un despliegue con build propio. channel y executablePath pueden apuntar ambos a un navegador, y la documentación de Puppeteer describe channel como la búsqueda de una instalación normal de Chrome en una ubicación conocida del sistema. Configurar los dos deja dos respuestas para la misma pregunta, y la que gana es un detalle de implementación que se elimina en lugar de probarse.

Por qué la forma plana se malinterpreta con facilidad

Un objeto plano no ofrece ninguna señal estructural sobre qué claves son de nivel de lanzamiento y cuáles pertenecen a un contexto. En Playwright, una clave mal colocada cae en otro objeto y se ignora; en Puppeteer, una clave que no existe en LaunchOptions simplemente no forma parte de las opciones, y puppeteer.launch() no tiene motivo para quejarse. El resultado es el mismo desenlace silencioso por otra vía: el servidor arranca y uno de los ajustes nunca se aplicó.

La comprobación práctica consiste en escribir el objeto y comparar sus claves con los miembros documentados de LaunchOptions. Todo lo que no aparezca en esa lista no es una opción de lanzamiento, sea cual sea su aspecto.

Qué hace Puppeteer con una ruta que no puede usar

executablePath apunta a un archivo, y Puppeteer arranca ese archivo. Si la ruta no existe, no se resuelve en el directorio de trabajo del servidor, o existe pero la cuenta que ejecuta el servidor no puede ejecutarla, el lanzador produce un error en lugar de sustituir otro binario; esto se sigue de que Puppeteer entrega la ruta indicada al proceso que engendra, y es una inferencia sobre el comportamiento del lanzador, no una frase que la página de LaunchOptions afirme. Ese comportamiento importa porque es lo contrario del caso silencioso: una ruta equivocada que sí existe produce un navegador funcional que no es el pretendido, mientras que una ruta inexistente produce un error que el operador ve.

El fallo que se esconde es, por tanto, el de la ruta válida que apunta a otro sitio. Un servidor que sustituye la ruta por una por defecto cuando su configuración falta produce exactamente eso: un ejecutable válido, un navegador funcional y ningún error. La comprobación tiene que comparar la ruta observada con la pretendida, no limitarse a verificar que una ruta cualquiera existe.

Qué añade el servidor por encima

Un servidor MCP basado en Puppeteer es un envoltorio: lee su propia configuración, construye un objeto LaunchOptions y llama a puppeteer.launch(). Todo lo que el servidor añade entre su configuración y esa llamada resulta invisible desde fuera. Por eso la lectura de comprobación pesa más aquí que en un script directo, donde el objeto de opciones es el que escribió el operador.

La variante Playwright de esta envoltura, con su forma anidada y el respaldo silencioso a un navegador incluido, se trata en configuración de Playwright MCP para un build Chromium propio, y el reparto de responsabilidades entre un bucle de agente y el proceso del navegador está en navegador headless para agentes IA. Un servidor puede añadir un array args por defecto, un valor por defecto para headless o un valor de ignoreDefaultArgs. Un servidor que agrega argumentos propios produce un argv que es la unión de los del operador y los suyos, en un orden que el operador no eligió. Leer el proceso en ejecución es la única forma de ver esa unión.

Por qué el argv efectivo importa más que la configuración

Dos ejecuciones pueden compartir un archivo de configuración y arrancar navegadores distintos si la versión del servidor cambió sus valores por defecto entre ambas. Un servidor que empieza a añadir un flag nuevo, o que deja de añadir uno, modifica el argv efectivo sin tocar el archivo del operador. Como el navegador lee el argv y no la configuración del servidor, el comportamiento observable sigue al argv.

De ahí la razón para registrar el argv efectivo junto a la observación, y no solo la configuración que lo produjo. Una configuración más una versión del servidor es reproducible; una configuración sola deja de serlo en cuanto el servidor puede cambiar lo que añade.

En qué se diferencia de la forma de Playwright MCP

Los dos ecosistemas responden a la misma pregunta con estructuras diferentes, y las diferencias son justamente las que rompen una configuración copiada.

AspectoPlaywright MCPObjeto de lanzamiento de Puppeteer
Dónde va executablePathbrowser.launchOptions.executablePathexecutablePath de primer nivel
Dónde va argsbrowser.launchOptions.argsargs de primer nivel
Valor de headlessBooleano headless dentro de launchOptionsBooleano o la cadena 'shell'
Ajustes de nivel de contextoObjeto browser.contextOptions aparteNo forman parte de LaunchOptions; el servidor o el código fijan el viewport en otro sitio
Persistencia del perfiluserDataDir bajo browseruserDataDir en el primer nivel
Conexión a un navegador en ejecucióncdpEndpoint o remoteEndpointbrowserWSEndpoint o connect(), fuera de LaunchOptions

Las dos primeras filas explican por qué un bloque JSON de Playwright MCP pegado en un servidor de Puppeteer no hace nada: las claves no existen en ninguno de los dos lugares donde el servidor de Puppeteer mira. La tercera fila es más sutil, porque headless: true es válido en ambos, así que una copia puede parecer funcional mientras la estructura que la rodea es incorrecta.

La separación de contexto no existe aquí

Playwright separa las opciones de lanzamiento de las opciones de contexto, y por eso el viewport, el locale y los permisos viven en un objeto distinto. LaunchOptions de Puppeteer no tiene esa separación: el viewport y los ajustes de nivel de página se aplican después del lanzamiento, mediante page.setViewport() o un descriptor de dispositivo. Una configuración que espera que un bloque contextOptions transporte un locale no encontrará ninguno.

Esa diferencia pesa cuando una tarea depende de un locale o de una zona horaria. Bajo Playwright MCP, ambos pertenecen al contexto y se configuran antes de que la página exista. Bajo Puppeteer son argumentos del navegador dentro de args o llamadas de script posteriores al lanzamiento, y el servidor tiene que exponer una forma de hacer una de las dos cosas.

Las llamadas de emulación son una tercera vía

page.emulate() y page.setViewport() cambian lo que la página informa una vez arrancado el navegador, lo que las convierte en un tercer lugar del que pueden provenir un locale, una zona horaria o un viewport. Una carga de trabajo que fija un locale mediante args, otro mediante una llamada de emulación y un tercero mediante los valores por defecto del servidor tiene tres fuentes y ninguna respuesta única.

La regla que mantiene esto manejable es una sola fuente por propiedad. args cubre todo lo que el navegador necesita saber antes de la primera petición, y una llamada de emulación queda para lo que la página necesita después de cargar. Cada propiedad registra la vía que usó, porque una comparación posterior entre una ejecución que fijó el locale en args y otra que lo fijó por emulación no está comparando lo mismo.

Dos nociones distintas de perfil

userDataDir de Puppeteer y un argumento de perfil de BotBrowser describen ambos estado persistente, y no son la misma cosa. userDataDir es un directorio de datos de usuario de Chromium que Puppeteer reenvía. Un perfil de BotBrowser es un archivo de configuración cifrado que el binario carga mediante su propio flag. Configurar uno no configura el otro, y un servidor que solo expone userDataDir no tiene hueco para el flag de perfil salvo que reenvíe args arbitrarios.

Pasar el perfil y el modo headless

El flag del perfil es un argumento del navegador, así que ocupa un elemento de args. El modo headless es una opción de Puppeteer, así que va en headless. Confundir los dos es el siguiente error que revisar después de la confusión de formas.

{
  "executablePath": "/opt/botbrowser/chrome",
  "headless": true,
  "args": ["--bot-profile=/etc/botbrowser/profile.enc", "--bot-locale=en-US", "--no-first-run"]
}

Cada elemento es un argumento y no lleva comillas de shell. Una sola cadena con espacios es un argumento malformado. El tercer elemento del ejemplo es un flag corriente de Chromium y no uno de BotBrowser, detalle que importa porque args mezcla ambos tipos sin problema: Puppeteer no los distingue.

Comillas, escapes y la capa JSON

Si la configuración es JSON, los argumentos atraviesan dos capas: el escape de cadenas de JSON y después el vector de argumentos. Una ruta de Windows como C:\botbrowser\chrome.exe necesita sus barras invertidas escapadas en JSON, y un valor que contenga una comilla necesita el escape de JSON en lugar del de shell. Acertar una capa y fallar la otra produce una ruta que parece correcta en el archivo y es incorrecta en el lanzamiento.

Los valores se mantienen libres de caracteres que exijan escape siempre que sea posible. Una ruta de perfil sin espacios y sin comillas evita toda esa clase de problema, y una ruta absoluta bajo un directorio corto se verifica carácter por carácter con menos esfuerzo que una larga.

Headless, 'shell' y qué cambia

Puppeteer documenta headless como un valor que acepta true o la cadena 'shell', donde true selecciona el modo headless nuevo y 'shell' el antiguo. La distinción no es cosmética: los dos modos difieren en cómo compone el navegador y en qué APIs informan qué. Una carga de trabajo que compara renderizado o lee una capacidad multimedia registra el modo que usó cada ejecución, porque dos ejecuciones en modos distintos no son la misma medición.

Cuándo ignoreDefaultArgs cambia la respuesta

ignoreDefaultArgs acepta true o un array de nombres de flags. Con true, Puppeteer deja de añadir sus argumentos por defecto, así que el navegador arranca solo con lo que contenga args. Con un array, los valores por defecto nombrados se filtran y el resto permanece. Ambas formas cambian el argv efectivo, y ambas son el tipo de ajuste que un servidor puede aplicar por su cuenta.

La consecuencia operativa es que un flag documentado de BotBrowser puede quedar neutralizado por un valor por defecto que añade Puppeteer, y un valor por defecto que Puppeteer espera puede desaparecer por ignoreDefaultArgs. Ninguno de los dos es visible en el archivo de configuración, y por eso el paso de verificación lee el proceso en lugar de la configuración.

pipe y cómo habla el servidor con el navegador

pipe: true transporta el protocolo de automatización por la entrada y la salida estándar en lugar de abrir un WebSocket. Para un servidor que ejecuta el navegador como proceso hijo, eso elimina un puerto a la escucha de la ecuación, lo que cambia tanto los modos de fallo como la superficie de seguridad. Una configuración que da por hecho la existencia de un puerto de depuración no se sostiene cuando pipe está activo, y una herramienta que se conecta por puerto no alcanza un navegador que habla por pipe.

El transporte de cada ejecución se registra. Un resultado obtenido por pipe y otro obtenido por puerto provienen del mismo navegador solo si el resto de la configuración coincide, y el transporte forma parte de esa configuración y no es un detalle de implementación.

Verificar el binario que arrancó en realidad

La verificación tiene tres lecturas, y responden a preguntas distintas. La ruta del proceso demuestra qué archivo se ejecutó. El user agent describe lo que informa la página, que un build propio puede configurar con independencia del binario. Un marcador de build demuestra una capacidad que el build estándar no tiene.

La primera lectura es la del proceso. En el host que lanzó el servidor, el proceso hijo se localiza y su ejecutable se compara con la ruta configurada. Un servidor que no reenvió executablePath muestra en su lugar una ruta bajo el directorio de caché de Puppeteer, y esa única comparación resuelve la pregunta sin ninguna prueba a nivel de página.

LecturaQué demuestraQué no demuestra
Ejecutable del proceso hijoQué archivo ejecutó el servidorQue el perfil se cargó
navigator.userAgentLo que informa la páginaQué binario lo produjo
Marcador de buildQue una capacidad concreta está presenteQue la configuración llegó a ella por la vía pretendida
Evidencia de carga del perfilQue el perfil cifrado se aceptóQue el contenido de la página es correcto

Cuando el árbol de procesos no es visible

Un servidor que se ejecuta dentro de un contenedor, en un host remoto o en un sandbox puede no exponer sus procesos hijos al shell del operador. En ese caso la ruta tiene que salir desde dentro del entorno que posee el proceso, mediante una línea de registro que escribe el servidor, una entrada de /proc o una herramienta de diagnóstico que ofrezca la plataforma. El requisito no cambia: la comparación entre el ejecutable observado y el configurado se hace desde donde la observación esté disponible.

Cuando ninguna de esas vías existe, un marcador de build dentro de la página es la evidencia de reserva, y su valor es más débil. Un marcador demuestra que una capacidad está presente; no demuestra que el proceso que sirve la página sea el que configuró el operador.

Registrar una verificación

La ruta configurada, la ruta observada, el modo headless, los args efectivos y el resultado del marcador se guardan juntos con una marca de tiempo. Una ejecución posterior compara entonces sin volver a deducir cuál era la línea base. Cuando una actualización del servidor cambia sus args por defecto, ese registro es lo que hace visible el cambio.

Qué no puede ver un cliente

Un cliente MCP ve las herramientas que publica el servidor. No ve el objeto de lanzamiento, ni el árbol de procesos, ni los argumentos que se pasaron. Desde el lado del cliente, un servidor mal configurado es indistinguible de uno correcto, y por eso la verificación se solicita a través de la sesión y se registra fuera de ella.

Tabla de síntomas para la vía de Puppeteer

SíntomaCausaConfirmaciónCorrección
El navegador arranca sin ajustes de perfilEl flag de perfil falta en argsargs no tiene ningún elemento que empiece por el flag de perfilUn elemento de array por flag
El navegador arranca desde el directorio de cachéexecutablePath nunca llegó al objeto de lanzamientoLa ruta del proceso hijo se resuelve bajo la caché de PuppeteerMover la clave al primer nivel del objeto de lanzamiento
El servidor termina con un error de lanzamientoRuta ausente o no ejecutableLa ruta no se resuelve para la cuenta del servidorUna ruta absoluta que la cuenta pueda ejecutar
Los flags se comportan distinto tras actualizar el servidorEl servidor cambió sus args por defectoEl argv efectivo difiere del registradoRegistrar el argv y fijar la versión del servidor
Una herramienta no alcanza el navegadorTransporte incompatible entre pipe y puertoLa configuración activa pipe mientras la herramienta espera un puertoUn solo transporte por despliegue

La segunda fila es la que sobrevive a una sesión aparentemente correcta. Un servidor que cayó a su navegador incluido responde bien a todas las llamadas de herramientas, y la única evidencia es la ruta del proceso.

FAQ

¿Se puede reutilizar una configuración de Playwright MCP en un servidor de Puppeteer?

No. Playwright MCP anida executablePath y args bajo browser.launchOptions, y Puppeteer espera ambos en el primer nivel de su objeto de lanzamiento. Un bloque pegado deja al servidor de Puppeteer sin ruta de ejecutable, así que arranca su navegador incluido e informa éxito.

¿headless: 'shell' es lo mismo que headless: false?

No. 'shell' selecciona la implementación headless antigua, que sigue ejecutándose sin ventana visible pero compone e informa de forma distinta al modo headless nuevo. false ejecuta un navegador con interfaz. Registrar cuál de los tres usó cada ejecución mantiene comparables dos mediciones.

¿userDataDir sustituye a un perfil de BotBrowser?

No. userDataDir es un directorio de datos de usuario de Chromium que Puppeteer pasa al navegador. Un perfil de BotBrowser es un archivo de configuración cifrado que el binario carga mediante su propio flag en args. Un servidor que solo expone userDataDir no tiene hueco para el perfil salvo que reenvíe argumentos arbitrarios.

Conclusión práctica

El objeto de lanzamiento se escribe plano, con executablePath, args y headless como hermanos, y el flag del perfil ocupa un elemento propio dentro de args. Después se lee el proceso hijo en el host y se compara su ejecutable con la ruta configurada antes de dar por bueno cualquier resultado de la sesión.

La siguiente acción es un solo lanzamiento: arrancar el servidor, pedir al cliente una única navegación y leer la ruta del proceso hijo en el host. Si coincide con la ruta configurada, el binario queda demostrado. Si apunta a la caché de Puppeteer, el servidor ignoró la opción y cada resultado describe un navegador que nadie eligió.

Sources

#Puppeteer MCP#Servidor MCP#executablePath#Automatización De Navegador#Agente De IA

Este artículo forma parte de la guía: Despliegue y versión del navegador: mapa operativo

Ejecuta la misma comprobación en BotBrowser

Carga un perfil, mantén dentro del navegador las señales que acabas de leer y confirma el resultado en tu propia máquina antes del despliegue.