Pular para o conteúdo
< samuelsantana.dev />
Voltar para o BlogIlustração de um robô de IA operando um painel Angular no navegador: ativa o tema escuro, ajusta o filtro de preço máximo e dispara um aviso para leitor de tela.

O Frontend Guiado por IA: WebMCP, Tools e Acessibilidade no Angular

Samuel Santana
Publicado em 20 de julho de 2026
AngularIA

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:

  1. Usuário: "Tenho saldo suficiente para pagar por estes itens?"
  2. Agente de IA (Pensa): "Para responder, eu preciso saber o que está no carrinho e qual é o saldo."
  3. Agente de IA (Ação): O agente do navegador chama as ferramentas get_cart e get_balance que a página registrou; elas executam no próprio JavaScript da página.
  4. Angular (WebMCP): Lê o valor atual dos Signals e devolve apenas esses dados pontuais para a IA.
  5. 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...