API MCP: bridge, relay e ferramentas
Referência do bridge do navegador, exportações Node do relay e das 18 ferramentas MCP publicadas pelo WU.
Três interfaces com responsabilidades diferentes
O WU integra um bridge no navegador e um servidor MCP no Node. O cliente de ferramentas fala JSON-RPC por stdio com o servidor; este usa um socket WebSocket autenticado para solicitar operações à página conectada.
| Interface | Ambiente | Acesso |
|---|---|---|
wu.mcp |
Navegador | connect, disconnect, isConnected |
wu-framework/mcp-server |
Node | Configuração, listener, servidor e helpers de protocolo |
Ferramentas wu_* |
Cliente MCP | Catálogo publicado por tools/list |
Importar wu-framework/mcp-server não inicia um listener nem assume stdin/stdout. Executar wu-mcp-server, ou chamar main(env), inicia o serviço. A receita executável de IA/MCP conecta todas as pontas com um navegador real e uma action local sem provedor LLM.
Bridge do 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 e sessionToken vêm de uma integração confiável do host. O token não deve fazer parte de URLs, bundles públicos ou estado persistido no store.
| Método | Parâmetros | Retorno e ciclo de vida |
|---|---|---|
connect(url?, options?) |
URL WebSocket; padrão ws://localhost:19100; token obrigatório, exceto com { insecure: true } em loopback |
Promise<void> de carregamento/início. Inicia o handshake após abrir o socket |
disconnect() |
Nenhum | void. Fecha o socket, interrompe reconexões e libera a inscrição e os interceptores do bridge |
isConnected() |
Nenhum | boolean. Verifica o socket aberto e o estado de autenticação conhecido pelo cliente |
O bridge 4.0 autentica o relay com um handshake HMAC mútuo v2 sem enviar o token. Atualize bridge e relay juntos: relays anteriores ao 4.0 não entendem v2; o novo relay mantém v1 para bridges antigos. A ausência de token falha com WU_MCP_TOKEN_REQUIRED. O modo local exige { insecure: true } e WU_MCP_INSECURE=1 no relay. Fora de loopback exige wss://; uma URL insegura falha com WU_MCP_INSECURE_URL.
O bridge evita abrir outra conexão se já estiver conectando ou conectado. Uma desconexão acidental pode iniciar reconexões; disconnect() cancela essa intenção. Fechar o MCP não desmonta automaticamente todas as apps nem cancela promises arbitrárias de handlers já iniciados.
Configuração do servidor
import { loadConfig } from 'wu-framework/mcp-server';
const config = loadConfig(process.env);
loadConfig valida as variáveis e retorna um objeto imutável. Não imprime nem expõe o token a um cliente. Valores inválidos lançam um erro em vez de recorrer silenciosamente ao padrão.
| Variável | Padrão | Contrato |
|---|---|---|
WU_MCP_TOKEN |
Obrigatório | String de 16–4096 bytes UTF-8 |
WU_MCP_HOST |
127.0.0.1 |
Host do listener; sem espaços nem / |
WU_MCP_PORT |
19100 |
Inteiro 1–65535 |
WU_MCP_TIMEOUT |
30000 |
Timeout por comando ao bridge, 50–300000 ms |
WU_MCP_AUTH_TIMEOUT |
5000 |
Tempo para autenticar, 50–60000 ms |
WU_MCP_MAX_PAYLOAD |
1048576 |
Limite WebSocket, 1024–16777216 bytes |
WU_MCP_ORIGINS |
HTTP(S) loopback | Lista de origens exatas separadas por vírgulas, sem caminho |
WU_MCP_INSECURE |
Desativado | Apenas 1 permite omitir o token. Se houver um token, ele continua obrigatório |
O resultado contém host, port, timeout, authTimeout, maxPayload, token, insecure e allowedOrigins. Esta última propriedade é um Set ou null, não um array enviado ao navegador.
Origin é verificado durante o upgrade HTTP do WebSocket. Um Origin ausente ou não permitido recebe 403. Depois, o handshake de autenticação é concluído. O relay permite um bridge autenticado ativo por vez; conhecer o token não permite substituir silenciosamente uma sessão já ativa.
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();
A construção inicia o listener. ready() resolve quando pode escutar e rejeita em caso de falha de bind; não espera uma página. Você pode injetar { logger } como segundo argumento do construtor; ele deve expor error(...args). Os logs do serviço devem ir para stderr para não corromper o protocolo de stdout.
| Método | Retorno | Falhas ou limites |
|---|---|---|
ready() |
Promise<Puente> |
Rejeita se a inicialização falhar |
address() |
Endereço do listener | Não indica se há uma página conectada |
conectado() |
boolean |
Leitura instantânea; a sessão pode fechar depois |
enviar(command, params = {}) |
Promise<unknown> |
Rejeita sem bridge, por desconexão, timeout ou erro de resposta |
close() |
Promise<void> |
Rejeita pendências, fecha sockets e listener; não reinicia o serviço |
enviar recebe o nome do comando do bridge, por exemplo list_actions. O nome MCP externo correspondente é wu_list_actions. Não confunda os dois níveis.
Cada solicitação mantém um identificador, um timeout e o socket que deve responder. O relay remove a entrada pendente ao concluir ou falhar. close() libera as pendências e força o encerramento de sockets que não fecharam após o prazo de fechamento.
Construir ou iniciar o 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) cria o listener, aguarda sua inicialização, registra ferramentas e conecta o transporte stdio. Instala o tratamento de SIGINT/SIGTERM para fechar servidor e ponte. Resolve a inicialização e não retorna um objeto para gerenciar a sessão. Em um cliente externo, o executável equivalente é wu-mcp-server.
Para um host que administra explicitamente seu ciclo de vida:
| Exportação | Contrato |
|---|---|
createMcpServer(puente, config) |
Retorna um servidor com connect(transport) e close(); registra as 18 ferramentas |
createCommandRunner(puente) |
Retorna uma função (commandName, params?) => Promise<WuMcpToolResult> que adapta a resposta do bridge |
O host fornece um transporte MCP compatível a connect. Ao fechar uma integração embarcada, feche tanto o servidor MCP quanto Puente; um não substitui o ciclo de vida do outro. A receita para download usa o executável empacotado e um cliente stdio mínimo, sem precisar importar classes privadas do SDK incluído no bundle.
Catálogo de ferramentas
Este é o catálogo registrado pelo servidor. Os parâmetros das ferramentas passam pelos schemas do relay; as permissões de execução são verificadas novamente no bridge ou em wu.ai.execute.
| Ferramenta | Argumentos | Resultado principal | Permissão da página |
|---|---|---|---|
wu_conexion |
{} |
{ puerto, host, requiere_token, app_conectada } |
Não precisa de página conectada |
wu_list_actions |
{} |
{ actions, count } |
Bridge autenticado; listar não concede execução |
wu_execute_action |
{ action, params? } |
{ action, success, result?, reason? } |
executeActions e permissões da action |
wu_status |
{} |
Framework, apps, chaves raiz, quantidade de ações/eventos | Bridge autenticado; chaves raiz exigem readStore |
wu_list_apps |
{} |
Descritores e estado das apps | Bridge autenticado |
wu_list_events |
{ limit? } |
Eventos recentes com dados sensíveis ocultados | 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 proibidos |
wu_navigate |
{ route } |
{ navigated: route } |
emitEvents; writeStore para atualizar currentPath |
wu_snapshot |
{ appName? } |
App, árvore acessível e timestamp | readDOM |
wu_screenshot |
{ selector?, quality? } |
Captura DOM, metadados ou erro | readDOM |
wu_click |
{ selector?, text? } |
Elemento ativado ou erro | modifyDOM |
wu_type |
{ selector, text, clear?, submit? } |
Resultado da escrita ou erro | modifyDOM |
wu_console |
{ level?, limit? } |
Entradas do console com dados sensíveis ocultados | readDiagnostics |
wu_network |
{ method?, status?, limit? } |
Metadados de rede filtrados | readDiagnostics |
wu_mount_app |
{ appName, container? } |
App montada | modifyDOM e o contrato normal de montagem |
wu_unmount_app |
{ appName } |
App desmontada | modifyDOM |
Detalhes dos parâmetros:
limitde eventos: inteiro de 1–200, padrão 20. O buffer circular contém no máximo 200 entradas.limitde console e rede: inteiro de 1–500, padrão 50. O console aceitalevel: 'all' | 'log' | 'warn' | 'error', padrãoall; a rede aceitastatusentre 100 e 599.quality: 0–1, padrão 0.8 do bridge. O helper usa captura DOM/foreignObject e PNG por padrão; essa qualidade não modifica a compressão PNG nem garante a fidelidade de uma captura privilegiada.clearesubmit:falsepor padrão. Escrever pode emitir eventos do controle;submit: truepode acionar o envio de um formulário.event: string de 1–200 caracteres. Os prefixoswu:,system:eapp:são rejeitados no relay e no bridge.pathvazio na leitura seleciona a raiz; a escrita exige um path não vazio. Os dados de diagnóstico passam pela ocultação de dados sensíveis antes de sair do bridge.
wu_navigate emite shell:navigate: o shell deve tratar esse evento. O resultado isolado não prova que a página mudou, e o gate usado é emitEvents, não o flag IA navigate. wu_status retorna storeKeys: [] quando readStore está desativado.
JSON-RPC e resultados negativos
Após inicializar a sessão MCP, uma chamada a uma action tem esta forma:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "wu_execute_action",
"arguments": { "action": "sum_local", "params": { "a": 2, "b": 3 } }
}
}
O servidor entrega conteúdo textual em content[0].text. A função createCommandRunner marca isError quando o bridge retorna um campo error, ou quando o transporte falha. Um resultado de ação com success: false pode permanecer dentro de um envelope isError: false.
Verifique a camada MCP e a camada da action separadamente. Não interprete um HTTP correto, um socket aberto ou um envelope sem isError como prova de que o handler obteve autorização e terminou com sucesso.
tools/list lista as ferramentas da versão instalada. wu_list_actions lista outro catálogo: os handlers que a página registrou no subsistema de IA. Para executar uma action, ambos os registros e as permissões adequadas são necessários.
Helpers de autenticação e nomes
| Exportação | Assinatura | Comportamento |
|---|---|---|
isOriginAllowed |
(origin, allowedOrigins = null) => boolean |
Com null, permite apenas origens HTTP(S) exatas de loopback |
tokenMatches |
(candidate, expectedDigest) => boolean |
Compara o SHA-256 do candidato com um digest esperado de 32 bytes usando timingSafeEqual |
isReservedEvent |
(eventName) => boolean |
Detecta os três namespaces internos reservados |
tokenMatches espera um digest, não o token em texto como segundo argumento. Um digest de tamanho incorreto pode causar um erro de comparação. Esses helpers não abrem sockets nem concedem permissões de domínio.
Cancelamento, segurança e testes
O protocolo desta implementação não propaga um cancelamento MCP até o handler de wu.ai.execute. A receita cancela uma espera e observa que o cálculo já iniciado continua. disconnect e close liberam transporte e pendências, mas não implementam compensação de efeitos.
Use ações próprias limitadas e um serviço autenticado para operações privilegiadas. As flags de IA do navegador são controles técnicos dessa página; não substituem autorização de negócio, idempotência nem um registro durável de operações.
node examples/mcp/local-relay.mjs
node scripts/verify-server-examples.mjs --mcp
O teste inclui handshake, token incorreto, ferramentas e ações reais, soma, schema inválido, permissões negadas, evento reservado, cancelamento observável e limpeza. O token é gerado em memória, todos os listeners são locais e não é necessário um provedor LLM. Você pode baixar as receitas completas ou revisar ações de IA e MCP executáveis.