🧠FFVAcademy
🛠️

Criando Agents Customizados: do subagent ao MCP

19 min de leitura·+90 XP
Pré-requisitos (0/1)0%

Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.

Gerenciar agents é metade do jogo. A outra metade é criar os seus próprios: subagents especializados que seu time usa todo dia, MCP servers que conectam seu sistema a qualquer agent compatível, e uma biblioteca interna de agents que reduz o custo marginal de automação perto de zero. Neste módulo, construímos exemplos reais — não demos.

Três formas de criar um agent

FormaO que éQuando usar
SubagentAgent definido em arquivo (.md com frontmatter) invocado por agent principalTarefa repetida no time: review, research, test writing
MCP serverServidor stdio/HTTP que expõe tools/resources/promptsConectar agent a sistema externo (Jira, GitHub, DB, API interna)
SDK customAgent programático usando Anthropic SDK / Agent SDKProduto ou pipeline onde agent é parte do código, não do editor

Subagent: anatomia (Claude Code)

Um subagent no Claude Code vive em .claude/agents/<nome>.md (por projeto) ou em ~/.claude/agents/ (global). Frontmatter declara metadados; corpo é o system prompt.

markdown
---
name: security-review
description: >
  Use para revisar PRs com foco em vulnerabilidades e supply chain.
  Invoque proativamente antes de merges em módulos de auth, pagamento ou IO externo.
model: sonnet
tools:
  - Read
  - Grep
  - Glob
  - Bash
---

# Security Review Agent

Você é revisor de segurança sênior. Seu trabalho é revisar um conjunto
de mudanças (diff ou PR) e produzir um relatório curto e acionável.

## Missão
Encontrar: (a) vulnerabilidades classe OWASP Top 10 (injection, authz,
broken auth, IDOR, XSS, SSRF, deserialization); (b) secrets vazados;
(c) dependência suspeita (typosquat, sem manutenção, fork malicioso);
(d) pattern anti-security (shell=True, eval, pickle de input externo,
subprocess com input não sanitizado).

## Método
1. Leia o diff/PR completo antes de comentar.
2. Para cada arquivo tocado, considere: entrada do usuário? sanitização?
   output encoding? uso de crypto? I/O com sistema?
3. Procure strings suspeitas: "password", "token", "secret",
   "private_key" em código (não em .env ou vault ref).
4. Verifique dependências adicionadas em package.json/requirements.txt:
   autor, última versão, popularidade.
5. Produza um relatório: [CRITICAL/HIGH/MEDIUM/LOW] arquivo:linha -
   descrição e mitigação.

## Regras
- Seja específico: aponte linha e trecho.
- Se não achar nada, diga com convicção.
- Não re-analise código não tocado pelo PR.
- Nunca rode comando destrutivo.

## Saída esperada
Relatório em Markdown: resumo executivo + lista de achados por severidade.
💡
Por que funciona. (1) Nome e descrição deixam o agent principal saber quando invocar. (2) Model declara qual modelo usar — Haiku pra tarefas leves, Sonnet pra balanço, Opus pra raciocínio pesado. (3) Tools restringem capacidades (read-only aqui — não precisa escrever). (4) System prompt é curto, focado e termina com formato de saída claro.

Subagent de pesquisa (paralelo e ágil)

markdown
---
name: research
description: >
  Explore o repositório para responder perguntas técnicas.
  Ótimo para "como funciona X?", "onde é Y definido?", "quais impactos de mudar Z?".
model: haiku
tools:
  - Read
  - Grep
  - Glob
---

# Research Agent

Você é um explorador de código. Sua missão é entender partes do
repositório e retornar SUMÁRIOS CURTOS ao invés de arquivos completos.

## Método
1. Planeje 2-3 queries Grep/Glob antes de abrir arquivo.
2. Abra só os arquivos relevantes (não leia tudo).
3. Para cada arquivo, cite caminho e linhas específicas.
4. Seu output final deve ter <= 400 palavras.

## Formato da resposta
- Resposta direta (1 parágrafo)
- Evidências (bullet list com file.ts:LL)
- Próximos passos sugeridos (opcional)

## Nunca
- Listar arquivo inteiro sem necessidade.
- Responder "li tudo" sem evidência.
- Sair da pergunta do orquestrador.

Como agent principal invoca subagents

💬Usuário faz pergunta complexa1
"Adicione idempotência em /payments seguindo a spec docs/specs/idempotency.md"
plano
🧠Orchestrator planeja2
Decide: preciso de research, architect, security.
dispara
🔬research subagentparalelo
Mapeia handlers existentes, retorna sumário.
dispara
🏗️architect subagentparalelo
Propõe design: storage, locking, concurrency.
implementa
✍️Orchestrator escreve código4
Com sumários em mão, produz diff focado.
revisa
🛡️security subagent5
Review do diff. Retorna achados.
aplica
🚀Orchestrator entrega PR6
Sumário + diff + evidências + relatório de security.

MCP: protocolo de ferramentas

MCP padroniza como modelos conectam a sistemas externos. Um MCP server expõe tools (funções chamáveis), resources (dados consultáveis) e prompts (templates). Qualquer agent compatível (Claude Code, Claude.ai, Cursor, VS Code com plugin) consome o mesmo server.

🗺️ Fluxo MCP
🤖Agentcliente MCP
Descobre servers disponíveis no startup.
list_tools
🔌MCP serverstdio/HTTP
Declara tools com schema JSON.
call_tool
🛠️Tool executacódigo seu
Acessa Jira/DB/API/file.
retorna
📥Resultado volta ao agentJSON
Agent usa no raciocínio.

MCP server mínimo em Node/TypeScript

typescript
// mcp-jira-server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

const server = new Server(
  { name: 'jira-bridge', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'get_issue',
      description: 'Recupera uma issue do Jira por chave (ex: INGEST-123).',
      inputSchema: {
        type: 'object',
        properties: {
          key: { type: 'string', description: 'Chave da issue' },
        },
        required: ['key'],
      },
    },
    {
      name: 'list_sprint_issues',
      description: 'Lista issues do sprint ativo de um board.',
      inputSchema: {
        type: 'object',
        properties: {
          boardId: { type: 'number' },
        },
        required: ['boardId'],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const { name, arguments: args } = req.params;

  if (name === 'get_issue') {
    const res = await fetch(`${process.env.JIRA_URL}/rest/api/3/issue/${args.key}`, {
      headers: {
        Authorization: `Bearer ${process.env.JIRA_TOKEN}`,
        Accept: 'application/json',
      },
    });
    if (!res.ok) {
      return { content: [{ type: 'text', text: `Erro: ${res.status}` }], isError: true };
    }
    const data = await res.json();
    return {
      content: [{ type: 'text', text: JSON.stringify({
        key: data.key,
        summary: data.fields.summary,
        status: data.fields.status.name,
        assignee: data.fields.assignee?.displayName ?? 'unassigned',
        description: data.fields.description,
      }, null, 2) }],
    };
  }

  if (name === 'list_sprint_issues') {
    // ... lógica análoga
  }

  return { content: [{ type: 'text', text: 'Tool não encontrada' }], isError: true };
});

const transport = new StdioServerTransport();
await server.connect(transport);
console.error('jira-bridge MCP server running');
bash
# package.json
{
  "name": "mcp-jira-server",
  "bin": { "mcp-jira": "dist/mcp-jira-server.js" },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  }
}

# Build e registro no agent
npm run build

# Em .claude/mcp.json ou ~/.claude/mcp_settings.json
{
  "mcpServers": {
    "jira": {
      "command": "node",
      "args": ["/path/mcp-jira-server/dist/mcp-jira-server.js"],
      "env": {
        "JIRA_URL": "https://empresa.atlassian.net",
        "JIRA_TOKEN": "${JIRA_TOKEN}"
      }
    }
  }
}

MCP na prática: um agente que fecha PR com issue

Com MCP Jira + MCP GitHub (oficial), o agent faz sozinho:

  1. 1. Usuário: “Resolve INGEST-123”.
  2. 2. Agent chama get_issue(key: INGEST-123) → lê título, descrição, critério de aceite.
  3. 3. Research subagent mapeia arquivos relevantes.
  4. 4. Agent implementa, escreve testes, abre PR via MCP GitHub (create_pr).
  5. 5. Agent atualiza issue no Jira (update_issue) com link do PR.
  6. 6. Humano revisa PR em 10 min — foca no que importa.

Agent via SDK (programático)

Quando o agent é parte do seu produto (não do editor), use o SDK. Exemplo com o Anthropic SDK em TypeScript:

typescript
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const tools = [
  {
    name: 'get_order',
    description: 'Retorna detalhes de um pedido.',
    input_schema: {
      type: 'object',
      properties: { orderId: { type: 'string' } },
      required: ['orderId'],
    },
  },
  {
    name: 'refund_order',
    description: 'Reembolsa pedido. Requer confirmação humana antes de chamar.',
    input_schema: {
      type: 'object',
      properties: {
        orderId: { type: 'string' },
        amount: { type: 'number' },
      },
      required: ['orderId', 'amount'],
    },
  },
];

async function runAgent(userMessage: string) {
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: userMessage },
  ];

  while (true) {
    const res = await client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 2048,
      tools,
      messages,
    });

    messages.push({ role: 'assistant', content: res.content });

    if (res.stop_reason === 'end_turn') return res.content;

    if (res.stop_reason === 'tool_use') {
      const toolResults: Anthropic.ToolResultBlockParam[] = [];
      for (const block of res.content) {
        if (block.type !== 'tool_use') continue;

        // Gate humano em ações destrutivas
        if (block.name === 'refund_order') {
          const ok = await askHumanApproval(block.input);
          if (!ok) {
            toolResults.push({
              type: 'tool_result',
              tool_use_id: block.id,
              content: 'Reembolso negado pelo humano.',
              is_error: true,
            });
            continue;
          }
        }

        const result = await executeTool(block.name, block.input);
        toolResults.push({
          type: 'tool_result',
          tool_use_id: block.id,
          content: JSON.stringify(result),
        });
      }
      messages.push({ role: 'user', content: toolResults });
    }
  }
}

Sandbox e ciclo de vida

Worktree isoladoClaude Code oferece isolation: "worktree" — agent trabalha em cópia do repo, commits só voltam via PR.
Container sandboxPara comandos perigosos, rode em container descartável (docker run --rm). Isola FS, rede, processo.
VM para máxima segurançaFirecracker, Kata, ou qemu quando precisa isolar de kernel (raro, mas existe).
LimitesTimeouts por tool call, memória/CPU em container, max iterations no loop do agent.
CleanupWorktree sem mudanças = descartada automaticamente. Container com --rm. VMs com TTL.
AuditTodo comando vira linha no log com timestamp, agent id, tool, input/output. Exportado pra SIEM.

Bibliotecas de agents: como o time escala

Times maduros constroem um catálogo interno. Um repo ou pasta no monorepo com:

bash
infra/agents/
├── agents/
│   ├── security-review.md      # subagent para PR review
│   ├── research.md              # exploração rápida
│   ├── architect.md             # design de feature
│   ├── test-writer.md           # escreve testes
│   ├── migration-executor.md    # migrations SQL seguras
│   └── runbook-responder.md     # responde incident seguindo runbook
├── mcp-servers/
│   ├── jira-bridge/             # MCP server de Jira
│   ├── sentry-bridge/           # MCP server de Sentry (ler erros prod)
│   ├── grafana-bridge/          # consulta dashboards
│   └── feature-flag-bridge/     # GrowthBook toggle
├── policies/
│   ├── CLAUDE.md                # regras globais para agents
│   └── security-policy.md       # o que nunca fazer
└── README.md                    # como usar
Efeito.Onboarding de novo dev dobra de velocidade: agent já entende o repo, já sabe abrir PR do jeito certo, já tem runbook de incidente conectado. “Como faço X aqui?” vira “qual agent uso?”.

Dois cenários reais

📋 Time faz ~40 PRs/semana e review de segurança atrasa tudo

Subagent security-review + gate no CI

Subagent roda no PR quando há arquivos sensíveis (auth, pagamento, IO). Publica comentário com findings. Reviewer humano focaliza nos HIGH/CRITICAL. PRs sem finding mergeiam mais rápido.

📋 Product quer que IA abra tickets completos a partir de feedback de cliente no Intercom

SDK + MCP (Intercom, Jira, GitHub)

Pipeline programático (não agent de editor). Agent lê ticket, gera spec, cria issue no Jira, abre PR draft com esqueleto. Humano ajusta.

Alt: Só subagentideal pra dev, não pra integração contínua com produto externo.

Perguntas típicas

Diferença entre subagent e MCP?

Subagent = agente com prompt/tools/modelo próprios que o agent principal chama. MCP = protocolo pra expor tools/resources a qualquer agent. Muitas vezes andam juntos: subagent usa tools expostas por MCP server.

Preciso hospedar MCP server em algum lugar?

Depende. Stdio MCP server roda local (ótimo para dev). HTTP MCP server serve pra times (hospeda em Cloud Run/K8s). Sempre com auth.

Posso rodar agent 100% offline?

Com modelo local (Llama, Qwen via Ollama) sim, mas qualidade cai. Agent de produção séria ainda depende de modelo frontier. Sandbox e tools podem ser 100% locais.

Quantos subagents é demais?

Se você não lembra pra que serve cada um, tem demais. 6-10 subagents cobrem bem a maioria dos times. Prefira poucos com responsabilidade clara a muitos sobrepostos.

Como versiono agents?

Versione no Git junto do código. Mudança em .claude/agents/ vai por PR como código. Isso permite rollback, code review do prompt e evolução coletiva.
Take-aways. (1) Subagent = especialista. Prompt curto + tools mínimas + modelo barato quando cabe. (2) MCP = USB-C dos LLMs. Escreva 1 server, use em todo agent compatível. (3) SDK quando agent vira produto. (4) Sandbox e permissões não são opcionais. (5) Catálogo interno de agents multiplica produtividade do time. (6) Próxima trilha: testes profissionais que dão rede para agent e humano.
🧩

Quiz rápido

4 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo