1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Agent SDK reference - Python
6
7> Complete API reference for the Python Agent SDK, including all functions, types, and classes.
8
9## Installation
10
11```bash theme={null}
12pip install claude-agent-sdk
13```
14
15## Choosing between `query()` and `ClaudeSDKClient`
16
17The Python SDK provides two ways to interact with Claude Code:
18
19### Quick comparison
20
21| Feature | `query()` | `ClaudeSDKClient` |
22| :------------------ | :---------------------------- | :--------------------------------- |
23| **Session** | Creates new session each time | Reuses same session |
24| **Conversation** | Single exchange | Multiple exchanges in same context |
25| **Connection** | Managed automatically | Manual control |
26| **Streaming Input** | ✅ Supported | ✅ Supported |
27| **Interrupts** | ❌ Not supported | ✅ Supported |
28| **Hooks** | ✅ Supported | ✅ Supported |
29| **Custom Tools** | ✅ Supported | ✅ Supported |
30| **Continue Chat** | ❌ New session each time | ✅ Maintains conversation |
31| **Use Case** | One-off tasks | Continuous conversations |
32
33### When to use `query()` (new session each time)
34
35**Best for:**
36
37* One-off questions where you don't need conversation history
38* Independent tasks that don't require context from previous exchanges
39* Simple automation scripts
40* When you want a fresh start each time
41
42### When to use `ClaudeSDKClient` (continuous conversation)
43
44**Best for:**
45
46* **Continuing conversations** - When you need Claude to remember context
47* **Follow-up questions** - Building on previous responses
48* **Interactive applications** - Chat interfaces, REPLs
49* **Response-driven logic** - When next action depends on Claude's response
50* **Session control** - Managing conversation lifecycle explicitly
51
52## Functions
53
54### `query()`
55
56Creates a new session for each interaction with Claude Code. Returns an async iterator that yields messages as they arrive. Each call to `query()` starts fresh with no memory of previous interactions.
57
58```python theme={null}
59async def query(
60 *,
61 prompt: str | AsyncIterable[dict[str, Any]],
62 options: ClaudeAgentOptions | None = None,
63 transport: Transport | None = None
64) -> AsyncIterator[Message]
65```
66
67#### Parameters
68
69| Parameter | Type | Description |
70| :---------- | :--------------------------- | :------------------------------------------------------------------------- |
71| `prompt` | `str \| AsyncIterable[dict]` | The input prompt as a string or async iterable for streaming mode |
72| `options` | `ClaudeAgentOptions \| None` | Optional configuration object (defaults to `ClaudeAgentOptions()` if None) |
73| `transport` | `Transport \| None` | Optional custom transport for communicating with the CLI process |
74
75#### Returns
76
77Returns an `AsyncIterator[Message]` that yields messages from the conversation.
78
79#### Example - With options
80
81```python theme={null}
82import asyncio
83from claude_agent_sdk import query, ClaudeAgentOptions
84
85
86async def main():
87 options = ClaudeAgentOptions(
88 system_prompt="You are an expert Python developer",
89 permission_mode="acceptEdits",
90 cwd="/home/user/project",
91 )
92
93 async for message in query(prompt="Create a Python web server", options=options):
94 print(message)
95
96
97asyncio.run(main())
98```
99
100### `tool()`
101
102Decorator for defining MCP tools with type safety.
103
104```python theme={null}
105def tool(
106 name: str,
107 description: str,
108 input_schema: type | dict[str, Any],
109 annotations: ToolAnnotations | None = None
110) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]
111```
112
113#### Parameters
114
115| Parameter | Type | Description |
116| :------------- | :----------------------------------------------- | :------------------------------------------------------------------ |
117| `name` | `str` | Unique identifier for the tool |
118| `description` | `str` | Human-readable description of what the tool does |
119| `input_schema` | `type \| dict[str, Any]` | Schema defining the tool's input parameters (see below) |
120| `annotations` | [`ToolAnnotations`](#tool-annotations)` \| None` | Optional MCP tool annotations providing behavioral hints to clients |
121
122#### Input schema options
123
1241. **Simple type mapping** (recommended):
125
126 ```python theme={null}
127 {"text": str, "count": int, "enabled": bool}
128 ```
129
1302. **JSON Schema format** (for complex validation):
131 ```python theme={null}
132 {
133 "type": "object",
134 "properties": {
135 "text": {"type": "string"},
136 "count": {"type": "integer", "minimum": 0},
137 },
138 "required": ["text"],
139 }
140 ```
141
142#### Returns
143
144A decorator function that wraps the tool implementation and returns an `SdkMcpTool` instance.
145
146#### Example
147
148```python theme={null}
149from claude_agent_sdk import tool
150from typing import Any
151
152
153@tool("greet", "Greet a user", {"name": str})
154async def greet(args: dict[str, Any]) -> dict[str, Any]:
155 return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}
156```
157
158#### `ToolAnnotations`
159
160Re-exported from `mcp.types` (also available as `from claude_agent_sdk import ToolAnnotations`). All fields are optional hints; clients should not rely on them for security decisions.
161
162| Field | Type | Default | Description |
163| :---------------- | :------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
164| `title` | `str \| None` | `None` | Human-readable title for the tool |
165| `readOnlyHint` | `bool \| None` | `False` | If `True`, the tool does not modify its environment |
166| `destructiveHint` | `bool \| None` | `True` | If `True`, the tool may perform destructive updates (only meaningful when `readOnlyHint` is `False`) |
167| `idempotentHint` | `bool \| None` | `False` | If `True`, repeated calls with the same arguments have no additional effect (only meaningful when `readOnlyHint` is `False`) |
168| `openWorldHint` | `bool \| None` | `True` | If `True`, the tool interacts with external entities (for example, web search). If `False`, the tool's domain is closed (for example, a memory tool) |
169
170```python theme={null}
171from claude_agent_sdk import tool, ToolAnnotations
172from typing import Any
173
174
175@tool(
176 "search",
177 "Search the web",
178 {"query": str},
179 annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
180)
181async def search(args: dict[str, Any]) -> dict[str, Any]:
182 return {"content": [{"type": "text", "text": f"Results for: {args['query']}"}]}
183```
184
185### `create_sdk_mcp_server()`
186
187Create an in-process MCP server that runs within your Python application.
188
189```python theme={null}
190def create_sdk_mcp_server(
191 name: str,
192 version: str = "1.0.0",
193 tools: list[SdkMcpTool[Any]] | None = None
194) -> McpSdkServerConfig
195```
196
197#### Parameters
198
199| Parameter | Type | Default | Description |
200| :-------- | :------------------------------ | :-------- | :---------------------------------------------------- |
201| `name` | `str` | - | Unique identifier for the server |
202| `version` | `str` | `"1.0.0"` | Server version string |
203| `tools` | `list[SdkMcpTool[Any]] \| None` | `None` | List of tool functions created with `@tool` decorator |
204
205#### Returns
206
207Returns an `McpSdkServerConfig` object that can be passed to `ClaudeAgentOptions.mcp_servers`.
208
209#### Example
210
211```python theme={null}
212from claude_agent_sdk import tool, create_sdk_mcp_server
213
214
215@tool("add", "Add two numbers", {"a": float, "b": float})
216async def add(args):
217 return {"content": [{"type": "text", "text": f"Sum: {args['a'] + args['b']}"}]}
218
219
220@tool("multiply", "Multiply two numbers", {"a": float, "b": float})
221async def multiply(args):
222 return {"content": [{"type": "text", "text": f"Product: {args['a'] * args['b']}"}]}
223
224
225calculator = create_sdk_mcp_server(
226 name="calculator",
227 version="2.0.0",
228 tools=[add, multiply], # Pass decorated functions
229)
230
231# Use with Claude
232options = ClaudeAgentOptions(
233 mcp_servers={"calc": calculator},
234 allowed_tools=["mcp__calc__add", "mcp__calc__multiply"],
235)
236```
237
238### `list_sessions()`
239
240Lists past sessions with metadata. Filter by project directory or list sessions across all projects. Synchronous; returns immediately.
241
242```python theme={null}
243def list_sessions(
244 directory: str | None = None,
245 limit: int | None = None,
246 include_worktrees: bool = True
247) -> list[SDKSessionInfo]
248```
249
250#### Parameters
251
252| Parameter | Type | Default | Description |
253| :------------------ | :------------ | :------ | :------------------------------------------------------------------------------------ |
254| `directory` | `str \| None` | `None` | Directory to list sessions for. When omitted, returns sessions across all projects |
255| `limit` | `int \| None` | `None` | Maximum number of sessions to return |
256| `include_worktrees` | `bool` | `True` | When `directory` is inside a git repository, include sessions from all worktree paths |
257
258#### Return type: `SDKSessionInfo`
259
260| Property | Type | Description |
261| :-------------- | :------------ | :------------------------------------------------------------------- |
262| `session_id` | `str` | Unique session identifier |
263| `summary` | `str` | Display title: custom title, auto-generated summary, or first prompt |
264| `last_modified` | `int` | Last modified time in milliseconds since epoch |
265| `file_size` | `int \| None` | Session file size in bytes (`None` for remote storage backends) |
266| `custom_title` | `str \| None` | User-set session title |
267| `first_prompt` | `str \| None` | First meaningful user prompt in the session |
268| `git_branch` | `str \| None` | Git branch at the end of the session |
269| `cwd` | `str \| None` | Working directory for the session |
270| `tag` | `str \| None` | User-set session tag (see [`tag_session()`](#tag-session)) |
271| `created_at` | `int \| None` | Session creation time in milliseconds since epoch |
272
273#### Example
274
275Print the 10 most recent sessions for a project. Results are sorted by `last_modified` descending, so the first item is the newest. Omit `directory` to search across all projects.
276
277```python theme={null}
278from claude_agent_sdk import list_sessions
279
280for session in list_sessions(directory="/path/to/project", limit=10):
281 print(f"{session.summary} ({session.session_id})")
282```
283
284### `get_session_messages()`
285
286Retrieves messages from a past session. Synchronous; returns immediately.
287
288```python theme={null}
289def get_session_messages(
290 session_id: str,
291 directory: str | None = None,
292 limit: int | None = None,
293 offset: int = 0
294) -> list[SessionMessage]
295```
296
297#### Parameters
298
299| Parameter | Type | Default | Description |
300| :----------- | :------------ | :------- | :---------------------------------------------------------------- |
301| `session_id` | `str` | required | The session ID to retrieve messages for |
302| `directory` | `str \| None` | `None` | Project directory to look in. When omitted, searches all projects |
303| `limit` | `int \| None` | `None` | Maximum number of messages to return |
304| `offset` | `int` | `0` | Number of messages to skip from the start |
305
306#### Return type: `SessionMessage`
307
308| Property | Type | Description |
309| :------------------- | :----------------------------- | :------------------------ |
310| `type` | `Literal["user", "assistant"]` | Message role |
311| `uuid` | `str` | Unique message identifier |
312| `session_id` | `str` | Session identifier |
313| `message` | `Any` | Raw message content |
314| `parent_tool_use_id` | `None` | Reserved for future use |
315
316#### Example
317
318```python theme={null}
319from claude_agent_sdk import list_sessions, get_session_messages
320
321sessions = list_sessions(limit=1)
322if sessions:
323 messages = get_session_messages(sessions[0].session_id)
324 for msg in messages:
325 print(f"[{msg.type}] {msg.uuid}")
326```
327
328### `get_session_info()`
329
330Reads metadata for a single session by ID without scanning the full project directory. Synchronous; returns immediately.
331
332```python theme={null}
333def get_session_info(
334 session_id: str,
335 directory: str | None = None,
336) -> SDKSessionInfo | None
337```
338
339#### Parameters
340
341| Parameter | Type | Default | Description |
342| :----------- | :------------ | :------- | :--------------------------------------------------------------------- |
343| `session_id` | `str` | required | UUID of the session to look up |
344| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |
345
346Returns [`SDKSessionInfo`](#return-type-sdk-session-info), or `None` if the session is not found.
347
348#### Example
349
350Look up a single session's metadata without scanning the project directory. Useful when you already have a session ID from a previous run.
351
352```python theme={null}
353from claude_agent_sdk import get_session_info
354
355info = get_session_info("550e8400-e29b-41d4-a716-446655440000")
356if info:
357 print(f"{info.summary} (branch: {info.git_branch}, tag: {info.tag})")
358```
359
360### `rename_session()`
361
362Renames a session by appending a custom-title entry. Repeated calls are safe; the most recent title wins. Synchronous.
363
364```python theme={null}
365def rename_session(
366 session_id: str,
367 title: str,
368 directory: str | None = None,
369) -> None
370```
371
372#### Parameters
373
374| Parameter | Type | Default | Description |
375| :----------- | :------------ | :------- | :--------------------------------------------------------------------- |
376| `session_id` | `str` | required | UUID of the session to rename |
377| `title` | `str` | required | New title. Must be non-empty after stripping whitespace |
378| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |
379
380Raises `ValueError` if `session_id` is not a valid UUID or `title` is empty; `FileNotFoundError` if the session cannot be found.
381
382#### Example
383
384Rename the most recent session so it's easier to find later. The new title appears in [`SDKSessionInfo.custom_title`](#return-type-sdk-session-info) on subsequent reads.
385
386```python theme={null}
387from claude_agent_sdk import list_sessions, rename_session
388
389sessions = list_sessions(directory="/path/to/project", limit=1)
390if sessions:
391 rename_session(sessions[0].session_id, "Refactor auth module")
392```
393
394### `tag_session()`
395
396Tags a session. Pass `None` to clear the tag. Repeated calls are safe; the most recent tag wins. Synchronous.
397
398```python theme={null}
399def tag_session(
400 session_id: str,
401 tag: str | None,
402 directory: str | None = None,
403) -> None
404```
405
406#### Parameters
407
408| Parameter | Type | Default | Description |
409| :----------- | :------------ | :------- | :--------------------------------------------------------------------- |
410| `session_id` | `str` | required | UUID of the session to tag |
411| `tag` | `str \| None` | required | Tag string, or `None` to clear. Unicode-sanitized before storing |
412| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |
413
414Raises `ValueError` if `session_id` is not a valid UUID or `tag` is empty after sanitization; `FileNotFoundError` if the session cannot be found.
415
416#### Example
417
418Tag a session, then filter by that tag on a later read. Pass `None` to clear an existing tag.
419
420```python theme={null}
421from claude_agent_sdk import list_sessions, tag_session
422
423# Tag a session
424tag_session("550e8400-e29b-41d4-a716-446655440000", "needs-review")
425
426# Later: find all sessions with that tag
427for session in list_sessions(directory="/path/to/project"):
428 if session.tag == "needs-review":
429 print(session.summary)
430```
431
432## Classes
433
434### `ClaudeSDKClient`
435
436**Maintains a conversation session across multiple exchanges.** This is the Python equivalent of how the TypeScript SDK's `query()` function works internally - it creates a client object that can continue conversations.
437
438#### Key Features
439
440* **Session continuity**: Maintains conversation context across multiple `query()` calls
441* **Same conversation**: The session retains previous messages
442* **Interrupt support**: Can stop execution mid-task
443* **Explicit lifecycle**: You control when the session starts and ends
444* **Response-driven flow**: Can react to responses and send follow-ups
445* **Custom tools and hooks**: Supports custom tools (created with `@tool` decorator) and hooks
446
447```python theme={null}
448class ClaudeSDKClient:
449 def __init__(self, options: ClaudeAgentOptions | None = None, transport: Transport | None = None)
450 async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None
451 async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None
452 async def receive_messages(self) -> AsyncIterator[Message]
453 async def receive_response(self) -> AsyncIterator[Message]
454 async def interrupt(self) -> None
455 async def set_permission_mode(self, mode: str) -> None
456 async def set_model(self, model: str | None = None) -> None
457 async def rewind_files(self, user_message_id: str) -> None
458 async def get_mcp_status(self) -> McpStatusResponse
459 async def reconnect_mcp_server(self, server_name: str) -> None
460 async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None
461 async def stop_task(self, task_id: str) -> None
462 async def get_server_info(self) -> dict[str, Any] | None
463 async def disconnect(self) -> None
464```
465
466#### Methods
467
468| Method | Description |
469| :---------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
470| `__init__(options)` | Initialize the client with optional configuration |
471| `connect(prompt)` | Connect to Claude with an optional initial prompt or message stream |
472| `query(prompt, session_id)` | Send a new request in streaming mode |
473| `receive_messages()` | Receive all messages from Claude as an async iterator |
474| `receive_response()` | Receive messages until and including a ResultMessage |
475| `interrupt()` | Send interrupt signal (only works in streaming mode) |
476| `set_permission_mode(mode)` | Change the permission mode for the current session |
477| `set_model(model)` | Change the model for the current session. Pass `None` to reset to default |
478| `rewind_files(user_message_id)` | Restore files to their state at the specified user message. Requires `enable_file_checkpointing=True`. See [File checkpointing](/en/agent-sdk/file-checkpointing) |
479| `get_mcp_status()` | Get the status of all configured MCP servers. Returns [`McpStatusResponse`](#mcp-status-response) |
480| `reconnect_mcp_server(server_name)` | Retry connecting to an MCP server that failed or was disconnected |
481| `toggle_mcp_server(server_name, enabled)` | Enable or disable an MCP server mid-session. Disabling removes its tools |
482| `stop_task(task_id)` | Stop a running background task. A [`TaskNotificationMessage`](#task-notification-message) with status `"stopped"` follows in the message stream |
483| `get_server_info()` | Get server information including session ID and capabilities |
484| `disconnect()` | Disconnect from Claude |
485
486#### Context Manager Support
487
488The client can be used as an async context manager for automatic connection management:
489
490```python theme={null}
491async with ClaudeSDKClient() as client:
492 await client.query("Hello Claude")
493 async for message in client.receive_response():
494 print(message)
495```
496
497> **Important:** When iterating over messages, avoid using `break` to exit early as this can cause asyncio cleanup issues. Instead, let the iteration complete naturally or use flags to track when you've found what you need.
498
499#### Example - Continuing a conversation
500
501```python theme={null}
502import asyncio
503from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
504
505
506async def main():
507 async with ClaudeSDKClient() as client:
508 # First question
509 await client.query("What's the capital of France?")
510
511 # Process response
512 async for message in client.receive_response():
513 if isinstance(message, AssistantMessage):
514 for block in message.content:
515 if isinstance(block, TextBlock):
516 print(f"Claude: {block.text}")
517
518 # Follow-up question - the session retains the previous context
519 await client.query("What's the population of that city?")
520
521 async for message in client.receive_response():
522 if isinstance(message, AssistantMessage):
523 for block in message.content:
524 if isinstance(block, TextBlock):
525 print(f"Claude: {block.text}")
526
527 # Another follow-up - still in the same conversation
528 await client.query("What are some famous landmarks there?")
529
530 async for message in client.receive_response():
531 if isinstance(message, AssistantMessage):
532 for block in message.content:
533 if isinstance(block, TextBlock):
534 print(f"Claude: {block.text}")
535
536
537asyncio.run(main())
538```
539
540#### Example - Streaming input with ClaudeSDKClient
541
542```python theme={null}
543import asyncio
544from claude_agent_sdk import ClaudeSDKClient
545
546
547async def message_stream():
548 """Generate messages dynamically."""
549 yield {
550 "type": "user",
551 "message": {"role": "user", "content": "Analyze the following data:"},
552 }
553 await asyncio.sleep(0.5)
554 yield {
555 "type": "user",
556 "message": {"role": "user", "content": "Temperature: 25°C, Humidity: 60%"},
557 }
558 await asyncio.sleep(0.5)
559 yield {
560 "type": "user",
561 "message": {"role": "user", "content": "What patterns do you see?"},
562 }
563
564
565async def main():
566 async with ClaudeSDKClient() as client:
567 # Stream input to Claude
568 await client.query(message_stream())
569
570 # Process response
571 async for message in client.receive_response():
572 print(message)
573
574 # Follow-up in same session
575 await client.query("Should we be concerned about these readings?")
576
577 async for message in client.receive_response():
578 print(message)
579
580
581asyncio.run(main())
582```
583
584#### Example - Using interrupts
585
586```python theme={null}
587import asyncio
588from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage
589
590
591async def interruptible_task():
592 options = ClaudeAgentOptions(allowed_tools=["Bash"], permission_mode="acceptEdits")
593
594 async with ClaudeSDKClient(options=options) as client:
595 # Start a long-running task
596 await client.query("Count from 1 to 100 slowly, using the bash sleep command")
597
598 # Let it run for a bit
599 await asyncio.sleep(2)
600
601 # Interrupt the task
602 await client.interrupt()
603 print("Task interrupted!")
604
605 # Drain the interrupted task's messages (including its ResultMessage)
606 async for message in client.receive_response():
607 if isinstance(message, ResultMessage):
608 print(f"Interrupted task finished with subtype={message.subtype!r}")
609 # subtype is "error_during_execution" for interrupted tasks
610
611 # Send a new command
612 await client.query("Just say hello instead")
613
614 # Now receive the new response
615 async for message in client.receive_response():
616 if isinstance(message, ResultMessage) and message.subtype == "success":
617 print(f"New result: {message.result}")
618
619
620asyncio.run(interruptible_task())
621```
622
623<Note>
624 **Buffer behavior after interrupt:** `interrupt()` sends a stop signal but does not clear the message buffer. Messages already produced by the interrupted task, including its `ResultMessage` (with `subtype="error_during_execution"`), remain in the stream. You must drain them with `receive_response()` before reading the response to a new query. If you send a new query immediately after `interrupt()` and call `receive_response()` only once, you'll receive the interrupted task's messages, not the new query's response.
625</Note>
626
627#### Example - Advanced permission control
628
629```python theme={null}
630from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
631from claude_agent_sdk.types import (
632 PermissionResultAllow,
633 PermissionResultDeny,
634 ToolPermissionContext,
635)
636
637
638async def custom_permission_handler(
639 tool_name: str, input_data: dict, context: ToolPermissionContext
640) -> PermissionResultAllow | PermissionResultDeny:
641 """Custom logic for tool permissions."""
642
643 # Block writes to system directories
644 if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
645 return PermissionResultDeny(
646 message="System directory write not allowed", interrupt=True
647 )
648
649 # Redirect sensitive file operations
650 if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):
651 safe_path = f"./sandbox/{input_data['file_path']}"
652 return PermissionResultAllow(
653 updated_input={**input_data, "file_path": safe_path}
654 )
655
656 # Allow everything else
657 return PermissionResultAllow(updated_input=input_data)
658
659
660async def main():
661 options = ClaudeAgentOptions(
662 can_use_tool=custom_permission_handler, allowed_tools=["Read", "Write", "Edit"]
663 )
664
665 async with ClaudeSDKClient(options=options) as client:
666 await client.query("Update the system config file")
667
668 async for message in client.receive_response():
669 # Will use sandbox path instead
670 print(message)
671
672
673asyncio.run(main())
674```
675
676## Types
677
678<Note>
679 **`@dataclass` vs `TypedDict`:** This SDK uses two kinds of types. Classes decorated with `@dataclass` (such as `ResultMessage`, `AgentDefinition`, `TextBlock`) are object instances at runtime and support attribute access: `msg.result`. Classes defined with `TypedDict` (such as `ThinkingConfigEnabled`, `McpStdioServerConfig`, `SyncHookJSONOutput`) are **plain dicts at runtime** and require key access: `config["budget_tokens"]`, not `config.budget_tokens`. The `ClassName(field=value)` call syntax works for both, but only dataclasses produce objects with attributes.
680</Note>
681
682### `SdkMcpTool`
683
684Definition for an SDK MCP tool created with the `@tool` decorator.
685
686```python theme={null}
687@dataclass
688class SdkMcpTool(Generic[T]):
689 name: str
690 description: str
691 input_schema: type[T] | dict[str, Any]
692 handler: Callable[[T], Awaitable[dict[str, Any]]]
693 annotations: ToolAnnotations | None = None
694```
695
696| Property | Type | Description |
697| :------------- | :----------------------------------------- | :--------------------------------------------------------------------------------------------------------- |
698| `name` | `str` | Unique identifier for the tool |
699| `description` | `str` | Human-readable description |
700| `input_schema` | `type[T] \| dict[str, Any]` | Schema for input validation |
701| `handler` | `Callable[[T], Awaitable[dict[str, Any]]]` | Async function that handles tool execution |
702| `annotations` | `ToolAnnotations \| None` | Optional MCP tool annotations (e.g., `readOnlyHint`, `destructiveHint`, `openWorldHint`). From `mcp.types` |
703
704### `Transport`
705
706Abstract base class for custom transport implementations. Use this to communicate with the Claude process over a custom channel (for example, a remote connection instead of a local subprocess).
707
708<Warning>
709 This is a low-level internal API. The interface may change in future releases. Custom implementations must be updated to match any interface changes.
710</Warning>
711
712```python theme={null}
713from abc import ABC, abstractmethod
714from collections.abc import AsyncIterator
715from typing import Any
716
717
718class Transport(ABC):
719 @abstractmethod
720 async def connect(self) -> None: ...
721
722 @abstractmethod
723 async def write(self, data: str) -> None: ...
724
725 @abstractmethod
726 def read_messages(self) -> AsyncIterator[dict[str, Any]]: ...
727
728 @abstractmethod
729 async def close(self) -> None: ...
730
731 @abstractmethod
732 def is_ready(self) -> bool: ...
733
734 @abstractmethod
735 async def end_input(self) -> None: ...
736```
737
738| Method | Description |
739| :---------------- | :-------------------------------------------------------------------------- |
740| `connect()` | Connect the transport and prepare for communication |
741| `write(data)` | Write raw data (JSON + newline) to the transport |
742| `read_messages()` | Async iterator that yields parsed JSON messages |
743| `close()` | Close the connection and clean up resources |
744| `is_ready()` | Returns `True` if the transport can send and receive |
745| `end_input()` | Close the input stream (for example, close stdin for subprocess transports) |
746
747Import: `from claude_agent_sdk import Transport`
748
749### `ClaudeAgentOptions`
750
751Configuration dataclass for Claude Code queries.
752
753```python theme={null}
754@dataclass
755class ClaudeAgentOptions:
756 tools: list[str] | ToolsPreset | None = None
757 allowed_tools: list[str] = field(default_factory=list)
758 system_prompt: str | SystemPromptPreset | None = None
759 mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
760 permission_mode: PermissionMode | None = None
761 continue_conversation: bool = False
762 resume: str | None = None
763 max_turns: int | None = None
764 max_budget_usd: float | None = None
765 disallowed_tools: list[str] = field(default_factory=list)
766 model: str | None = None
767 fallback_model: str | None = None
768 betas: list[SdkBeta] = field(default_factory=list)
769 output_format: dict[str, Any] | None = None
770 permission_prompt_tool_name: str | None = None
771 cwd: str | Path | None = None
772 cli_path: str | Path | None = None
773 settings: str | None = None
774 add_dirs: list[str | Path] = field(default_factory=list)
775 env: dict[str, str] = field(default_factory=dict)
776 extra_args: dict[str, str | None] = field(default_factory=dict)
777 max_buffer_size: int | None = None
778 debug_stderr: Any = sys.stderr # Deprecated
779 stderr: Callable[[str], None] | None = None
780 can_use_tool: CanUseTool | None = None
781 hooks: dict[HookEvent, list[HookMatcher]] | None = None
782 user: str | None = None
783 include_partial_messages: bool = False
784 fork_session: bool = False
785 agents: dict[str, AgentDefinition] | None = None
786 setting_sources: list[SettingSource] | None = None
787 sandbox: SandboxSettings | None = None
788 plugins: list[SdkPluginConfig] = field(default_factory=list)
789 max_thinking_tokens: int | None = None # Deprecated: use thinking instead
790 thinking: ThinkingConfig | None = None
791 effort: Literal["low", "medium", "high", "max"] | None = None
792 enable_file_checkpointing: bool = False
793```
794
795| Property | Type | Default | Description |
796| :---------------------------- | :------------------------------------------------ | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
797| `tools` | `list[str] \| ToolsPreset \| None` | `None` | Tools configuration. Use `{"type": "preset", "preset": "claude_code"}` for Claude Code's default tools |
798| `allowed_tools` | `list[str]` | `[]` | Tools to auto-approve without prompting. This does not restrict Claude to only these tools; unlisted tools fall through to `permission_mode` and `can_use_tool`. Use `disallowed_tools` to block tools. See [Permissions](/en/agent-sdk/permissions#allow-and-deny-rules) |
799| `system_prompt` | `str \| SystemPromptPreset \| None` | `None` | System prompt configuration. Pass a string for custom prompt, or use `{"type": "preset", "preset": "claude_code"}` for Claude Code's system prompt. Add `"append"` to extend the preset |
800| `mcp_servers` | `dict[str, McpServerConfig] \| str \| Path` | `{}` | MCP server configurations or path to config file |
801| `permission_mode` | `PermissionMode \| None` | `None` | Permission mode for tool usage |
802| `continue_conversation` | `bool` | `False` | Continue the most recent conversation |
803| `resume` | `str \| None` | `None` | Session ID to resume |
804| `max_turns` | `int \| None` | `None` | Maximum agentic turns (tool-use round trips) |
805| `max_budget_usd` | `float \| None` | `None` | Maximum budget in USD for the session |
806| `disallowed_tools` | `list[str]` | `[]` | Tools to always deny. Deny rules are checked first and override `allowed_tools` and `permission_mode` (including `bypassPermissions`) |
807| `enable_file_checkpointing` | `bool` | `False` | Enable file change tracking for rewinding. See [File checkpointing](/en/agent-sdk/file-checkpointing) |
808| `model` | `str \| None` | `None` | Claude model to use |
809| `fallback_model` | `str \| None` | `None` | Fallback model to use if the primary model fails |
810| `betas` | `list[SdkBeta]` | `[]` | Beta features to enable. See [`SdkBeta`](#sdk-beta) for available options |
811| `output_format` | `dict[str, Any] \| None` | `None` | Output format for structured responses (e.g., `{"type": "json_schema", "schema": {...}}`). See [Structured outputs](/en/agent-sdk/structured-outputs) for details |
812| `permission_prompt_tool_name` | `str \| None` | `None` | MCP tool name for permission prompts |
813| `cwd` | `str \| Path \| None` | `None` | Current working directory |
814| `cli_path` | `str \| Path \| None` | `None` | Custom path to the Claude Code CLI executable |
815| `settings` | `str \| None` | `None` | Path to settings file |
816| `add_dirs` | `list[str \| Path]` | `[]` | Additional directories Claude can access |
817| `env` | `dict[str, str]` | `{}` | Environment variables |
818| `extra_args` | `dict[str, str \| None]` | `{}` | Additional CLI arguments to pass directly to the CLI |
819| `max_buffer_size` | `int \| None` | `None` | Maximum bytes when buffering CLI stdout |
820| `debug_stderr` | `Any` | `sys.stderr` | *Deprecated* - File-like object for debug output. Use `stderr` callback instead |
821| `stderr` | `Callable[[str], None] \| None` | `None` | Callback function for stderr output from CLI |
822| `can_use_tool` | [`CanUseTool`](#can-use-tool) ` \| None` | `None` | Tool permission callback function. See [Permission types](#can-use-tool) for details |
823| `hooks` | `dict[HookEvent, list[HookMatcher]] \| None` | `None` | Hook configurations for intercepting events |
824| `user` | `str \| None` | `None` | User identifier |
825| `include_partial_messages` | `bool` | `False` | Include partial message streaming events. When enabled, [`StreamEvent`](#stream-event) messages are yielded |
826| `fork_session` | `bool` | `False` | When resuming with `resume`, fork to a new session ID instead of continuing the original session |
827| `agents` | `dict[str, AgentDefinition] \| None` | `None` | Programmatically defined subagents |
828| `plugins` | `list[SdkPluginConfig]` | `[]` | Load custom plugins from local paths. See [Plugins](/en/agent-sdk/plugins) for details |
829| `sandbox` | [`SandboxSettings`](#sandbox-settings) ` \| None` | `None` | Configure sandbox behavior programmatically. See [Sandbox settings](#sandbox-settings) for details |
830| `setting_sources` | `list[SettingSource] \| None` | `None` (no settings) | Control which filesystem settings to load. When omitted, no settings are loaded. **Note:** Must include `"project"` to load CLAUDE.md files |
831| `max_thinking_tokens` | `int \| None` | `None` | *Deprecated* - Maximum tokens for thinking blocks. Use `thinking` instead |
832| `thinking` | [`ThinkingConfig`](#thinking-config) ` \| None` | `None` | Controls extended thinking behavior. Takes precedence over `max_thinking_tokens` |
833| `effort` | `Literal["low", "medium", "high", "max"] \| None` | `None` | Effort level for thinking depth |
834
835### `OutputFormat`
836
837Configuration for structured output validation. Pass this as a `dict` to the `output_format` field on `ClaudeAgentOptions`:
838
839```python theme={null}
840# Expected dict shape for output_format
841{
842 "type": "json_schema",
843 "schema": {...}, # Your JSON Schema definition
844}
845```
846
847| Field | Required | Description |
848| :------- | :------- | :------------------------------------------------- |
849| `type` | Yes | Must be `"json_schema"` for JSON Schema validation |
850| `schema` | Yes | JSON Schema definition for output validation |
851
852### `SystemPromptPreset`
853
854Configuration for using Claude Code's preset system prompt with optional additions.
855
856```python theme={null}
857class SystemPromptPreset(TypedDict):
858 type: Literal["preset"]
859 preset: Literal["claude_code"]
860 append: NotRequired[str]
861```
862
863| Field | Required | Description |
864| :------- | :------- | :------------------------------------------------------------ |
865| `type` | Yes | Must be `"preset"` to use a preset system prompt |
866| `preset` | Yes | Must be `"claude_code"` to use Claude Code's system prompt |
867| `append` | No | Additional instructions to append to the preset system prompt |
868
869### `SettingSource`
870
871Controls which filesystem-based configuration sources the SDK loads settings from.
872
873```python theme={null}
874SettingSource = Literal["user", "project", "local"]
875```
876
877| Value | Description | Location |
878| :---------- | :------------------------------------------- | :---------------------------- |
879| `"user"` | Global user settings | `~/.claude/settings.json` |
880| `"project"` | Shared project settings (version controlled) | `.claude/settings.json` |
881| `"local"` | Local project settings (gitignored) | `.claude/settings.local.json` |
882
883#### Default behavior
884
885When `setting_sources` is **omitted** or **`None`**, the SDK does **not** load any filesystem settings. This provides isolation for SDK applications.
886
887#### Why use setting\_sources
888
889**Load all filesystem settings (legacy behavior):**
890
891```python theme={null}
892# Load all settings like SDK v0.0.x did
893from claude_agent_sdk import query, ClaudeAgentOptions
894
895async for message in query(
896 prompt="Analyze this code",
897 options=ClaudeAgentOptions(
898 setting_sources=["user", "project", "local"] # Load all settings
899 ),
900):
901 print(message)
902```
903
904**Load only specific setting sources:**
905
906```python theme={null}
907# Load only project settings, ignore user and local
908async for message in query(
909 prompt="Run CI checks",
910 options=ClaudeAgentOptions(
911 setting_sources=["project"] # Only .claude/settings.json
912 ),
913):
914 print(message)
915```
916
917**Testing and CI environments:**
918
919```python theme={null}
920# Ensure consistent behavior in CI by excluding local settings
921async for message in query(
922 prompt="Run tests",
923 options=ClaudeAgentOptions(
924 setting_sources=["project"], # Only team-shared settings
925 permission_mode="bypassPermissions",
926 ),
927):
928 print(message)
929```
930
931**SDK-only applications:**
932
933```python theme={null}
934# Define everything programmatically (default behavior)
935# No filesystem dependencies - setting_sources defaults to None
936async for message in query(
937 prompt="Review this PR",
938 options=ClaudeAgentOptions(
939 # setting_sources=None is the default, no need to specify
940 agents={...},
941 mcp_servers={...},
942 allowed_tools=["Read", "Grep", "Glob"],
943 ),
944):
945 print(message)
946```
947
948**Loading CLAUDE.md project instructions:**
949
950```python theme={null}
951# Load project settings to include CLAUDE.md files
952async for message in query(
953 prompt="Add a new feature following project conventions",
954 options=ClaudeAgentOptions(
955 system_prompt={
956 "type": "preset",
957 "preset": "claude_code", # Use Claude Code's system prompt
958 },
959 setting_sources=["project"], # Required to load CLAUDE.md from project
960 allowed_tools=["Read", "Write", "Edit"],
961 ),
962):
963 print(message)
964```
965
966#### Settings precedence
967
968When multiple sources are loaded, settings are merged with this precedence (highest to lowest):
969
9701. Local settings (`.claude/settings.local.json`)
9712. Project settings (`.claude/settings.json`)
9723. User settings (`~/.claude/settings.json`)
973
974Programmatic options (like `agents`, `allowed_tools`) always override filesystem settings.
975
976### `AgentDefinition`
977
978Configuration for a subagent defined programmatically.
979
980```python theme={null}
981@dataclass
982class AgentDefinition:
983 description: str
984 prompt: str
985 tools: list[str] | None = None
986 model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None
987 skills: list[str] | None = None
988 memory: Literal["user", "project", "local"] | None = None
989 mcpServers: list[str | dict[str, Any]] | None = None
990```
991
992| Field | Required | Description |
993| :------------ | :------- | :-------------------------------------------------------------------------------------------------- |
994| `description` | Yes | Natural language description of when to use this agent |
995| `prompt` | Yes | The agent's system prompt |
996| `tools` | No | Array of allowed tool names. If omitted, inherits all tools |
997| `model` | No | Model override for this agent. If omitted, uses the main model |
998| `skills` | No | List of skill names available to this agent |
999| `memory` | No | Memory source for this agent: `"user"`, `"project"`, or `"local"` |
1000| `mcpServers` | No | MCP servers available to this agent. Each entry is a server name or an inline `{name: config}` dict |
1001
1002### `PermissionMode`
1003
1004Permission modes for controlling tool execution.
1005
1006```python theme={null}
1007PermissionMode = Literal[
1008 "default", # Standard permission behavior
1009 "acceptEdits", # Auto-accept file edits
1010 "plan", # Planning mode - no execution
1011 "dontAsk", # Deny anything not pre-approved instead of prompting
1012 "bypassPermissions", # Bypass all permission checks (use with caution)
1013]
1014```
1015
1016### `CanUseTool`
1017
1018Type alias for tool permission callback functions.
1019
1020```python theme={null}
1021CanUseTool = Callable[
1022 [str, dict[str, Any], ToolPermissionContext], Awaitable[PermissionResult]
1023]
1024```
1025
1026The callback receives:
1027
1028* `tool_name`: Name of the tool being called
1029* `input_data`: The tool's input parameters
1030* `context`: A `ToolPermissionContext` with additional information
1031
1032Returns a `PermissionResult` (either `PermissionResultAllow` or `PermissionResultDeny`).
1033
1034### `ToolPermissionContext`
1035
1036Context information passed to tool permission callbacks.
1037
1038```python theme={null}
1039@dataclass
1040class ToolPermissionContext:
1041 signal: Any | None = None # Future: abort signal support
1042 suggestions: list[PermissionUpdate] = field(default_factory=list)
1043```
1044
1045| Field | Type | Description |
1046| :------------ | :----------------------- | :----------------------------------------- |
1047| `signal` | `Any \| None` | Reserved for future abort signal support |
1048| `suggestions` | `list[PermissionUpdate]` | Permission update suggestions from the CLI |
1049
1050### `PermissionResult`
1051
1052Union type for permission callback results.
1053
1054```python theme={null}
1055PermissionResult = PermissionResultAllow | PermissionResultDeny
1056```
1057
1058### `PermissionResultAllow`
1059
1060Result indicating the tool call should be allowed.
1061
1062```python theme={null}
1063@dataclass
1064class PermissionResultAllow:
1065 behavior: Literal["allow"] = "allow"
1066 updated_input: dict[str, Any] | None = None
1067 updated_permissions: list[PermissionUpdate] | None = None
1068```
1069
1070| Field | Type | Default | Description |
1071| :-------------------- | :------------------------------- | :-------- | :---------------------------------------- |
1072| `behavior` | `Literal["allow"]` | `"allow"` | Must be "allow" |
1073| `updated_input` | `dict[str, Any] \| None` | `None` | Modified input to use instead of original |
1074| `updated_permissions` | `list[PermissionUpdate] \| None` | `None` | Permission updates to apply |
1075
1076### `PermissionResultDeny`
1077
1078Result indicating the tool call should be denied.
1079
1080```python theme={null}
1081@dataclass
1082class PermissionResultDeny:
1083 behavior: Literal["deny"] = "deny"
1084 message: str = ""
1085 interrupt: bool = False
1086```
1087
1088| Field | Type | Default | Description |
1089| :---------- | :---------------- | :------- | :----------------------------------------- |
1090| `behavior` | `Literal["deny"]` | `"deny"` | Must be "deny" |
1091| `message` | `str` | `""` | Message explaining why the tool was denied |
1092| `interrupt` | `bool` | `False` | Whether to interrupt the current execution |
1093
1094### `PermissionUpdate`
1095
1096Configuration for updating permissions programmatically.
1097
1098```python theme={null}
1099@dataclass
1100class PermissionUpdate:
1101 type: Literal[
1102 "addRules",
1103 "replaceRules",
1104 "removeRules",
1105 "setMode",
1106 "addDirectories",
1107 "removeDirectories",
1108 ]
1109 rules: list[PermissionRuleValue] | None = None
1110 behavior: Literal["allow", "deny", "ask"] | None = None
1111 mode: PermissionMode | None = None
1112 directories: list[str] | None = None
1113 destination: (
1114 Literal["userSettings", "projectSettings", "localSettings", "session"] | None
1115 ) = None
1116```
1117
1118| Field | Type | Description |
1119| :------------ | :---------------------------------------- | :---------------------------------------------- |
1120| `type` | `Literal[...]` | The type of permission update operation |
1121| `rules` | `list[PermissionRuleValue] \| None` | Rules for add/replace/remove operations |
1122| `behavior` | `Literal["allow", "deny", "ask"] \| None` | Behavior for rule-based operations |
1123| `mode` | `PermissionMode \| None` | Mode for setMode operation |
1124| `directories` | `list[str] \| None` | Directories for add/remove directory operations |
1125| `destination` | `Literal[...] \| None` | Where to apply the permission update |
1126
1127### `PermissionRuleValue`
1128
1129A rule to add, replace, or remove in a permission update.
1130
1131```python theme={null}
1132@dataclass
1133class PermissionRuleValue:
1134 tool_name: str
1135 rule_content: str | None = None
1136```
1137
1138### `ToolsPreset`
1139
1140Preset tools configuration for using Claude Code's default tool set.
1141
1142```python theme={null}
1143class ToolsPreset(TypedDict):
1144 type: Literal["preset"]
1145 preset: Literal["claude_code"]
1146```
1147
1148### `ThinkingConfig`
1149
1150Controls extended thinking behavior. A union of three configurations:
1151
1152```python theme={null}
1153class ThinkingConfigAdaptive(TypedDict):
1154 type: Literal["adaptive"]
1155
1156
1157class ThinkingConfigEnabled(TypedDict):
1158 type: Literal["enabled"]
1159 budget_tokens: int
1160
1161
1162class ThinkingConfigDisabled(TypedDict):
1163 type: Literal["disabled"]
1164
1165
1166ThinkingConfig = ThinkingConfigAdaptive | ThinkingConfigEnabled | ThinkingConfigDisabled
1167```
1168
1169| Variant | Fields | Description |
1170| :--------- | :---------------------- | :------------------------------------------- |
1171| `adaptive` | `type` | Claude adaptively decides when to think |
1172| `enabled` | `type`, `budget_tokens` | Enable thinking with a specific token budget |
1173| `disabled` | `type` | Disable thinking |
1174
1175Because these are `TypedDict` classes, they're plain dicts at runtime. Either construct them as dict literals or call the class like a constructor; both produce a `dict`. Access fields with `config["budget_tokens"]`, not `config.budget_tokens`:
1176
1177```python theme={null}
1178from claude_agent_sdk import ClaudeAgentOptions, ThinkingConfigEnabled
1179
1180# Option 1: dict literal (recommended, no import needed)
1181options = ClaudeAgentOptions(thinking={"type": "enabled", "budget_tokens": 20000})
1182
1183# Option 2: constructor-style (returns a plain dict)
1184config = ThinkingConfigEnabled(type="enabled", budget_tokens=20000)
1185print(config["budget_tokens"]) # 20000
1186# config.budget_tokens would raise AttributeError
1187```
1188
1189### `SdkBeta`
1190
1191Literal type for SDK beta features.
1192
1193```python theme={null}
1194SdkBeta = Literal["context-1m-2025-08-07"]
1195```
1196
1197Use with the `betas` field in `ClaudeAgentOptions` to enable beta features.
1198
1199<Warning>
1200 The `context-1m-2025-08-07` beta is retired as of April 30, 2026. Passing this header with Claude Sonnet 4.5 or Sonnet 4 has no effect, and requests that exceed the standard 200k-token context window return an error. To use a 1M-token context window, migrate to [Claude Sonnet 4.6 or Claude Opus 4.6](https://platform.claude.com/docs/en/about-claude/models/overview), which include 1M context at standard pricing with no beta header required.
1201</Warning>
1202
1203### `McpSdkServerConfig`
1204
1205Configuration for SDK MCP servers created with `create_sdk_mcp_server()`.
1206
1207```python theme={null}
1208class McpSdkServerConfig(TypedDict):
1209 type: Literal["sdk"]
1210 name: str
1211 instance: Any # MCP Server instance
1212```
1213
1214### `McpServerConfig`
1215
1216Union type for MCP server configurations.
1217
1218```python theme={null}
1219McpServerConfig = (
1220 McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig
1221)
1222```
1223
1224#### `McpStdioServerConfig`
1225
1226```python theme={null}
1227class McpStdioServerConfig(TypedDict):
1228 type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility
1229 command: str
1230 args: NotRequired[list[str]]
1231 env: NotRequired[dict[str, str]]
1232```
1233
1234#### `McpSSEServerConfig`
1235
1236```python theme={null}
1237class McpSSEServerConfig(TypedDict):
1238 type: Literal["sse"]
1239 url: str
1240 headers: NotRequired[dict[str, str]]
1241```
1242
1243#### `McpHttpServerConfig`
1244
1245```python theme={null}
1246class McpHttpServerConfig(TypedDict):
1247 type: Literal["http"]
1248 url: str
1249 headers: NotRequired[dict[str, str]]
1250```
1251
1252### `McpServerStatusConfig`
1253
1254The configuration of an MCP server as reported by [`get_mcp_status()`](#methods). This is the union of all [`McpServerConfig`](#mcp-server-config) transport variants plus an output-only `claudeai-proxy` variant for servers proxied through claude.ai.
1255
1256```python theme={null}
1257McpServerStatusConfig = (
1258 McpStdioServerConfig
1259 | McpSSEServerConfig
1260 | McpHttpServerConfig
1261 | McpSdkServerConfigStatus
1262 | McpClaudeAIProxyServerConfig
1263)
1264```
1265
1266`McpSdkServerConfigStatus` is the serializable form of [`McpSdkServerConfig`](#mcp-sdk-server-config) with only `type` (`"sdk"`) and `name` (`str`) fields; the in-process `instance` is omitted. `McpClaudeAIProxyServerConfig` has `type` (`"claudeai-proxy"`), `url` (`str`), and `id` (`str`) fields.
1267
1268### `McpStatusResponse`
1269
1270Response from [`ClaudeSDKClient.get_mcp_status()`](#methods). Wraps the list of server statuses under the `mcpServers` key.
1271
1272```python theme={null}
1273class McpStatusResponse(TypedDict):
1274 mcpServers: list[McpServerStatus]
1275```
1276
1277### `McpServerStatus`
1278
1279Status of a connected MCP server, contained in [`McpStatusResponse`](#mcp-status-response).
1280
1281```python theme={null}
1282class McpServerStatus(TypedDict):
1283 name: str
1284 status: McpServerConnectionStatus # "connected" | "failed" | "needs-auth" | "pending" | "disabled"
1285 serverInfo: NotRequired[McpServerInfo]
1286 error: NotRequired[str]
1287 config: NotRequired[McpServerStatusConfig]
1288 scope: NotRequired[str]
1289 tools: NotRequired[list[McpToolInfo]]
1290```
1291
1292| Field | Type | Description |
1293| :----------- | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1294| `name` | `str` | Server name |
1295| `status` | `str` | One of `"connected"`, `"failed"`, `"needs-auth"`, `"pending"`, or `"disabled"` |
1296| `serverInfo` | `dict` (optional) | Server name and version (`{"name": str, "version": str}`) |
1297| `error` | `str` (optional) | Error message if the server failed to connect |
1298| `config` | [`McpServerStatusConfig`](#mcp-server-status-config) (optional) | Server configuration. Same shape as [`McpServerConfig`](#mcp-server-config) (stdio, SSE, HTTP, or SDK), plus a `claudeai-proxy` variant for servers connected through claude.ai |
1299| `scope` | `str` (optional) | Configuration scope |
1300| `tools` | `list` (optional) | Tools provided by this server, each with `name`, `description`, and `annotations` fields |
1301
1302### `SdkPluginConfig`
1303
1304Configuration for loading plugins in the SDK.
1305
1306```python theme={null}
1307class SdkPluginConfig(TypedDict):
1308 type: Literal["local"]
1309 path: str
1310```
1311
1312| Field | Type | Description |
1313| :----- | :----------------- | :--------------------------------------------------------- |
1314| `type` | `Literal["local"]` | Must be `"local"` (only local plugins currently supported) |
1315| `path` | `str` | Absolute or relative path to the plugin directory |
1316
1317**Example:**
1318
1319```python theme={null}
1320plugins = [
1321 {"type": "local", "path": "./my-plugin"},
1322 {"type": "local", "path": "/absolute/path/to/plugin"},
1323]
1324```
1325
1326For complete information on creating and using plugins, see [Plugins](/en/agent-sdk/plugins).
1327
1328## Message Types
1329
1330### `Message`
1331
1332Union type of all possible messages.
1333
1334```python theme={null}
1335Message = (
1336 UserMessage
1337 | AssistantMessage
1338 | SystemMessage
1339 | ResultMessage
1340 | StreamEvent
1341 | RateLimitEvent
1342)
1343```
1344
1345### `UserMessage`
1346
1347User input message.
1348
1349```python theme={null}
1350@dataclass
1351class UserMessage:
1352 content: str | list[ContentBlock]
1353 uuid: str | None = None
1354 parent_tool_use_id: str | None = None
1355 tool_use_result: dict[str, Any] | None = None
1356```
1357
1358| Field | Type | Description |
1359| :------------------- | :-------------------------- | :---------------------------------------------------- |
1360| `content` | `str \| list[ContentBlock]` | Message content as text or content blocks |
1361| `uuid` | `str \| None` | Unique message identifier |
1362| `parent_tool_use_id` | `str \| None` | Tool use ID if this message is a tool result response |
1363| `tool_use_result` | `dict[str, Any] \| None` | Tool result data if applicable |
1364
1365### `AssistantMessage`
1366
1367Assistant response message with content blocks.
1368
1369```python theme={null}
1370@dataclass
1371class AssistantMessage:
1372 content: list[ContentBlock]
1373 model: str
1374 parent_tool_use_id: str | None = None
1375 error: AssistantMessageError | None = None
1376 usage: dict[str, Any] | None = None
1377 message_id: str | None = None
1378```
1379
1380| Field | Type | Description |
1381| :------------------- | :------------------------------------------------------------- | :------------------------------------------------------------------------------ |
1382| `content` | `list[ContentBlock]` | List of content blocks in the response |
1383| `model` | `str` | Model that generated the response |
1384| `parent_tool_use_id` | `str \| None` | Tool use ID if this is a nested response |
1385| `error` | [`AssistantMessageError`](#assistant-message-error) ` \| None` | Error type if the response encountered an error |
1386| `usage` | `dict[str, Any] \| None` | Per-message token usage (same keys as [`ResultMessage.usage`](#result-message)) |
1387| `message_id` | `str \| None` | API message ID. Multiple messages from one turn share the same ID |
1388
1389### `AssistantMessageError`
1390
1391Possible error types for assistant messages.
1392
1393```python theme={null}
1394AssistantMessageError = Literal[
1395 "authentication_failed",
1396 "billing_error",
1397 "rate_limit",
1398 "invalid_request",
1399 "server_error",
1400 "max_output_tokens",
1401 "unknown",
1402]
1403```
1404
1405### `SystemMessage`
1406
1407System message with metadata.
1408
1409```python theme={null}
1410@dataclass
1411class SystemMessage:
1412 subtype: str
1413 data: dict[str, Any]
1414```
1415
1416### `ResultMessage`
1417
1418Final result message with cost and usage information.
1419
1420```python theme={null}
1421@dataclass
1422class ResultMessage:
1423 subtype: str
1424 duration_ms: int
1425 duration_api_ms: int
1426 is_error: bool
1427 num_turns: int
1428 session_id: str
1429 total_cost_usd: float | None = None
1430 usage: dict[str, Any] | None = None
1431 result: str | None = None
1432 stop_reason: str | None = None
1433 structured_output: Any = None
1434 model_usage: dict[str, Any] | None = None
1435```
1436
1437The `usage` dict contains the following keys when present:
1438
1439| Key | Type | Description |
1440| ----------------------------- | ----- | ---------------------------------------- |
1441| `input_tokens` | `int` | Total input tokens consumed. |
1442| `output_tokens` | `int` | Total output tokens generated. |
1443| `cache_creation_input_tokens` | `int` | Tokens used to create new cache entries. |
1444| `cache_read_input_tokens` | `int` | Tokens read from existing cache entries. |
1445
1446The `model_usage` dict maps model names to per-model usage. The inner dict keys use camelCase because the value is passed through unmodified from the underlying CLI process, matching the TypeScript [`ModelUsage`](/en/agent-sdk/typescript#model-usage) type:
1447
1448| Key | Type | Description |
1449| -------------------------- | ------- | ------------------------------------------ |
1450| `inputTokens` | `int` | Input tokens for this model. |
1451| `outputTokens` | `int` | Output tokens for this model. |
1452| `cacheReadInputTokens` | `int` | Cache read tokens for this model. |
1453| `cacheCreationInputTokens` | `int` | Cache creation tokens for this model. |
1454| `webSearchRequests` | `int` | Web search requests made by this model. |
1455| `costUSD` | `float` | Cost in USD for this model. |
1456| `contextWindow` | `int` | Context window size for this model. |
1457| `maxOutputTokens` | `int` | Maximum output token limit for this model. |
1458
1459### `StreamEvent`
1460
1461Stream event for partial message updates during streaming. Only received when `include_partial_messages=True` in `ClaudeAgentOptions`. Import via `from claude_agent_sdk.types import StreamEvent`.
1462
1463```python theme={null}
1464@dataclass
1465class StreamEvent:
1466 uuid: str
1467 session_id: str
1468 event: dict[str, Any] # The raw Claude API stream event
1469 parent_tool_use_id: str | None = None
1470```
1471
1472| Field | Type | Description |
1473| :------------------- | :--------------- | :-------------------------------------------------- |
1474| `uuid` | `str` | Unique identifier for this event |
1475| `session_id` | `str` | Session identifier |
1476| `event` | `dict[str, Any]` | The raw Claude API stream event data |
1477| `parent_tool_use_id` | `str \| None` | Parent tool use ID if this event is from a subagent |
1478
1479### `RateLimitEvent`
1480
1481Emitted when rate limit status changes (for example, from `"allowed"` to `"allowed_warning"`). Use this to warn users before they hit a hard limit, or to back off when status is `"rejected"`.
1482
1483```python theme={null}
1484@dataclass
1485class RateLimitEvent:
1486 rate_limit_info: RateLimitInfo
1487 uuid: str
1488 session_id: str
1489```
1490
1491| Field | Type | Description |
1492| :---------------- | :---------------------------------- | :----------------------- |
1493| `rate_limit_info` | [`RateLimitInfo`](#rate-limit-info) | Current rate limit state |
1494| `uuid` | `str` | Unique event identifier |
1495| `session_id` | `str` | Session identifier |
1496
1497### `RateLimitInfo`
1498
1499Rate limit state carried by [`RateLimitEvent`](#rate-limit-event).
1500
1501```python theme={null}
1502RateLimitStatus = Literal["allowed", "allowed_warning", "rejected"]
1503RateLimitType = Literal[
1504 "five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage"
1505]
1506
1507
1508@dataclass
1509class RateLimitInfo:
1510 status: RateLimitStatus
1511 resets_at: int | None = None
1512 rate_limit_type: RateLimitType | None = None
1513 utilization: float | None = None
1514 overage_status: RateLimitStatus | None = None
1515 overage_resets_at: int | None = None
1516 overage_disabled_reason: str | None = None
1517 raw: dict[str, Any] = field(default_factory=dict)
1518```
1519
1520| Field | Type | Description |
1521| :------------------------ | :------------------------ | :---------------------------------------------------------------------------------------------------- |
1522| `status` | `RateLimitStatus` | Current status. `"allowed_warning"` means approaching the limit; `"rejected"` means the limit was hit |
1523| `resets_at` | `int \| None` | Unix timestamp when the rate limit window resets |
1524| `rate_limit_type` | `RateLimitType \| None` | Which rate limit window applies |
1525| `utilization` | `float \| None` | Fraction of the rate limit consumed (0.0 to 1.0) |
1526| `overage_status` | `RateLimitStatus \| None` | Status of pay-as-you-go overage usage, if applicable |
1527| `overage_resets_at` | `int \| None` | Unix timestamp when the overage window resets |
1528| `overage_disabled_reason` | `str \| None` | Why overage is unavailable, if status is `"rejected"` |
1529| `raw` | `dict[str, Any]` | Full raw dict from the CLI, including fields not modeled above |
1530
1531### `TaskStartedMessage`
1532
1533Emitted when a background task starts. A background task is anything tracked outside the main turn: a backgrounded Bash command, a subagent spawned via the Agent tool, or a remote agent. The `task_type` field tells you which. This naming is unrelated to the `Task`-to-`Agent` tool rename.
1534
1535```python theme={null}
1536@dataclass
1537class TaskStartedMessage(SystemMessage):
1538 task_id: str
1539 description: str
1540 uuid: str
1541 session_id: str
1542 tool_use_id: str | None = None
1543 task_type: str | None = None
1544```
1545
1546| Field | Type | Description |
1547| :------------ | :------------ | :------------------------------------------------------------------------------- |
1548| `task_id` | `str` | Unique identifier for the task |
1549| `description` | `str` | Description of the task |
1550| `uuid` | `str` | Unique message identifier |
1551| `session_id` | `str` | Session identifier |
1552| `tool_use_id` | `str \| None` | Associated tool use ID |
1553| `task_type` | `str \| None` | Which kind of background task: `"local_bash"`, `"local_agent"`, `"remote_agent"` |
1554
1555### `TaskUsage`
1556
1557Token and timing data for a background task.
1558
1559```python theme={null}
1560class TaskUsage(TypedDict):
1561 total_tokens: int
1562 tool_uses: int
1563 duration_ms: int
1564```
1565
1566### `TaskProgressMessage`
1567
1568Emitted periodically with progress updates for a running background task.
1569
1570```python theme={null}
1571@dataclass
1572class TaskProgressMessage(SystemMessage):
1573 task_id: str
1574 description: str
1575 usage: TaskUsage
1576 uuid: str
1577 session_id: str
1578 tool_use_id: str | None = None
1579 last_tool_name: str | None = None
1580```
1581
1582| Field | Type | Description |
1583| :--------------- | :------------ | :---------------------------------- |
1584| `task_id` | `str` | Unique identifier for the task |
1585| `description` | `str` | Current status description |
1586| `usage` | `TaskUsage` | Token usage for this task so far |
1587| `uuid` | `str` | Unique message identifier |
1588| `session_id` | `str` | Session identifier |
1589| `tool_use_id` | `str \| None` | Associated tool use ID |
1590| `last_tool_name` | `str \| None` | Name of the last tool the task used |
1591
1592### `TaskNotificationMessage`
1593
1594Emitted when a task completes, fails, or is stopped.
1595
1596```python theme={null}
1597@dataclass
1598class TaskNotificationMessage(SystemMessage):
1599 task_id: str
1600 status: TaskNotificationStatus # "completed" | "failed" | "stopped"
1601 output_file: str
1602 summary: str
1603 uuid: str
1604 session_id: str
1605 tool_use_id: str | None = None
1606 usage: TaskUsage | None = None
1607```
1608
1609| Field | Type | Description |
1610| :------------ | :----------------------- | :----------------------------------------------- |
1611| `task_id` | `str` | Unique identifier for the task |
1612| `status` | `TaskNotificationStatus` | One of `"completed"`, `"failed"`, or `"stopped"` |
1613| `output_file` | `str` | Path to the task output file |
1614| `summary` | `str` | Summary of the task result |
1615| `uuid` | `str` | Unique message identifier |
1616| `session_id` | `str` | Session identifier |
1617| `tool_use_id` | `str \| None` | Associated tool use ID |
1618| `usage` | `TaskUsage \| None` | Final token usage for the task |
1619
1620## Content Block Types
1621
1622### `ContentBlock`
1623
1624Union type of all content blocks.
1625
1626```python theme={null}
1627ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
1628```
1629
1630### `TextBlock`
1631
1632Text content block.
1633
1634```python theme={null}
1635@dataclass
1636class TextBlock:
1637 text: str
1638```
1639
1640### `ThinkingBlock`
1641
1642Thinking content block (for models with thinking capability).
1643
1644```python theme={null}
1645@dataclass
1646class ThinkingBlock:
1647 thinking: str
1648 signature: str
1649```
1650
1651### `ToolUseBlock`
1652
1653Tool use request block.
1654
1655```python theme={null}
1656@dataclass
1657class ToolUseBlock:
1658 id: str
1659 name: str
1660 input: dict[str, Any]
1661```
1662
1663### `ToolResultBlock`
1664
1665Tool execution result block.
1666
1667```python theme={null}
1668@dataclass
1669class ToolResultBlock:
1670 tool_use_id: str
1671 content: str | list[dict[str, Any]] | None = None
1672 is_error: bool | None = None
1673```
1674
1675## Error Types
1676
1677### `ClaudeSDKError`
1678
1679Base exception class for all SDK errors.
1680
1681```python theme={null}
1682class ClaudeSDKError(Exception):
1683 """Base error for Claude SDK."""
1684```
1685
1686### `CLINotFoundError`
1687
1688Raised when Claude Code CLI is not installed or not found.
1689
1690```python theme={null}
1691class CLINotFoundError(CLIConnectionError):
1692 def __init__(
1693 self, message: str = "Claude Code not found", cli_path: str | None = None
1694 ):
1695 """
1696 Args:
1697 message: Error message (default: "Claude Code not found")
1698 cli_path: Optional path to the CLI that was not found
1699 """
1700```
1701
1702### `CLIConnectionError`
1703
1704Raised when connection to Claude Code fails.
1705
1706```python theme={null}
1707class CLIConnectionError(ClaudeSDKError):
1708 """Failed to connect to Claude Code."""
1709```
1710
1711### `ProcessError`
1712
1713Raised when the Claude Code process fails.
1714
1715```python theme={null}
1716class ProcessError(ClaudeSDKError):
1717 def __init__(
1718 self, message: str, exit_code: int | None = None, stderr: str | None = None
1719 ):
1720 self.exit_code = exit_code
1721 self.stderr = stderr
1722```
1723
1724### `CLIJSONDecodeError`
1725
1726Raised when JSON parsing fails.
1727
1728```python theme={null}
1729class CLIJSONDecodeError(ClaudeSDKError):
1730 def __init__(self, line: str, original_error: Exception):
1731 """
1732 Args:
1733 line: The line that failed to parse
1734 original_error: The original JSON decode exception
1735 """
1736 self.line = line
1737 self.original_error = original_error
1738```
1739
1740## Hook Types
1741
1742For a comprehensive guide on using hooks with examples and common patterns, see the [Hooks guide](/en/agent-sdk/hooks).
1743
1744### `HookEvent`
1745
1746Supported hook event types.
1747
1748```python theme={null}
1749HookEvent = Literal[
1750 "PreToolUse", # Called before tool execution
1751 "PostToolUse", # Called after tool execution
1752 "PostToolUseFailure", # Called when a tool execution fails
1753 "UserPromptSubmit", # Called when user submits a prompt
1754 "Stop", # Called when stopping execution
1755 "SubagentStop", # Called when a subagent stops
1756 "PreCompact", # Called before message compaction
1757 "Notification", # Called for notification events
1758 "SubagentStart", # Called when a subagent starts
1759 "PermissionRequest", # Called when a permission decision is needed
1760]
1761```
1762
1763<Note>
1764 The TypeScript SDK supports additional hook events not yet available in Python: `SessionStart`, `SessionEnd`, `Setup`, `TeammateIdle`, `TaskCompleted`, `ConfigChange`, `WorktreeCreate`, and `WorktreeRemove`.
1765</Note>
1766
1767### `HookCallback`
1768
1769Type definition for hook callback functions.
1770
1771```python theme={null}
1772HookCallback = Callable[[HookInput, str | None, HookContext], Awaitable[HookJSONOutput]]
1773```
1774
1775Parameters:
1776
1777* `input`: Strongly-typed hook input with discriminated unions based on `hook_event_name` (see [`HookInput`](#hook-input))
1778* `tool_use_id`: Optional tool use identifier (for tool-related hooks)
1779* `context`: Hook context with additional information
1780
1781Returns a [`HookJSONOutput`](#hook-json-output) that may contain:
1782
1783* `decision`: `"block"` to block the action
1784* `systemMessage`: System message to add to the transcript
1785* `hookSpecificOutput`: Hook-specific output data
1786
1787### `HookContext`
1788
1789Context information passed to hook callbacks.
1790
1791```python theme={null}
1792class HookContext(TypedDict):
1793 signal: Any | None # Future: abort signal support
1794```
1795
1796### `HookMatcher`
1797
1798Configuration for matching hooks to specific events or tools.
1799
1800```python theme={null}
1801@dataclass
1802class HookMatcher:
1803 matcher: str | None = (
1804 None # Tool name or pattern to match (e.g., "Bash", "Write|Edit")
1805 )
1806 hooks: list[HookCallback] = field(
1807 default_factory=list
1808 ) # List of callbacks to execute
1809 timeout: float | None = (
1810 None # Timeout in seconds for all hooks in this matcher (default: 60)
1811 )
1812```
1813
1814### `HookInput`
1815
1816Union type of all hook input types. The actual type depends on the `hook_event_name` field.
1817
1818```python theme={null}
1819HookInput = (
1820 PreToolUseHookInput
1821 | PostToolUseHookInput
1822 | PostToolUseFailureHookInput
1823 | UserPromptSubmitHookInput
1824 | StopHookInput
1825 | SubagentStopHookInput
1826 | PreCompactHookInput
1827 | NotificationHookInput
1828 | SubagentStartHookInput
1829 | PermissionRequestHookInput
1830)
1831```
1832
1833### `BaseHookInput`
1834
1835Base fields present in all hook input types.
1836
1837```python theme={null}
1838class BaseHookInput(TypedDict):
1839 session_id: str
1840 transcript_path: str
1841 cwd: str
1842 permission_mode: NotRequired[str]
1843```
1844
1845| Field | Type | Description |
1846| :---------------- | :--------------- | :---------------------------------- |
1847| `session_id` | `str` | Current session identifier |
1848| `transcript_path` | `str` | Path to the session transcript file |
1849| `cwd` | `str` | Current working directory |
1850| `permission_mode` | `str` (optional) | Current permission mode |
1851
1852### `PreToolUseHookInput`
1853
1854Input data for `PreToolUse` hook events.
1855
1856```python theme={null}
1857class PreToolUseHookInput(BaseHookInput):
1858 hook_event_name: Literal["PreToolUse"]
1859 tool_name: str
1860 tool_input: dict[str, Any]
1861 tool_use_id: str
1862 agent_id: NotRequired[str]
1863 agent_type: NotRequired[str]
1864```
1865
1866| Field | Type | Description |
1867| :---------------- | :---------------------- | :----------------------------------------------------------------- |
1868| `hook_event_name` | `Literal["PreToolUse"]` | Always "PreToolUse" |
1869| `tool_name` | `str` | Name of the tool about to be executed |
1870| `tool_input` | `dict[str, Any]` | Input parameters for the tool |
1871| `tool_use_id` | `str` | Unique identifier for this tool use |
1872| `agent_id` | `str` (optional) | Subagent identifier, present when the hook fires inside a subagent |
1873| `agent_type` | `str` (optional) | Subagent type, present when the hook fires inside a subagent |
1874
1875### `PostToolUseHookInput`
1876
1877Input data for `PostToolUse` hook events.
1878
1879```python theme={null}
1880class PostToolUseHookInput(BaseHookInput):
1881 hook_event_name: Literal["PostToolUse"]
1882 tool_name: str
1883 tool_input: dict[str, Any]
1884 tool_response: Any
1885 tool_use_id: str
1886 agent_id: NotRequired[str]
1887 agent_type: NotRequired[str]
1888```
1889
1890| Field | Type | Description |
1891| :---------------- | :----------------------- | :----------------------------------------------------------------- |
1892| `hook_event_name` | `Literal["PostToolUse"]` | Always "PostToolUse" |
1893| `tool_name` | `str` | Name of the tool that was executed |
1894| `tool_input` | `dict[str, Any]` | Input parameters that were used |
1895| `tool_response` | `Any` | Response from the tool execution |
1896| `tool_use_id` | `str` | Unique identifier for this tool use |
1897| `agent_id` | `str` (optional) | Subagent identifier, present when the hook fires inside a subagent |
1898| `agent_type` | `str` (optional) | Subagent type, present when the hook fires inside a subagent |
1899
1900### `PostToolUseFailureHookInput`
1901
1902Input data for `PostToolUseFailure` hook events. Called when a tool execution fails.
1903
1904```python theme={null}
1905class PostToolUseFailureHookInput(BaseHookInput):
1906 hook_event_name: Literal["PostToolUseFailure"]
1907 tool_name: str
1908 tool_input: dict[str, Any]
1909 tool_use_id: str
1910 error: str
1911 is_interrupt: NotRequired[bool]
1912 agent_id: NotRequired[str]
1913 agent_type: NotRequired[str]
1914```
1915
1916| Field | Type | Description |
1917| :---------------- | :------------------------------ | :----------------------------------------------------------------- |
1918| `hook_event_name` | `Literal["PostToolUseFailure"]` | Always "PostToolUseFailure" |
1919| `tool_name` | `str` | Name of the tool that failed |
1920| `tool_input` | `dict[str, Any]` | Input parameters that were used |
1921| `tool_use_id` | `str` | Unique identifier for this tool use |
1922| `error` | `str` | Error message from the failed execution |
1923| `is_interrupt` | `bool` (optional) | Whether the failure was caused by an interrupt |
1924| `agent_id` | `str` (optional) | Subagent identifier, present when the hook fires inside a subagent |
1925| `agent_type` | `str` (optional) | Subagent type, present when the hook fires inside a subagent |
1926
1927### `UserPromptSubmitHookInput`
1928
1929Input data for `UserPromptSubmit` hook events.
1930
1931```python theme={null}
1932class UserPromptSubmitHookInput(BaseHookInput):
1933 hook_event_name: Literal["UserPromptSubmit"]
1934 prompt: str
1935```
1936
1937| Field | Type | Description |
1938| :---------------- | :---------------------------- | :-------------------------- |
1939| `hook_event_name` | `Literal["UserPromptSubmit"]` | Always "UserPromptSubmit" |
1940| `prompt` | `str` | The user's submitted prompt |
1941
1942### `StopHookInput`
1943
1944Input data for `Stop` hook events.
1945
1946```python theme={null}
1947class StopHookInput(BaseHookInput):
1948 hook_event_name: Literal["Stop"]
1949 stop_hook_active: bool
1950```
1951
1952| Field | Type | Description |
1953| :----------------- | :---------------- | :------------------------------ |
1954| `hook_event_name` | `Literal["Stop"]` | Always "Stop" |
1955| `stop_hook_active` | `bool` | Whether the stop hook is active |
1956
1957### `SubagentStopHookInput`
1958
1959Input data for `SubagentStop` hook events.
1960
1961```python theme={null}
1962class SubagentStopHookInput(BaseHookInput):
1963 hook_event_name: Literal["SubagentStop"]
1964 stop_hook_active: bool
1965 agent_id: str
1966 agent_transcript_path: str
1967 agent_type: str
1968```
1969
1970| Field | Type | Description |
1971| :---------------------- | :------------------------ | :------------------------------------- |
1972| `hook_event_name` | `Literal["SubagentStop"]` | Always "SubagentStop" |
1973| `stop_hook_active` | `bool` | Whether the stop hook is active |
1974| `agent_id` | `str` | Unique identifier for the subagent |
1975| `agent_transcript_path` | `str` | Path to the subagent's transcript file |
1976| `agent_type` | `str` | Type of the subagent |
1977
1978### `PreCompactHookInput`
1979
1980Input data for `PreCompact` hook events.
1981
1982```python theme={null}
1983class PreCompactHookInput(BaseHookInput):
1984 hook_event_name: Literal["PreCompact"]
1985 trigger: Literal["manual", "auto"]
1986 custom_instructions: str | None
1987```
1988
1989| Field | Type | Description |
1990| :-------------------- | :-------------------------- | :--------------------------------- |
1991| `hook_event_name` | `Literal["PreCompact"]` | Always "PreCompact" |
1992| `trigger` | `Literal["manual", "auto"]` | What triggered the compaction |
1993| `custom_instructions` | `str \| None` | Custom instructions for compaction |
1994
1995### `NotificationHookInput`
1996
1997Input data for `Notification` hook events.
1998
1999```python theme={null}
2000class NotificationHookInput(BaseHookInput):
2001 hook_event_name: Literal["Notification"]
2002 message: str
2003 title: NotRequired[str]
2004 notification_type: str
2005```
2006
2007| Field | Type | Description |
2008| :------------------ | :------------------------ | :--------------------------- |
2009| `hook_event_name` | `Literal["Notification"]` | Always "Notification" |
2010| `message` | `str` | Notification message content |
2011| `title` | `str` (optional) | Notification title |
2012| `notification_type` | `str` | Type of notification |
2013
2014### `SubagentStartHookInput`
2015
2016Input data for `SubagentStart` hook events.
2017
2018```python theme={null}
2019class SubagentStartHookInput(BaseHookInput):
2020 hook_event_name: Literal["SubagentStart"]
2021 agent_id: str
2022 agent_type: str
2023```
2024
2025| Field | Type | Description |
2026| :---------------- | :------------------------- | :--------------------------------- |
2027| `hook_event_name` | `Literal["SubagentStart"]` | Always "SubagentStart" |
2028| `agent_id` | `str` | Unique identifier for the subagent |
2029| `agent_type` | `str` | Type of the subagent |
2030
2031### `PermissionRequestHookInput`
2032
2033Input data for `PermissionRequest` hook events. Allows hooks to handle permission decisions programmatically.
2034
2035```python theme={null}
2036class PermissionRequestHookInput(BaseHookInput):
2037 hook_event_name: Literal["PermissionRequest"]
2038 tool_name: str
2039 tool_input: dict[str, Any]
2040 permission_suggestions: NotRequired[list[Any]]
2041```
2042
2043| Field | Type | Description |
2044| :----------------------- | :----------------------------- | :---------------------------------------- |
2045| `hook_event_name` | `Literal["PermissionRequest"]` | Always "PermissionRequest" |
2046| `tool_name` | `str` | Name of the tool requesting permission |
2047| `tool_input` | `dict[str, Any]` | Input parameters for the tool |
2048| `permission_suggestions` | `list[Any]` (optional) | Suggested permission updates from the CLI |
2049
2050### `HookJSONOutput`
2051
2052Union type for hook callback return values.
2053
2054```python theme={null}
2055HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput
2056```
2057
2058#### `SyncHookJSONOutput`
2059
2060Synchronous hook output with control and decision fields.
2061
2062```python theme={null}
2063class SyncHookJSONOutput(TypedDict):
2064 # Control fields
2065 continue_: NotRequired[bool] # Whether to proceed (default: True)
2066 suppressOutput: NotRequired[bool] # Hide stdout from transcript
2067 stopReason: NotRequired[str] # Message when continue is False
2068
2069 # Decision fields
2070 decision: NotRequired[Literal["block"]]
2071 systemMessage: NotRequired[str] # Warning message for user
2072 reason: NotRequired[str] # Feedback for Claude
2073
2074 # Hook-specific output
2075 hookSpecificOutput: NotRequired[HookSpecificOutput]
2076```
2077
2078<Note>
2079 Use `continue_` (with underscore) in Python code. It is automatically converted to `continue` when sent to the CLI.
2080</Note>
2081
2082#### `HookSpecificOutput`
2083
2084A `TypedDict` containing the hook event name and event-specific fields. The shape depends on the `hookEventName` value. For full details on available fields per hook event, see [Control execution with hooks](/en/agent-sdk/hooks#outputs).
2085
2086A discriminated union of event-specific output types. The `hookEventName` field determines which fields are valid.
2087
2088```python theme={null}
2089class PreToolUseHookSpecificOutput(TypedDict):
2090 hookEventName: Literal["PreToolUse"]
2091 permissionDecision: NotRequired[Literal["allow", "deny", "ask"]]
2092 permissionDecisionReason: NotRequired[str]
2093 updatedInput: NotRequired[dict[str, Any]]
2094 additionalContext: NotRequired[str]
2095
2096
2097class PostToolUseHookSpecificOutput(TypedDict):
2098 hookEventName: Literal["PostToolUse"]
2099 additionalContext: NotRequired[str]
2100 updatedMCPToolOutput: NotRequired[Any]
2101
2102
2103class PostToolUseFailureHookSpecificOutput(TypedDict):
2104 hookEventName: Literal["PostToolUseFailure"]
2105 additionalContext: NotRequired[str]
2106
2107
2108class UserPromptSubmitHookSpecificOutput(TypedDict):
2109 hookEventName: Literal["UserPromptSubmit"]
2110 additionalContext: NotRequired[str]
2111
2112
2113class NotificationHookSpecificOutput(TypedDict):
2114 hookEventName: Literal["Notification"]
2115 additionalContext: NotRequired[str]
2116
2117
2118class SubagentStartHookSpecificOutput(TypedDict):
2119 hookEventName: Literal["SubagentStart"]
2120 additionalContext: NotRequired[str]
2121
2122
2123class PermissionRequestHookSpecificOutput(TypedDict):
2124 hookEventName: Literal["PermissionRequest"]
2125 decision: dict[str, Any]
2126
2127
2128HookSpecificOutput = (
2129 PreToolUseHookSpecificOutput
2130 | PostToolUseHookSpecificOutput
2131 | PostToolUseFailureHookSpecificOutput
2132 | UserPromptSubmitHookSpecificOutput
2133 | NotificationHookSpecificOutput
2134 | SubagentStartHookSpecificOutput
2135 | PermissionRequestHookSpecificOutput
2136)
2137```
2138
2139#### `AsyncHookJSONOutput`
2140
2141Async hook output that defers hook execution.
2142
2143```python theme={null}
2144class AsyncHookJSONOutput(TypedDict):
2145 async_: Literal[True] # Set to True to defer execution
2146 asyncTimeout: NotRequired[int] # Timeout in milliseconds
2147```
2148
2149<Note>
2150 Use `async_` (with underscore) in Python code. It is automatically converted to `async` when sent to the CLI.
2151</Note>
2152
2153### Hook Usage Example
2154
2155This example registers two hooks: one that blocks dangerous bash commands like `rm -rf /`, and another that logs all tool usage for auditing. The security hook only runs on Bash commands (via the `matcher`), while the logging hook runs on all tools.
2156
2157```python theme={null}
2158from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
2159from typing import Any
2160
2161
2162async def validate_bash_command(
2163 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
2164) -> dict[str, Any]:
2165 """Validate and potentially block dangerous bash commands."""
2166 if input_data["tool_name"] == "Bash":
2167 command = input_data["tool_input"].get("command", "")
2168 if "rm -rf /" in command:
2169 return {
2170 "hookSpecificOutput": {
2171 "hookEventName": "PreToolUse",
2172 "permissionDecision": "deny",
2173 "permissionDecisionReason": "Dangerous command blocked",
2174 }
2175 }
2176 return {}
2177
2178
2179async def log_tool_use(
2180 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
2181) -> dict[str, Any]:
2182 """Log all tool usage for auditing."""
2183 print(f"Tool used: {input_data.get('tool_name')}")
2184 return {}
2185
2186
2187options = ClaudeAgentOptions(
2188 hooks={
2189 "PreToolUse": [
2190 HookMatcher(
2191 matcher="Bash", hooks=[validate_bash_command], timeout=120
2192 ), # 2 min for validation
2193 HookMatcher(
2194 hooks=[log_tool_use]
2195 ), # Applies to all tools (default 60s timeout)
2196 ],
2197 "PostToolUse": [HookMatcher(hooks=[log_tool_use])],
2198 }
2199)
2200
2201async for message in query(prompt="Analyze this codebase", options=options):
2202 print(message)
2203```
2204
2205## Tool Input/Output Types
2206
2207Documentation of input/output schemas for all built-in Claude Code tools. While the Python SDK doesn't export these as types, they represent the structure of tool inputs and outputs in messages.
2208
2209### Agent
2210
2211**Tool name:** `Agent` (previously `Task`, which is still accepted as an alias)
2212
2213**Input:**
2214
2215```python theme={null}
2216{
2217 "description": str, # A short (3-5 word) description of the task
2218 "prompt": str, # The task for the agent to perform
2219 "subagent_type": str, # The type of specialized agent to use
2220}
2221```
2222
2223**Output:**
2224
2225```python theme={null}
2226{
2227 "result": str, # Final result from the subagent
2228 "usage": dict | None, # Token usage statistics
2229 "total_cost_usd": float | None, # Total cost in USD
2230 "duration_ms": int | None, # Execution duration in milliseconds
2231}
2232```
2233
2234### AskUserQuestion
2235
2236**Tool name:** `AskUserQuestion`
2237
2238Asks the user clarifying questions during execution. See [Handle approvals and user input](/en/agent-sdk/user-input#handle-clarifying-questions) for usage details.
2239
2240**Input:**
2241
2242```python theme={null}
2243{
2244 "questions": [ # Questions to ask the user (1-4 questions)
2245 {
2246 "question": str, # The complete question to ask the user
2247 "header": str, # Very short label displayed as a chip/tag (max 12 chars)
2248 "options": [ # The available choices (2-4 options)
2249 {
2250 "label": str, # Display text for this option (1-5 words)
2251 "description": str, # Explanation of what this option means
2252 }
2253 ],
2254 "multiSelect": bool, # Set to true to allow multiple selections
2255 }
2256 ],
2257 "answers": dict | None, # User answers populated by the permission system
2258}
2259```
2260
2261**Output:**
2262
2263```python theme={null}
2264{
2265 "questions": [ # The questions that were asked
2266 {
2267 "question": str,
2268 "header": str,
2269 "options": [{"label": str, "description": str}],
2270 "multiSelect": bool,
2271 }
2272 ],
2273 "answers": dict[str, str], # Maps question text to answer string
2274 # Multi-select answers are comma-separated
2275}
2276```
2277
2278### Bash
2279
2280**Tool name:** `Bash`
2281
2282**Input:**
2283
2284```python theme={null}
2285{
2286 "command": str, # The command to execute
2287 "timeout": int | None, # Optional timeout in milliseconds (max 600000)
2288 "description": str | None, # Clear, concise description (5-10 words)
2289 "run_in_background": bool | None, # Set to true to run in background
2290}
2291```
2292
2293**Output:**
2294
2295```python theme={null}
2296{
2297 "output": str, # Combined stdout and stderr output
2298 "exitCode": int, # Exit code of the command
2299 "killed": bool | None, # Whether command was killed due to timeout
2300 "shellId": str | None, # Shell ID for background processes
2301}
2302```
2303
2304### Edit
2305
2306**Tool name:** `Edit`
2307
2308**Input:**
2309
2310```python theme={null}
2311{
2312 "file_path": str, # The absolute path to the file to modify
2313 "old_string": str, # The text to replace
2314 "new_string": str, # The text to replace it with
2315 "replace_all": bool | None, # Replace all occurrences (default False)
2316}
2317```
2318
2319**Output:**
2320
2321```python theme={null}
2322{
2323 "message": str, # Confirmation message
2324 "replacements": int, # Number of replacements made
2325 "file_path": str, # File path that was edited
2326}
2327```
2328
2329### Read
2330
2331**Tool name:** `Read`
2332
2333**Input:**
2334
2335```python theme={null}
2336{
2337 "file_path": str, # The absolute path to the file to read
2338 "offset": int | None, # The line number to start reading from
2339 "limit": int | None, # The number of lines to read
2340}
2341```
2342
2343**Output (Text files):**
2344
2345```python theme={null}
2346{
2347 "content": str, # File contents with line numbers
2348 "total_lines": int, # Total number of lines in file
2349 "lines_returned": int, # Lines actually returned
2350}
2351```
2352
2353**Output (Images):**
2354
2355```python theme={null}
2356{
2357 "image": str, # Base64 encoded image data
2358 "mime_type": str, # Image MIME type
2359 "file_size": int, # File size in bytes
2360}
2361```
2362
2363### Write
2364
2365**Tool name:** `Write`
2366
2367**Input:**
2368
2369```python theme={null}
2370{
2371 "file_path": str, # The absolute path to the file to write
2372 "content": str, # The content to write to the file
2373}
2374```
2375
2376**Output:**
2377
2378```python theme={null}
2379{
2380 "message": str, # Success message
2381 "bytes_written": int, # Number of bytes written
2382 "file_path": str, # File path that was written
2383}
2384```
2385
2386### Glob
2387
2388**Tool name:** `Glob`
2389
2390**Input:**
2391
2392```python theme={null}
2393{
2394 "pattern": str, # The glob pattern to match files against
2395 "path": str | None, # The directory to search in (defaults to cwd)
2396}
2397```
2398
2399**Output:**
2400
2401```python theme={null}
2402{
2403 "matches": list[str], # Array of matching file paths
2404 "count": int, # Number of matches found
2405 "search_path": str, # Search directory used
2406}
2407```
2408
2409### Grep
2410
2411**Tool name:** `Grep`
2412
2413**Input:**
2414
2415```python theme={null}
2416{
2417 "pattern": str, # The regular expression pattern
2418 "path": str | None, # File or directory to search in
2419 "glob": str | None, # Glob pattern to filter files
2420 "type": str | None, # File type to search
2421 "output_mode": str | None, # "content", "files_with_matches", or "count"
2422 "-i": bool | None, # Case insensitive search
2423 "-n": bool | None, # Show line numbers
2424 "-B": int | None, # Lines to show before each match
2425 "-A": int | None, # Lines to show after each match
2426 "-C": int | None, # Lines to show before and after
2427 "head_limit": int | None, # Limit output to first N lines/entries
2428 "multiline": bool | None, # Enable multiline mode
2429}
2430```
2431
2432**Output (content mode):**
2433
2434```python theme={null}
2435{
2436 "matches": [
2437 {
2438 "file": str,
2439 "line_number": int | None,
2440 "line": str,
2441 "before_context": list[str] | None,
2442 "after_context": list[str] | None,
2443 }
2444 ],
2445 "total_matches": int,
2446}
2447```
2448
2449**Output (files\_with\_matches mode):**
2450
2451```python theme={null}
2452{
2453 "files": list[str], # Files containing matches
2454 "count": int, # Number of files with matches
2455}
2456```
2457
2458### NotebookEdit
2459
2460**Tool name:** `NotebookEdit`
2461
2462**Input:**
2463
2464```python theme={null}
2465{
2466 "notebook_path": str, # Absolute path to the Jupyter notebook
2467 "cell_id": str | None, # The ID of the cell to edit
2468 "new_source": str, # The new source for the cell
2469 "cell_type": "code" | "markdown" | None, # The type of the cell
2470 "edit_mode": "replace" | "insert" | "delete" | None, # Edit operation type
2471}
2472```
2473
2474**Output:**
2475
2476```python theme={null}
2477{
2478 "message": str, # Success message
2479 "edit_type": "replaced" | "inserted" | "deleted", # Type of edit performed
2480 "cell_id": str | None, # Cell ID that was affected
2481 "total_cells": int, # Total cells in notebook after edit
2482}
2483```
2484
2485### WebFetch
2486
2487**Tool name:** `WebFetch`
2488
2489**Input:**
2490
2491```python theme={null}
2492{
2493 "url": str, # The URL to fetch content from
2494 "prompt": str, # The prompt to run on the fetched content
2495}
2496```
2497
2498**Output:**
2499
2500```python theme={null}
2501{
2502 "response": str, # AI model's response to the prompt
2503 "url": str, # URL that was fetched
2504 "final_url": str | None, # Final URL after redirects
2505 "status_code": int | None, # HTTP status code
2506}
2507```
2508
2509### WebSearch
2510
2511**Tool name:** `WebSearch`
2512
2513**Input:**
2514
2515```python theme={null}
2516{
2517 "query": str, # The search query to use
2518 "allowed_domains": list[str] | None, # Only include results from these domains
2519 "blocked_domains": list[str] | None, # Never include results from these domains
2520}
2521```
2522
2523**Output:**
2524
2525```python theme={null}
2526{
2527 "results": [{"title": str, "url": str, "snippet": str, "metadata": dict | None}],
2528 "total_results": int,
2529 "query": str,
2530}
2531```
2532
2533### TodoWrite
2534
2535**Tool name:** `TodoWrite`
2536
2537**Input:**
2538
2539```python theme={null}
2540{
2541 "todos": [
2542 {
2543 "content": str, # The task description
2544 "status": "pending" | "in_progress" | "completed", # Task status
2545 "activeForm": str, # Active form of the description
2546 }
2547 ]
2548}
2549```
2550
2551**Output:**
2552
2553```python theme={null}
2554{
2555 "message": str, # Success message
2556 "stats": {"total": int, "pending": int, "in_progress": int, "completed": int},
2557}
2558```
2559
2560### BashOutput
2561
2562**Tool name:** `BashOutput`
2563
2564**Input:**
2565
2566```python theme={null}
2567{
2568 "bash_id": str, # The ID of the background shell
2569 "filter": str | None, # Optional regex to filter output lines
2570}
2571```
2572
2573**Output:**
2574
2575```python theme={null}
2576{
2577 "output": str, # New output since last check
2578 "status": "running" | "completed" | "failed", # Current shell status
2579 "exitCode": int | None, # Exit code when completed
2580}
2581```
2582
2583### KillBash
2584
2585**Tool name:** `KillBash`
2586
2587**Input:**
2588
2589```python theme={null}
2590{
2591 "shell_id": str # The ID of the background shell to kill
2592}
2593```
2594
2595**Output:**
2596
2597```python theme={null}
2598{
2599 "message": str, # Success message
2600 "shell_id": str, # ID of the killed shell
2601}
2602```
2603
2604### ExitPlanMode
2605
2606**Tool name:** `ExitPlanMode`
2607
2608**Input:**
2609
2610```python theme={null}
2611{
2612 "plan": str # The plan to run by the user for approval
2613}
2614```
2615
2616**Output:**
2617
2618```python theme={null}
2619{
2620 "message": str, # Confirmation message
2621 "approved": bool | None, # Whether user approved the plan
2622}
2623```
2624
2625### ListMcpResources
2626
2627**Tool name:** `ListMcpResources`
2628
2629**Input:**
2630
2631```python theme={null}
2632{
2633 "server": str | None # Optional server name to filter resources by
2634}
2635```
2636
2637**Output:**
2638
2639```python theme={null}
2640{
2641 "resources": [
2642 {
2643 "uri": str,
2644 "name": str,
2645 "description": str | None,
2646 "mimeType": str | None,
2647 "server": str,
2648 }
2649 ],
2650 "total": int,
2651}
2652```
2653
2654### ReadMcpResource
2655
2656**Tool name:** `ReadMcpResource`
2657
2658**Input:**
2659
2660```python theme={null}
2661{
2662 "server": str, # The MCP server name
2663 "uri": str, # The resource URI to read
2664}
2665```
2666
2667**Output:**
2668
2669```python theme={null}
2670{
2671 "contents": [
2672 {"uri": str, "mimeType": str | None, "text": str | None, "blob": str | None}
2673 ],
2674 "server": str,
2675}
2676```
2677
2678## Advanced Features with ClaudeSDKClient
2679
2680### Building a Continuous Conversation Interface
2681
2682```python theme={null}
2683from claude_agent_sdk import (
2684 ClaudeSDKClient,
2685 ClaudeAgentOptions,
2686 AssistantMessage,
2687 TextBlock,
2688)
2689import asyncio
2690
2691
2692class ConversationSession:
2693 """Maintains a single conversation session with Claude."""
2694
2695 def __init__(self, options: ClaudeAgentOptions | None = None):
2696 self.client = ClaudeSDKClient(options)
2697 self.turn_count = 0
2698
2699 async def start(self):
2700 await self.client.connect()
2701 print("Starting conversation session. Claude will remember context.")
2702 print(
2703 "Commands: 'exit' to quit, 'interrupt' to stop current task, 'new' for new session"
2704 )
2705
2706 while True:
2707 user_input = input(f"\n[Turn {self.turn_count + 1}] You: ")
2708
2709 if user_input.lower() == "exit":
2710 break
2711 elif user_input.lower() == "interrupt":
2712 await self.client.interrupt()
2713 print("Task interrupted!")
2714 continue
2715 elif user_input.lower() == "new":
2716 # Disconnect and reconnect for a fresh session
2717 await self.client.disconnect()
2718 await self.client.connect()
2719 self.turn_count = 0
2720 print("Started new conversation session (previous context cleared)")
2721 continue
2722
2723 # Send message - the session retains all previous messages
2724 await self.client.query(user_input)
2725 self.turn_count += 1
2726
2727 # Process response
2728 print(f"[Turn {self.turn_count}] Claude: ", end="")
2729 async for message in self.client.receive_response():
2730 if isinstance(message, AssistantMessage):
2731 for block in message.content:
2732 if isinstance(block, TextBlock):
2733 print(block.text, end="")
2734 print() # New line after response
2735
2736 await self.client.disconnect()
2737 print(f"Conversation ended after {self.turn_count} turns.")
2738
2739
2740async def main():
2741 options = ClaudeAgentOptions(
2742 allowed_tools=["Read", "Write", "Bash"], permission_mode="acceptEdits"
2743 )
2744 session = ConversationSession(options)
2745 await session.start()
2746
2747
2748# Example conversation:
2749# Turn 1 - You: "Create a file called hello.py"
2750# Turn 1 - Claude: "I'll create a hello.py file for you..."
2751# Turn 2 - You: "What's in that file?"
2752# Turn 2 - Claude: "The hello.py file I just created contains..." (remembers!)
2753# Turn 3 - You: "Add a main function to it"
2754# Turn 3 - Claude: "I'll add a main function to hello.py..." (knows which file!)
2755
2756asyncio.run(main())
2757```
2758
2759### Using Hooks for Behavior Modification
2760
2761```python theme={null}
2762from claude_agent_sdk import (
2763 ClaudeSDKClient,
2764 ClaudeAgentOptions,
2765 HookMatcher,
2766 HookContext,
2767)
2768import asyncio
2769from typing import Any
2770
2771
2772async def pre_tool_logger(
2773 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
2774) -> dict[str, Any]:
2775 """Log all tool usage before execution."""
2776 tool_name = input_data.get("tool_name", "unknown")
2777 print(f"[PRE-TOOL] About to use: {tool_name}")
2778
2779 # You can modify or block the tool execution here
2780 if tool_name == "Bash" and "rm -rf" in str(input_data.get("tool_input", {})):
2781 return {
2782 "hookSpecificOutput": {
2783 "hookEventName": "PreToolUse",
2784 "permissionDecision": "deny",
2785 "permissionDecisionReason": "Dangerous command blocked",
2786 }
2787 }
2788 return {}
2789
2790
2791async def post_tool_logger(
2792 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
2793) -> dict[str, Any]:
2794 """Log results after tool execution."""
2795 tool_name = input_data.get("tool_name", "unknown")
2796 print(f"[POST-TOOL] Completed: {tool_name}")
2797 return {}
2798
2799
2800async def user_prompt_modifier(
2801 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
2802) -> dict[str, Any]:
2803 """Add context to user prompts."""
2804 original_prompt = input_data.get("prompt", "")
2805
2806 # Add a timestamp as additional context for Claude to see
2807 from datetime import datetime
2808
2809 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2810
2811 return {
2812 "hookSpecificOutput": {
2813 "hookEventName": "UserPromptSubmit",
2814 "additionalContext": f"[Submitted at {timestamp}] Original prompt: {original_prompt}",
2815 }
2816 }
2817
2818
2819async def main():
2820 options = ClaudeAgentOptions(
2821 hooks={
2822 "PreToolUse": [
2823 HookMatcher(hooks=[pre_tool_logger]),
2824 HookMatcher(matcher="Bash", hooks=[pre_tool_logger]),
2825 ],
2826 "PostToolUse": [HookMatcher(hooks=[post_tool_logger])],
2827 "UserPromptSubmit": [HookMatcher(hooks=[user_prompt_modifier])],
2828 },
2829 allowed_tools=["Read", "Write", "Bash"],
2830 )
2831
2832 async with ClaudeSDKClient(options=options) as client:
2833 await client.query("List files in current directory")
2834
2835 async for message in client.receive_response():
2836 # Hooks will automatically log tool usage
2837 pass
2838
2839
2840asyncio.run(main())
2841```
2842
2843### Real-time Progress Monitoring
2844
2845```python theme={null}
2846from claude_agent_sdk import (
2847 ClaudeSDKClient,
2848 ClaudeAgentOptions,
2849 AssistantMessage,
2850 ToolUseBlock,
2851 ToolResultBlock,
2852 TextBlock,
2853)
2854import asyncio
2855
2856
2857async def monitor_progress():
2858 options = ClaudeAgentOptions(
2859 allowed_tools=["Write", "Bash"], permission_mode="acceptEdits"
2860 )
2861
2862 async with ClaudeSDKClient(options=options) as client:
2863 await client.query("Create 5 Python files with different sorting algorithms")
2864
2865 # Monitor progress in real-time
2866 async for message in client.receive_response():
2867 if isinstance(message, AssistantMessage):
2868 for block in message.content:
2869 if isinstance(block, ToolUseBlock):
2870 if block.name == "Write":
2871 file_path = block.input.get("file_path", "")
2872 print(f"Creating: {file_path}")
2873 elif isinstance(block, ToolResultBlock):
2874 print("Completed tool execution")
2875 elif isinstance(block, TextBlock):
2876 print(f"Claude says: {block.text[:100]}...")
2877
2878 print("Task completed!")
2879
2880
2881asyncio.run(monitor_progress())
2882```
2883
2884## Example Usage
2885
2886### Basic file operations (using query)
2887
2888```python theme={null}
2889from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
2890import asyncio
2891
2892
2893async def create_project():
2894 options = ClaudeAgentOptions(
2895 allowed_tools=["Read", "Write", "Bash"],
2896 permission_mode="acceptEdits",
2897 cwd="/home/user/project",
2898 )
2899
2900 async for message in query(
2901 prompt="Create a Python project structure with setup.py", options=options
2902 ):
2903 if isinstance(message, AssistantMessage):
2904 for block in message.content:
2905 if isinstance(block, ToolUseBlock):
2906 print(f"Using tool: {block.name}")
2907
2908
2909asyncio.run(create_project())
2910```
2911
2912### Error handling
2913
2914```python theme={null}
2915from claude_agent_sdk import query, CLINotFoundError, ProcessError, CLIJSONDecodeError
2916
2917try:
2918 async for message in query(prompt="Hello"):
2919 print(message)
2920except CLINotFoundError:
2921 print(
2922 "Claude Code CLI not found. Try reinstalling: pip install --force-reinstall claude-agent-sdk"
2923 )
2924except ProcessError as e:
2925 print(f"Process failed with exit code: {e.exit_code}")
2926except CLIJSONDecodeError as e:
2927 print(f"Failed to parse response: {e}")
2928```
2929
2930### Streaming mode with client
2931
2932```python theme={null}
2933from claude_agent_sdk import ClaudeSDKClient
2934import asyncio
2935
2936
2937async def interactive_session():
2938 async with ClaudeSDKClient() as client:
2939 # Send initial message
2940 await client.query("What's the weather like?")
2941
2942 # Process responses
2943 async for msg in client.receive_response():
2944 print(msg)
2945
2946 # Send follow-up
2947 await client.query("Tell me more about that")
2948
2949 # Process follow-up response
2950 async for msg in client.receive_response():
2951 print(msg)
2952
2953
2954asyncio.run(interactive_session())
2955```
2956
2957### Using custom tools with ClaudeSDKClient
2958
2959```python theme={null}
2960from claude_agent_sdk import (
2961 ClaudeSDKClient,
2962 ClaudeAgentOptions,
2963 tool,
2964 create_sdk_mcp_server,
2965 AssistantMessage,
2966 TextBlock,
2967)
2968import asyncio
2969from typing import Any
2970
2971
2972# Define custom tools with @tool decorator
2973@tool("calculate", "Perform mathematical calculations", {"expression": str})
2974async def calculate(args: dict[str, Any]) -> dict[str, Any]:
2975 try:
2976 result = eval(args["expression"], {"__builtins__": {}})
2977 return {"content": [{"type": "text", "text": f"Result: {result}"}]}
2978 except Exception as e:
2979 return {
2980 "content": [{"type": "text", "text": f"Error: {str(e)}"}],
2981 "is_error": True,
2982 }
2983
2984
2985@tool("get_time", "Get current time", {})
2986async def get_time(args: dict[str, Any]) -> dict[str, Any]:
2987 from datetime import datetime
2988
2989 current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2990 return {"content": [{"type": "text", "text": f"Current time: {current_time}"}]}
2991
2992
2993async def main():
2994 # Create SDK MCP server with custom tools
2995 my_server = create_sdk_mcp_server(
2996 name="utilities", version="1.0.0", tools=[calculate, get_time]
2997 )
2998
2999 # Configure options with the server
3000 options = ClaudeAgentOptions(
3001 mcp_servers={"utils": my_server},
3002 allowed_tools=["mcp__utils__calculate", "mcp__utils__get_time"],
3003 )
3004
3005 # Use ClaudeSDKClient for interactive tool usage
3006 async with ClaudeSDKClient(options=options) as client:
3007 await client.query("What's 123 * 456?")
3008
3009 # Process calculation response
3010 async for message in client.receive_response():
3011 if isinstance(message, AssistantMessage):
3012 for block in message.content:
3013 if isinstance(block, TextBlock):
3014 print(f"Calculation: {block.text}")
3015
3016 # Follow up with time query
3017 await client.query("What time is it now?")
3018
3019 async for message in client.receive_response():
3020 if isinstance(message, AssistantMessage):
3021 for block in message.content:
3022 if isinstance(block, TextBlock):
3023 print(f"Time: {block.text}")
3024
3025
3026asyncio.run(main())
3027```
3028
3029## Sandbox Configuration
3030
3031### `SandboxSettings`
3032
3033Configuration for sandbox behavior. Use this to enable command sandboxing and configure network restrictions programmatically.
3034
3035```python theme={null}
3036class SandboxSettings(TypedDict, total=False):
3037 enabled: bool
3038 autoAllowBashIfSandboxed: bool
3039 excludedCommands: list[str]
3040 allowUnsandboxedCommands: bool
3041 network: SandboxNetworkConfig
3042 ignoreViolations: SandboxIgnoreViolations
3043 enableWeakerNestedSandbox: bool
3044```
3045
3046| Property | Type | Default | Description |
3047| :-------------------------- | :------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3048| `enabled` | `bool` | `False` | Enable sandbox mode for command execution |
3049| `autoAllowBashIfSandboxed` | `bool` | `True` | Auto-approve bash commands when sandbox is enabled |
3050| `excludedCommands` | `list[str]` | `[]` | Commands that always bypass sandbox restrictions (e.g., `["docker"]`). These run unsandboxed automatically without model involvement |
3051| `allowUnsandboxedCommands` | `bool` | `True` | Allow the model to request running commands outside the sandbox. When `True`, the model can set `dangerouslyDisableSandbox` in tool input, which falls back to the [permissions system](#permissions-fallback-for-unsandboxed-commands) |
3052| `network` | [`SandboxNetworkConfig`](#sandbox-network-config) | `None` | Network-specific sandbox configuration |
3053| `ignoreViolations` | [`SandboxIgnoreViolations`](#sandbox-ignore-violations) | `None` | Configure which sandbox violations to ignore |
3054| `enableWeakerNestedSandbox` | `bool` | `False` | Enable a weaker nested sandbox for compatibility |
3055
3056<Note>
3057 **Filesystem and network access restrictions** are NOT configured via sandbox settings. Instead, they are derived from [permission rules](/en/settings#permission-settings):
3058
3059 * **Filesystem read restrictions**: Read deny rules
3060 * **Filesystem write restrictions**: Edit allow/deny rules
3061 * **Network restrictions**: WebFetch allow/deny rules
3062
3063 Use sandbox settings for command execution sandboxing, and permission rules for filesystem and network access control.
3064</Note>
3065
3066#### Example usage
3067
3068```python theme={null}
3069from claude_agent_sdk import query, ClaudeAgentOptions, SandboxSettings
3070
3071sandbox_settings: SandboxSettings = {
3072 "enabled": True,
3073 "autoAllowBashIfSandboxed": True,
3074 "network": {"allowLocalBinding": True},
3075}
3076
3077async for message in query(
3078 prompt="Build and test my project",
3079 options=ClaudeAgentOptions(sandbox=sandbox_settings),
3080):
3081 print(message)
3082```
3083
3084<Warning>
3085 **Unix socket security**: The `allowUnixSockets` option can grant access to powerful system services. For example, allowing `/var/run/docker.sock` effectively grants full host system access through the Docker API, bypassing sandbox isolation. Only allow Unix sockets that are strictly necessary and understand the security implications of each.
3086</Warning>
3087
3088### `SandboxNetworkConfig`
3089
3090Network-specific configuration for sandbox mode.
3091
3092```python theme={null}
3093class SandboxNetworkConfig(TypedDict, total=False):
3094 allowLocalBinding: bool
3095 allowUnixSockets: list[str]
3096 allowAllUnixSockets: bool
3097 httpProxyPort: int
3098 socksProxyPort: int
3099```
3100
3101| Property | Type | Default | Description |
3102| :-------------------- | :---------- | :------ | :---------------------------------------------------------------- |
3103| `allowLocalBinding` | `bool` | `False` | Allow processes to bind to local ports (e.g., for dev servers) |
3104| `allowUnixSockets` | `list[str]` | `[]` | Unix socket paths that processes can access (e.g., Docker socket) |
3105| `allowAllUnixSockets` | `bool` | `False` | Allow access to all Unix sockets |
3106| `httpProxyPort` | `int` | `None` | HTTP proxy port for network requests |
3107| `socksProxyPort` | `int` | `None` | SOCKS proxy port for network requests |
3108
3109### `SandboxIgnoreViolations`
3110
3111Configuration for ignoring specific sandbox violations.
3112
3113```python theme={null}
3114class SandboxIgnoreViolations(TypedDict, total=False):
3115 file: list[str]
3116 network: list[str]
3117```
3118
3119| Property | Type | Default | Description |
3120| :-------- | :---------- | :------ | :------------------------------------------ |
3121| `file` | `list[str]` | `[]` | File path patterns to ignore violations for |
3122| `network` | `list[str]` | `[]` | Network patterns to ignore violations for |
3123
3124### Permissions Fallback for Unsandboxed Commands
3125
3126When `allowUnsandboxedCommands` is enabled, the model can request to run commands outside the sandbox by setting `dangerouslyDisableSandbox: True` in the tool input. These requests fall back to the existing permissions system, meaning your `can_use_tool` handler will be invoked, allowing you to implement custom authorization logic.
3127
3128<Note>
3129 **`excludedCommands` vs `allowUnsandboxedCommands`:**
3130
3131 * `excludedCommands`: A static list of commands that always bypass the sandbox automatically (e.g., `["docker"]`). The model has no control over this.
3132 * `allowUnsandboxedCommands`: Lets the model decide at runtime whether to request unsandboxed execution by setting `dangerouslyDisableSandbox: True` in the tool input.
3133</Note>
3134
3135```python theme={null}
3136from claude_agent_sdk import (
3137 query,
3138 ClaudeAgentOptions,
3139 HookMatcher,
3140 PermissionResultAllow,
3141 PermissionResultDeny,
3142 ToolPermissionContext,
3143)
3144
3145
3146async def can_use_tool(
3147 tool: str, input: dict, context: ToolPermissionContext
3148) -> PermissionResultAllow | PermissionResultDeny:
3149 # Check if the model is requesting to bypass the sandbox
3150 if tool == "Bash" and input.get("dangerouslyDisableSandbox"):
3151 # The model is requesting to run this command outside the sandbox
3152 print(f"Unsandboxed command requested: {input.get('command')}")
3153
3154 if is_command_authorized(input.get("command")):
3155 return PermissionResultAllow()
3156 return PermissionResultDeny(
3157 message="Command not authorized for unsandboxed execution"
3158 )
3159 return PermissionResultAllow()
3160
3161
3162# Required: dummy hook keeps the stream open for can_use_tool
3163async def dummy_hook(input_data, tool_use_id, context):
3164 return {"continue_": True}
3165
3166
3167async def prompt_stream():
3168 yield {
3169 "type": "user",
3170 "message": {"role": "user", "content": "Deploy my application"},
3171 }
3172
3173
3174async def main():
3175 async for message in query(
3176 prompt=prompt_stream(),
3177 options=ClaudeAgentOptions(
3178 sandbox={
3179 "enabled": True,
3180 "allowUnsandboxedCommands": True, # Model can request unsandboxed execution
3181 },
3182 permission_mode="default",
3183 can_use_tool=can_use_tool,
3184 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
3185 ),
3186 ):
3187 print(message)
3188```
3189
3190This pattern enables you to:
3191
3192* **Audit model requests**: Log when the model requests unsandboxed execution
3193* **Implement allowlists**: Only permit specific commands to run unsandboxed
3194* **Add approval workflows**: Require explicit authorization for privileged operations
3195
3196<Warning>
3197 Commands running with `dangerouslyDisableSandbox: True` have full system access. Ensure your `can_use_tool` handler validates these requests carefully.
3198
3199 If `permission_mode` is set to `bypassPermissions` and `allow_unsandboxed_commands` is enabled, the model can autonomously execute commands outside the sandbox without any approval prompts. This combination effectively allows the model to escape sandbox isolation silently.
3200</Warning>
3201
3202## See also
3203
3204* [SDK overview](/en/agent-sdk/overview) - General SDK concepts
3205* [TypeScript SDK reference](/en/agent-sdk/typescript) - TypeScript SDK documentation
3206* [CLI reference](/en/cli-reference) - Command-line interface
3207* [Common workflows](/en/common-workflows) - Step-by-step guides