【Claude Code 連載 第105回】Python SDK リファレンスの歩き方(agent-sdk/python)

スポンサーリンク
【Claude Code 連載 第105回】Python SDK リファレンスの歩き方(agent-sdk/python) 用語解説
【Claude Code 連載 第105回】Python SDK リファレンスの歩き方(agent-sdk/python)
この記事は約34分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

スポンサーリンク

背景

この連載では、Claude Codeの公式ドキュメントを1ページずつ読み解いていきます。公式ドキュメントは情報が網羅されている分、「結局どの機能を、どんな場面で使えばいいのか」は自分で考える必要があり、読むのに意外と時間がかかります。そこで、私が実務で使うために読み込んだ内容を「使う場面→実例コード」の順に整理して残していくことにしました。専門家の解説というより、一次情報を読んだ記録です。推測や動作を確認していない部分には、その都度そう書きます。

公式ドキュメント:https://code.claude.com/docs/ja/agent-sdk/python

1. 一言でいうと

Python 版 Agent SDK のすべての関数・クラス・型を並べた、公式の完全リファレンスです。中心は、1回きりの問い合わせに使う query() と、会話を続けるための ClaudeSDKClient クラスの2本立てで、そこに ClaudeAgentOptions、メッセージとコンテンツブロックの型、例外の型、フックの型、組み込みツールの入出力の型、サンドボックスの設定が続きます。第104回の TypeScript リファレンスと同じ役割のページですが、Python 特有の作法と、TypeScript とは既定値が逆になっている箇所があり、そこを知らずに使うと危ない、というのがこの回の要点です。

原文は約4,000行、コードブロックは169本あります。第104回と同じく、この記事は地図と、これまでの回に出てこなかった情報、そしてTypeScript との違いに絞ります。


2. どういう場面で役立つか

シーン1:query() と ClaudeSDKClient のどちらを使うか決める

これまでの回で何度も「Python では ClaudeSDKClient が必要」と書いてきました(第79回の制御メソッド、第84回の MCP の再接続、第91回の複数ターンなど)。このページの比較表で、どちらを使うかを一度で判断できます。

シーン2:例外とエラーの結果を正しく見分ける

ResultError の属性と、「subtype が success なのに失敗している」場合の見分け方が、このページで明確に書かれています。第97回・第98回で見た判定のしかたを、もう一段正確にする情報です。

シーン3:TypeScript で書いた知識を Python に移す

TypeScript と Python でオプション名、取り出し方、既定値が違う箇所を確かめる場面です。特にサンドボックスの既定値は逆なので、移植のときに見落とすと守りが外れます(3-6)。

不要・向かないケース

  • TypeScript を使っている:第104回の TypeScript リファレンスを引きます。
  • SDK を初めて使う:辞書なので最初に読むものではありません。第78回のクイックスタートと機能ごとのガイドから入ります。
  • セッションストアを使う関数を探している:第93回で見た list_sessions_from_store() などのストア対応の関数は、今回貼られたページの見出しには出てきません。それらは第93回のセッションストレージのページで確かめる必要があります(筆者の確認)。

3. コードと仕組みの解説

原文の fenced コードブロックは169本です。大半は型の定義なので、この記事では9本だけを引用し、残りは表と散文に圧縮します。引用するのは、(1) インストールと中心の定義(query()、ClaudeSDKClient)、(2) このページで初めて出てくる書き方(TypedDict によるツールの入力スキーマ、割り込み)、(3) エラーの扱い(ResultError とその使い方)、(4) TypeScript と既定値が違うサンドボックスの例です。コードは原文から機械的に抜き出したもので、入れ子に由来する先頭インデントを外した以外は変えていません。原文は注記で、シグネチャのブロックや、裸の async for/async with の断片は説明用で、実行するには async def main(): ... で包んで asyncio.run(main()) を呼ぶ必要がある、と書いています。

3-0. この記事に出てくる主なバージョン要件

機能バージョン
ResultError、forward_subagent_text、ToolAnnotations の snake_case 名と型付きの maxResultSizeCharsPython Agent SDK 0.2.140 以降
resume_session_at・resume_drops_turn、ResultMessage の originPython Agent SDK 0.2.137 以降
verbatim_promptsPython Agent SDK 0.2.158 以降と Claude Code v2.1.248 以降
skills の不正な名前を事前に拒否Python Agent SDK 0.2.129 以降
model_usage の thinkingTokensPython Agent SDK 0.2.150 以降
maxResultSizeChars をキーワード引数で渡せるPython Agent SDK 0.1.55〜0.2.139

3-1. インストール

原文は、仮想環境に入れるよう書いています。最近の Debian・Ubuntu・Homebrew の Python では、システムの Python に pip install すると error: externally-managed-environment で失敗するからです。

python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk

uv や Windows の PowerShell、API キーの設定は、第78回のクイックスタートに回されています。

3-2. query() と ClaudeSDKClient の選び方

原文の比較表です。

機能query()ClaudeSDKClient
セッション既定で新しいセッションを作る同じセッションを使い回す
会話1回のやりとり同じ文脈での複数のやりとり
接続自動で管理手動で制御
ストリーミング入力対応対応
割り込み非対応対応
フック対応対応
カスタムツール対応対応
会話を続けるcontinue_conversation か resume で手動自動
用途1回きりのタスク続く会話

チャット画面のような対話型のアプリや、次の動きが Claude の応答次第で変わる場合は ClaudeSDKClient を使う、というのが原文の指針です。

query() の定義です。

async def query(
    *,
    prompt: str | AsyncIterable[dict[str, Any]],
    options: ClaudeAgentOptions | None = None,
    transport: Transport | None = None
) -> AsyncIterator[Message]

transport は、CLI のプロセスとの通信を差し替えるための引数です(3-4)。各 query() 呼び出しは、continue_conversation=True か resume を渡さない限り、前のやりとりを覚えていません(第87回)。

ClaudeSDKClient

原文は ClaudeSDKClient を「TypeScript SDK の query() 関数が内部でどう動いているかの Python 版」と説明しています。第104回の TypeScript の Query オブジェクトが持っていた制御メソッドは、Python ではこのクラスにあります。

class ClaudeSDKClient:
    def __init__(self, options: ClaudeAgentOptions | None = None, transport: Transport | None = None)
    async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None
    async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None
    async def receive_messages(self) -> AsyncIterator[Message]
    async def receive_response(self) -> AsyncIterator[Message]
    async def interrupt(self) -> None
    async def set_permission_mode(self, mode: PermissionMode) -> None
    async def set_model(self, model: str | None = None) -> None
    async def rewind_files(self, user_message_id: str) -> None
    async def get_mcp_status(self) -> McpStatusResponse
    async def get_context_usage(self) -> ContextUsageResponse
    async def reconnect_mcp_server(self, server_name: str) -> None
    async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None
    async def stop_task(self, task_id: str) -> None
    async def get_server_info(self) -> dict[str, Any] | None
    async def disconnect(self) -> None

TypeScript の Query のメソッドとの対応を整理します。

Python(ClaudeSDKClient)TypeScript(Query)備考
interrupt()interrupt()ストリーミングモードでのみ
set_permission_mode()・set_model()setPermissionMode()・setModel()set_model に None を渡すと既定のモデルに戻る
rewind_files()rewindFiles()enable_file_checkpointing=True が必要(第88回)
get_mcp_status()・reconnect_mcp_server()・toggle_mcp_server()mcpServerStatus()・reconnectMcpServer()・toggleMcpServer()第84回
get_context_usage()getContextUsage()/context と同じデータ
stop_task()stopTask()停止すると "stopped" の TaskNotificationMessage が続く
get_server_info()initializationResult()コマンドや出力スタイルなどの初期化情報
(なし)applyFlagSettings()・updateSettings()・readFile()・reloadPlugins() などTypeScript だけにある

表は筆者の整理です。Python には、第104回で見た startup()・prewarm()・resolveSettings() に当たる関数も、このページには見当たりません(第93回で「startup() は Python に無い」と見たとおり)。

ClaudeSDKClient は async with で使えば接続の管理を任せられます。原文はここで1つ注意しています。メッセージを読んでいる途中で break で抜けるのは避ける。asyncio の後片付けの問題を起こしうるので、最後まで読み切るか、目的のものを見つけたかをフラグで記録します。

割り込みの後は「読み切る」

このページで初めて詳しく出てくるのが、割り込みの扱いです。

import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage


async def interruptible_task():
    options = ClaudeAgentOptions(allowed_tools=["Bash"], permission_mode="acceptEdits")

    async with ClaudeSDKClient(options=options) as client:
        # Start a long-running task
        await client.query("Count from 1 to 100 slowly, using the bash sleep command")

        # Let it run for a bit
        await asyncio.sleep(2)

        # Interrupt the task
        await client.interrupt()
        print("Task interrupted!")

        # Drain the interrupted task's messages (including its ResultMessage)
        async for message in client.receive_response():
            if isinstance(message, ResultMessage):
                print(f"Interrupted task: terminal_reason={message.terminal_reason!r}")
                # terminal_reason is "aborted_streaming" or "aborted_tools"
                # for interrupted turns

        # Send a new command
        await client.query("Just say hello instead")

        # Now receive the new response
        async for message in client.receive_response():
            if isinstance(message, ResultMessage) and message.subtype == "success":
                print(f"New result: {message.result}")


asyncio.run(interruptible_task())

原文の注記が要点です。interrupt() は停止の合図を送るだけで、メッセージのバッファは空にしません。 割り込まれたタスクがすでに出したメッセージ(その ResultMessage を含む)はストリームに残ります。だから例は、割り込みの後でいったん receive_response() で読み切り(ドレイン)、それから新しい query() を送っています。割り込みの直後に新しい問い合わせを送って receive_response() を1回だけ呼ぶと、受け取るのは割り込まれたタスクのメッセージで、新しい問い合わせの応答ではありません。

割り込まれたターンの terminal_reason は "aborted_streaming" か "aborted_tools" です。原文によれば、interrupt=True 付きの PermissionResultDeny を返す権限コールバックも、同じく中止の原因になります。

原文の「高度な権限制御」の例には、コメントで「ゲートしたいツールを allowed_tools にも書かない。許可ルールは can_use_tool より先に呼び出しを承認する」と書かれています。第80回の評価順序の、最もよくある落とし穴です。

3-3. ツール:入力スキーマに TypedDict が使える

第83回で、Python のツールの入力スキーマは「名前→型の dict(全部必須、列挙は書けない)か、完全な JSON Schema」と見ました。このページには、3つ目の書き方として TypedDict が載っています。

from typing import Annotated, Any, NotRequired, TypedDict
from claude_agent_sdk import tool


class ForecastArgs(TypedDict):
    latitude: Annotated[float, "Latitude coordinate"]
    hours: NotRequired[Annotated[int, "How many hours of forecast to return"]]


@tool("get_forecast", "Get the hourly forecast for a location", ForecastArgs)
async def get_forecast(args: dict[str, Any]) -> dict[str, Any]:
    hours = args.get("hours", 12)
    return {"content": [{"type": "text", "text": f"{hours}-hour forecast for {args['latitude']}"}]}

NotRequired で包んだキーは required から外れるので、省略できる引数を型で表せます。Annotated[type, "description"] の形で包むと、フィールドの説明も付けられます(簡単な dict 形式でも使える)。Python 3.11 以降は typing から、3.10 は typing_extensions から TypedDict と NotRequired を import します(3.10 では SDK が typing_extensions を入れる)。

第83回では「省略可能な引数はスキーマから外して説明文に書き、args.get() で読む」のが Python の作法でしたが、TypedDict を使えば、省略可能であることをスキーマとして Claude に伝えられます(筆者の対応づけ)。例でも hours を args.get("hours", 12) で読んでいる点は同じです。

ツールの注釈 ToolAnnotations には、maxResultSizeChars という Claude Code 独自の項目があります。テキストの結果を、ファイルに保存せずに会話の中にそのまま置いておく文字数の上限(最大 500,000)で、第84回で見た「大きな結果はファイルに保存される」の、ツール単位の調整です。原文は、注釈はヒントであってセキュリティの判断に使ってはならないとも書いています。

一方、Python の create_sdk_mcp_server() の引数は name・version・tools の3つで、第104回の TypeScript にあった instructions・alwaysLoad・timeout はありません。tool() にも searchHint・alwaysLoad がありません(筆者の比較)。

3-4. 型:dataclass と TypedDict の違い

原文は型の節の冒頭で、Python 特有の注意を置いています。

種類例実行時の姿読み方
@dataclassResultMessage、AgentDefinition、TextBlockオブジェクト属性:msg.result
TypedDictThinkingConfigEnabled、McpStdioServerConfig、SyncHookJSONOutputただの辞書キー:config["budget_tokens"](config.budget_tokens ではない)

ClassName(field=value) の書き方はどちらでも通りますが、属性を持つオブジェクトになるのは dataclass だけです。TypedDict の値を属性で読もうとすると失敗するので、型の定義を見てどちらかを確かめる必要があります。第92回で見た model_usage の値(ModelUsage)も TypedDict で、キーは CLI から来たままの camelCase(inputTokens など)です。

ClaudeAgentOptions の、このページで初めて出てくる項目

ClaudeAgentOptions は約50の項目を持つ dataclass です。これまでの回で扱っていないもの、または新しい注意が付いたものを拾います。

項目内容
userPOSIX で、Claude Code のサブプロセスを動かす OS のユーザー。親プロセスの環境(HOME を含む)を保ち、cwd で動く
max_buffer_sizeCLI の標準出力をバッファするときの最大バイト数
mcp_servers辞書のほか、設定ファイルへのパスも渡せる
session_id有効な UUID であること。fork_session が無い限り、continue_conversation や resume と組み合わせられない
setting_sourcesskills を設定してこの項目を設定しないと、ユーザーとプロジェクトのソースだけが読まれる。ローカル設定も読ませたいなら明示する
system_prompt文字列・プリセット・{"type": "custom", "prompt": "..."} に加え、{"type": "file", "path": "..."} で大きなプロンプトをファイルから読む(第86回の Argument list too long 対策)
task_budgetAPI 側のトークン予算。task-budgets-2026-03-13 のベータヘッダーで送られる
debug_stderr非推奨で、SDK は値を無視する。stderr コールバックを使う
env引き継いだ環境の上に重ねる(TypeScript は置き換え)

表は筆者の整理です。setting_sources の注意は、第100回の「省略は ["user", "project", "local"] と同じ」に対する Python の例外です。skills を指定しただけで、.claude/settings.local.json と CLAUDE.local.md が読まれなくなることになります(筆者の指摘)。ローカルの権限ルールやフックに頼っている場合は、setting_sources を明示する必要があります。

debug_stderr が無視される点も、第97回の「本当のエラー出力は stderr コールバックで取る」と合わせて覚えておく価値があります。古いコードで debug_stderr に頼っていると、何も記録されません。

Transport は、CLI のプロセスとの通信をリモート接続などに差し替えるための抽象基底クラスです。原文は低レベルの内部 API で、将来インターフェースが変わりうると警告しています。

ToolPermissionContext(can_use_tool の第3引数)には、第81回では出てこなかった title(「Claude wants to read foo.txt」のような完全な確認文)、display_name(「Read file」のような短い名詞句。ボタンの文言向き)、description(確認画面の副題)があります。確認画面を自分で作るときの文言に使えます。

3-5. メッセージと例外

「success なのに失敗」を見分ける

このページで最も重要な情報の1つが、ResultMessage の is_error の説明です。

  • is_error は、error_* の subtype では常に True。
  • subtype="success" でも、最後のモデルへのリクエストが失敗した場合は True。エージェントのループは完了したが、最後の API 呼び出しがエラーを返した、という状態です。
  • そのとき result には API エラーの文字列が入ることがありますが、空のこともあるので、api_error_status と直前の AssistantMessage を確かめます。

Python の dataclass は全種類の結果を1つの形にまとめているので、その subtype に関係のないフィールドは None になります。

例外の側では、ResultError がこれを反映しています。

class ResultError(ProcessError):
    subtype: str | None  # "error_max_turns", "error_during_execution", ...; "success" when the run ended on a failed request
    errors: list[str]  # an empty list when the result message reported none
    result: str | None
    api_error_status: int | None
    terminal_reason: str | None  # "max_turns", "api_error", ...; check this before subtype
    session_id: str | None
    data: dict[str, Any]  # the raw result message payload

コメントにあるとおり、subtype の前に terminal_reason を確かめます。最後のリクエストが失敗した(API エラーなど)場合、Claude Code は subtype を "success" と報告し、原因を terminal_reason("api_error" など)に書きます。設定した上限(max_turns や max_budget_usd)で終わった場合は error_* の subtype になります。

筆者の指摘:第97回・第98回では「成功の判定は subtype が success であることと、structured_output があることの両方」と見ました。このページの情報を加えると、subtype が success でも is_error が True なら失敗です。結果の判定は、subtype・is_error・terminal_reason を合わせて見るのが確実です。原文の書き方からは TypeScript の結果メッセージも同じ形(is_error と terminal_reason を持つ)なので、言語を問わず当てはまると考えられます。

例外の型の階層は次のとおりです。

例外親いつ
ClaudeSDKErrorExceptionすべての SDK エラーの基底
CLIConnectionErrorClaudeSDKErrorClaude Code に接続できない
CLINotFoundErrorCLIConnectionErrorCLI が入っていない・見つからない
ProcessErrorClaudeSDKErrorプロセスが失敗した(exit_code・stderr)
ResultErrorProcessErrorエラーの結果メッセージで終わった(0.2.140 以降)
CLIJSONDecodeErrorClaudeSDKErrorJSON の解析に失敗(line・original_error)

表は原文の定義から筆者が整理したものです。CLINotFoundError が CLIConnectionError の子である点は、第97回の「接続の失敗」と「見つからない」を同じ CLIConnectionError の except 節で受けられる、ということです。

原文のエラー処理の例は、4種類の例外を順に受けます。

import asyncio

from claude_agent_sdk import (
    query,
    CLINotFoundError,
    ProcessError,
    ResultError,
    CLIJSONDecodeError,
)


async def main():
    try:
        async for message in query(prompt="Hello"):
            print(message)
    except CLINotFoundError:
        print(
            "Claude Code CLI not found. Try reinstalling: pip install --force-reinstall claude-agent-sdk"
        )
    # Catch ResultError before ProcessError, which it subclasses. Its message
    # carries the error text. A failed final request, such as an API error,
    # arrives with subtype "success", so branch on terminal_reason first.
    except ResultError as e:
        if e.terminal_reason == "api_error":
            print(f"API request failed: {e}")
        else:
            print(f"Query ended with an error result ({e.terminal_reason or e.subtype}): {e}")
    except ProcessError as e:
        print(f"Process failed with exit code: {e.exit_code}")
    except CLIJSONDecodeError as e:
        print(f"Failed to parse response: {e}")


asyncio.run(main())

ResultError を ProcessError より先に書いているのは、第97回のとおり、子のクラスを先に捕まえないと親の節で受け取られてしまうからです。

その他のメッセージの注意

型注意
SystemMessage専用の dataclass を持たない subtype はすべてこれで届く(subtype と data)。ターン間のセッションの状態を追うには CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS=1 を設定し、session_state_changed の message.data["state"] を読む。receive_response() は ResultMessage で止まり、状態のメッセージは結果の後に来ることがあるので receive_messages() で読む
StreamEventparent_tool_use_id は常に None(第101回)
ResultMessage の originストリーミング入力で、自分のプロンプトへの結果(None か {"kind": "human"})と、バックグラウンドタスクの通知などで差し込まれたターンの結果を見分ける
ResultMessage の usageメインループだけ。トークンと費用の集計は model_usage を使う(第92回)

HookEvent は Python では10種類(PreToolUse・PostToolUse・PostToolUseFailure・UserPromptSubmit・Stop・SubagentStop・PreCompact・Notification・SubagentStart・PermissionRequest)で、第82回・第104回で見た TypeScript の33種類より少ないことが、型の定義でも確かめられます。

3-6. サンドボックス:既定値が TypeScript と逆

このページで最も注意すべき違いがここです。原文の注記をそのまま要約します。

  • サンドボックスはプラットフォームの対応に依存し、Linux では bubblewrap や socat などが要る。
  • Python では既定で、enabled が True でもサンドボックスが起動できない場合、コマンドはサンドボックスなしで実行され、stderr に警告が出るだけ。
  • これは TypeScript SDK と違う。TypeScript では failIfUnavailable が既定で true(第104回)。
  • 止めたいなら、サンドボックスの設定に "failIfUnavailable": True を入れる。SDK は宣言されたフィールドと一緒に failIfUnavailable を Claude Code に渡し、起動できなければ error_during_execution の結果を出す(1回きりの query() ならその後に例外)。

原文の使用例は、この failIfUnavailable を明示しています。

import asyncio

from claude_agent_sdk import query, ClaudeAgentOptions

sandbox_settings = {
    "enabled": True,
    "autoAllowBashIfSandboxed": True,
    "failIfUnavailable": True,
    "network": {"allowLocalBinding": True},
}


async def main():
    try:
        async for message in query(
            prompt="Build and test my project",
            options=ClaudeAgentOptions(sandbox=sandbox_settings),
        ):
            print(message)
    except Exception as error:
        # A single-shot query() raises after yielding an error result,
        # such as when failIfUnavailable is set and the sandbox can't start.
        print(f"Session ended with an error: {error}")


asyncio.run(main())

SandboxSettings は TypedDict で、宣言されているのは enabled・autoAllowBashIfSandboxed・excludedCommands・allowUnsandboxedCommands・network・ignoreViolations・enableWeakerNestedSandbox です。failIfUnavailable と filesystem は宣言には入っていませんが、SDK が Claude Code に渡す、と原文は書いています。サンドボックスのコマンドに特定のファイルを書かせないなら、filesystem の denyWrite に絶対パス(例:"/home/user/project/.env")を書きます。

autoAllowBashIfSandboxed と allowUnsandboxedCommands の既定が True なのは TypeScript と同じです。サンドボックスの外での実行を求める要求(dangerouslyDisableSandbox)は権限システムに回るので、can_use_tool で判断します。

import asyncio
from claude_agent_sdk import (
    query,
    ClaudeAgentOptions,
    HookMatcher,
    PermissionResultAllow,
    PermissionResultDeny,
    ToolPermissionContext,
)


def is_command_authorized(command: str | None) -> bool:
    # Replace with your own authorization logic
    return False



async def can_use_tool(
    tool: str, input: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
    # Check if the model is requesting to bypass the sandbox
    if tool == "Bash" and input.get("dangerouslyDisableSandbox"):
        # The model is requesting to run this command outside the sandbox
        print(f"Unsandboxed command requested: {input.get('command')}")

        if is_command_authorized(input.get("command")):
            return PermissionResultAllow()
        return PermissionResultDeny(
            message="Command not authorized for unsandboxed execution"
        )
    return PermissionResultAllow()


# Required: dummy hook keeps the stream open for can_use_tool
async def dummy_hook(input_data, tool_use_id, context):
    return {"continue_": True}


async def prompt_stream():
    yield {
        "type": "user",
        "message": {"role": "user", "content": "Deploy my application"},
    }


async def main():
    async for message in query(
        prompt=prompt_stream(),
        options=ClaudeAgentOptions(
            sandbox={
                "enabled": True,
                "allowUnsandboxedCommands": True,  # Model can request unsandboxed execution
            },
            permission_mode="default",
            can_use_tool=can_use_tool,
            hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
        ),
    ):
        print(message)


asyncio.run(main())

この例には、第81回で見た Python の「必須の回避策」がそのまま入っています。can_use_tool を使うために、{"continue_": True} を返すだけのダミーの PreToolUse フック(コメントは「can_use_tool のためにストリームを開いたままにする」)と、プロンプトをジェネレーター(prompt_stream())で渡す形です。認可の判定 is_command_authorized は、例では常に False を返すので、サンドボックスの外での実行はすべて拒否されます。原文の警告は第104回と同じで、bypassPermissions と allowUnsandboxedCommands が両方有効だと、モデルは確認なしにサンドボックスの外へ静かに抜け出せます。

筆者の指摘:Python では、守りを効かせるために明示すべき値が TypeScript より1つ多いことになります。

確認する値Python の既定TypeScript の既定守りとして使うなら
failIfUnavailable実質 False(起動できなければサンドボックスなしで実行)trueTrue を明示
allowUnsandboxedCommandsTruetrueFalse を明示
autoAllowBashIfSandboxedTruetrueサンドボックスが確実に動く前提でのみ任せる

表は筆者の整理です。特に1行目は、サンドボックスが入っていない環境(コンテナの中に bubblewrap が無い、など)で、警告だけを出してサンドボックスなしで Bash を動かし、しかも autoAllowBashIfSandboxed の既定で自動承認する、という組み合わせになりえます。第97回で「stderr コールバックを最初から付ける」としたのは、こうした警告を拾うためでもあります。TypeScript から Python に移植するときは、必ずこの表を確かめるべきです。

3-7. 圧縮したその他の節

節何が載っているかいつ引くか
セッション操作の関数list_sessions()・get_session_messages()・get_session_info()・rename_session()・tag_session()・fork_session()第87回の操作を Python で書くとき
AgentDefinition・McpServerConfig・SdkPluginConfigサブエージェント・MCP サーバー・プラグインの設定の型(SdkPluginConfig は type と path だけで、TypeScript の skipMcpDiscovery は無い)第89回・第84回・第99回
ThinkingConfig・EffortLevel・TaskBudget思考と effort の設定、タスク予算モデルの振る舞いを調整するとき
コンテンツブロック型TextBlock・ThinkingBlock・ToolUseBlock・ToolResultBlockAssistantMessage・UserMessage の中身を読むとき(第102回)
Hook 型HookCallback・HookContext・HookMatcher・各入力型・HookJSONOutput第82回
ツール入出力型組み込みツールごとの入力と出力(Agent・Bash・Read・TaskCreate など)tool_use の入力や結果を読むとき
継続的な会話の例ClaudeSDKClient を1つ接続したまま、new で切断・再接続して新しいセッション、exit で終了する対話プログラムチャット型のアプリの雛形

表は筆者の整理です。継続的な会話の例は、disconnect() してから connect() し直すと前の文脈が消えて新しいセッションになる、という使い方を示しています。


4. まとめ

  • Python SDK の入口は query()(1回きり、割り込み不可)と ClaudeSDKClient(会話を続ける、割り込み可)。ClaudeSDKClient は TypeScript の query() の内部の Python 版で、制御メソッドはここにある。startup()・prewarm()・resolveSettings()・applyFlagSettings() に当たるものはこのページに無い。
  • インストールは仮想環境で(システムの Python は externally-managed-environment で失敗)。
  • interrupt() はバッファを空にしない。割り込みの後は receive_response() で読み切ってから次を送る。break で途中で抜けない。
  • ツールの入力スキーマに TypedDict(NotRequired・Annotated) が使え、省略可能な引数を表せる。maxResultSizeChars で結果のインライン上限を調整。
  • dataclass は属性、TypedDict は辞書のキーで読む。
  • ClaudeAgentOptions:user、mcp_servers にファイルパス、skills だけ設定すると setting_sources はユーザーとプロジェクトのみ、debug_stderr は無視される、env は重ねる。
  • subtype="success" でも is_error=True なら失敗。ResultError は terminal_reason を先に見る。例外は ResultError を ProcessError より先に捕まえる。
  • HookEvent は Python では10種類。
  • サンドボックスは起動できないと既定でサンドボックスなしで動く(TypeScript と逆)。守りにするなら failIfUnavailable を True、allowUnsandboxedCommands を False と明示する。

次回予告

次回は 「Agent SDK の例」(agent-sdk/examples) を取り上げる予定です。

第76回から続けてきた Agent SDK の回は、機能ごとのガイドと、今回までの2つのリファレンスでほぼ一巡しました。次回は、これらの機能を組み合わせて実際に動くエージェントを作った例を通じて、ここまでの部品が1つのアプリの中でどう組み合わさるのかを見ていきます。


よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました