Erstelle einen Claude-Code-ähnlichen AI-Agenten mit Claude Agent SDK (TypeScript)
Updated on
Wenn du schon einmal Claude Code oder andere AI-Coding-Copiloten benutzt hast, kennst du das magische Gefühl: Du tippst Fragen in ein Panel, und eine AI, die sich an die Sitzung „erinnert“, hilft dir beim Nachdenken über Code, beim Ausführen von Tools und dabei, aus Sackgassen herauszukommen.
Mit dem Claude Agent SDK kannst du deinen eigenen Agenten im Stil von Claude Code in Node.js mit überraschend wenig Code bauen.
In dieser Anleitung gehen wir Schritt für Schritt durch:
- Einrichten eines TypeScript-Projekts
- Senden deiner ersten Anfrage mit
query() - Führen einer langlebigen Gesprächssitzung
- Aufbau einer Command-Line-Chat-Erfahrung
- Hinzufügen eines Mathe-Tools über MCP, damit der Agent einen „Taschenrechner verwenden“ kann
Alle Beispiele verwenden TypeScript + Node.js und sind bewusst klar strukturiert und benannt, damit du sie leicht in dein eigenes Projekt übernehmen kannst.
1. Projekt-Setup
Erstelle ein neues Projekt:
mkdir dev-assistant-agent
cd dev-assistant-agent
npm init -yInstalliere Abhängigkeiten:
npm install @anthropic-ai/claude-agent-sdk
npm install typescript tsx @types/node
npx tsc --initWir verwenden außerdem dotenv, um Umgebungsvariablen zu laden:
npm install dotenvErstelle eine .env-Datei im Projektroot:
ANTHROPIC_API_KEY=your_api_key_here
ANTHROPIC_BASE_URL=https://api.anthropic.comUnd ein kleines Config-Modul:
// 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. First Contact: ein minimaler Agent-Call
Der Kern des Claude Agent SDK ist die Funktion query(). Statt einen großen String zurückzugeben, liefert sie einen Async Iterator von Nachrichten (Systemnachrichten, Assistant-Nachrichten usw.). Du kannst diese streamen und verarbeiten, während sie eintreffen.
Erstelle ein einfaches „Hello“-Beispiel:
// 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);
}
}
}
}
}Entry Point:
// src/main.ts
import { runHelloAgent } from './agent/helloAgent';
async function main() {
console.log('Launching hello agent...');
await runHelloAgent();
}
main().catch(console.error);Starte das Ganze:
npx tsx src/main.tsDu solltest sehen, wie Claude beschreibt, was es tun kann – das bestätigt, dass das SDK korrekt eingebunden ist.
3. Conversation Sessions: Dem Agenten ein Gedächtnis geben
Eine einzelne Antwort ist nett; ein Assistent, der sich erinnert, ist besser.
Das Claude Agent SDK unterstützt Sessions. Jede Unterhaltung hat eine session_id. Wenn du eine neue query() startest, kannst du die Option resume nutzen, um eine bestehende Session fortzusetzen.
Hier ist eine kleine Abstraktion, die einen Prompt sendet und eine Session-ID zurückliefert, falls das SDK eine vergibt:
// 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 };
}Du kannst das so testen:
// src/examples/sessionDemo.ts
import { sendMessageWithSession } from '../agent/sessionClient';
export async function runSessionDemo() {
let sessionId: string | undefined;
// Erste Frage
const first = await sendMessageWithSession('Hello, who are you?', sessionId);
sessionId = first.sessionId;
// Zweite Frage, gleiche Unterhaltung
const second = await sendMessageWithSession(
'What did I just ask you?',
sessionId
);
sessionId = second.sessionId;
}Jetzt kann Claude Fragen zu früheren Turns beantworten, weil du dieselbe Session wiederverwendest.
4. Aufbau einer CLI-Chat-Erfahrung
Lass uns daraus eine kleine Command-Line-Chat-App machen.
Wir werden:
- das
readline-Modul von Node verwenden - eine
sessionIdüber mehrere Turns hinweg behalten - jede User-Nachricht über
sendMessageWithSessionsenden
Erstelle eine Datei nur für die CLI-Schleife:
// 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);
});
}Ändere den Entry Point so, dass er die CLI benutzt:
// src/main.ts
import { startCliConversation } from './cli/chatLoop';
startCliConversation().catch(console.error);Starte:
npx tsx src/main.tsJetzt hast du ein einfaches „Claude Code ohne UI“: ein Terminal, das Kontext über mehrere Turns hinweg hält und in Echtzeit antwortet.
5. Dem Agenten Tools über MCP geben (Beispiel: Math-Tool)
Einer der spannendsten Teile von Claude Code ist die Tool-Nutzung – das Modell kann selbst entscheiden, wann es:
- einen Taschenrechner aufruft
- Dateien durchsucht
- Kommandos ausführt
- APIs abfragt
Mit dem Claude Agent SDK kannst du Tools über das Model Context Protocol (MCP) hinzufügen. Wir implementieren ein Mathe-Tool, damit der Agent Ausdrücke präzise auswerten kann, statt Kopfrechnen zu machen.
5.1 Zusätzliche Abhängigkeiten installieren
Wir verwenden mathjs (opens in a new tab) und zod für Schema-Validierung:
npm install mathjs zod@3.25.76(Die Verwendung von Zod 3.x vermeidet Kompatibilitätsprobleme mit neueren Zod-Versionen.)
5.2 Ein kleiner Mathe-Helper
// 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 Ein Tool für den Agenten definieren
Das SDK stellt einen tool-Helper bereit. Wir definieren ein Tool namens numeric_calculator, das einen expression-String entgegennimmt.
// 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 Tools über einen MCP-Server bereitstellen
Jetzt bündeln wir ein oder mehrere Tools in einem MCP-Server:
// 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 Die CLI für Tool-Unterstützung erweitern
Wir erstellen eine tool-fähige Version des Message-Handlers und binden sie dann in unsere CLI-Schleife ein.
// 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':
// Tool-Ergebnisse kommen als spezielle User-Nachricht zurück
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 };
}Jetzt eine separate CLI-Schleife, die den toolfähigen Client nutzt:
// 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);
});
}Aktualisiere src/main.ts, um die tool-fähige Version zu starten:
// src/main.ts
import { startCliWithTools } from './cli/chatWithTools';
startCliWithTools().catch(console.error);Wenn du jetzt fragst:
You: (2454 + 23546)^2 / 32solltest du Folgendes sehen:
- ein Log, dass das Tool aufgerufen wurde
- das numerische Ergebnis als Tool-Resultat
- eine natürlichsprachliche Erklärung von Claude, die die Antwort zusammenfasst
Glückwunsch – du hast jetzt einen Claude-ähnlichen Agenten, der chatten, sich erinnern und Tools aufrufen kann.
6. Ideen zur Erweiterung deines Agenten
Sobald dieses Grundgerüst läuft, kannst du weitere Features im Stil von Claude Code hinzufügen:
-
Codebase-Tools
Tools, die Dateien aus deinem Repository lesen, Text durchsuchen oder große Dateien zusammenfassen. -
Execution-Tools
Tools, die Tests, Skripte oder kleine Code-Snippets in einer kontrollierten Umgebung ausführen. -
Projektbewusste System-Prompts
Integriere Projekt-Metadaten, Architektur-Dokumente oder Styleguides in densystemPrompt, damit sich der Agent „projekt-nativ“ anfühlt. -
Persistente Sessions pro User
Speicheresession_idin einer Datenbank, verknüpft mit deinen User-Accounts, damit jede Person einen langlebigen Assistenten hat.
Das Claude Agent SDK liefert dir die Bausteine. Wie „Claude Code–ähnlich“ dein Agent werden soll, hängt ganz von den Tools, Prompts und der UI ab, die du darum herum baust.