Claude Code SDKの機能を拡張するためのカスタムツールの構築と統合
createSdkMcpServer
とtool
ヘルパー関数を使用して、型安全なカスタムツールを定義します:
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-code";
import { z } from "zod";
// カスタムツールを持つSDK MCPサーバーを作成
const customServer = createSdkMcpServer({
name: "my-custom-tools",
version: "1.0.0",
tools: [
tool(
"get_weather",
"Get current weather for a location",
{
location: z.string().describe("City name or coordinates"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius").describe("Temperature units")
},
async (args) => {
// 天気APIを呼び出し
const response = await fetch(
`https://api.weather.com/v1/current?q=${args.location}&units=${args.units}`
);
const data = await response.json();
return {
content: [{
type: "text",
text: `Temperature: ${data.temp}°\nConditions: ${data.conditions}\nHumidity: ${data.humidity}%`
}]
};
}
)
]
});
mcpServers
オプションを通じて、カスタムサーバーを辞書/オブジェクトとしてquery
関数に渡します。
prompt
パラメータには非同期ジェネレータ/イテラブルを使用する必要があります - 単純な文字列はMCPサーバーでは動作しません。mcp__{server_name}__{tool_name}
my-custom-tools
のget_weather
という名前のツールはmcp__my-custom-tools__get_weather
になりますallowedTools
オプションを通じて、Claudeが使用できるツールを制御できます:
import { query } from "@anthropic-ai/claude-code";
// ストリーミング入力でカスタムツールをクエリで使用
async function* generateMessages() {
yield {
type: "user" as const,
message: {
role: "user" as const,
content: "What's the weather in San Francisco?"
}
};
}
for await (const message of query({
prompt: generateMessages(), // ストリーミング入力に非同期ジェネレータを使用
options: {
mcpServers: {
"my-custom-tools": customServer // 配列ではなくオブジェクト/辞書として渡す
},
// オプションでClaudeが使用できるツールを指定
allowedTools: [
"mcp__my-custom-tools__get_weather", // 天気ツールを許可
// 必要に応じて他のツールを追加
],
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", "Perform calculations", { /* ... */ }, async (args) => { /* ... */ }),
tool("translate", "Translate text", { /* ... */ }, async (args) => { /* ... */ }),
tool("search_web", "Search the web", { /* ... */ }, async (args) => { /* ... */ })
]
});
// ストリーミング入力で特定のツールのみを許可
async function* generateMessages() {
yield {
type: "user" as const,
message: {
role: "user" as const,
content: "Calculate 5 + 3 and translate 'hello' to Spanish"
}
};
}
for await (const message of query({
prompt: generateMessages(), // ストリーミング入力に非同期ジェネレータを使用
options: {
mcpServers: {
utilities: multiToolServer
},
allowedTools: [
"mcp__utilities__calculate", // 計算機を許可
"mcp__utilities__translate", // 翻訳機を許可
// "mcp__utilities__search_web" は許可されない
]
}
})) {
// メッセージを処理
}
@tool
デコレータは型安全性のための様々なスキーマ定義アプローチをサポートします:
import { z } from "zod";
tool(
"process_data",
"Process structured data with type safety",
{
// Zodスキーマはランタイム検証とTypeScript型の両方を定義
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はスキーマに基づいて完全に型付けされる
// TypeScriptは知っている:args.data.nameは文字列、args.data.ageは数値など
console.log(`Processing ${args.data.name}'s data as ${args.format}`);
// 処理ロジックをここに
return {
content: [{
type: "text",
text: `Processed data for ${args.data.name}`
}]
};
}
)
tool(
"fetch_data",
"Fetch data from an API",
{
endpoint: z.string().url().describe("API endpoint URL")
},
async (args) => {
try {
const response = await fetch(args.endpoint);
if (!response.ok) {
return {
content: [{
type: "text",
text: `API error: ${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: `Failed to fetch data: ${error.message}`
}]
};
}
}
)
const databaseServer = createSdkMcpServer({
name: "database-tools",
version: "1.0.0",
tools: [
tool(
"query_database",
"Execute a database query",
{
query: z.string().describe("SQL query to execute"),
params: z.array(z.any()).optional().describe("Query parameters")
},
async (args) => {
const results = await db.query(args.query, args.params || []);
return {
content: [{
type: "text",
text: `Found ${results.length} rows:\n${JSON.stringify(results, null, 2)}`
}]
};
}
)
]
});
const apiGatewayServer = createSdkMcpServer({
name: "api-gateway",
version: "1.0.0",
tools: [
tool(
"api_request",
"Make authenticated API requests to external services",
{
service: z.enum(["stripe", "github", "openai", "slack"]).describe("Service to call"),
endpoint: z.string().describe("API endpoint path"),
method: z.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP method"),
body: z.record(z.any()).optional().describe("Request body"),
query: z.record(z.string()).optional().describe("Query parameters")
},
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",
"Perform mathematical calculations",
{
expression: z.string().describe("Mathematical expression to evaluate"),
precision: z.number().optional().default(2).describe("Decimal precision")
},
async (args) => {
try {
// 本番環境では安全な数学評価ライブラリを使用
const result = eval(args.expression); // 例のみ!
const formatted = Number(result).toFixed(args.precision);
return {
content: [{
type: "text",
text: `${args.expression} = ${formatted}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error: Invalid expression - ${error.message}`
}]
};
}
}
),
tool(
"compound_interest",
"Calculate compound interest for an investment",
{
principal: z.number().positive().describe("Initial investment amount"),
rate: z.number().describe("Annual interest rate (as decimal, e.g., 0.05 for 5%)"),
time: z.number().positive().describe("Investment period in years"),
n: z.number().positive().default(12).describe("Compounding frequency per year")
},
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: `Investment Analysis:\n` +
`Principal: $${args.principal.toFixed(2)}\n` +
`Rate: ${(args.rate * 100).toFixed(2)}%\n` +
`Time: ${args.time} years\n` +
`Compounding: ${args.n} times per year\n\n` +
`Final Amount: $${amount.toFixed(2)}\n` +
`Interest Earned: $${interest.toFixed(2)}\n` +
`Return: ${((interest / args.principal) * 100).toFixed(2)}%`
}]
};
}
)
]
});
Was this page helpful?