
O Frontend Guiado por IA: WebMCP, Tools e Acessibilidade no Angular
Durante anos, o papel do frontend foi muito claro: buscar dados em uma API (REST ou GraphQL), gerenciar o estado local e renderizar HTML. Nós construímos aplicações "burras" que dependiam exclusivamente do clique do usuário para executar qualquer ação.
Mas a integração profunda da Inteligência Artificial no nosso ecossistema mudou as regras do jogo. A IA deixou de ser apenas um chat isolado no canto da tela. Hoje, modelos atuam como Agentes Autônomos que operam o software para o usuário.
Neste novo paradigma, o frontend deixa de ser apenas um exibidor de dados e passa a atuar como os olhos e as mãos da IA. E é exatamente aqui que três conceitos colidem na arquitetura Angular moderna: WebMCP (Web Model Context Protocol), Agent Skills (aqui, ações expostas ao agente como tools) e acessibilidade (CDK a11y e Angular Aria).
Neste artigo, vamos dissecar como preparar sua aplicação Angular para hospedar agentes de IA de forma nativa, com bom desempenho e sem sacrificar a acessibilidade.
1. WebMCP: O Frontend como Provedor de Contexto (Na Prática)
Para entender o WebMCP, precisamos olhar para o pesadelo que é tentar dar contexto para uma Inteligência Artificial hoje.
Imagine que você tem um e-commerce. O usuário abre o assistente de IA na tela do carrinho e digita: "Tenho saldo suficiente na carteira para pagar por estes itens?"
O problema: a IA é cega. Ela roda em um servidor remoto (OpenAI, Anthropic, etc.) e não faz ideia de quais itens estão na tela do usuário, muito menos qual é o saldo dele.
A "Gambiarra" Tradicional (Context Stuffing)
Historicamente, a solução do mercado para isso era forçar o frontend a enviar todo o contexto a cada mensagem. Sempre que o usuário apertava Enter, nós fazíamos algo assim antes de chamar a API da IA:
const promptOculto = `
O usuário está na tela de carrinho.
Itens no carrinho: ${JSON.stringify(this.cartSignal())}
Saldo do usuário: ${this.userBalanceSignal()}
Pergunta do usuário: "Tenho saldo suficiente?"
`;
Isso é péssimo. Se o carrinho tiver 50 itens, você gasta milhares de tokens (e dinheiro) a cada mensagem, enviando dados que a IA talvez nem precise para responder àquela pergunta específica.
A Solução: Web Model Context Protocol (WebMCP)
Criado inicialmente pela Anthropic e adotado como um padrão aberto da indústria, o MCP funciona como se fosse uma API REST, mas construída especificamente para Agentes de IA consumirem. O WebMCP leva a ideia para o navegador: é uma proposta de API web (não o MCP em si) incubada no W3C Web Machine Learning Community Group, com editores do Google e da Microsoft, e ainda experimental — no Chrome, só funciona com a flag chrome://flags/#enable-webmcp-testing ou via origin trial.
Em vez de empurrar os dados cegamente para a IA a cada requisição (Push), o frontend expõe ferramentas (tools) e a IA puxa a informação apenas quando precisa (Pull).
No Angular 22, registramos essas ferramentas com uma API experimental do próprio framework, que as entrega ao WebMCP do navegador. Veja como a inversão de controle acontece na prática:
import { Injectable, declareExperimentalWebMcpTool, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class AppContextOrchestrator {
// Nosso estado reativo comum
cartItems = signal([{ id: 1, name: 'Teclado Mecânico', price: 450 }]);
userBalance = signal(500);
constructor() {
this.registerWebMcpTools();
}
private registerWebMcpTools() {
// O WebMCP expõe apenas ferramentas (tools); ler estado vira uma tool sem argumentos.
// API experimental do Angular 22: sem WebMCP no navegador, o registro é ignorado.
// 1. Expõe o Carrinho como uma tool somente leitura
declareExperimentalWebMcpTool({
name: 'get_cart',
description: 'Retorna os itens do carrinho de compras do usuário, em JSON.',
inputSchema: { type: 'object', properties: {} },
execute: () => JSON.stringify(this.cartItems()), // Lê o Signal no momento da chamada
});
// 2. Expõe o Saldo como uma tool somente leitura
declareExperimentalWebMcpTool({
name: 'get_balance',
description: 'Retorna o saldo atual da carteira virtual do usuário.',
inputSchema: { type: 'object', properties: {} },
execute: () => this.userBalance().toString(),
});
}
}
O Novo Fluxo Guiado por IA
Com o WebMCP rodando no Angular, a conversa do exemplo anterior funciona assim:
- Usuário: "Tenho saldo suficiente para pagar por estes itens?"
- Agente de IA (Pensa): "Para responder, eu preciso saber o que está no carrinho e qual é o saldo."
- Agente de IA (Ação): O agente do navegador chama as ferramentas
get_carteget_balanceque a página registrou; elas executam no próprio JavaScript da página. - Angular (WebMCP): Lê o valor atual dos Signals e devolve apenas esses dados pontuais para a IA.
- Agente de IA (Responde): "O teclado custa 450 reais e você tem 500 reais de saldo. Sim, você tem saldo suficiente!"
Essa arquitetura reduz o desperdício de tokens, mantém o contexto sempre em tempo real de acordo com os Signals da aplicação e transforma o seu frontend em um Servidor de Contexto inteligente.
2. Agent Skills: Transformando Angular Signals em Ações
Se o WebMCP é como a IA "vê" a aplicação, as Agent Skills são como ela "toca" nela. Um aviso de nomenclatura: chamo de "skill" cada ação que o agente pode disparar, mas o termo técnico é tool (function calling). Agent Skills, como padrão, é outra coisa: o formato aberto criado pela Anthropic em que cada skill é uma pasta com um SKILL.md (metadados + instruções) que agentes de código carregam sob demanda — o Angular publica as suas em angular/skills.
Em vez de a IA responder com texto, nós a instruímos a responder com chamadas de função (Function Calling). No Angular moderno, nós mapeamos essas "Skills" diretamente para atualizações de Signals ou rotas.
Imagine um e-commerce. O usuário diz ao microfone (ou digita): "Mude para o tema escuro e me mostre apenas os tênis de corrida abaixo de 300 reais".
Veja como orquestrar isso no Angular usando um serviço focado em registrar Skills:
import { Injectable, inject, signal } from '@angular/core';
import { Router } from '@angular/router';
// Interface que define o contrato da Skill para o Agente de IA
interface AgentSkill {
name: string;
description: string;
inputSchema: object;
execute: (args: any) => Promise<void> | void;
}
@Injectable({ providedIn: 'root' })
export class AiAgentOrchestrator {
private router = inject(Router);
// O Estado da Aplicação (O que a IA vai enxergar/alterar via MCP)
theme = signal<'light' | 'dark'>('light');
priceFilter = signal<number | null>(null);
// Registro das habilidades que o Agente pode invocar
private registeredSkills = new Map<string, AgentSkill>();
constructor() {
this.registerCoreSkills();
}
private registerCoreSkills() {
this.registeredSkills.set('changeTheme', {
name: 'changeTheme',
description: 'Altera o tema visual da aplicação. Argumentos aceitos: "light" ou "dark".',
inputSchema: {
type: 'object',
properties: { theme: { type: 'string', enum: ['light', 'dark'] } },
required: ['theme'],
},
execute: ({ theme }) => this.theme.set(theme)
});
this.registeredSkills.set('filterProducts', {
name: 'filterProducts',
description: 'Aplica um filtro de preço máximo na listagem de produtos.',
inputSchema: {
type: 'object',
properties: { maxPrice: { type: 'number' } },
required: ['maxPrice'],
},
execute: ({ maxPrice }) => this.priceFilter.set(maxPrice)
});
this.registeredSkills.set('navigate', {
name: 'navigate',
description: 'Navega o usuário para uma rota específica, como "/carrinho" ou "/perfil".',
inputSchema: {
type: 'object',
properties: { path: { type: 'string' } },
required: ['path'],
},
execute: async ({ path }) => { await this.router.navigate([path]); }
});
}
// Método chamado pelo cliente WebMCP/LLM quando decide usar uma ferramenta
executeSkill(skillName: string, args: any) {
const skill = this.registeredSkills.get(skillName);
if (skill) {
skill.execute(args);
} else {
console.warn(`A IA tentou executar uma skill inexistente: ${skillName}`);
}
}
}
Nesta arquitetura, a IA se torna uma "usuária fantasma". O AiAgentOrchestrator traduz as intenções da inteligência artificial em mudanças de estado reais (Signals), atualizando a View de forma granular (Fine-Grained Reactivity) e sem depender de Zone.js.
3. O Desafio Invisível: Angular Aria e Acessibilidade
Aqui entramos no erro mais grave cometido por times que integram IA no frontend: destruir a acessibilidade.
Quando um usuário clica no botão "Filtrar", ele sabe que a tela vai mudar porque ele iniciou a ação. Mas quando a IA filtra os produtos autonomamente, o DOM (Document Object Model) muda de repente. Para um usuário que depende de leitores de tela (Screen Readers), a interface simplesmente "quebrou" ou "sumiu" silenciosamente.
Como a IA opera a UI dinamicamente, nós precisamos avisar o usuário sobre o que o Agente acabou de fazer. O @angular/cdk/a11y resolve isso de forma elegante com o LiveAnnouncer. Não confundir com o Angular Aria (@angular/aria): ele reúne diretivas headless que implementam padrões WAI-ARIA — Listbox, Combobox, Menu, Tabs, Tree, Grid, entre outros — cuidando de teclado, foco e atributos ARIA, mas não inclui um anunciador; o anúncio fica com o CDK.
Vamos refatorar nosso orquestrador para ser acessível:
import { Injectable, inject, signal } from '@angular/core';
import { LiveAnnouncer } from '@angular/cdk/a11y';
@Injectable({ providedIn: 'root' })
export class AiAgentOrchestrator {
private announcer = inject(LiveAnnouncer); // Injetando o CDK A11y
priceFilter = signal<number | null>(null);
// ... (código de registro omitido para brevidade)
private registerCoreSkills() {
this.registeredSkills.set('filterProducts', {
name: 'filterProducts',
description: 'Aplica um filtro de preço máximo.',
inputSchema: {
type: 'object',
properties: { maxPrice: { type: 'number' } },
required: ['maxPrice'],
},
execute: ({ maxPrice }) => {
// 1. Atualiza o estado visual
this.priceFilter.set(maxPrice);
// 2. Anuncia a mudança para o leitor de tela (Acessibilidade)
this.announcer.announce(`A Inteligência Artificial aplicou um filtro para produtos até ${maxPrice} reais.`, 'polite');
}
});
}
}
O valor 'polite' (parâmetro politeness do announce) é crucial. Ele diz ao leitor de tela do SO (como VoiceOver ou NVDA) para aguardar o usuário parar de interagir antes de falar, evitando interromper a navegação bruscamente.
Se a IA executar uma ação de emergência (ex: deslogar o usuário por questões de segurança), usaríamos 'assertive' para interromper tudo e avisar o usuário imediatamente.
Conclusão
A transição de interfaces estáticas para "Hospedeiras de Agentes" ainda depende de APIs experimentais, mas é a arquitetura que vale começar a projetar hoje.
Ao combinar o WebMCP para fornecer o contexto da aplicação à IA, as Agent Skills amarradas ao sistema reativo de Signals, e o LiveAnnouncer do CDK para manter a mutação do DOM acessível, nós criamos sistemas que não são apenas inteligentes, mas robustos, performáticos e inclusivos.
O frontend não é mais um consumidor passivo de APIs. Ele agora é o ambiente de trabalho (workspace) do seu Agente de Inteligência Artificial. E o Angular nos dá um bom ferramental para orquestrar essa complexidade sem abrir mão de controle.
Comentários
Carregando comentários...