Claude Code は対話で使う分には強力ですが、「夜中にcronで回したい」「自分のFastAPIに組み込みたい」となると話が変わります。私もそこで詰まりました。
そこで触り始めたのが Claude Agent SDK です。Claude Code とほぼ同じエージェントループを、自分のPythonコードから呼べる公式SDK。先週、社内向けの依存ライブラリ監査ボットを2時間半でプロトタイプできて、正直拍子抜けしました。
この記事では、Agent SDK の最小構成から、ツール登録、permission 設計、unattended 運用での落とし穴までを、実際に動いたコードベースでまとめます。
結論:Claude Agent SDK は「Claude Code の中身をライブラリ化したもの」
先に結論から書きます。Claude Agent SDK は新しいフレームワークというより、Claude Code の agent loop を Python / TypeScript から直接ドライブできるようにした薄いラッパーです。
公式リポジトリの説明によると、SDK は CLI バイナリを内包した単一の pip パッケージとして配布されており、別途 CLI を入れる必要はありません。Anthropic の Messages API を直接叩くのとは別物で、ファイル操作・シェル・WebSearch などの組み込みツールを最初から持っているのが特徴です。
つまり、こういう用途にハマります。
- スケジュール実行(cron / GitHub Actions)で動かしたいエージェント
- 自分の SaaS や社内ツールに組み込みたい AI 機能
- Slack bot のように、対話 UI を自分で持ちたいケース
逆に「ターミナルで自分が叩く」だけなら、素の Claude Code CLI で十分です。SDK を選ぶ理由は headless で動かしたい から、ここに尽きます。
なぜ素のMessages APIではなくAgent SDKを使うのか
私が最初にハマったのがここでした。「Anthropic の Python SDK(anthropic パッケージ)で tool_use ループを自分で書けばいいのでは?」と。
結論、書けるけど書きたくないです。
Messages API を直接使うと、tool_use ブロックを受け取ってツール実行して結果を tool_result で返す、というループを自分で組む必要があります。さらに context compaction、permission gating、streaming、MCP クライアント実装、エラーリトライ、これら全部を自前で抱えることになります。
Agent SDK はこの「ループ周りのグルーコード」を肩代わりしてくれます。実際に SDK ベースで書き直したとき、500行近くあったループ処理が query() の async for 一発で消えました。これは想像以上に効きました。
もう一点、地味だけど効いてくる差があります。anthropic パッケージと claude-agent-sdk は別物 という点。クラス名も async パターンも違うので、ネット上のチュートリアルを混ぜて読むと事故ります。新規で組むなら最初から SDK 一本に寄せたほうが安全です。
Claude Agent SDK のインストールと最小コード
環境要件はシンプルで、Python 3.10 以上(3.10〜3.13 サポート)、それと Node.js 18+ があれば動きます。Node が必要なのは、内部の CLI が node 製だからです。
pip install claude-agent-sdk
export ANTHROPIC_API_KEY="sk-ant-..."
これだけで動きます。CLI を別途 install する必要はありません。最小の動作確認はこう。
import anyio
from claude_agent_sdk import query
async def main():
async for message in query(prompt="What is 2 + 2?"):
print(message)
anyio.run(main)
query() は async iterator を返すので、async for で1メッセージずつストリームできます。各メッセージは Claude の思考、ツール呼び出し、ツール結果、最終出力のどれかです。
古い claude-code-sdk パッケージを触ったことがある人向けの注意。ClaudeCodeOptions は ClaudeAgentOptions にリネームされています。2025年9月のリブランディングで、コード用途以外(法務 bot、SRE bot など)にも使われ始めたことが理由らしいです。古いコードはそのままだと動きません。
ClaudeAgentOptions で挙動を制御する
ここからが本番です。query() に options を渡して、エージェントの権限・モデル・思考予算を絞っていきます。
from claude_agent_sdk import query, ClaudeAgentOptions
options = ClaudeAgentOptions(
system_prompt="You are a senior Python reviewer. Follow PEP 8.",
allowed_tools=["Read", "Edit", "Glob", "Grep"],
permission_mode="acceptEdits",
cwd="/path/to/project",
max_turns=20,
)
async for msg in query(prompt="Refactor utils.py for readability", options=options):
print(msg)
押さえるべきは、unattended 運用で 絶対に省略してはいけない2つの設定 です。
max_turns:暴走防止。私は最初 20 にしていますpermission_mode:権限ポリシーの明示
permission_mode には default / acceptEdits / plan / dontAsk / bypassPermissions があり、特に cron で回すなら dontAsk が安全 です。事前に許可していないツール呼び出しは、プロンプトを出さずに deny します。bypassPermissions は名前の通り全部素通しなので、本番で使うのは怖いです。
もうひとつ、2026年4月時点で 1Mトークンコンテキストのβヘッダ(context-1m-2025-08-07)は廃止されています。Sonnet 4.6 や Opus 4.6 以降は標準で 1M context を持っているので、ヘッダ無しでそのまま使えます。古いサンプルコードをコピペすると、エラーで返ってくるので注意してください。
カスタムツールを@toolデコレータで足す
ここが SDK のいちばん気持ちいいところでした。
組み込みツール(Read / Write / Edit / Bash / Glob / Grep / WebSearch / WebFetch)に加えて、自分の Python 関数をそのままツールとして登録できます。仕組みは in-process の MCP サーバー で、外部プロセスを立てなくていいので起動オーバーヘッドがゼロです。
from claude_agent_sdk import (
tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient,
)
@tool("fetch_order", "Fetch order status by order ID", {"order_id": str})
async def fetch_order(args):
# 実際は DB や内部 API を叩く
return {
"content": [
{"type": "text", "text": f"Order {args['order_id']} is shipped"}
]
}
server = create_sdk_mcp_server(
name="internal-tools",
version="1.0.0",
tools=[fetch_order],
)
options = ClaudeAgentOptions(
mcp_servers={"internal": server},
allowed_tools=["mcp__internal__fetch_order"],
)
ツール権限の名前は mcp__<server-name>__<tool-name> という規則です。これを allowed_tools に書かないと、登録しても Claude が呼べません。最初これでハマって30分溶かしました。
地味だけど効くTipsをひとつ。ツールの description は関数名より重要 です。Claude はスキーマじゃなくて自然言語の説明文を読んでツールを選びます。“Order fetcher” より “Fetch order status by order ID” のように命令形+目的語で書いたほうが、選択精度が体感で変わります。
hookで「rm -rf /」を確実にブロックする
permission_mode だけでは不安なケース、あります。たとえば Bash ツールを許可した瞬間、理論上は rm -rf / を打たれる可能性が残ります。
そこで hook の出番です。SDK では Python 関数として hook を渡せます。CLI 版の settings.json と違って、シェルじゃなく直接コールバックを書けるのが楽。
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
async def validate_bash(input_data, tool_use_id, context):
if input_data["tool_name"] == "Bash":
cmd = input_data["tool_input"].get("command", "")
if "rm -rf /" in cmd or "sudo" in cmd:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command blocked",
}
}
return {}
options = ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Edit"],
hooks={"PreToolUse": [HookMatcher(hooks=[validate_bash])]},
)
フック可能なイベントは PreToolUse / PostToolUse / Stop / SessionStart / SessionEnd / UserPromptSubmit などがあります。ここで一つクセを覚えておくと、SessionStart / SessionEnd を SDK のコールバックとして登録できるのは TypeScript だけで、Python では settings ファイル側のシェルフックで定義する ことになります。クロス言語の細かい差なので、ドキュメントは都度確認したほうが安全です。
もう一点、Python のフック関数では async_ というアンダースコア付きの名前を使います。CLI 送信時に自動で async に変換される仕様で、これは Python の予約語回避のためです。
2026年に追加されたAdvisor toolという選択肢
2026年5月の Code with Claude で、新しい組み合わせが発表されました。Advisor tool です。
これは「Opus を advisor(計画・レビュー担当)、Sonnet を executor(実行担当)としてペアにする」API 機能で、ベータヘッダ advisor-tool-2026-03-01 で利用できます。Opus がタスクを計画してレビューし、Sonnet が各ステップを実行する、という構造。
なぜこれが効くかというと、シニアエンジニアの実際の働き方に近いからです。計画とレビューはモデル能力が要る一方、コード生成のような実行は throughput が要る。性質が違う仕事を別のモデルで分担するほうが、コスト・品質の両面で理にかなっています。
私のチームで試した感覚では、長めの refactor タスク(3〜5ファイル横断)で Opus 単体より体感のレビュー品質が上がりました。コスト面は計測中なので断言は避けますが、執行部分が Sonnet なのでフル Opus よりは確実に下がるはず、というのが Anthropic 公式の説明です。
headless運用で詰まる3つの落とし穴
3週間ほど SDK で動くエージェントを回してみて、踏んだ落とし穴を共有します。
1. CLINotFoundError で起動失敗
他のパッケージと環境を共有していると、bundle されたCLIがPATHで見つからないことがあります。ClaudeAgentOptions(cli_path="/path/to/claude") で明示するか、curl -fsSL https://claude.ai/install.sh | bash で system-wide に入れて回避します。
2. query() は会話を覚えない
デフォルトでは query() を呼ぶたびに新しいセッションが始まります。前回の続きをやらせたいなら continue_conversation=True を渡すか、resume に session_id を渡す必要があります。これに気づかず「なぜ毎回ゼロから自己紹介してくるんだ…」と1時間悩みました。
3. ストリーム停止のタイムアウト挙動
2026年6月の Claude Code 更新で、API無音時の警告メッセージは20秒沈黙してから出るようになりました(以前は10秒)。つまり「20秒程度応答が来ない」のは正常範囲です。 unattended スクリプト側でこれより短いタイムアウトを切ると、無駄に再起動してしまいます。
まとめ:今日からやる3つのアクション
ここまでで Agent SDK の全体像と、unattended で動かすための最低限の設計が見えたはずです。最後に、今日から手を動かすための具体的な3ステップを置いておきます。
pip install claude-agent-sdkしてquery()のhello worldを動かす:5分で終わります。動いた瞬間に「これは Claude Code を Python に閉じ込めただけだ」と腹落ちするはず@toolデコレータで自分の業務関数を1つだけツール化する:DB読み取り関数や社内API ラッパーが現実的。description は命令形で書くpermission_mode="dontAsk"+ PreToolUse hook の二段構えで cron に乗せる:いきなりbypassPermissionsで本番に流すと事故ります。私もヒヤッとしました
Agent SDK は「Claude Code を1日中触っている人ほど、その延長として組み込める」設計になっています。CLI で使い慣れた permission / hook / subagent の概念がそのまま API で出てくるので、学習コストは思ったより低いはずです。
夜中に勝手に動いてくれるエージェント、一度作るとやめられなくなります。
参考リンク
- Claude Agent SDK reference - Python (Anthropic 公式) — Python SDK の関数・型・クラスの公式リファレンス
- Quickstart - Claude Agent SDK (Anthropic 公式) — バグ修正エージェントを作る公式チュートリアル
- claude-agent-sdk-python (GitHub) —
@toolデコレータと in-process MCP の実装例 - Code execution tool - Claude API Docs — Python/bash サンドボックス実行ツールの仕様
- Releasebot - Claude Code Updates June 2026 — 2026年6月のCLI更新内容(stream-stall 20秒化など)