Was this page helpful?
pip install claude-agent-sdkquery() und ClaudeSDKClientDas Python SDK bietet zwei Möglichkeiten, um mit Claude Code zu interagieren:
| Feature | query() | ClaudeSDKClient |
|---|---|---|
| Session | Erstellt jedes Mal eine neue Sitzung | Verwendet dieselbe Sitzung erneut |
| Conversation | Einzelner Austausch | Mehrere Austausche im gleichen Kontext |
| Connection | Automatisch verwaltet | Manuelle Kontrolle |
| Streaming Input | ✅ Unterstützt | ✅ Unterstützt |
| Interrupts | ❌ Nicht unterstützt | ✅ Unterstützt |
| Hooks | ❌ Nicht unterstützt | ✅ Unterstützt |
| Custom Tools | ❌ Nicht unterstützt | ✅ Unterstützt |
| Continue Chat | ❌ Jedes Mal neue Sitzung | ✅ Behält Konversation bei |
| Use Case | Einmalige Aufgaben | Kontinuierliche Konversationen |
query() verwendet werden sollte (Jedes Mal neue Sitzung)Am besten für:
ClaudeSDKClient verwendet werden sollte (Kontinuierliche Konversation)Am besten für:
query()Erstellt eine neue Sitzung für jede Interaktion mit Claude Code. Gibt einen asynchronen Iterator zurück, der Nachrichten bei ihrer Ankunft liefert. Jeder Aufruf von query() startet neu ohne Erinnerung an vorherige Interaktionen.
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None
) -> AsyncIterator[Message]| Parameter | Type | Beschreibung |
|---|---|---|
prompt | str | AsyncIterable[dict] | Die Eingabeaufforderung als Zeichenkette oder asynchroner Iterator für den Streaming-Modus |
options | ClaudeAgentOptions | None | Optionales Konfigurationsobjekt (standardmäßig ClaudeAgentOptions(), wenn None) |
Gibt einen AsyncIterator[Message] zurück, der Nachrichten aus der Konversation liefert.
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
system_prompt="You are an expert Python developer",
permission_mode='acceptEdits',
cwd="/home/user/project"
)
async for message in query(
prompt="Create a Python web server",
options=options
):
print(message)
asyncio.run(main())tool()Dekorator zum Definieren von MCP-Tools mit Typsicherheit.
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]| Parameter | Type | Beschreibung |
|---|---|---|
name | str | Eindeutige Kennung für das Tool |
description | str | Für Menschen lesbare Beschreibung, was das Tool tut |
input_schema | type | dict[str, Any] | Schema, das die Eingabeparameter des Tools definiert (siehe unten) |
Einfache Typ-Zuordnung (empfohlen):
{"text": str, "count": int, "enabled": bool}JSON-Schema-Format (für komplexe Validierung):
{
"type": "object",
"properties": {
"text": {"type": "string"},
"count": {"type": "integer", "minimum": 0}
},
"required": ["text"]
}Eine Dekoratorfunktion, die die Tool-Implementierung umhüllt und eine SdkMcpTool-Instanz zurückgibt.
from claude_agent_sdk import tool
from typing import Any
@tool("greet", "Greet a user", {"name": str})
async def greet(args: dict[str, Any]) -> dict[str, Any]:
return {
"content": [{
"type": "text",
"text": f"Hello, {args['name']}!"
}]
}create_sdk_mcp_server()Erstellen Sie einen In-Process-MCP-Server, der in Ihrer Python-Anwendung ausgeführt wird.
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig| Parameter | Type | Standard | Beschreibung |
|---|---|---|---|
name | str | - | Eindeutige Kennung für den Server |
version | str | "1.0.0" | Server-Versionsnummer |
tools | list[SdkMcpTool[Any]] | None | None | Liste von Tool-Funktionen, die mit dem @tool-Dekorator erstellt wurden |
Gibt ein McpSdkServerConfig-Objekt zurück, das an ClaudeAgentOptions.mcp_servers übergeben werden kann.
from claude_agent_sdk import tool, create_sdk_mcp_server
@tool("add", "Add two numbers", {"a": float, "b": float})
async def add(args):
return {
"content": [{
"type": "text",
"text": f"Sum: {args['a'] + args['b']}"
}]
}
@tool("multiply", "Multiply two numbers", {"a": float, "b": float})
async def multiply(args):
return {
"content": [{
"type": "text",
"text": f"Product: {args['a'] * args['b']}"
}]
}
calculator = create_sdk_mcp_server(
name="calculator",
version="2.0.0",
tools=[add, multiply] # Pass decorated functions
)
# Use with Claude
options = ClaudeAgentOptions(
mcp_servers={"calc": calculator},
allowed_tools=["mcp__calc__add", "mcp__calc__multiply"]
)ClaudeSDKClientBehält eine Konversationssitzung über mehrere Austausche hinweg bei. Dies ist das Python-Äquivalent dazu, wie die query()-Funktion des TypeScript SDK intern funktioniert - sie erstellt ein Client-Objekt, das Konversationen fortsetzen kann.
query()-Aufrufe hinweg bei@tool-Dekorator) und Hooksclass ClaudeSDKClient:
def __init__(self, options: ClaudeAgentOptions | 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 rewind_files(self, user_message_uuid: str) -> None
async def disconnect(self) -> None| Methode | Beschreibung |
|---|---|
__init__(options) | Initialisieren Sie den Client mit optionaler Konfiguration |
connect(prompt) | Verbindung zu Claude mit optionaler Anfangseingabe oder Nachrichtenstrom |
query(prompt, session_id) | Senden Sie eine neue Anfrage im Streaming-Modus |
receive_messages() | Empfangen Sie alle Nachrichten von Claude als asynchronen Iterator |
receive_response() | Empfangen Sie Nachrichten bis einschließlich einer ResultMessage |
interrupt() | Senden Sie ein Interrupt-Signal (funktioniert nur im Streaming-Modus) |
rewind_files(user_message_uuid) | Stellen Sie Dateien in ihren Zustand bei der angegebenen Benutzernachricht wieder her. Erfordert . Siehe |
Der Client kann als asynchroner Context Manager für automatische Verbindungsverwaltung verwendet werden:
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)Wichtig: Vermeiden Sie beim Iterieren über Nachrichten die Verwendung von
break, um vorzeitig zu beenden, da dies zu asyncio-Bereinigungsproblemen führen kann. Lassen Sie die Iteration stattdessen natürlich abschließen oder verwenden Sie Flags, um zu verfolgen, wann Sie das Gewünschte gefunden haben.
import asyncio
from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
async def main():
async with ClaudeSDKClient() as client:
# First question
await client.query("What's the capital of France?")
# Process response
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
# Follow-up question - Claude remembers the previous context
await client.query("What's the population of that city?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
# Another follow-up - still in the same conversation
await client.query("What are some famous landmarks there?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
asyncio.run(main())import asyncio
from claude_agent_sdk import ClaudeSDKClient
async def message_stream():
"""Generate messages dynamically."""
yield {"type": "text", "text": "Analyze the following data:"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "Temperature: 25°C"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "Humidity: 60%"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "What patterns do you see?"}
async def main():
async with ClaudeSDKClient() as client:
# Stream input to Claude
await client.query(message_stream())
# Process response
async for message in client.receive_response():
print(message)
# Follow-up in same session
await client.query("Should we be concerned about these readings?")
async for message in client.receive_response():
print(message)
asyncio.run(main())import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
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")
# Let it run for a bit
await asyncio.sleep(2)
# Interrupt the task
await client.interrupt()
print("Task interrupted!")
# Send a new command
await client.query("Just say hello instead")
async for message in client.receive_response():
# Process the new response
pass
asyncio.run(interruptible_task())from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions
)
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
async def custom_permission_handler(
tool_name: str,
input_data: dict,
context: dict
) -> PermissionResultAllow | PermissionResultDeny:
"""Custom logic for tool permissions."""
# Block writes to system directories
if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
return PermissionResultDeny(
message="System directory write not allowed",
interrupt=True
)
# Redirect sensitive file operations
if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):
safe_path = f"./sandbox/{input_data['file_path']}"
return PermissionResultAllow(
updated_input={**input_data, "file_path": safe_path}
)
# Allow everything else
return PermissionResultAllow(updated_input=input_data)
async def main():
options = ClaudeAgentOptions(
can_use_tool=custom_permission_handler,
allowed_tools=["Read", "Write", "Edit"]
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Update the system config file")
async for message in client.receive_response():
# Will use sandbox path instead
print(message)
asyncio.run(main())SdkMcpToolDefinition für ein SDK MCP Tool, das mit dem @tool-Dekorator erstellt wurde.
@dataclass
class SdkMcpTool(Generic[T]):
name: str
description: str
input_schema: type[T] | dict[str, Any]
handler: Callable[[T], Awaitable[dict[str, Any]]]| Eigenschaft | Type | Beschreibung |
|---|---|---|
name | str | Eindeutige Kennung für das Tool |
description | str | Für Menschen lesbare Beschreibung |
input_schema | type[T] | dict[str, Any] | Schema für Eingabevalidierung |
handler | Callable[[T], Awaitable[dict[str, Any]]] | Asynchrone Funktion, die die Tool-Ausführung handhabt |
ClaudeAgentOptionsKonfigurationsdatenklasse für Claude Code-Abfragen.
@dataclass
class ClaudeAgentOptions:
tools: list[str] | ToolsPreset | None = None
allowed_tools: list[str] = field(default_factory=list)
system_prompt: str | SystemPromptPreset | None = None
mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
permission_mode: PermissionMode | None = None
continue_conversation: bool = False
resume: str | None = None
max_turns: int | None = None
max_budget_usd: float | None = None
disallowed_tools: list[str] = field(default_factory=list)
model: str | None = None
fallback_model: str | None = None
betas: list[SdkBeta] = field(default_factory=list)
output_format: OutputFormat | None = None
permission_prompt_tool_name: str | None = None
cwd: str | Path | None = None
cli_path: str | Path | None = None
settings: str | None = None
add_dirs: list[str | Path] = field(default_factory=list)
env: dict[str, str] = field(default_factory=dict)
extra_args: dict[str, str | None] = field(default_factory=dict)
max_buffer_size: int | None = None
debug_stderr: Any = sys.stderr # Deprecated
stderr: Callable[[str], None] | None = None
can_use_tool: CanUseTool | None = None
hooks: dict[HookEvent, list[HookMatcher]] | None = None
user: str | None = None
include_partial_messages: bool = False
fork_session: bool = False
agents: dict[str, AgentDefinition] | None = None
setting_sources: list[SettingSource] | None = None
max_thinking_tokens: int | None = None| Eigenschaft | Type | Standard | Beschreibung |
|---|---|---|---|
tools | list[str] | ToolsPreset | None | None | Tools-Konfiguration. Verwenden Sie {"type": "preset", "preset": "claude_code"} für die Standard-Tools von Claude Code |
allowed_tools | list[str] | [] | Liste der zulässigen Tool-Namen |
system_prompt | str | SystemPromptPreset | None | None | System-Prompt-Konfiguration. Übergeben Sie eine Zeichenkette für einen benutzerdefinierten Prompt oder verwenden Sie für Claude Codes System-Prompt. Fügen Sie hinzu, um den Preset zu erweitern |
OutputFormatKonfiguration für die Validierung strukturierter Ausgaben.
class OutputFormat(TypedDict):
type: Literal["json_schema"]
schema: dict[str, Any]| Feld | Erforderlich | Beschreibung |
|---|---|---|
type | Ja | Muss "json_schema" für JSON-Schema-Validierung sein |
schema | Ja | JSON-Schema-Definition für Ausgabevalidierung |
SystemPromptPresetKonfiguration für die Verwendung von Claude Codes voreingestelltem System-Prompt mit optionalen Ergänzungen.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| Feld | Erforderlich | Beschreibung |
|---|---|---|
type | Ja | Muss "preset" sein, um einen voreingestellten System-Prompt zu verwenden |
preset | Ja | Muss "claude_code" sein, um Claude Codes System-Prompt zu verwenden |
append | Nein | Zusätzliche Anweisungen, die an den voreingestellten System-Prompt angehängt werden |
SettingSourceKontrolliert, welche dateisystembasierte Konfigurationsquellen das SDK lädt.
SettingSource = Literal["user", "project", "local"]| Wert | Beschreibung | Ort |
|---|---|---|
"user" | Globale Benutzereinstellungen | ~/.claude/settings.json |
"project" | Gemeinsame Projekteinstellungen (versionskontrolliert) | .claude/settings.json |
"local" | Lokale Projekteinstellungen (gitignoriert) | .claude/settings.local.json |
Wenn setting_sources weggelassen oder None ist, lädt das SDK keine Dateisystem-Einstellungen. Dies bietet Isolation für SDK-Anwendungen.
Alle Dateisystem-Einstellungen laden (Legacy-Verhalten):
# Load all settings like SDK v0.0.x did
from claude_agent_sdk import query, ClaudeAgentOptions
async for message in query(
prompt="Analyze this code",
options=ClaudeAgentOptions(
setting_sources=["user", "project", "local"] # Load all settings
)
):
print(message)Nur bestimmte Einstellungsquellen laden:
# Load only project settings, ignore user and local
async for message in query(
prompt="Run CI checks",
options=ClaudeAgentOptions(
setting_sources=["project"] # Only .claude/settings.json
)
):
print(message)Test- und CI-Umgebungen:
# Ensure consistent behavior in CI by excluding local settings
async for message in query(
prompt="Run tests",
options=ClaudeAgentOptions(
setting_sources=["project"], # Only team-shared settings
permission_mode="bypassPermissions"
)
):
print(message)SDK-only-Anwendungen:
# Define everything programmatically (default behavior)
# No filesystem dependencies - setting_sources defaults to None
async for message in query(
prompt="Review this PR",
options=ClaudeAgentOptions(
# setting_sources=None is the default, no need to specify
agents={ /* ... */ },
mcp_servers={ /* ... */ },
allowed_tools=["Read", "Grep", "Glob"]
)
):
print(message)CLAUDE.md-Projektanweisungen laden:
# Load project settings to include CLAUDE.md files
async for message in query(
prompt="Add a new feature following project conventions",
options=ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code" # Use Claude Code's system prompt
},
setting_sources=["project"], # Required to load CLAUDE.md from project
allowed_tools=["Read", "Write", "Edit"]
)
):
print(message)Wenn mehrere Quellen geladen werden, werden Einstellungen mit dieser Priorität zusammengeführt (höchste bis niedrigste):
.claude/settings.local.json).claude/settings.json)~/.claude/settings.json)Programmgesteuerte Optionen (wie agents, allowed_tools) überschreiben immer Dateisystem-Einstellungen.
AgentDefinitionKonfiguration für einen programmgesteuert definierten Subagenten.
@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None| Feld | Erforderlich | Beschreibung |
|---|---|---|
description | Ja | Natürlichsprachige Beschreibung, wann dieser Agent verwendet werden sollte |
tools | Nein | Array der zulässigen Tool-Namen. Wenn weggelassen, erbt alle Tools |
prompt | Ja | Der System-Prompt des Agenten |
model | Nein | Modellüberschreibung für diesen Agenten. Wenn weggelassen, verwendet das Hauptmodell |
PermissionModeBerechtigungsmodi zur Kontrolle der Tool-Ausführung.
PermissionMode = Literal[
"default", # Standard permission behavior
"acceptEdits", # Auto-accept file edits
"plan", # Planning mode - no execution
"bypassPermissions" # Bypass all permission checks (use with caution)
]CanUseToolTyp-Alias für Tool-Berechtigungsrückruf-Funktionen.
CanUseTool = Callable[
[str, dict[str, Any], ToolPermissionContext],
Awaitable[PermissionResult]
]Der Rückruf erhält:
tool_name: Name des aufgerufenen Toolsinput_data: Die Eingabeparameter des Toolscontext: Ein ToolPermissionContext mit zusätzlichen InformationenGibt ein PermissionResult zurück (entweder PermissionResultAllow oder PermissionResultDeny).
ToolPermissionContextKontextinformationen, die an Tool-Berechtigungsrückrufe übergeben werden.
@dataclass
class ToolPermissionContext:
signal: Any | None = None # Future: abort signal support
suggestions: list[PermissionUpdate] = field(default_factory=list)| Feld | Typ | Beschreibung |
|---|---|---|
signal | Any | None | Reserviert für zukünftige Abbruchsignal-Unterstützung |
suggestions | list[PermissionUpdate] | Berechtigungsaktualisierungsvorschläge aus der CLI |
PermissionResultUnion-Typ für Berechtigungsrückrufergebnisse.
PermissionResult = PermissionResultAllow | PermissionResultDenyPermissionResultAllowErgebnis, das angibt, dass der Tool-Aufruf zulässig sein sollte.
@dataclass
class PermissionResultAllow:
behavior: Literal["allow"] = "allow"
updated_input: dict[str, Any] | None = None
updated_permissions: list[PermissionUpdate] | None = None| Feld | Typ | Standard | Beschreibung |
|---|---|---|---|
behavior | Literal["allow"] | "allow" | Muss "allow" sein |
updated_input | dict[str, Any] | None | None | Geänderte Eingabe zur Verwendung statt Original |
updated_permissions | list[PermissionUpdate] | None | None | Berechtigungsaktualisierungen zum Anwenden |
PermissionResultDenyErgebnis, das angibt, dass der Tool-Aufruf verweigert werden sollte.
@dataclass
class PermissionResultDeny:
behavior: Literal["deny"] = "deny"
message: str = ""
interrupt: bool = False| Feld | Typ | Standard | Beschreibung |
|---|---|---|---|
behavior | Literal["deny"] | "deny" | Muss "deny" sein |
message | str | "" | Nachricht, die erklärt, warum das Tool verweigert wurde |
interrupt | bool | False | Ob die aktuelle Ausführung unterbrochen werden soll |
PermissionUpdateKonfiguration zum programmgesteuerten Aktualisieren von Berechtigungen.
@dataclass
class PermissionUpdate:
type: Literal[
"addRules",
"replaceRules",
"removeRules",
"setMode",
"addDirectories",
"removeDirectories",
]
rules: list[PermissionRuleValue] | None = None
behavior: Literal["allow", "deny", "ask"] | None = None
mode: PermissionMode | None = None
directories: list[str] | None = None
destination: Literal["userSettings", "projectSettings", "localSettings", "session"] | None = None| Feld | Typ | Beschreibung |
|---|---|---|
type | Literal[...] | Der Typ der Berechtigungsaktualisierungsoperation |
rules | list[PermissionRuleValue] | None | Regeln für Add/Replace/Remove-Operationen |
behavior | Literal["allow", "deny", "ask"] | None | Verhalten für regelbasierte Operationen |
mode | PermissionMode | None | Modus für setMode-Operation |
directories |
SdkBetaLiteral-Typ für SDK-Beta-Funktionen.
SdkBeta = Literal["context-1m-2025-08-07"]Verwenden Sie mit dem betas-Feld in ClaudeAgentOptions, um Beta-Funktionen zu aktivieren.
McpSdkServerConfigKonfiguration für SDK-MCP-Server, die mit create_sdk_mcp_server() erstellt wurden.
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfigUnion-Typ für MCP-Serverkonfigurationen.
McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfigMcpStdioServerConfigclass McpStdioServerConfig(TypedDict):
type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility
command: str
args: NotRequired[list[str]]
env: NotRequired[dict[str, str]]McpSSEServerConfigclass McpSSEServerConfig(TypedDict):
type: Literal["sse"]
url: str
headers: NotRequired[dict[str, str]]McpHttpServerConfigclass McpHttpServerConfig(TypedDict):
type: Literal["http"]
url: str
headers: NotRequired[dict[str, str]]SdkPluginConfigKonfiguration zum Laden von Plugins im SDK.
class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str| Feld | Typ | Beschreibung |
|---|---|---|
type | Literal["local"] | Muss "local" sein (derzeit werden nur lokale Plugins unterstützt) |
path | str | Absoluter oder relativer Pfad zum Plugin-Verzeichnis |
Beispiel:
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"}
]Vollständige Informationen zum Erstellen und Verwenden von Plugins finden Sie unter Plugins.
MessageUnion-Typ aller möglichen Nachrichten.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEventUserMessageBenutzereingabe-Nachricht.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessageAssistent-Antwortnachricht mit Inhaltsblöcken.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessageSystemnachricht mit Metadaten.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]ResultMessageEndgültige Ergebnisnachricht mit Kosten- und Nutzungsinformationen.
@dataclass
class ResultMessage:
subtype: str
duration_ms: int
duration_api_ms: int
is_error: bool
num_turns: int
session_id: str
total_cost_usd: float | None = None
usage: dict[str, Any] | None = None
result: str | None = None
structured_output: Any = NoneStreamEventStream-Ereignis für teilweise Nachrichtenupdates während des Streamings. Wird nur empfangen, wenn include_partial_messages=True in ClaudeAgentOptions.
@dataclass
class StreamEvent:
uuid: str
session_id: str
event: dict[str, Any] # The raw Anthropic API stream event
parent_tool_use_id: str | None = None| Feld | Typ | Beschreibung |
|---|---|---|
uuid | str | Eindeutige Kennung für dieses Ereignis |
session_id | str | Sitzungskennung |
event | dict[str, Any] | Die rohen Anthropic-API-Stream-Ereignisdaten |
parent_tool_use_id | str | None | Übergeordnete Tool-Use-ID, wenn dieses Ereignis von einem Subagenten stammt |
ContentBlockUnion-Typ aller Inhaltsblöcke.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlockTextinhaltsblock.
@dataclass
class TextBlock:
text: strThinkingBlockThinking-Inhaltsblock (für Modelle mit Thinking-Fähigkeit).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlockTool-Use-Anforderungsblock.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlockTool-Ausführungsergebnis-Block.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneClaudeSDKErrorBasis-Ausnahmeklasse für alle SDK-Fehler.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""CLINotFoundErrorWird ausgelöst, wenn Claude Code CLI nicht installiert oder nicht gefunden ist.
class CLINotFoundError(CLIConnectionError):
def __init__(self, message: str = "Claude Code not found", cli_path: str | None = None):
"""
Args:
message: Error message (default: "Claude Code not found")
cli_path: Optional path to the CLI that was not found
"""CLIConnectionErrorWird ausgelöst, wenn die Verbindung zu Claude Code fehlschlägt.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""ProcessErrorWird ausgelöst, wenn der Claude Code-Prozess fehlschlägt.
class ProcessError(ClaudeSDKError):
def __init__(self, message: str, exit_code: int | None = None, stderr: str | None = None):
self.exit_code = exit_code
self.stderr = stderrCLIJSONDecodeErrorWird ausgelöst, wenn JSON-Parsing fehlschlägt.
class CLIJSONDecodeError(ClaudeSDKError):
def __init__(self, line: str, original_error: Exception):
"""
Args:
line: The line that failed to parse
original_error: The original JSON decode exception
"""
self.line = line
self.original_error = original_errorEinen umfassenden Leitfaden zur Verwendung von Hooks mit Beispielen und häufigen Mustern finden Sie im Hooks-Leitfaden.
HookEventUnterstützte Hook-Ereignistypen. Beachten Sie, dass das Python SDK aufgrund von Setup-Einschränkungen die Hooks SessionStart, SessionEnd und Notification nicht unterstützt.
HookEvent = Literal[
"PreToolUse", # Called before tool execution
"PostToolUse", # Called after tool execution
"UserPromptSubmit", # Called when user submits a prompt
"Stop", # Called when stopping execution
"SubagentStop", # Called when a subagent stops
"PreCompact" # Called before message compaction
]HookCallbackTypdefinition für Hook-Rückruf-Funktionen.
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]Parameter:
input_data: Hook-spezifische Eingabedaten (siehe Hooks-Leitfaden)tool_use_id: Optionale Tool-Use-Kennung (für Tool-bezogene Hooks)context: Hook-Kontext mit zusätzlichen InformationenGibt ein Wörterbuch zurück, das möglicherweise enthalten kann:
decision: "block" zum Blockieren der AktionsystemMessage: Systemnachricht zum Hinzufügen zum TranskripthookSpecificOutput: Hook-spezifische AusgabedatenHookContextKontextinformationen, die an Hook-Rückrufe übergeben werden.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcherKonfiguration zum Abgleichen von Hooks mit bestimmten Ereignissen oder Tools.
@dataclass
class HookMatcher:
matcher: str | None = None # Tool name or pattern to match (e.g., "Bash", "Write|Edit")
hooks: list[HookCallback] = field(default_factory=list) # List of callbacks to execute
timeout: float | None = None # Timeout in seconds for all hooks in this matcher (default: 60)HookInputUnion-Typ aller Hook-Eingabetypen. Der tatsächliche Typ hängt vom hook_event_name-Feld ab.
HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
)BaseHookInputBasisfelder, die in allen Hook-Eingabetypen vorhanden sind.
class BaseHookInput(TypedDict):
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]| Feld | Typ | Beschreibung |
|---|---|---|
session_id | str | Aktuelle Sitzungskennung |
transcript_path | str | Pfad zur Sitzungstranskriptdatei |
cwd | str | Aktuelles Arbeitsverzeichnis |
permission_mode | str (optional) | Aktueller Berechtigungsmodus |
PreToolUseHookInputEingabedaten für PreToolUse-Hook-Ereignisse.
class PreToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["PreToolUse"] | Immer "PreToolUse" |
tool_name | str | Name des Tools, das ausgeführt werden soll |
tool_input | dict[str, Any] | Eingabeparameter für das Tool |
PostToolUseHookInputEingabedaten für PostToolUse-Hook-Ereignisse.
class PostToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["PostToolUse"] | Immer "PostToolUse" |
tool_name | str | Name des Tools, das ausgeführt wurde |
tool_input | dict[str, Any] | Eingabeparameter, die verwendet wurden |
tool_response | Any | Antwort aus der Tool-Ausführung |
UserPromptSubmitHookInputEingabedaten für UserPromptSubmit-Hook-Ereignisse.
class UserPromptSubmitHookInput(BaseHookInput):
hook_event_name: Literal["UserPromptSubmit"]
prompt: str| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["UserPromptSubmit"] | Immer "UserPromptSubmit" |
prompt | str | Die vom Benutzer eingereichte Eingabeaufforderung |
StopHookInputEingabedaten für Stop-Hook-Ereignisse.
class StopHookInput(BaseHookInput):
hook_event_name: Literal["Stop"]
stop_hook_active: bool| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["Stop"] | Immer "Stop" |
stop_hook_active | bool | Ob der Stop-Hook aktiv ist |
SubagentStopHookInputEingabedaten für SubagentStop-Hook-Ereignisse.
class SubagentStopHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["SubagentStop"] | Immer "SubagentStop" |
stop_hook_active | bool | Ob der Stop-Hook aktiv ist |
PreCompactHookInputEingabedaten für PreCompact-Hook-Ereignisse.
class PreCompactHookInput(BaseHookInput):
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None| Feld | Typ | Beschreibung |
|---|---|---|
hook_event_name | Literal["PreCompact"] | Immer "PreCompact" |
trigger | Literal["manual", "auto"] | Was die Komprimierung ausgelöst hat |
custom_instructions | str | None | Benutzerdefinierte Anweisungen für die Komprimierung |
HookJSONOutputUnion-Typ für Hook-Rückruf-Rückgabewerte.
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutputSyncHookJSONOutputSynchrone Hook-Ausgabe mit Kontroll- und Entscheidungsfeldern.
class SyncHookJSONOutput(TypedDict):
# Control fields
continue_: NotRequired[bool] # Whether to proceed (default: True)
suppressOutput: NotRequired[bool] # Hide stdout from transcript
stopReason: NotRequired[str] # Message when continue is False
# Decision fields
decision: NotRequired[Literal["block"]]
systemMessage: NotRequired[str] # Warning message for user
reason: NotRequired[str] # Feedback for Claude
# Hook-specific output
hookSpecificOutput: NotRequired[dict[str, Any]]Verwenden Sie continue_ (mit Unterstrich) im Python-Code. Es wird automatisch in continue konvertiert, wenn es an die CLI gesendet wird.
AsyncHookJSONOutputAsynchrone Hook-Ausgabe, die Hook-Ausführung aufschiebt.
class AsyncHookJSONOutput(TypedDict):
async_: Literal[True] # Set to True to defer execution
asyncTimeout: NotRequired[int] # Timeout in millisecondsVerwenden Sie async_ (mit Unterstrich) im Python-Code. Es wird automatisch in async konvertiert, wenn es an die CLI gesendet wird.
Dieses Beispiel registriert zwei Hooks: einen, der gefährliche Bash-Befehle wie rm -rf / blockiert, und einen anderen, der alle Tool-Nutzung für Auditing protokolliert. Der Sicherheits-Hook wird nur auf Bash-Befehle ausgeführt (über den matcher), während der Logging-Hook auf alle Tools angewendet wird.
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
from typing import Any
async def validate_bash_command(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Validate and potentially block dangerous bash commands."""
if input_data['tool_name'] == 'Bash':
command = input_data['tool_input'].get('command', '')
if 'rm -rf /' in command:
return {
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
'permissionDecision': 'deny',
'permissionDecisionReason': 'Dangerous command blocked'
}
}
return {}
async def log_tool_use(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Log all tool usage for auditing."""
print(f"Tool used: {input_data.get('tool_name')}")
return {}
options = ClaudeAgentOptions(
hooks={
'PreToolUse': [
HookMatcher(matcher='Bash', hooks=[validate_bash_command], timeout=120), # 2 min for validation
HookMatcher(hooks=[log_tool_use]) # Applies to all tools (default 60s timeout)
],
'PostToolUse': [
HookMatcher(hooks=[log_tool_use])
]
}
)
async for message in query(
prompt="Analyze this codebase",
options=options
):
print(message)Dokumentation von Eingabe-/Ausgabeschemas für alle integrierten Claude Code-Tools. Obwohl das Python SDK diese nicht als Typen exportiert, stellen sie die Struktur von Tool-Eingaben und -Ausgaben in Nachrichten dar.
Tool-Name: Task
Eingabe:
{
"description": str, # A short (3-5 word) description of the task
"prompt": str, # The task for the agent to perform
"subagent_type": str # The type of specialized agent to use
}Ausgabe:
{
"result": str, # Final result from the subagent
"usage": dict | None, # Token usage statistics
"total_cost_usd": float | None, # Total cost in USD
"duration_ms": int | None # Execution duration in milliseconds
}Tool-Name: AskUserQuestion
Stellt dem Benutzer während der Ausführung Klärungsfragen. Siehe Genehmigungen und Benutzereingaben verarbeiten für Verwendungsdetails.
Eingabe:
{
"questions": [ # Questions to ask the user (1-4 questions)
{
"question": str, # The complete question to ask the user
"header": str, # Very short label displayed as a chip/tag (max 12 chars)
"options": [ # The available choices (2-4 options)
{
"label": str, # Display text for this option (1-5 words)
"description": str # Explanation of what this option means
}
],
"multiSelect": bool # Set to true to allow multiple selections
}
],
"answers": dict | None # User answers populated by the permission system
}Ausgabe:
{
"questions": [ # The questions that were asked
{
"question": str,
"header": str,
"options": [{"label": str, "description": str}],
"multiSelect": bool
}
],
"answers": dict[str, str] # Maps question text to answer string
# Multi-select answers are comma-separated
}Tool-Name: Bash
Eingabe:
{
"command": str, # The command to execute
"timeout": int | None, # Optional timeout in milliseconds (max 600000)
"description": str | None, # Clear, concise description (5-10 words)
"run_in_background": bool | None # Set to true to run in background
}Ausgabe:
{
"output": str, # Combined stdout and stderr output
"exitCode": int, # Exit code of the command
"killed": bool | None, # Whether command was killed due to timeout
"shellId": str | None # Shell ID for background processes
}Tool-Name: Edit
Eingabe:
{
"file_path": str, # The absolute path to the file to modify
"old_string": str, # The text to replace
"new_string": str, # The text to replace it with
"replace_all": bool | None # Replace all occurrences (default False)
}Ausgabe:
{
"message": str, # Confirmation message
"replacements": int, # Number of replacements made
"file_path": str # File path that was edited
}Tool-Name: Read
Eingabe:
{
"file_path": str, # The absolute path to the file to read
"offset": int | None, # The line number to start reading from
"limit": int | None # The number of lines to read
}Ausgabe (Textdateien):
{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int # Lines actually returned
}Ausgabe (Bilder):
{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int # File size in bytes
}Tool-Name: Write
Eingabe:
{
"file_path": str, # The absolute path to the file to write
"content": str # The content to write to the file
}Ausgabe:
{
"message": str, # Success message
"bytes_written": int, # Number of bytes written
"file_path": str # File path that was written
}Tool-Name: Glob
Eingabe:
{
"pattern": str, # The glob pattern to match files against
"path": str | None # The directory to search in (defaults to cwd)
}Ausgabe:
{
"matches": list[str], # Array of matching file paths
"count": int, # Number of matches found
"search_path": str # Search directory used
}Tool-Name: Grep
Eingabe:
{
"pattern": str, # The regular expression pattern
"path": str | None, # File or directory to search in
"glob": str | None, # Glob pattern to filter files
"type": str | None, # File type to search
"output_mode": str | None, # "content", "files_with_matches", or "count"
"-i": bool | None, # Case insensitive search
"-n": bool | None, # Show line numbers
"-B": int | None, # Lines to show before each match
"-A": int | None, # Lines to show after each match
"-C": int | None, # Lines to show before and after
"head_limit": int | None, # Limit output to first N lines/entries
"multiline": bool | None # Enable multiline mode
}Ausgabe (content-Modus):
{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None
}
],
"total_matches": int
}Ausgabe (files_with_matches-Modus):
{
"files": list[str], # Files containing matches
"count": int # Number of files with matches
}Tool-Name: NotebookEdit
Eingabe:
{
"notebook_path": str, # Absolute path to the Jupyter notebook
"cell_id": str | None, # The ID of the cell to edit
"new_source": str, # The new source for the cell
"cell_type": "code" | "markdown" | None, # The type of the cell
"edit_mode": "replace" | "insert" | "delete" | None # Edit operation type
}Ausgabe:
{
"message": str, # Success message
"edit_type": "replaced" | "inserted" | "deleted", # Type of edit performed
"cell_id": str | None, # Cell ID that was affected
"total_cells": int # Total cells in notebook after edit
}Tool-Name: WebFetch
Eingabe:
{
"url": str, # The URL to fetch content from
"prompt": str # The prompt to run on the fetched content
}Ausgabe:
{
"response": str, # AI model's response to the prompt
"url": str, # URL that was fetched
"final_url": str | None, # Final URL after redirects
"status_code": int | None # HTTP status code
}Tool-Name: WebSearch
Eingabe:
{
"query": str, # The search query to use
"allowed_domains": list[str] | None, # Only include results from these domains
"blocked_domains": list[str] | None # Never include results from these domains
}Ausgabe:
{
"results": [
{
"title": str,
"url": str,
"snippet": str,
"metadata": dict | None
}
],
"total_results": int,
"query": str
}Tool-Name: TodoWrite
Eingabe:
{
"todos": [
{
"content": str, # The task description
"status": "pending" | "in_progress" | "completed", # Task status
"activeForm": str # Active form of the description
}
]
}Ausgabe:
{
"message": str, # Success message
"stats": {
"total": int,
"pending": int,
"in_progress": int,
"completed": int
}
}Tool-Name: BashOutput
Eingabe:
{
"bash_id": str, # The ID of the background shell
"filter": str | None # Optional regex to filter output lines
}Ausgabe:
{
"output": str, # New output since last check
"status": "running" | "completed" | "failed", # Current shell status
"exitCode": int | None # Exit code when completed
}Tool-Name: KillBash
Eingabe:
{
"shell_id": str # The ID of the background shell to kill
}Ausgabe:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}Tool-Name: ExitPlanMode
Eingabe:
{
"plan": str # The plan to run by the user for approval
}Ausgabe:
{
"message": str, # Confirmation message
"approved": bool | None # Whether user approved the plan
}Tool-Name: ListMcpResources
Eingabe:
{
"server": str | None # Optional server name to filter resources by
}Ausgabe:
{
"resources": [
{
"uri": str,
"name": str,
"description": str | None,
"mimeType": str | None,
"server": str
}
],
"total": int
}Tool-Name: ReadMcpResource
Eingabe:
{
"server": str, # The MCP server name
"uri": str # The resource URI to read
}Ausgabe:
{
"contents": [
{
"uri": str,
"mimeType": str | None,
"text": str | None,
"blob": str | None
}
],
"server": str
}from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
import asyncio
class ConversationSession:
"""Maintains a single conversation session with Claude."""
def __init__(self, options: ClaudeAgentOptions = None):
self.client = ClaudeSDKClient(options)
self.turn_count = 0
async def start(self):
await self.client.connect()
print("Starting conversation session. Claude will remember context.")
print("Commands: 'exit' to quit, 'interrupt' to stop current task, 'new' for new session")
while True:
user_input = input(f"\n[Turn {self.turn_count + 1}] You: ")
if user_input.lower() == 'exit':
break
elif user_input.lower() == 'interrupt':
await self.client.interrupt()
print("Task interrupted!")
continue
elif user_input.lower() == 'new':
# Disconnect and reconnect for a fresh session
await self.client.disconnect()
await self.client.connect()
self.turn_count = 0
print("Started new conversation session (previous context cleared)")
continue
# Send message - Claude remembers all previous messages in this session
await self.client.query(user_input)
self.turn_count += 1
# Process response
print(f"[Turn {self.turn_count}] Claude: ", end="")
async for message in self.client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text, end="")
print() # New line after response
await self.client.disconnect()
print(f"Conversation ended after {self.turn_count} turns.")
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"],
permission_mode="acceptEdits"
)
session = ConversationSession(options)
await session.start()
# Example conversation:
# Turn 1 - You: "Create a file called hello.py"
# Turn 1 - Claude: "I'll create a hello.py file for you..."
# Turn 2 - You: "What's in that file?"
# Turn 2 - Claude: "The hello.py file I just created contains..." (remembers!)
# Turn 3 - You: "Add a main function to it"
# Turn 3 - Claude: "I'll add a main function to hello.py..." (knows which file!)
asyncio.run(main())from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
HookMatcher,
HookContext
)
import asyncio
from typing import Any
async def pre_tool_logger(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Log all tool usage before execution."""
tool_name = input_data.get('tool_name', 'unknown')
print(f"[PRE-TOOL] About to use: {tool_name}")
# You can modify or block the tool execution here
if tool_name == "Bash" and "rm -rf" in str(input_data.get('tool_input', {})):
return {
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
'permissionDecision': 'deny',
'permissionDecisionReason': 'Dangerous command blocked'
}
}
return {}
async def post_tool_logger(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Log results after tool execution."""
tool_name = input_data.get('tool_name', 'unknown')
print(f"[POST-TOOL] Completed: {tool_name}")
return {}
async def user_prompt_modifier(
input_data: dict[str, Any],
tool_use_id: str | None,
context: HookContext
) -> dict[str, Any]:
"""Add context to user prompts."""
original_prompt = input_data.get('prompt', '')
# Add timestamp to all prompts
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {
'hookSpecificOutput': {
'hookEventName': 'UserPromptSubmit',
'updatedPrompt': f"[{timestamp}] {original_prompt}"
}
}
async def main():
options = ClaudeAgentOptions(
hooks={
'PreToolUse': [
HookMatcher(hooks=[pre_tool_logger]),
HookMatcher(matcher='Bash', hooks=[pre_tool_logger])
],
'PostToolUse': [
HookMatcher(hooks=[post_tool_logger])
],
'UserPromptSubmit': [
HookMatcher(hooks=[user_prompt_modifier])
]
},
allowed_tools=["Read", "Write", "Bash"]
)
async with ClaudeSDKClient(options=options) as client:
await client.query("List files in current directory")
async for message in client.receive_response():
# Hooks will automatically log tool usage
pass
asyncio.run(main())from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
ToolUseBlock,
ToolResultBlock,
TextBlock
)
import asyncio
async def monitor_progress():
options = ClaudeAgentOptions(
allowed_tools=["Write", "Bash"],
permission_mode="acceptEdits"
)
async with ClaudeSDKClient(options=options) as client:
await client.query(
"Create 5 Python files with different sorting algorithms"
)
# Monitor progress in real-time
files_created = []
async for message in client.receive_messages():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
if block.name == "Write":
file_path = block.input.get("file_path", "")
print(f"🔨 Creating: {file_path}")
elif isinstance(block, ToolResultBlock):
print(f"✅ Completed tool execution")
elif isinstance(block, TextBlock):
print(f"💭 Claude says: {block.text[:100]}...")
# Check if we've received the final result
if hasattr(message, 'subtype') and message.subtype in ['success', 'error']:
print(f"\n🎯 Task completed!")
break
asyncio.run(monitor_progress())from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
import asyncio
async def create_project():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"],
permission_mode='acceptEdits',
cwd="/home/user/project"
)
async for message in query(
prompt="Create a Python project structure with setup.py",
options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
print(f"Using tool: {block.name}")
asyncio.run(create_project())from claude_agent_sdk import (
query,
CLINotFoundError,
ProcessError,
CLIJSONDecodeError
)
try:
async for message in query(prompt="Hello"):
print(message)
except CLINotFoundError:
print("Please install Claude Code: npm install -g @anthropic-ai/claude-code")
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}")from claude_agent_sdk import ClaudeSDKClient
import asyncio
async def interactive_session():
async with ClaudeSDKClient() as client:
# Send initial message
await client.query("What's the weather like?")
# Process responses
async for msg in client.receive_response():
print(msg)
# Send follow-up
await client.query("Tell me more about that")
# Process follow-up response
async for msg in client.receive_response():
print(msg)
asyncio.run(interactive_session())from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
tool,
create_sdk_mcp_server,
AssistantMessage,
TextBlock
)
import asyncio
from typing import Any
# Define custom tools with @tool decorator
@tool("calculate", "Perform mathematical calculations", {"expression": str})
async def calculate(args: dict[str, Any]) -> dict[str, Any]:
try:
result = eval(args["expression"], {"__builtins__": {}})
return {
"content": [{
"type": "text",
"text": f"Result: {result}"
}]
}
except Exception as e:
return {
"content": [{
"type": "text",
"text": f"Error: {str(e)}"
}],
"is_error": True
}
@tool("get_time", "Get current time", {})
async def get_time(args: dict[str, Any]) -> dict[str, Any]:
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {
"content": [{
"type": "text",
"text": f"Current time: {current_time}"
}]
}
async def main():
# Create SDK MCP server with custom tools
my_server = create_sdk_mcp_server(
name="utilities",
version="1.0.0",
tools=[calculate, get_time]
)
# Configure options with the server
options = ClaudeAgentOptions(
mcp_servers={"utils": my_server},
allowed_tools=[
"mcp__utils__calculate",
"mcp__utils__get_time"
]
)
# Use ClaudeSDKClient for interactive tool usage
async with ClaudeSDKClient(options=options) as client:
await client.query("What's 123 * 456?")
# Process calculation response
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Calculation: {block.text}")
# Follow up with time query
await client.query("What time is it now?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Time: {block.text}")
asyncio.run(main())SandboxSettingsKonfiguration für das Sandbox-Verhalten. Verwenden Sie dies, um Befehls-Sandboxing zu aktivieren und Netzwerkbeschränkungen programmgesteuert zu konfigurieren.
class SandboxSettings(TypedDict, total=False):
enabled: bool
autoAllowBashIfSandboxed: bool
excludedCommands: list[str]
allowUnsandboxedCommands: bool
network: SandboxNetworkConfig
ignoreViolations: SandboxIgnoreViolations
enableWeakerNestedSandbox: bool| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
enabled | bool | False | Aktivieren Sie den Sandbox-Modus für die Befehlsausführung |
autoAllowBashIfSandboxed | bool | False | Bash-Befehle automatisch genehmigen, wenn die Sandbox aktiviert ist |
excludedCommands | list[str] | [] | Befehle, die immer Sandbox-Beschränkungen umgehen (z. B. ["docker"]). Diese werden automatisch ohne Modellbeteiligung unsandboxed ausgeführt |
Dateisystem- und Netzwerkzugriffsbeschränkungen werden NICHT über Sandbox-Einstellungen konfiguriert. Stattdessen werden sie von Berechtigungsregeln abgeleitet:
Verwenden Sie Sandbox-Einstellungen für Befehls-Ausführungs-Sandboxing und Berechtigungsregeln für Dateisystem- und Netzwerkzugriffskontrolle.
from claude_agent_sdk import query, ClaudeAgentOptions, SandboxSettings
sandbox_settings: SandboxSettings = {
"enabled": True,
"autoAllowBashIfSandboxed": True,
"network": {
"allowLocalBinding": True
}
}
async for message in query(
prompt="Build and test my project",
options=ClaudeAgentOptions(sandbox=sandbox_settings)
):
print(message)Unix-Socket-Sicherheit: Die Option allowUnixSockets kann Zugriff auf leistungsstarke Systemdienste gewähren. Beispielsweise ermöglicht das Zulassen von /var/run/docker.sock effektiv vollständigen Host-Systemzugriff über die Docker-API und umgeht die Sandbox-Isolierung. Lassen Sie nur Unix-Sockets zu, die unbedingt erforderlich sind, und verstehen Sie die Sicherheitsauswirkungen jedes einzelnen.
SandboxNetworkConfigNetzwerkspezifische Konfiguration für den Sandbox-Modus.
class SandboxNetworkConfig(TypedDict, total=False):
allowLocalBinding: bool
allowUnixSockets: list[str]
allowAllUnixSockets: bool
httpProxyPort: int
socksProxyPort: int| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
allowLocalBinding | bool | False | Ermöglichen Sie Prozessen, sich an lokale Ports zu binden (z. B. für Dev-Server) |
allowUnixSockets | list[str] | [] | Unix-Socket-Pfade, auf die Prozesse zugreifen können (z. B. Docker-Socket) |
allowAllUnixSockets | bool | False | Ermöglichen Sie Zugriff auf alle Unix-Sockets |
SandboxIgnoreViolationsKonfiguration zum Ignorieren spezifischer Sandbox-Verstöße.
class SandboxIgnoreViolations(TypedDict, total=False):
file: list[str]
network: list[str]| Eigenschaft | Typ | Standard | Beschreibung |
|---|---|---|---|
file | list[str] | [] | Dateipfad-Muster zum Ignorieren von Verstößen |
network | list[str] | [] | Netzwerkmuster zum Ignorieren von Verstößen |
Wenn allowUnsandboxedCommands aktiviert ist, kann das Modell anfordern, Befehle außerhalb der Sandbox auszuführen, indem es dangerouslyDisableSandbox: True in der Tool-Eingabe setzt. Diese Anfragen fallen auf das vorhandene Berechtigungssystem zurück, was bedeutet, dass Ihr can_use_tool-Handler aufgerufen wird, sodass Sie benutzerdefinierte Autorisierungslogik implementieren können.
excludedCommands vs allowUnsandboxedCommands:
excludedCommands: Eine statische Liste von Befehlen, die die Sandbox immer automatisch umgehen (z. B. ["docker"]). Das Modell hat keine Kontrolle darüber.allowUnsandboxedCommands: Ermöglicht dem Modell, zur Laufzeit zu entscheiden, ob die Ausführung außerhalb der Sandbox angefordert werden soll, indem dangerouslyDisableSandbox: True in der Tool-Eingabe gesetzt wird.from claude_agent_sdk import query, ClaudeAgentOptions
async def can_use_tool(tool: str, input: dict) -> bool:
# Check if the model is requesting to bypass the sandbox
if tool == "Bash" and input.get("dangerouslyDisableSandbox"):
# The model wants to run this command outside the sandbox
print(f"Unsandboxed command requested: {input.get('command')}")
# Return True to allow, False to deny
return is_command_authorized(input.get("command"))
return True
async def main():
async for message in query(
prompt="Deploy my application",
options=ClaudeAgentOptions(
sandbox={
"enabled": True,
"allowUnsandboxedCommands": True # Model can request unsandboxed execution
},
permission_mode="default",
can_use_tool=can_use_tool
)
):
print(message)Dieses Muster ermöglicht es Ihnen:
Befehle, die mit dangerouslyDisableSandbox: True ausgeführt werden, haben vollständigen Systemzugriff. Stellen Sie sicher, dass Ihr can_use_tool-Handler diese Anfragen sorgfältig validiert.
Wenn permission_mode auf bypassPermissions gesetzt ist und allow_unsandboxed_commands aktiviert ist, kann das Modell autonom Befehle außerhalb der Sandbox ausführen, ohne dass Genehmigungsaufforderungen angezeigt werden. Diese Kombination ermöglicht dem Modell effektiv, die Sandbox-Isolierung stillschweigend zu umgehen.
enable_file_checkpointing=Truedisconnect() | Trennen Sie die Verbindung zu Claude |
{"type": "preset", "preset": "claude_code"}"append"mcp_servers | dict[str, McpServerConfig] | str | Path | {} | MCP-Server-Konfigurationen oder Pfad zur Konfigurationsdatei |
permission_mode | PermissionMode | None | None | Berechtigungsmodus für die Tool-Nutzung |
continue_conversation | bool | False | Setzen Sie die neueste Konversation fort |
resume | str | None | None | Sitzungs-ID zum Fortsetzen |
max_turns | int | None | None | Maximale Konversationsdrehungen |
max_budget_usd | float | None | None | Maximales Budget in USD für die Sitzung |
disallowed_tools | list[str] | [] | Liste der nicht zulässigen Tool-Namen |
enable_file_checkpointing | bool | False | Aktivieren Sie die Dateiveränderungsverfolgung zum Zurückspulen. Siehe File checkpointing |
model | str | None | None | Zu verwendendes Claude-Modell |
fallback_model | str | None | None | Fallback-Modell, das verwendet wird, wenn das primäre Modell fehlschlägt |
betas | list[SdkBeta] | [] | Beta-Funktionen zum Aktivieren. Siehe SdkBeta für verfügbare Optionen |
output_format | OutputFormat | None | None | Definieren Sie das Ausgabeformat für Agent-Ergebnisse. Siehe Structured outputs für Details |
permission_prompt_tool_name | str | None | None | MCP-Tool-Name für Berechtigungseingabeaufforderungen |
cwd | str | Path | None | None | Aktuelles Arbeitsverzeichnis |
cli_path | str | Path | None | None | Benutzerdefinierter Pfad zur Claude Code CLI-Ausführungsdatei |
settings | str | None | None | Pfad zur Einstellungsdatei |
add_dirs | list[str | Path] | [] | Zusätzliche Verzeichnisse, auf die Claude zugreifen kann |
env | dict[str, str] | {} | Umgebungsvariablen |
extra_args | dict[str, str | None] | {} | Zusätzliche CLI-Argumente, die direkt an die CLI übergeben werden |
max_buffer_size | int | None | None | Maximale Bytes beim Puffern der CLI-Standardausgabe |
debug_stderr | Any | sys.stderr | Veraltet - Dateiähnliches Objekt für Debug-Ausgabe. Verwenden Sie stattdessen den stderr-Callback |
stderr | Callable[[str], None] | None | None | Callback-Funktion für stderr-Ausgabe von CLI |
can_use_tool | CanUseTool | None | None | Tool-Berechtigungscallback-Funktion. Siehe Permission types für Details |
hooks | dict[HookEvent, list[HookMatcher]] | None | None | Hook-Konfigurationen zum Abfangen von Ereignissen |
user | str | None | None | Benutzerkennung |
include_partial_messages | bool | False | Schließen Sie partielle Nachrichtenstreaming-Ereignisse ein. Wenn aktiviert, werden StreamEvent-Nachrichten geliefert |
fork_session | bool | False | Beim Fortsetzen mit resume zu einer neuen Sitzungs-ID verzweigen, anstatt die ursprüngliche Sitzung fortzusetzen |
agents | dict[str, AgentDefinition] | None | None | Programmgesteuert definierte Subagenten |
plugins | list[SdkPluginConfig] | [] | Laden Sie benutzerdefinierte Plugins aus lokalen Pfaden. Siehe Plugins für Details |
sandbox | SandboxSettings | None | None | Konfigurieren Sie das Sandbox-Verhalten programmgesteuert. Siehe Sandbox settings für Details |
setting_sources | list[SettingSource] | None | None (keine Einstellungen) | Kontrollieren Sie, welche Dateisystem-Einstellungen geladen werden. Wenn weggelassen, werden keine Einstellungen geladen. Hinweis: Muss "project" enthalten, um CLAUDE.md-Dateien zu laden |
max_thinking_tokens | int | None | None | Maximale Token für Thinking-Blöcke |
list[str] | None| Verzeichnisse für Add/Remove-Verzeichnis-Operationen |
destination | Literal[...] | None | Wo die Berechtigungsaktualisierung angewendet werden soll |
allowUnsandboxedCommands | bool | False | Ermöglichen Sie dem Modell, die Ausführung von Befehlen außerhalb der Sandbox anzufordern. Wenn True, kann das Modell dangerouslyDisableSandbox in der Tool-Eingabe setzen, was auf das Berechtigungssystem zurückfällt |
network | SandboxNetworkConfig | None | Netzwerkspezifische Sandbox-Konfiguration |
ignoreViolations | SandboxIgnoreViolations | None | Konfigurieren Sie, welche Sandbox-Verstöße ignoriert werden sollen |
enableWeakerNestedSandbox | bool | False | Aktivieren Sie eine schwächere verschachtelte Sandbox für Kompatibilität |
httpProxyPortint |
None |
| HTTP-Proxy-Port für Netzwerkanfragen |
socksProxyPort | int | None | SOCKS-Proxy-Port für Netzwerkanfragen |