Claude Agent SDK実践ガイド — 移行手順・Subagents・Hooks・Skillsで構築する本番AIエージェント
2025年9月、Claude Code SDKは「Claude Agent SDK」にリネームされた。単なる名前変更ではない。SubagentsとHooksというプログラマティック制御機構が加わり、AIエージェントの設計パラダイムが変わった。本記事では、移行の実務手順から、SDK固有のSubagents・Hooksを使った本番エージェント構築パターンまでを、動くコードとともに解説する。
Claude Code SDK → Agent SDKで何が変わったのか
リネームの背景と設計思想の転換
もともとClaude Code SDKは「Claude Codeをライブラリとして使う」ための薄いラッパーだった。しかし、エージェントアプリケーション構築のプラットフォームとしての位置づけが明確になるにつれ、「Code」という名前が実態と合わなくなった。Agent SDKは、ツールループ・ファイル操作・Bash実行を内蔵したフルエージェントランタイムであり、素のAPI呼び出しを提供するAnthropic Client SDK(@anthropic-ai/sdk)とは明確に異なるレイヤーだ。
破壊的変更の全体像
主な変更点を整理する。
- パッケージ名:
@anthropic-ai/claude-code→@anthropic-ai/claude-agent-sdk(Python:claude-agent-sdk) - 型名: Python側で
ClaudeAgentOptionsに統一、AgentDefinition型の追加 - 新機能:
options.agentsによるSubagents定義、options.hooksによるライフサイクル制御、tool()+createSdkMcpServer()によるインプロセスMCPツール - PermissionMode:
default、acceptEdits、bypassPermissions、plan等の選択肢
query() の基本シグネチャ — AsyncGeneratorで SDKMessage をストリーム返却する構造 — は変わっていないため、移行のハードルは低い。
5分で完了する移行手順
パッケージ入れ替えとimport修正
# TypeScript
npm uninstall @anthropic-ai/claude-code
npm install @anthropic-ai/claude-agent-sdk
# Python
pip uninstall claude-code-sdk
pip install claude-agent-sdk
import文の一括置換はシンプルだ。
# プロジェクト全体で一括置換
find src -name '*.ts' -exec sed -i '' \
's/@anthropic-ai\/claude-code/@anthropic-ai\/claude-agent-sdk/g' {} +
型名・デフォルト値の変更対応
TypeScript側の query() 呼び出しは、import先のパッケージ名さえ変えればほぼそのまま動く。
// Before
import { query } from '@anthropic-ai/claude-code';
// After
import { query } from '@anthropic-ai/claude-agent-sdk';
const q = query({
prompt: 'プロジェクトのREADMEを生成して',
options: {
model: 'sonnet',
maxTurns: 30,
cwd: process.cwd(),
permissionMode: 'bypassPermissions',
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep'],
},
});
for await (const msg of q) {
if (msg.type === 'result' && msg.subtype === 'success') {
console.log(msg.result);
console.log(`Cost: $${msg.total_cost_usd}`);
}
}
正直、import書き換えだけで動いたときは拍子抜けした。
Subagents — プログラマティックなエージェント分離
CLIのYAMLサブエージェント(.claude/agents/)との違い
ここが最も混同されやすいポイントだ。CLI版のサブエージェントは .claude/agents/*.yml にYAML frontmatterで定義し、Claude Code UIから選択して使う。一方、SDK版Subagentsは options.agents に AgentDefinition の辞書として渡す、完全にプログラマティックな仕組みだ。
AgentDefinitionの設計と実装
以下はリサーチャー・実装者・レビュアーの3エージェントを定義し、連携させる例だ(Python SDK)。
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AgentDefinition
agents = {
"researcher": AgentDefinition(
description="Web検索でトレンドと技術情報を調査する",
tools=["WebSearch", "WebFetch", "Write"],
prompt="あなたはリサーチャーです。指定されたトピックについてWeb検索で情報を収集し、findings.mdに調査結果をまとめてください。",
model="haiku"
),
"implementer": AgentDefinition(
description="調査結果に基づいてコードを実装する",
tools=["Read", "Write", "Edit", "Bash", "Glob"],
prompt="あなたは実装担当です。findings.mdを読み、仕様に沿ってコードを実装してください。",
model="sonnet"
),
"reviewer": AgentDefinition(
description="実装コードのレビューと改善提案を行う",
tools=["Read", "Glob", "Grep", "Bash"],
prompt="あなたはコードレビュアーです。実装されたコードの品質・セキュリティ・パフォーマンスを評価してください。",
model="sonnet"
),
}
options = ClaudeAgentOptions(
permission_mode="bypassPermissions",
system_prompt="あなたはリードエージェントです。researcher→implementer→reviewerの順にタスクを委譲してください。",
allowed_tools=["Agent"], # Agentツールでサブエージェントを呼び出す
agents=agents,
model="sonnet"
)
各サブエージェントは独自のコンテキストで動作し、使えるツールもモデルも個別に指定できる。コスト最適化のために調査フェーズはHaiku、実装フェーズはSonnetといった使い分けが可能だ。
Hooks — SDKレベルのライフサイクル制御
CLI版Hooks(settings.json)との違い
CLI版Hooksは settings.json に command や http 型で外部プロセスを呼び出す仕組みだ。SDK版Hooksは options.hooks にネイティブ関数のコールバックとして定義する。外部プロセスの起動オーバーヘッドがなく、TypeScript/Pythonの型安全な世界で完結する。
実装パターン: ツール実行のフィルタリングと監査ログ
import { query } from '@anthropic-ai/claude-agent-sdk';
import type { HookJSONOutput } from '@anthropic-ai/claude-agent-sdk';
const q = query({
prompt: 'プロジェクトをリファクタリングして',
options: {
model: 'sonnet',
maxTurns: 50,
permissionMode: 'bypassPermissions',
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep'],
hooks: {
PreToolUse: [
{
matcher: 'Bash',
hooks: [
async (input: any): Promise<HookJSONOutput> => {
const command: string = input.tool_input?.command || '';
// 危険なコマンドをブロック
if (command.includes('rm -rf') || command.includes('DROP TABLE')) {
return {
decision: 'block',
stopReason: `危険なコマンドを検出: ${command}`,
continue: false,
};
}
return { continue: true };
},
],
},
],
PostToolUse: [
{
matcher: null, // 全ツールにマッチ
hooks: [
async (input: any): Promise<HookJSONOutput> => {
// 全ツール実行を監査ログに記録
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
tool: input.tool_name,
duration_ms: input.duration_ms,
}));
return { continue: true };
},
],
},
],
},
},
});
matcher にはパイプ区切りのパターン("Write|Edit|MultiEdit")を指定でき、null で全ツールにマッチする。
Skills systemとMCPサーバー統合
tool() と createSdkMcpServer() を使えば、独自のツールをインプロセスMCPサーバーとしてエージェントに統合できる。
import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';
const dbServer = createSdkMcpServer({
name: 'database',
version: '1.0.0',
tools: [
tool(
'search_products',
'商品データベースを検索する',
{
keyword: z.string().describe('検索キーワード'),
limit: z.number().optional().describe('取得件数(デフォルト10)'),
},
async (args) => {
const results = await db.query(
'SELECT * FROM products WHERE name LIKE ?',
[`%${args.keyword}%`]
);
return {
content: [{ type: 'text', text: JSON.stringify(results) }],
};
}
),
],
});
const q = query({
prompt: '最新のワイヤレスイヤホンを検索して比較表を作って',
options: {
model: 'sonnet',
maxTurns: 20,
mcpServers: { database: dbServer },
allowedTools: [
'mcp__database__search_products', // mcp__{server名}__{tool名}
'Write',
],
},
});
Zodスキーマでツール引数を定義し、型安全にハンドラを実装できる。個人的にはこの設計が一番気に入っている。
実践: 自律開発デーモンをAgent SDKで再設計する
現行アーキテクチャ(CLI spawn方式)の課題
現行の自律開発デーモンでは、spawn("claude", ["-p", "--dangerously-skip-permissions"]) でCLIプロセスを起動し、stdinにプロンプトをパイプ、stdoutのテキスト出力を文字列として受け取っている。
// Before: CLI spawn方式(claude-runner.ts)
const child = spawn('claude', ['-p', '--dangerously-skip-permissions'], {
cwd: workDir,
env: getCleanEnv(),
stdio: ['pipe', 'pipe', 'pipe'],
});
child.stdin.write(prompt);
child.stdin.end();
let output = '';
child.stdout.on('data', (chunk) => { output += chunk.toString(); });
child.stderr.on('data', (chunk) => {
// レート制限はstderrの文字列パターンマッチで検出...
});
課題は明確だ。プロセス管理のオーバーヘッド、stdoutの非構造化テキスト解析、stderrの文字列マッチに依存するレート制限検出。
Agent SDK方式への設計比較
// After: Agent SDK方式
import { query } from '@anthropic-ai/claude-agent-sdk';
const q = query({
prompt,
options: {
cwd: workDir,
model: 'sonnet',
maxTurns: 30,
permissionMode: 'bypassPermissions',
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'WebSearch', 'WebFetch'],
},
});
for await (const msg of q) {
if (msg.type === 'assistant' && msg.message) {
for (const block of msg.message.content) {
if (block.type === 'tool_use') {
log.info(`Tool: ${block.name}`);
}
}
}
if (msg.type === 'result' && msg.subtype === 'success') {
return { output: msg.result, cost: msg.total_cost_usd };
}
}
構造化された SDKMessage で型安全にメッセージを処理でき、total_cost_usd でコスト追跡、session_id でセッション復元も可能になる。Subagentsでパイプラインステージ(アイデア生成・実装・レビュー)を分離し、Hooksでレート制限検出をPostToolUseに統合する — CLIの文字列解析から脱却できる設計だ。
なお、Managed Agents APIはクラウド側でエージェントを実行する仕組みであり、Agent SDKのようにローカルマシン上でCLIプロセスを制御するものとは用途が異なる。常時稼働デーモンのようなユースケースではAgent SDKが適している。
本番運用のハマりポイントと対策
PermissionModeの選択基準: bypassPermissions はバッチ処理や自律デーモン向け。対話型アプリケーションでは plan モードで実行前にユーザー確認を挟むのが安全だ。
環境変数のクリーンアップ: Agent SDKは内部でCLIをspawnするため、CLAUDECODE_* / CLAUDE_CODE_* 系の環境変数が残っているとネスト検出でエラーになる。env オプションでクリーンな環境を渡すこと。
AsyncGeneratorのリソースリーク: for await...of を途中で break した場合、内部プロセスが残る可能性がある。AbortController を使って明示的に中断するのが安全だ。
const abortController = new AbortController();
const q = query({
prompt: '...',
options: { abortController },
});
// タイムアウト時の安全な中断
setTimeout(() => abortController.abort(), 10 * 60 * 1000);
バージョン固定: SDKはアクティブに開発が進んでおり、マイナーバージョンでも挙動が変わることがある。package.json では ^ ではなく固定バージョンの指定を推奨する。
Claude Agent SDKは単なるリネームではなく、CLIベースの設定から脱却してプログラマティックにエージェントの振る舞いを制御するための転換点だ。Subagentsでエージェントを責務ごとに分離し、Hooksでツール実行のライフサイクルを制御し、MCPツール統合で外部システムと接続する。CLI版の設定ベース機能(settings.json Hooks、.claude/agents/ YAML)との違いを理解した上で、プロジェクトの要件に応じてSDKレベルの制御を活用してほしい。
