Criando Agents Customizados: do subagent ao MCP
- ⬜🎛️ Gerenciando Agents: orquestração, contexto e custo(Engenharia de Software Moderna)
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
| Forma | O que é | Quando usar |
|---|---|---|
| Subagent | Agent definido em arquivo (.md com frontmatter) invocado por agent principal | Tarefa repetida no time: review, research, test writing |
| MCP server | Servidor stdio/HTTP que expõe tools/resources/prompts | Conectar agent a sistema externo (Jira, GitHub, DB, API interna) |
| SDK custom | Agent programático usando Anthropic SDK / Agent SDK | Produto 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.
--- 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.
Subagent de pesquisa (paralelo e ágil)
--- 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
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.
MCP server mínimo em Node/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');# 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. Usuário: “Resolve INGEST-123”.
- 2. Agent chama
get_issue(key: INGEST-123)→ lê título, descrição, critério de aceite. - 3. Research subagent mapeia arquivos relevantes.
- 4. Agent implementa, escreve testes, abre PR via MCP GitHub (
create_pr). - 5. Agent atualiza issue no Jira (
update_issue) com link do PR. - 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:
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
Bibliotecas de agents: como o time escala
Times maduros constroem um catálogo interno. Um repo ou pasta no monorepo com:
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
Dois cenários reais
📋 Time faz ~40 PRs/semana e review de segurança atrasa tudo
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
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ó subagent — ideal pra dev, não pra integração contínua com produto externo.
Perguntas típicas
❓ Diferença entre subagent e MCP?
❓ Preciso hospedar MCP server em algum lugar?
❓ Posso rodar agent 100% offline?
❓ Quantos subagents é demais?
❓ Como versiono agents?
.claude/agents/ vai por PR como código. Isso permite rollback, code review do prompt e evolução coletiva.Quiz rápido
4 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito