Benutzerdefinierte Tools ermöglichen es Ihnen, die Fähigkeiten von Claude Code mit Ihrer eigenen Funktionalität durch In-Process-MCP-Server zu erweitern, wodurch Claude mit externen Diensten, APIs interagieren oder spezialisierte Operationen durchführen kann.
Verwenden Sie die Hilfsfunktionen createSdkMcpServer und tool, um typsichere benutzerdefinierte Tools zu definieren:
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
// Erstellen Sie einen SDK MCP-Server mit benutzerdefinierten Tools
const customServer = createSdkMcpServer({
name: "my-custom-tools",
version: "1.0.0",
tools: [
tool(
"get_weather",
"Aktuelle Temperatur für einen Standort anhand von Koordinaten abrufen",
{
latitude: z.number().describe("Breitengrad-Koordinate"),
longitude: z.number().describe("Längengrad-Koordinate")
},
async (args) => {
const response = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`);
const data = await response.json();
return {
content: [{
type: "text",
text: `Temperatur: ${data.current.temperature_2m}°F`
}]
};
}
)
]
});Übergeben Sie den benutzerdefinierten Server an die query-Funktion über die mcpServers-Option als Dictionary/Objekt.
Wichtig: Benutzerdefinierte MCP-Tools erfordern den Streaming-Eingabemodus. Sie müssen einen asynchronen Generator/Iterable für den prompt-Parameter verwenden - ein einfacher String funktioniert nicht mit MCP-Servern.
Wenn MCP-Tools Claude zur Verfügung gestellt werden, folgen ihre Namen einem spezifischen Format:
mcp__{server_name}__{tool_name}get_weather im Server my-custom-tools wird zu mcp__my-custom-tools__get_weatherSie können über die allowedTools-Option steuern, welche Tools Claude verwenden kann:
Wenn Ihr MCP-Server mehrere Tools hat, können Sie sie selektiv erlauben:
Der @tool-Decorator unterstützt verschiedene Schema-Definitionsansätze für Typsicherheit:
Behandeln Sie Fehler elegant, um aussagekräftiges Feedback zu geben:
import { query } from "@anthropic-ai/claude-agent-sdk";
// Verwenden Sie die benutzerdefinierten Tools in Ihrer Abfrage mit Streaming-Eingabe
async function* generateMessages() {
yield {
type: "user" as const,
message: {
role: "user" as const,
content: "Wie ist das Wetter in San Francisco?"
}
};
}
for await (const message of query({
prompt: generateMessages(), // Verwenden Sie asynchronen Generator für Streaming-Eingabe
options: {
mcpServers: {
"my-custom-tools": customServer // Als Objekt/Dictionary übergeben, nicht als Array
},
// Optional angeben, welche Tools Claude verwenden kann
allowedTools: [
"mcp__my-custom-tools__get_weather", // Das Wetter-Tool erlauben
// Weitere Tools nach Bedarf hinzufügen
],
maxTurns: 3
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}const multiToolServer = createSdkMcpServer({
name: "utilities",
version: "1.0.0",
tools: [
tool("calculate", "Berechnungen durchführen", { /* ... */ }, async (args) => { /* ... */ }),
tool("translate", "Text übersetzen", { /* ... */ }, async (args) => { /* ... */ }),
tool("search_web", "Das Web durchsuchen", { /* ... */ }, async (args) => { /* ... */ })
]
});
// Nur bestimmte Tools mit Streaming-Eingabe erlauben
async function* generateMessages() {
yield {
type: "user" as const,
message: {
role: "user" as const,
content: "Berechne 5 + 3 und übersetze 'hallo' ins Spanische"
}
};
}
for await (const message of query({
prompt: generateMessages(), // Verwenden Sie asynchronen Generator für Streaming-Eingabe
options: {
mcpServers: {
utilities: multiToolServer
},
allowedTools: [
"mcp__utilities__calculate", // Taschenrechner erlauben
"mcp__utilities__translate", // Übersetzer erlauben
// "mcp__utilities__search_web" ist NICHT erlaubt
]
}
})) {
// Nachrichten verarbeiten
}import { z } from "zod";
tool(
"process_data",
"Strukturierte Daten mit Typsicherheit verarbeiten",
{
// Zod-Schema definiert sowohl Laufzeitvalidierung als auch TypeScript-Typen
data: z.object({
name: z.string(),
age: z.number().min(0).max(150),
email: z.string().email(),
preferences: z.array(z.string()).optional()
}),
format: z.enum(["json", "csv", "xml"]).default("json")
},
async (args) => {
// args ist vollständig typisiert basierend auf dem Schema
// TypeScript weiß: args.data.name ist string, args.data.age ist number, etc.
console.log(`Verarbeite ${args.data.name}s Daten als ${args.format}`);
// Ihre Verarbeitungslogik hier
return {
content: [{
type: "text",
text: `Daten für ${args.data.name} verarbeitet`
}]
};
}
)tool(
"fetch_data",
"Daten von einer API abrufen",
{
endpoint: z.string().url().describe("API-Endpunkt-URL")
},
async (args) => {
try {
const response = await fetch(args.endpoint);
if (!response.ok) {
return {
content: [{
type: "text",
text: `API-Fehler: ${response.status} ${response.statusText}`
}]
};
}
const data = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Fehler beim Abrufen der Daten: ${error.message}`
}]
};
}
}
)const databaseServer = createSdkMcpServer({
name: "database-tools",
version: "1.0.0",
tools: [
tool(
"query_database",
"Eine Datenbankabfrage ausführen",
{
query: z.string().describe("Auszuführende SQL-Abfrage"),
params: z.array(z.any()).optional().describe("Abfrageparameter")
},
async (args) => {
const results = await db.query(args.query, args.params || []);
return {
content: [{
type: "text",
text: `${results.length} Zeilen gefunden:\n${JSON.stringify(results, null, 2)}`
}]
};
}
)
]
});const apiGatewayServer = createSdkMcpServer({
name: "api-gateway",
version: "1.0.0",
tools: [
tool(
"api_request",
"Authentifizierte API-Anfragen an externe Dienste stellen",
{
service: z.enum(["stripe", "github", "openai", "slack"]).describe("Aufzurufender Dienst"),
endpoint: z.string().describe("API-Endpunkt-Pfad"),
method: z.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP-Methode"),
body: z.record(z.any()).optional().describe("Anfragekörper"),
query: z.record(z.string()).optional().describe("Abfrageparameter")
},
async (args) => {
const config = {
stripe: { baseUrl: "https://api.stripe.com/v1", key: process.env.STRIPE_KEY },
github: { baseUrl: "https://api.github.com", key: process.env.GITHUB_TOKEN },
openai: { baseUrl: "https://api.openai.com/v1", key: process.env.OPENAI_KEY },
slack: { baseUrl: "https://slack.com/api", key: process.env.SLACK_TOKEN }
};
const { baseUrl, key } = config[args.service];
const url = new URL(`${baseUrl}${args.endpoint}`);
if (args.query) {
Object.entries(args.query).forEach(([k, v]) => url.searchParams.set(k, v));
}
const response = await fetch(url, {
method: args.method,
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: args.body ? JSON.stringify(args.body) : undefined
});
const data = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}]
};
}
)
]
});const calculatorServer = createSdkMcpServer({
name: "calculator",
version: "1.0.0",
tools: [
tool(
"calculate",
"Mathematische Berechnungen durchführen",
{
expression: z.string().describe("Auszuwertender mathematischer Ausdruck"),
precision: z.number().optional().default(2).describe("Dezimalgenauigkeit")
},
async (args) => {
try {
// Verwenden Sie eine sichere Mathematik-Evaluierungsbibliothek in der Produktion
const result = eval(args.expression); // Nur Beispiel!
const formatted = Number(result).toFixed(args.precision);
return {
content: [{
type: "text",
text: `${args.expression} = ${formatted}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Fehler: Ungültiger Ausdruck - ${error.message}`
}]
};
}
}
),
tool(
"compound_interest",
"Zinseszins für eine Investition berechnen",
{
principal: z.number().positive().describe("Anfangsinvestitionsbetrag"),
rate: z.number().describe("Jährlicher Zinssatz (als Dezimalzahl, z.B. 0,05 für 5%)"),
time: z.number().positive().describe("Investitionszeitraum in Jahren"),
n: z.number().positive().default(12).describe("Zinseszinshäufigkeit pro Jahr")
},
async (args) => {
const amount = args.principal * Math.pow(1 + args.rate / args.n, args.n * args.time);
const interest = amount - args.principal;
return {
content: [{
type: "text",
text: `Investitionsanalyse:\n` +
`Kapital: $${args.principal.toFixed(2)}\n` +
`Zinssatz: ${(args.rate * 100).toFixed(2)}%\n` +
`Zeit: ${args.time} Jahre\n` +
`Zinseszins: ${args.n} mal pro Jahr\n\n` +
`Endbetrag: $${amount.toFixed(2)}\n` +
`Verdiente Zinsen: $${interest.toFixed(2)}\n` +
`Rendite: ${((interest / args.principal) * 100).toFixed(2)}%`
}]
};
}
)
]
});