Skip to content

Construa um Agente de IA Estilo Claude Code com Claude Agent SDK (TypeScript)

Updated on

Se você já usou Claude Code ou outros copilotos de código com IA, sabe como é mágico: você digita perguntas em um painel, e uma IA que “lembra” da sessão ajuda a raciocinar sobre o código, executar ferramentas e destravar bloqueios.

Com o Claude Agent SDK, você pode criar seu próprio agente ao estilo Claude Code em Node.js com surpreendentemente pouco código.

Neste guia, vamos passar por:

  1. Como configurar um projeto em TypeScript
  2. Enviar sua primeira requisição com query()
  3. Manter uma sessão de conversa de longa duração
  4. Construir uma experiência de chat em linha de comando
  5. Adicionar uma ferramenta de matemática via MCP para o agente “usar uma calculadora”

Todos os exemplos usam TypeScript + Node.js e são intencionalmente estruturados e nomeados para serem fáceis de adaptar ao seu próprio projeto.


1. Configuração do Projeto

Crie um novo projeto:

mkdir dev-assistant-agent
cd dev-assistant-agent
npm init -y

Instale as dependências:

npm install @anthropic-ai/claude-agent-sdk
npm install typescript tsx @types/node
npx tsc --init

Também usaremos dotenv para carregar variáveis de ambiente:

npm install dotenv

Crie um arquivo .env na raiz do projeto:

ANTHROPIC_API_KEY=your_api_key_here
ANTHROPIC_BASE_URL=https://api.anthropic.com

E um pequeno módulo de configuração:

// src/config/runtime.ts
import 'dotenv/config';
 
export const API_KEY = process.env.ANTHROPIC_API_KEY;
export const BASE_URL = process.env.ANTHROPIC_BASE_URL;
 
if (!API_KEY) {
  throw new Error('ANTHROPIC_API_KEY is not set.');
}

2. Primeiro Contato: uma Chamada de Agente Minimalista

O núcleo do Claude Agent SDK é a função query(). Em vez de retornar uma única string grande, ela retorna um iterador assíncrono de mensagens (mensagens de sistema, do assistente etc.). Você pode fazer streaming e tratá-las conforme chegam.

Crie um exemplo simples de “hello”:

// src/agent/helloAgent.ts
import { query, type Query } from '@anthropic-ai/claude-agent-sdk';
 
export async function runHelloAgent() {
  const stream: Query = query({
    prompt: 'Hi Claude, what can you do?',
  });
 
  for await (const item of stream) {
    if (item.type === 'assistant') {
      for (const chunk of item.message.content) {
        if (chunk.type === 'text') {
          console.log(chunk.text);
        }
      }
    }
  }
}

Ponto de entrada:

// src/main.ts
import { runHelloAgent } from './agent/helloAgent';
 
async function main() {
  console.log('Launching hello agent...');
  await runHelloAgent();
}
 
main().catch(console.error);

Execute:

npx tsx src/main.ts

Você deve ver o Claude descrevendo o que ele pode fazer — isso confirma que o SDK está conectado corretamente.


3. Sessões de Conversa: Deixando o Agente “Lembrar”

Uma resposta única é legal; um assistente que lembra é melhor ainda.

O Claude Agent SDK oferece suporte a sessions. Cada conversa tem um session_id. Quando você inicia um novo query(), pode passar a opção resume para continuar uma sessão existente.

Aqui está uma pequena abstração que envia um prompt e retorna qualquer session ID que o SDK fornecer:

// src/agent/sessionClient.ts
import { query, type Query } from '@anthropic-ai/claude-agent-sdk';
 
export async function sendMessageWithSession(
  userText: string,
  previousSessionId?: string
): Promise<{ sessionId?: string }> {
  let activeSessionId = previousSessionId;
 
  const stream: Query = query({
    prompt: userText,
    options: {
      resume: previousSessionId,
    },
  });
 
  for await (const item of stream) {
    switch (item.type) {
      case 'system':
        if (item.subtype === 'init') {
          activeSessionId = item.session_id;
          console.log(`(session started: ${activeSessionId})`);
        }
        break;
      case 'assistant':
        for (const piece of item.message.content) {
          if (piece.type === 'text') {
            console.log(`Claude: ${piece.text}`);
          }
        }
        break;
    }
  }
 
  return { sessionId: activeSessionId };
}

Você pode testar assim:

// src/examples/sessionDemo.ts
import { sendMessageWithSession } from '../agent/sessionClient';
 
export async function runSessionDemo() {
  let sessionId: string | undefined;
 
  // Primeira pergunta
  const first = await sendMessageWithSession('Hello, who are you?', sessionId);
  sessionId = first.sessionId;
 
  // Segunda pergunta, mesma conversa
  const second = await sendMessageWithSession(
    'What did I just ask you?',
    sessionId
  );
  sessionId = second.sessionId;
}

Agora o Claude pode responder perguntas sobre turnos anteriores, porque você está reutilizando a mesma sessão.


4. Construindo uma Experiência de Chat em CLI

Vamos transformar isso em um pequeno app de chat em linha de comando.

Nós vamos:

  • Usar o módulo readline do Node
  • Manter um sessionId entre os turnos
  • Enviar cada mensagem do usuário via sendMessageWithSession

Crie um arquivo apenas para o loop da CLI:

// src/cli/chatLoop.ts
import readline from 'readline';
import { sendMessageWithSession } from '../agent/sessionClient';
 
export async function startCliConversation() {
  let sessionId: string | undefined;
 
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'You: ',
  });
 
  console.log('Interactive Claude Agent CLI');
  console.log('Type your message and press Enter. Ctrl+C to quit.\n');
  rl.prompt();
 
  rl.on('line', async (line) => {
    const trimmed = line.trim();
    if (!trimmed) {
      rl.prompt();
      return;
    }
 
    try {
      const { sessionId: newSessionId } = await sendMessageWithSession(
        trimmed,
        sessionId
      );
      sessionId = newSessionId;
    } catch (err) {
      console.error('Error talking to agent:', err);
    }
 
    rl.prompt();
  });
 
  rl.on('close', () => {
    console.log('\nGoodbye!');
    process.exit(0);
  });
}

Altere o ponto de entrada para usar a CLI:

// src/main.ts
import { startCliConversation } from './cli/chatLoop';
 
startCliConversation().catch(console.error);

Execute:

npx tsx src/main.ts

Agora você tem um “Claude Code sem UI”: um terminal que mantém contexto entre os turnos e responde em tempo real.


5. Dando Ferramentas ao Agente via MCP (Exemplo: Ferramenta de Matemática)

Uma das partes mais legais do Claude Code é o uso de ferramentas — o modelo pode decidir quando:

  • Chamar uma calculadora
  • Buscar arquivos
  • Executar comandos
  • Consultar APIs

Com o Claude Agent SDK, você pode adicionar ferramentas via Model Context Protocol (MCP). Vamos implementar uma ferramenta de matemática para que o agente possa avaliar expressões com precisão em vez de fazer “conta de cabeça”.

5.1 Instalar dependências extras

Usaremos mathjs (opens in a new tab) e zod para validação de schema:

npm install mathjs zod@3.25.76

(Usar Zod 3.x evita problemas de compatibilidade com versões mais novas do Zod.)

5.2 Um pequeno helper de matemática

// src/tools/mathEvaluator.ts
import * as math from 'mathjs';
 
export function evaluateMathExpression(expression: string): string {
  const result = math.evaluate(expression);
  return result.toString();
}

5.3 Definindo uma ferramenta para o agente

O SDK expõe um helper tool. Vamos definir uma ferramenta chamada numeric_calculator que recebe uma string expression.

// src/tools/mathTool.ts
import { tool } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';
import { evaluateMathExpression } from './mathEvaluator';
 
export const numericCalculatorTool = tool(
  'numeric_calculator',
  'Evaluate a mathematical expression using mathjs, e.g. "(2 + 3) * 4".',
  {
    expression: z.string().describe('A valid math expression.'),
  },
  async (args) => {
    const output = evaluateMathExpression(args.expression);
 
    return {
      content: [
        {
          type: 'text',
          text: output,
        },
      ],
    };
  }
);

5.4 Expondo ferramentas via um servidor MCP

Agora empacotamos uma ou mais ferramentas em um servidor MCP:

// src/mcp/toolkitServer.ts
import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
import { numericCalculatorTool } from '../tools/mathTool';
 
export const toolsetServer = createSdkMcpServer({
  name: 'toolset',
  version: '1.0.0',
  tools: [numericCalculatorTool],
});

5.5 Atualizando a CLI para suportar ferramentas

Vamos criar uma versão do handler de mensagens com suporte a ferramentas e depois trocá-la no nosso loop de CLI.

// src/agent/toolEnabledClient.ts
import { query, type Query } from '@anthropic-ai/claude-agent-sdk';
import { toolsetServer } from '../mcp/toolkitServer';
 
export async function sendMessageWithTools(
  userText: string,
  previousSessionId?: string
): Promise<{ sessionId?: string }> {
  let activeSessionId = previousSessionId;
 
  const stream: Query = query({
    prompt: userText,
    options: {
      resume: previousSessionId,
      systemPrompt:
        'You are a helpful assistant. When you need to do precise math, use the numeric_calculator tool instead of guessing.',
      mcpServers: {
        toolset: toolsetServer,
      },
      // Tool names follow: mcp__{server_name}__{tool_name}
      allowedTools: ['mcp__toolset__numeric_calculator'],
    },
  });
 
  for await (const item of stream) {
    switch (item.type) {
      case 'system':
        if (item.subtype === 'init') {
          activeSessionId = item.session_id;
          console.log(`(session: ${activeSessionId})`);
        }
        break;
 
      case 'assistant':
        for (const piece of item.message.content) {
          if (piece.type === 'text') {
            console.log(`Claude: ${piece.text}`);
          } else if (piece.type === 'tool_use') {
            console.log(
              `[tool call] ${piece.name} with input: ${JSON.stringify(
                piece.input
              )}`
            );
          }
        }
        break;
 
      case 'user':
        // Resultados de ferramentas retornam como uma mensagem especial de usuário
        for (const piece of item.message.content) {
          if (piece.type === 'tool_result') {
            process.stdout.write('[tool result] ');
            for (const inner of piece.content) {
              if (inner.type === 'text') {
                process.stdout.write(inner.text);
              }
            }
            process.stdout.write('\n');
          }
        }
        break;
    }
  }
 
  return { sessionId: activeSessionId };
}

Agora um loop de CLI separado que usa o client com ferramentas:

// src/cli/chatWithTools.ts
import readline from 'readline';
import { sendMessageWithTools } from '../agent/toolEnabledClient';
 
export async function startCliWithTools() {
  let sessionId: string | undefined;
 
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'You: ',
  });
 
  console.log('Claude Agent CLI (with math tool)');
  console.log('Ask me something like: (2454 + 23546)^2 / 32');
  rl.prompt();
 
  rl.on('line', async (line) => {
    const text = line.trim();
    if (!text) {
      rl.prompt();
      return;
    }
 
    try {
      const { sessionId: nextSessionId } = await sendMessageWithTools(
        text,
        sessionId
      );
      sessionId = nextSessionId;
    } catch (err) {
      console.error('Error:', err);
    }
 
    rl.prompt();
  });
 
  rl.on('close', () => {
    console.log('\nSession ended.');
    process.exit(0);
  });
}

Atualize src/main.ts para rodar essa versão com ferramentas:

// src/main.ts
import { startCliWithTools } from './cli/chatWithTools';
 
startCliWithTools().catch(console.error);

Agora, quando você perguntar:

You: (2454 + 23546)^2 / 32

Você deve ver:

  • Um log indicando que a ferramenta foi chamada
  • O resultado numérico impresso como resultado da ferramenta
  • Uma explicação em linguagem natural do Claude resumindo a resposta

Parabéns — agora você tem um agente estilo Claude que consegue conversar, lembrar e chamar ferramentas.


6. Ideias para Estender seu Agente

Depois que esse esqueleto estiver funcionando, você pode começar a adicionar mais recursos ao estilo Claude Code:

  • Ferramentas para codebase
    Ferramentas que leem arquivos do seu repositório, fazem busca textual ou resumem arquivos grandes.

  • Ferramentas de execução
    Ferramentas que rodam testes, scripts ou pequenos trechos de código em um ambiente controlado.

  • System prompts cientes do projeto
    Injete metadados do projeto, documentos de arquitetura ou guias de estilo no systemPrompt para fazer o agente parecer “nativo do projeto”.

  • Sessions persistentes por usuário
    Armazene o session_id em um banco de dados associado às contas de usuário, para que cada pessoa tenha um assistente de longa duração.

O Claude Agent SDK fornece os blocos de construção. O quão “Claude Code–like” seu agente será depende inteiramente das ferramentas, prompts e UI que você construir em volta dele.