daedalus
Daedalus
Control remoto del navegador mediante una extensión de Chrome. Puente de eval + hotfixes persistentes + control por pestaña + capturas de pantalla, CDP, cookies y captura de red en páginas donde Chrome ejecuta la extensión.
Instalación
Sábelo antes de instalar: la eval normal usa inyección en el mundo MAIN sin banner. Cuando una sonda CSP sin fuente no puede determinar que la compilación dinámica está disponible, Daedalus intenta el fallback con CDP; un adjuntado con éxito muestra el banner de Chrome «Daedalus ha comenzado a depurar este navegador» mientras se ejecuta ese fallback. Una sesión de CDP conservada o una captura de red puede mantener el adjuntado ese tiempo. El banner identifica un adjuntado del depurador, no una garantía de integridad de valor.
Carga la extensión sin empaquetar (extension/) en Chrome:
Visita
chrome://extensions.Activa Modo de desarrollador.
Haz clic en Cargar descomprimida y selecciona
extension/.
En la primera instalación se genera automáticamente un token único y se almacena en chrome.storage.local. Puedes verlo o cambiarlo desde la página de opciones de la extensión (icono de puzle → Daedalus → Opciones).
Para el raspado paralelo en varias aplicaciones, desactiva la limitación de pestañas en segundo plano de Chrome:
chrome --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgroundingCómo funciona
Token: se genera una sola vez en la instalación mediante
crypto.randomUUID()y se guarda enchrome.storage.local.Tab IDs: la API
tabsnativa de Chrome: cada pestaña se identifica por sutabIdde Chrome, y se registra al crearla/actualizarla con un latido de 30s usandochrome.alarms.Flujo SSE único:
background.jsabre una conexión SSEfetchpersistente (tab=extension) y envía los comandos entrantes a la pestaña correcta.Puente de página:
content.js(mundo ISOLADO) retransite mensajeswindow.GMentre el background ypage.js(mundo MAIN). La eval usa primero la inyecciónchrome.scriptingen el mundo MAIN. Cuando una sonda de compilación dinámica sin fuente no establece la disponibilidad, las páginas bloqueadas por CSP se enrutan a CDP; si falla el adjuntaje, se llega al relay de la página. Cada canal ejecuta con la semántica del mundoMAINde la página.Hotfixes: se guardan en una clave
chrome.storage.localgeneral (daedalus-hotfixes, no por token), se repiten en cada página de nivel superior elegible al cargar y están limitados por versión para los parchees no permanentes. Rotar el token no aísla ni borra este almacén.
Envío de comandos
Daedalus expone su superficie de comandos como servidor MCP en <your-bridge>/mcp (transporte streamable-HTTP). Antes de enviar una petición, exige que el valor Bearer coincida exactamente con el token del puente resuelto mediante la ruta de configuración existente de la CLI: TOKEN anula a DAEDALUS_TOKEN, incluido un proveedor _settings opcional. Sin ningún token configurado, la superficie MCP se cierra con un 401. Agrégalo a Claude Code (o a cualquier cliente MCP) con:
{
"mcpServers": {
"daedalus": {
"url": "https://daedalus.example.com/mcp",
"headers": { "Authorization": "Bearer <your-bridge-token>" }
}
}
}El token del puente es el que genera la extensión en la instalación; lo ves en la página de opciones de la extensión (icono de puzle → Daedalus → Opciones).
Ese ejemplo expone el servidor MCP con un hostname público, pero los hosts permitidos por defecto del transporte son solo de loopback (127.0.0.1:*,localhost:*): indica el hostname público en DAEDALUS_MCP_ALLOWED_HOSTS o se rechazan las peticiones proxy. Consulta «Servidor» en el apartado superior para ver los tres ajustes MCP.
40 herramientas en 7 grupos: tabs, eval/debug, media, cookies, CSS/blocking, hotfixes, network/CDP. Consulta la sección <mcp> de CLAUDE.md para la lista completa, o llama a tools/list` en el endpoint MCP.
Ayudante de verificación manual (sin necesidad de cliente MCP):
TOKEN=<tok> python3 scripts/mcp_probe.py list
TOKEN=<tok> python3 scripts/mcp_probe.py call title '{"tab_id":"<tabId>"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call screenshot '{"include_image":true}'CLI
Una CLI de shell se distribuye como una rueda daedalus-cli en este repositorio (daedalus_cli/), instalada como el comando daedalus. Lee DAEDALUS_URL y DAEDALUS_TOKEN del entorno, con TOKEN como anulación puntual e ID=<tabId> para dirigirte a una pestaña concreta (omítelo para transmitir a secas):
DAEDALUS_TOKEN=<tok> daedalus tabs
DAEDALUS_TOKEN=<tok> ID=<tabId> daedalus title
DAEDALUS_TOKEN=<tok> daedalus exec myid 'document.title'daedalus --help lista todos los subcomandos y cada uno tiene su propio --help. El código de exec que envías es una expresión o un cuerpo de función cuyo valor retorna como resultado; consulta «Enviar comandos» en el apartado para el contrato.
También hay una costura de importación opcional: si existe un módulo llamado _settings importable en sys.path, la CLI usa sus funciones setting(name, default) y required(name) para DAEDALUS_URL y DAEDALUS_TOKEN en lugar del cambio de entorno incorporado. La variable de entorno TOKEN sigue siendo la anulación de token puntual y tiene prioridad; sin _settings, DAEDALUS_URL usa su valor predeterminado y DAEDALUS_TOKEN es obligatorio. ID sigue la variable de entorno para la selección transversal.
O publica de forma atómica un archivo de comandos en bruto (sin MCP, sin CLI). La redirección directa al nombre .json final no se puede usar porque el flujo puede observar un archivo antes de que el escritor termine. Escribe un nombre vecino que termine en .tmp y, luego, redirige dentro del mismo directorio:
# Broadcast to all tabs
commands_dir="$DAEDALUS_DIR/commands"
final="$commands_dir/<token>.json"
tmp="$(mktemp "$commands_dir/.<token>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
mv "$tmp" "$final"
# Target a specific tab
final="$commands_dir/<token>_<tabId>.json"
tmp="$(mktemp "$commands_dir/.<token>_<tabId>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
mv "$tmp" "$final"El lector ignora los nombres .tmp vecinos. Si un escritor anterior deja JSON malformado en el nombre final, el lector lo deja sin tocar y reintenta en lugar de borrar una escritura posiblemente en curso. Después del rename atómico, el flujo SSE entrega y consume el comando. El resultado quede en $DAEDALUS_DIR/results/<token>_<tabId>.json (por pestaña) y $DAEDALUS_DIR/results/<token>.json (el último usuario gana). Las respuestas de inyección page-main y page-relay pueden llevar un campo exec_ms: el tiempo de ejecución en el contexto de la página en milisegundos. Una página puede falsear u omitir el campo en cualquiera de los canales. Las respuestas CDP no llevan exec_ms. Los resultados asociados a una entrega en cola también llevan roundtrip_ms: el tiempo completo observado por el servidor en milisegundos, desde la puesta en cola del comando (PUT /command) hasta la llegada del resultado (POST /result); abarca la espera en cola + entrega SSE + relay del cliente + ejecución + retorno, de modo que roundtrip_ms − exec_ms se aproxima a la transmisión / cola cuando ambos campos están presentes. Estas medidas usan tiempos de reloj diferentes: exec_ms usa performance.now() de la página, mientras que roundtrip_ms usa el reloj de pared del servidor en milisegundos, así que la diferencia es aproximada y no una resta exactaV. Un frame legacy sin _did no lleva roundtrip_ms.
Soporte asíncrono
El canal page-main predeterminado ejecuta los expresiones mediante eval de la página y los cuerpos de función con Function de la página; un envoltorio asíncrono lee await en el nivel superior. Antes de enviar el código, el background inyecta solo una sonda Function constante. Si devuelve true, la inyección de código se intenta una sola vez y el resultado es definitivo: se informa un valor, una excepción o un error de transporte de inyección sin reintentar el código por CDP. La página controla Function y pueda influir en esta pista de enrutamiento, pero la sonda no contiene el código enviado, así que elegir otro canal no puede duplicar sus efectos secundarios.
Si la sonda sin fuente no devuelve true, la mayoría de veces porque la CSP de la página bloquea la compilación dinámica, Daedalus intenta con CDP. Cuando se adjunta muestra el banner de depuración de Chrome. Runtime.evaluate usa el modo REPL para el await de que top-level; el código que contenga return se trata como cuerpo de función para una parsi una sonda envoltorio lo analiza como expresión. Esa sonda es una heurística del intérprete, no un límite de seguridad sin ejecución: el personal cualc puede escapulization de él y ejecutarse. Tras el envío de la primera evaluación de origen, todo resultado es definitivo. Solo una falla de adjuntaje o forma avance antes de que el código se ejecute llega al page relay`. El asentamiento de prometidos CDP se limita a 10 segundos, y el mando de objetos de resultado y excepción se libera a pesar de que una sesión o captura mantenida retenga al deprecador.
El código JavaScript evaluado en una página que no controlas devuelve un valor que esa página elige, independientemente del canal que lo ejecute. El campo world solo registra qué canal de ejecución activo; es metadata diagnóstico sobre CSP y comportamiento del depurador, no una señal de confianza. Los valores de world son page-main para las inyecciones normales, cdp para el inspector con fallback y page:<hostname> para el relay final, donde <hostname> es el location.hostname del script de contenido. El background añade el prefijo page:, así que el hostname del relay no puede entrar en cdp ni page-main: esto no dice nada sobre el valor devuelto. La CLI muestra ese campo como channel=..., el panel hace lo mismo y MCP exec, put, result y ping presionan el valor exacto de world. Ninguna de ellas asigna una clase de confianza.
Las inyecciones por defecto y el fallback CDP mantienen el comportamiento clásico de script para los casos documentados de los scripts no estrictos: with compila, los literales octales legales se aprovechan y una asignación sin introducción crea un global. El modo REPL de CDP también permite let y const repetidos. La espera de resultado CLI y MCP es de 15 segundos por defecto; un timeout del llamador no cancela el código que ya se ejecuta. El Sep relay espera por separado hasta 10 segundos por un código que contenga await, y 3 segundos en caso contrario, antes de informar timeout de fallback.
La correlación del relay queda sin filosofía y limitada: la pestaña del emisor debe coincidir, un mensaje aceptado consume el id aleatorio del relay, y no se registra ningún id hasta la sonda de inyección sin fuente y el fallback de CDP anterior a los dicho hayan terminado. Estos controles evitan la finalización transversal o duplicada del relay; no establecen integridad de valor.
Máximo hay 1.000 entradas de relay de página. Un nuevo fallback al límite de la capacidad recibe un único error de capacidad, sin desalojar trabajo en curso. Cada entrada registrada vence tras 300.000 ms y recibe un único error de timeout si se no la elimina una finalización de la misma pestaña o un fallo de envío anterior.
Panel de control
Una superficie de control en el navegador en <your-bridge>/dashboard, que controla todo el conjunto de ordenes sin la CLI: lista de aplicaciones en vivo, REPL de eval, capturas de pantalla, cookies, hotfixes, reglas de bloque, captura de red, CDP, inyección CSS, tiempos de fetch, explorador de carga.
Abre la URL en una pestaña que la extension controle.
Despláate a §12 Configurar y pega el token de la pagina de opciones de la extensión (icono de puzle → Daedalus → Opciones). Guarda.
El punto de estado SSE en la barra en la parte superior se vuelve cian cuando el ícono o dashboard está viendo eventos live.
Servido directamente por server.py desde el directorio dashboard/ del repositorio (JavaScript vanilla + módulos ES, sin build). Las actualizaciones en vivo llegan por entpo punto /stream: server.py coloca eventos en la cola de commands/<token>_dashboard/<ts>_<uuid>.json cuando /register actualiza una pestaña existente, y en los métodos pathways de /sync-tabs, /unregister y /result; /unregister envía incluso cuando no hay pestaña. El dashboard evacúa si kind:'event'.
Aviso: los scripts de contenido y de página de la extensión se inyectan en las páginas que coinciden, incluyendo la del dashboard. Los comandos de eval en difusión (exec -b) corren también una predefinida aplicación; mejor usa el targeting por pestañas o cierra el panel antes de emitir código disruptivo.
Auto-parche
Hotfixes
Persistencia de pequeños parches que se repiten en cada argumento de página elegible que la carga. Por medio de MCP: store_hotfix, list_hotfixes, clear_hotfix, clear_hotfixes, set_permanent. Ejemplo (mediante script probe):
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"my-fix","code":"console.log(\"patched\")"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"always-on","code":"console.log(\"baseline\")","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call set_permanent '{"fix_id":"my-fix","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call list_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfix '{"fix_id":"my-fix"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes '{"include_permanent":true}'Los hotfixes se controlan por versión por defecto. Cuando cambie la versión de la extensión, las correcciones restantes no permanentes se omiten en vez de borrarse; guardar una corrección actualiza el registro de la versión actual y activa hacia arriba otras no permanentes. Marca una corrección como permanente (con permanent: true o set_permanent) para que se repita usen cambios de versión. clear_hotfixes elimina las no permanentes y deja las permanentes por defecto; usa include_permanent: true para borrar todo el almacén completo.
Comandos de la extensión
El service worker del background acepta comandos con tipo (enviados por MCP o escribiendo un JSON {"type": "python"} en $DAEDALUS_DIR/commands/):
Comando | Propósito |
| Capturar la pestaña visible como PNG |
| Emitir llamadas directas al protocolo Chrome DevTools Protocol |
| Interceptación completa de solicitudes/respuestas mediante CDP |
| Acceso al depósito de cookies |
| Control de pestañas |
| Inyección de CSS por pestaña |
| Bloqueo mediante declarativeNetRequest |
| Gestión de hotfixes (las correcciones permanentes sobreviven a los cambios de versión) |
| Recargar la propia extensión desde el disco |
| Búfer circular de diagnóstico para los relés de |
GM Bridge
window.GM (en page.js, mundo MAIN) proporciona este subconjunto de estilo Tampermonkey:
Método | Descripción |
| Leer una clave de cadena no reservada de |
| Escribir una clave de cadena no reservada en el almacenamiento de toda la extensión |
| Eliminar una clave de cadena no reservada del almacenamiento de toda la extensión |
| Enumerar las claves de almacenamiento no reservadas |
| Realizar una petición HTTP retransmitida en segundo plano (inmune a CSP) |
| Inyectar CSS |
| Escribir en el portapapeles |
| Mostrar una notificación de escritorio |
| Abrir una nueva pestaña |
| Iniciar una descarga |
| Metadatos del script |
El acceso a las cookies es una capacidad del operador, no de la página: se realiza a través de los comandos cookies / set-cookie / remove-cookie / clear-cookies autenticados por token que se han indicado más arriba y, deliberadamente, no se expone al contexto de la página: page.js se ejecuta en cada página de nivel superior coincidente, que de otro modo podría leer cookies que su propio document.cookie no puede ver.
Arquitectura
Browser (matching tab) Server (your bridge host)
┌────────────────────────────────────────────┐ ┌──────────────────────┐
│ MAIN world │ │ bridge (server.py) │
│ ├─ page-main: default injection channel │ │ /stream?token │
│ └─ page.js: GM + relay channel │ │ watches commands/ │
│ ▲ │ │ │
│ │ window.postMessage │ │ /result writes │
│ content.js (ISOLATED) │ │ results/ │
│ ▲ │ │ │
│ │ chrome.runtime │ │ │
│ background.js (service worker) │ │ │
│ ├─ CDP: CSP fallback channel │ │ │
│ ├─ single SSE stream ◄────────────────────┼───┤ │
│ └─ POST result, fetch ────────────────────┼──►│ │
└────────────────────────────────────────────┘ └──────────────────────┘Un único flujo SSE: El proceso de fondo abre una única conexión SSE
fetch(tab=extension) y enruta los comandos a la pestaña objetivo mediantechrome.tabs.sendMessage. Un watchdog de 30 s fuerza la reconexión cuando los flujos quedan obsoletos.Enrutamiento por pestaña: los comandos se dirigen a un
tabIdconcreto de Chrome o se transmiten a todas las pestañas.Comportamiento del CSP:
GM.xmlhttpRequestenvía el trabajo HTTP alfetchdel background worker.evalnormalmente usa inyección en el mundoMAINsin banner. Cuando la sonda sin fuente indica que la compilación dinámica no está disponible, CDP proporciona el respaldo de CSP y muestra el banner del depurador de Chrome si se adjunta; si no se puede adjuntar, llega al relé de página/Blob. El valor deworldresultante describe ese canal y no hace ninguna afirmación de integridad.
Servidor
DAEDALUS_DIR=<data-dir> DAEDALUS_PORT=<port> DAEDALUS_TOKEN=<bridge-token> python3 server.pyserver.py se ejecuta bajo cualquier supervisor que uses (aquí, una unidad de systemd). Coloca un proxy inverso que término de TLS delante s tu exposición si lo expones más allá de localhost; el propio bridge habla HTTP plano. Las rutas de control y de almacenamiento del bridge comparan su token con un secreto configurado, resuelto mediante la ruta de configuración de la CLI: TOKEN es el valor de anulación puntual, y si no se establece, DAEDALUS_TOKEN es obligatorio (y un módulo _settings integrado puede proporcionarlo). La falta de configuración y los desajustes bloquean en modo seguro. Solo las rutas orientadas a la página POST /segment y GET /segment-status utilizan una capacidad con ámbito de trabajo en su lugar.
El etc. frontend MCP en ei. Acepta tres ajustes opcionales, documentados juntos porque describen un único listener: DAEDALUS_MCP_PORT (por defecto 8086) es el puerto de loopback al que se vincula; el handler de tools usa la URL de loopback realmente, incluso cuando DAEDALUS_PORT=0; DAEDALUS_LOCAL_URL anula explícitamente esa URL para un despliegue MCP independiente que pon delante de un bridge que se ejecuta en otro lugar; y DAEDALUS_MCP_ALLOWED_HOSTS (por defecto 127.0.0.1:*,localhost:*) es la lista de hosts permitidos por separado de comas que la protección contra rebinding de DNS acepta, así que exponer /mcp con un nombre de host público significa nombrar ese host ahí.
Endpoints: GET /stream, GET /tabs, GET /health, GET /dashboard[/<asset>], POST /register, POST /sync-tabs, POST /unregister, POST /poll, POST /result, PUT /command, GET /result, POST/GET/DELETE /upload, GET /screenshot, POST /segment-job, POST /segment + GET /segment-status. POST /segment-job requiere el token de bridge configurado; solo POST /segment y GET /segment-status aceptan el sig de ámbito de job. Se CLAUDE.md para notas sobre el payload y los endpoints.
POST /poll consume y elimina el archivo heredado de comando de difusión cuando está presente.
PUT /command añade a una cola FIFO de directorio por destino (los comandos consecutivos contra la misma pestaña ya no se sobrescriben), sella cada uno con un identifice de entrega (_did) para que a extensión pueda deducir un frame reenviado, y caduca por TTL los comandos no reclamados después de DAEDALUS_CMD_TTL segundos (por defecto 90). Un recolector ejecuta ese TTL y vacía los diretorios de cola vacíos incluso si ningún consumidor SSE llega a conectarse. GET /health informa del estado de stream/registro/última entrega para detectar un bridge muerto silenciosamente.
El bridge rechaza las entradas de autoridad authority repetidas en lugar de elegir un valor: esto incluye token en las cadenas de consulta o en los cuerpos JSON y job / sig en las rutas de taller de segmento, incluso si los valores repetidos son iguales o están vacíos. El transporte MCP también rechaza los campos de cabecera Authorization, Mcp-Session-Id, Host y Origin, además de los argumentos job repetidos para las herramientas de segmento.
Cuando la extensión publica un resultado, el servidor expone el campo _did del comando como deliveryId y asigna un nuevo resultGeneration. Los clientes de CLI y MCP que esperan, primero miran en la ranura de resultado compartida, comprueban el ID de comando y el deliveryId, y después consumen condicionalmente esa generación con GET /result?...&consume=1&expected=<resultGeneration>. Si otro resultado sustituye a la ranura entre esas solicitudes, la consumición condicional deja el nuevo resultado en su lugar y reporta que no se ha consumido. La opción consume=1 sin expected sigue siendo una lectura destructiva de compatibilidad de la ranura actual.
GET /stream mantiene la conexión SSE abierta indefinidamente, probando la vivacidad con un comentario keepalive cada DAEDALUS_STREAM_KEEPALIVE segundos (por defecto 15) en lugar de reciclar la conexión con un temporizador; DAEDALUS_STREAM_MAX_AGE (por defecto 3600) es solo un techo de último recurso. El camino de cierre queda anclado por tests/test_stream_lifecycle.py.
DAEDALUS_MAX_BODY_SIZE (por defecto 64 * 1024 * 1024 bytes, 64 MiB) límites los cuerpos de solicitud que minimizan los handlers POST, PUT y DELETE; cuando el cuerpo declarado supera el límite, recibe 413. Aumenta el valor cuando transmites segmentos u otros payloads más grandes.
Los valores del llamador respaldados por el sistema de ficheros usan una política de un componente de ruta: rechaza .., caracteres de control C0/C1 y surrogates, caracteres no, no válidos en rutas de Windows y nombres de dispositivos, puntos o espacios traseros, y codificaciones UTF-8 más largas de 240 bytes. Los tokens de bridge son más estrictos: también se rechazan puntos y guiones bajos. Otros nombres de job en UTF-8 son aceptados, por lo que los clientes deben codificarlos en la URL query.
POST /segment-job pone tres límites fijos en cada nuevo registro de job: DAEDALUS_MAX_SEGMENT_INDEX (por defecto 99999), `DAEDALUS_MAX_SEGMENTS_PER_JOB
Las rutas de control y almacenamiento del puente requieren el token del puente configurado; /segment y /segment-status son la única excepción de capacidad. El servidor compara los tokens de solicitud con el secreto resuelto desde TOKEN o DAEDALUS_TOKEN y rechaza las solicitudes cuando ningún secreto está configurado. POST /segment-job requiere ese token del puente porque crea la capacidad con ámbito de trabajo; el JavaScript no confiable de la página usa la capacidad solo para publicar y consultar ese trabajo. Quien tenga el token del puente puede controlar tu navegador. No expongas el puerto del puente más allá del bucle local sin un proxy inverso que remate TLS, y trata el token como una credencial.
Los resultados de evaluación no ofrecen ninguna garantía de integridad del valor. El JavaScript evaluado en una página que no controlas devuelve un valor que esa página puede elegir, sea cual sea el canal que lo ejecute. El campo world indica cómo se ejecutó el código enviado, no si hay que confiar en su valor: page-main es inyección ordinaria en el mundo MAIN, cdp es el respaldo CSP del inspector, y page:<hostname> es el relé. El prefijo obligatorio page: se añade fuera del contexto de la página, así que ningún hostname puede producir cdp ni page-main; esa resistencia a la colisión es solo descriptiva. En page-main, los enlaces eval y Function propiedad de la página pueden leer el código fuente enviado y también influir en el valor que devuelve.
La compilación de cdp evita resolver los enlaces eval y Function de la página, y la implementación obtiene los manejadores directos por referencia antes de serializarlos. Esos mecanismos de transporte no cambian el límite de confianza: el código fuente enviado sigue leyendo estado controlado por la página y puede encaminar cualquier valor, incluso un primitivo, a través de la maquinaria de promesas de la página antes de que CDP lo reciba. Siempre que el código fuente enviado use esas rutas controladas por la página, también en cdp la página puede elegir el valor devuelto.
El relé solo acepta la invocación almacenada asociada a ese identificador aleatorio, exige que la pestaña del remitente coincida y consume la entrada una sola vez. Esta degradación no expone el token del puente, la URL del servidor, el id de envío ni la ruta de resultado; no otorga autoridad adicional de la extensión ni del navegador, y no puede afectar a una invocación en otra pestaña. Esas propiedades protegen el encaminamiento y la autoridad del navegador; no convierten el marcador del relé ni su valor JavaScript devuelto en una señal de confianza.
Despliegue
El puente habla HTTP plano en el bucle local. Las rutas operación de control y de almacenamiento requieren el token configurado y fallan de forma segura si no está presente; solo /segment y /segment-status usan la capacidad asociada al trabajo. Hay dos cosas que deliberadamente NO hace, porque le corresponden a lo que tenga delante:
TLS y CORS.
server.pyno envía cabeceras CORS. Cuando el puente está en origenes cruzados, las llamadasfetchdel lado pagina del ejemplo del relé HLS necesitan acceso tanto aGET /segment-statuscomo aPOST /segment; el proxy debe permitir el origen de la página en ambas rutas y gestionar los métodos y cabeceras que exige el preflight del POST. Si el GET de estado está bloqueado, no está disponible, devuelve un resultado no 2xx o no es válido, el ejemplo trata trabajo como nuevo y vuelve a hacer POST de cada segmento. Esas escrituras siguen reemplazando los mismos ficheros por índice, pero la ejecución pierde el ahorro de la retoma/omisión. Si el despliegue no puede ofrecer esa política de CORS, pasa ambas solicitudes por el puente GM de la extensión.Servir las subidas almacenadas. El panel de control enlaza a las descargas en
/uploads/<path>, pero el puente no tiene esa ruta. No sirvas los nombres de subida ni los bytes aportados por el llamador directamente en el origen desde donde se sirve el panel; el contenido ejecutable compartiría origen con el almacenamiento del panel que tiene el token. O deja esos enlaces no disponibles, o consigue que/uploads/redirija a un origen de solo descarga separado que fuerceContent-Disposition: attachment, useapplication/octet-streamy envíeX-Content-Type-Options: nosniff.GET /upload(listado) yGET /screenshotlas sirve el propio puente y no necesitan ayuda del proxy.
Si lo ejecutas directamente con un token ya configurado, esas dos cosas simplemente no existen: todo lo demás funciona.
Archivos
Archivo | Descripción |
| Manifiesto MV3 |
| Service worker — SSE, envío de comandos, relé de fetch, capturas de pantalla, CDP, cookies, descargas |
| Relé de mensajes entre la página y el background. |
| Puente del mundo MAIN — |
| Interfaz de configuración de token / servidor |
| Servidor de depuración (también sirve el hilo del daemon MCP en 127.0.0.1:8086) |
| Servidor MCP: conecta la superficie de comandos de la extensión con |
| Cliente MCP mínimo para verificación manual |
| Comprueba/aplica cambios de consistencia de versión en todos los puntos |
| Puertas de consistencia de versión pre-commit y pre-push |
| El CLI shell, publicado como rumbo de |
| Superficie de control del navegador que |
| Scripts para ejecutar en una página mediante |
| Los grupos de pruebas; |
Ejemplos
En examples/ guardan scripts pensados para ejecutarlas dentro de una página con put. Cinco de los seis muestran una parte del puente más que de un sitio concreto; el de Discord es deliberadamente específico de sitio, porque desplazarse hacia atrás en una lista virtualizada es una técnica que no se puede mostrar sin una lista virtualizada real:
Ejemplo | Muestra |
| Un parche permanente del mundo MAIN en |
| El relé |
| Encontrar y eliminar una instancia del reproductor que no deja de reintentar |
| Rellenar un campo controlado por React para que el estado de React cambia de verdad |
| Desplazarse hacia atrás por una lista virtualizada de mensajes y extraer los datos |
| La llamada |
Reciben su configuración mediante sustitución de __PLACEHOLDER__ antes de enviarlas, porque put es un script que se envía y no tiene forma de pasar argumentos.
Cada una es el CUERPO de una función asíncrona, no script independiente: el puente envuelve lo que envías. Por eso la mayoría acaban en return de nivel superior, y una — scrape-discord-messages.js — también usa un await de nivel superior; ambos son válidas en el contexto real en que se ejecutan. node --check analiza cada archivo dentro del envoltorio CommonJS, que permite el return de nivel superior, pero rechaza el await de nivel superior; por tanto, ese archivo o normal falla la comprobación de sintaxis.
Desarrollo
La cadena de versión vive catre sitios varios por toda la extensión, en elpanel y en el paquete CLI. python3 scripts/check_versions.py es la lista: nombra cada lugar y dice cuántos ha encontraado, que es por lo que este párrafo no repite un total que quedaría desfasado en cuanto se añada otro. suben hacia arriba, all together, never by manual:
python3 scripts/check_versions.py --set 0.18.0 # rewrite every site
python3 scripts/check_versions.py # verify the working tree.githooks/pre-commit comprueba que el índice está entero, y .githooks/pre-push comprueba cada commit empujado para que no pueda aterrizar una subida a medio cerrarse. Cada clon debe suscribirse una vez — git no va a ejecutar los hooks si están en un directorio quegit ya tiene solo así:
git config core.hooksPath .githooksSi el navegador carga la extensión desde otro checkout distinto del que vas a modificar, instala también los hooks allí; y recuerda que recargar la extensión lee los ficheros efectos del navegador, no los tuyos.
This server cannot be installed
Maintenance
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
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Access Kernel's cloud-based browsers and app actions via MCP (remote HTTP + OAuth).
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Nitjsefnie-Harness-Commons/daedalus'
If you have feedback or need assistance with the MCP directory API, please join our Discord server