WU / DOCSUNIVERSAL MICROFRONTENDS
ES

API MCP: bridge, relay y herramientas

Referencia del bridge del navegador, exports Node del relay y las 18 herramientas MCP publicadas por WU.

Tres interfaces con responsabilidades distintas

WU integra un bridge en el navegador y un servidor MCP en Node. El cliente de herramientas habla JSON-RPC por stdio con el servidor; éste utiliza un socket WebSocket autenticado para pedir operaciones a la página conectada.

Interfaz Entorno Acceso
wu.mcp Navegador connect, disconnect, isConnected
wu-framework/mcp-server Node Configuración, listener, servidor y helpers de protocolo
Herramientas wu_* Cliente MCP Catálogo publicado mediante tools/list

Importar wu-framework/mcp-server no inicia un listener ni toma stdin/stdout. Ejecutar wu-mcp-server, o llamar main(env), sí inicia el servicio. La receta ejecutable de IA/MCP conecta todos los extremos con un navegador real y una action local sin proveedor LLM.

Bridge del navegador

import wu from 'wu-framework';

await wu.aiReady();
await wu.mcp.connect(relayUrl, { token: sessionToken });

// Consulta de estado; connect no espera el resultado de autenticación.
const connected = wu.mcp.isConnected();

// Al retirar la integración:
wu.mcp.disconnect();

relayUrl y sessionToken provienen de una integración confiable del host. El token no debe formar parte de URLs, bundles públicos ni estado persistido en el store.

Método Parámetros Retorno y ciclo de vida
connect(url?, options?) URL WebSocket; default ws://localhost:19100; token obligatorio salvo { insecure: true } en loopback Promise<void> de carga/inicio. Inicia el handshake después de abrir el socket
disconnect() Ninguno void. Cierra el socket, detiene reconexiones y libera suscripción e interceptores del bridge
isConnected() Ninguno boolean. Comprueba socket abierto y estado de autenticación conocido por el cliente

El bridge 4.0 autentica al relay mediante un handshake HMAC mutuo v2 sin enviar el token. Actualiza bridge y relay juntos: un relay anterior a 4.0 no entiende v2; el relay nuevo conserva v1 para bridges antiguos. Sin token falla con WU_MCP_TOKEN_REQUIRED. El modo local exige { insecure: true } y WU_MCP_INSECURE=1 en el relay. Fuera de loopback exige wss://; una URL insegura falla con WU_MCP_INSECURE_URL.

El bridge evita abrir otra conexión si ya está conectando o conectado. Una desconexión accidental puede iniciar reconexiones; disconnect() cancela esa intención. Cerrar MCP no desmonta automáticamente todas las apps ni cancela promesas arbitrarias de handlers ya iniciados.

Configuración del servidor

import { loadConfig } from 'wu-framework/mcp-server';

const config = loadConfig(process.env);

loadConfig valida las variables y devuelve un objeto inmutable. No imprime ni expone el token a un cliente. Los valores inválidos lanzan un error en lugar de caer silenciosamente al default.

Variable Default Contrato
WU_MCP_TOKEN Obligatorio String de 16–4096 bytes UTF-8
WU_MCP_HOST 127.0.0.1 Host del listener; sin espacios ni /
WU_MCP_PORT 19100 Entero 1–65535
WU_MCP_TIMEOUT 30000 Timeout por comando al bridge, 50–300000 ms
WU_MCP_AUTH_TIMEOUT 5000 Tiempo para autenticarse, 50–60000 ms
WU_MCP_MAX_PAYLOAD 1048576 Límite WebSocket, 1024–16777216 bytes
WU_MCP_ORIGINS HTTP(S) loopback Lista de origins exactos separados por comas, sin ruta
WU_MCP_INSECURE Desactivado Sólo 1 permite omitir token. Si hay un token, éste sigue siendo requerido

El resultado contiene host, port, timeout, authTimeout, maxPayload, token, insecure y allowedOrigins. Esta última propiedad es un Set o null, no un array que se envíe al navegador.

La comprobación de Origin sucede en el upgrade HTTP de WebSocket. Un Origin ausente o no permitido recibe 403. Después se completa el handshake de autenticación. El relay admite un bridge autenticado activo a la vez; conocer el token no permite reemplazar silenciosamente una sesión que ya está activa.

Listener Puente

import { Puente, loadConfig } from 'wu-framework/mcp-server';

const puente = new Puente(loadConfig(process.env));
await puente.ready();

// Estas consultas no requieren una página autenticada.
const address = puente.address();
const connected = puente.conectado();

// Cuando el host termine su sesión:
await puente.close();

La construcción inicia el listener. ready() resuelve cuando puede escuchar y rechaza ante un fallo de bind; no espera una página. Puedes inyectar { logger } como segundo argumento del constructor; debe exponer error(...args). Los logs del servicio deben ir a stderr para no corromper el protocolo de stdout.

Método Retorno Fallos o límites
ready() Promise<Puente> Rechaza si falla el arranque
address() Dirección del listener No indica si hay una página conectada
conectado() boolean Lectura instantánea; la sesión puede cerrarse después
enviar(command, params = {}) Promise<unknown> Rechaza sin bridge, por desconexión, timeout o error de respuesta
close() Promise<void> Rechaza pendientes, cierra sockets y listener; no vuelve a iniciar el servicio

enviar recibe el nombre del comando del bridge, por ejemplo list_actions. El nombre MCP externo correspondiente es wu_list_actions. No intercambies ambos niveles.

Cada request mantiene un identificador, un timeout y el socket que debe responder. El relay retira la entrada pendiente al completarse o fallar. close() libera pendientes y fuerza la terminación de sockets que no hayan cerrado tras el margen de cierre.

Construir o iniciar el servidor MCP

import { main } from 'wu-framework/mcp-server';

// El entorno debe contener la configuración de autenticación.
await main(process.env);

main(env) crea el listener, espera su arranque, registra herramientas y conecta el transporte stdio. Instala manejo de SIGINT/SIGTERM para cerrar servidor y puente. Resuelve el arranque y no devuelve un objeto para gestionar la sesión. Desde un cliente externo, el ejecutable equivalente es wu-mcp-server.

Para un host que administra explícitamente su lifecycle:

Export Contrato
createMcpServer(puente, config) Devuelve un servidor con connect(transport) y close(); registra las 18 herramientas
createCommandRunner(puente) Devuelve una función (commandName, params?) => Promise<WuMcpToolResult> que adapta la respuesta del bridge

El host suministra un transporte MCP compatible a connect. Al cerrar una integración embebida, cierra tanto el servidor MCP como Puente; uno no reemplaza el lifecycle del otro. La receta descargable utiliza el ejecutable empaquetado y un cliente stdio mínimo, sin necesitar importar clases privadas del SDK incluido en el bundle.

Catálogo de herramientas

Este es el catálogo registrado por el servidor. Los parámetros de herramientas pasan por los schemas del relay; los permisos de ejecución se comprueban de nuevo en el bridge o en wu.ai.execute.

Herramienta Argumentos Resultado principal Permiso de página
wu_conexion {} { puerto, host, requiere_token, app_conectada } No necesita página conectada
wu_list_actions {} { actions, count } Bridge autenticado; listar no concede ejecución
wu_execute_action { action, params? } { action, success, result?, reason? } executeActions y permisos de la action
wu_status {} Framework, apps, claves raíz, cantidad de acciones/eventos Bridge autenticado; las claves raíz requieren readStore
wu_list_apps {} Descriptores y estado de apps Bridge autenticado
wu_list_events { limit? } Eventos recientes redactados readDiagnostics
wu_get_state { path? } { path, value } readStore
wu_set_state { path, value } { path, value, updated: true } writeStore
wu_emit_event { event, data? } { emitted, data } emitEvents; namespaces internos prohibidos
wu_navigate { route } { navigated: route } emitEvents; writeStore para actualizar currentPath
wu_snapshot { appName? } App, árbol accesible y timestamp readDOM
wu_screenshot { selector?, quality? } Captura DOM, metadatos o error readDOM
wu_click { selector?, text? } Elemento activado o error modifyDOM
wu_type { selector, text, clear?, submit? } Resultado de escritura o error modifyDOM
wu_console { level?, limit? } Entradas de consola redactadas readDiagnostics
wu_network { method?, status?, limit? } Metadatos de red filtrados readDiagnostics
wu_mount_app { appName, container? } App montada modifyDOM y el contrato normal de montaje
wu_unmount_app { appName } App desmontada modifyDOM

Detalles de parámetros:

  • limit de eventos: entero de 1–200, default 20. El ring contiene como máximo 200 entradas.
  • limit de consola y red: entero de 1–500, default 50. Consola acepta level: 'all' | 'log' | 'warn' | 'error', default all; red acepta status entre 100 y 599.
  • quality: 0–1, default 0.8 del bridge. El helper usa captura DOM/foreignObject y PNG por defecto; esa calidad no modifica la compresión PNG ni garantiza fidelidad de captura privilegiada.
  • clear y submit: false por defecto. Escribir puede emitir eventos del control; submit: true puede activar el envío de un formulario.
  • event: string de 1–200 caracteres. Los prefijos wu:, system: y app: son rechazados en el relay y el bridge.
  • path vacío en lectura selecciona la raíz; escritura exige un path no vacío. Los datos diagnósticos pasan por redacción antes de salir del bridge.

wu_navigate emite shell:navigate: el shell debe manejar ese evento. El resultado no demuestra por sí solo que se haya cambiado la página y el gate utilizado es emitEvents, no el flag IA navigate. wu_status devuelve storeKeys: [] cuando readStore está desactivado.

JSON-RPC y resultados negativos

Después de inicializar la sesión MCP, una llamada a una action tiene esta forma:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "wu_execute_action",
    "arguments": { "action": "sum_local", "params": { "a": 2, "b": 3 } }
  }
}

El servidor entrega contenido textual en content[0].text. La función createCommandRunner marca isError cuando el bridge devuelve un campo error, o cuando falla el transporte. Un resultado de acción con success: false puede permanecer dentro de un sobre isError: false.

Comprueba la capa MCP y la capa de la action por separado. No interpretes un HTTP correcto, un socket abierto o un sobre sin isError como prueba de que el handler obtuvo autorización y terminó con éxito.

tools/list enumera herramientas de la versión instalada. wu_list_actions enumera otro catálogo: los handlers que la página registró en el subsistema IA. Para ejecutar una action hacen falta ambos registros y los permisos adecuados.

Helpers de autenticación y nombres

Export Firma Comportamiento
isOriginAllowed (origin, allowedOrigins = null) => boolean Con null, permite únicamente origins HTTP(S) exactos de loopback
tokenMatches (candidate, expectedDigest) => boolean Compara el SHA-256 del candidato con un digest esperado de 32 bytes usando timingSafeEqual
isReservedEvent (eventName) => boolean Detecta los tres namespaces internos reservados

tokenMatches espera un digest, no el token en texto como segundo argumento. Un digest de tamaño incorrecto puede provocar un error de comparación. Estos helpers no abren sockets ni conceden permisos de dominio.

Cancelación, seguridad y pruebas

El protocolo de esta implementación no propaga una cancelación MCP hasta el handler de wu.ai.execute. La receta cancela una espera y observa que el cálculo que ya había comenzado continúa. disconnect y close liberan transporte y pendientes, pero no implementan compensación de efectos.

Usa acciones propias acotadas y un servicio autenticado para operaciones privilegiadas. Los flags IA del browser son controles técnicos de esa página; no sustituyen autorización de negocio, idempotencia ni un registro durable de operaciones.

node examples/mcp/local-relay.mjs
node scripts/verify-server-examples.mjs --mcp

La prueba incluye handshake, token incorrecto, herramientas/acciones reales, suma, schema inválido, permisos denegados, evento reservado, cancelación observable y cleanup. El token se genera en memoria, todos los listeners son locales y no se requiere proveedor LLM. Puedes descargar las recetas completas o revisar acciones IA y MCP ejecutables.

WU SE CONSTRUYE CONTIGO

¿Encontraste un bug? ¿Tienes una idea?

Tu experiencia mejora WU. Cuéntanos qué falla o ayuda a sostener su desarrollo.