Python SDK for Claude Code enabling AI agents with tool usage, hooks, permissions, and bidirectional conversations
npx @tessl/cli install tessl/pypi-claude-agent-sdk@0.1.10
# Claude Agent SDK
1
2
Python SDK for Claude Code enabling developers to build AI agents with tool usage, hooks, permission control, and bidirectional conversations. Provides both simple query-based interactions and advanced streaming client capabilities for complex use cases.
3
4
## Package Information
5
6
- **Package Name**: claude-agent-sdk
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install claude-agent-sdk`
10
- **Python Version**: 3.10+
11
- **Dependencies**: anyio>=4.0.0, typing_extensions>=4.0.0 (Python <3.11), mcp>=0.1.0
12
13
## Core Imports
14
15
```python
16
from claude_agent_sdk import query, ClaudeSDKClient, ClaudeAgentOptions
17
```
18
19
All public exports are available from the main package namespace:
20
21
```python
22
from claude_agent_sdk import (
23
# Main functions
24
query,
25
__version__,
26
27
# Client
28
ClaudeSDKClient,
29
Transport,
30
31
# Configuration
32
ClaudeAgentOptions,
33
34
# Message types
35
UserMessage,
36
AssistantMessage,
37
SystemMessage,
38
ResultMessage,
39
StreamEvent,
40
Message,
41
42
# Content blocks
43
TextBlock,
44
ThinkingBlock,
45
ToolUseBlock,
46
ToolResultBlock,
47
ContentBlock,
48
49
# MCP server support
50
create_sdk_mcp_server,
51
tool,
52
SdkMcpTool,
53
McpServerConfig,
54
McpSdkServerConfig,
55
56
# Hook system
57
HookCallback,
58
HookContext,
59
HookInput,
60
HookJSONOutput,
61
HookMatcher,
62
63
# Permission system
64
PermissionMode,
65
CanUseTool,
66
ToolPermissionContext,
67
PermissionResult,
68
PermissionResultAllow,
69
PermissionResultDeny,
70
PermissionUpdate,
71
72
# Agent system
73
AgentDefinition,
74
SettingSource,
75
76
# Plugin support
77
SdkPluginConfig,
78
79
# Error types
80
ClaudeSDKError,
81
CLIConnectionError,
82
CLINotFoundError,
83
ProcessError,
84
CLIJSONDecodeError,
85
)
86
```
87
88
## Basic Usage
89
90
### Simple Query
91
92
The `query()` function provides the simplest way to interact with Claude:
93
94
```python
95
import anyio
96
from claude_agent_sdk import query, AssistantMessage, TextBlock
97
98
async def main():
99
# Simple question
100
async for message in query(prompt="What is 2 + 2?"):
101
if isinstance(message, AssistantMessage):
102
for block in message.content:
103
if isinstance(block, TextBlock):
104
print(block.text)
105
106
anyio.run(main)
107
```
108
109
### Query with Tools
110
111
Enable Claude to use file operations and shell commands:
112
113
```python
114
from claude_agent_sdk import query, ClaudeAgentOptions
115
116
async def main():
117
options = ClaudeAgentOptions(
118
allowed_tools=["Read", "Write", "Bash"],
119
permission_mode="acceptEdits", # Auto-accept file edits
120
cwd="/path/to/project"
121
)
122
123
async for message in query(
124
prompt="Create a hello.py file that prints Hello World",
125
options=options
126
):
127
print(message)
128
129
anyio.run(main)
130
```
131
132
### Interactive Client
133
134
For bidirectional conversations with state:
135
136
```python
137
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
138
139
async def main():
140
options = ClaudeAgentOptions(
141
allowed_tools=["Read", "Write"],
142
permission_mode="acceptEdits"
143
)
144
145
async with ClaudeSDKClient(options=options) as client:
146
# First query
147
await client.query("Create a todo.txt file")
148
async for msg in client.receive_response():
149
print(msg)
150
151
# Follow-up query in same session
152
await client.query("Add 'Buy groceries' to the file")
153
async for msg in client.receive_response():
154
print(msg)
155
156
anyio.run(main)
157
```
158
159
## Architecture
160
161
The SDK provides two interaction patterns:
162
163
1. **`query()` Function**: Stateless, one-shot interactions. Best for simple queries, batch processing, or when you don't need conversation context.
164
165
2. **`ClaudeSDKClient` Class**: Stateful, bidirectional streaming. Supports custom tools (via in-process MCP servers), hooks for intercepting operations, and multi-turn conversations with session management.
166
167
**Key Components**:
168
- **Messages**: Structured communication with `UserMessage`, `AssistantMessage`, `SystemMessage`, `ResultMessage`, `StreamEvent`
169
- **Content Blocks**: Message content as `TextBlock`, `ThinkingBlock`, `ToolUseBlock`, `ToolResultBlock`
170
- **Configuration**: Comprehensive options via `ClaudeAgentOptions` dataclass
171
- **MCP Servers**: In-process tool execution without subprocess overhead
172
- **Hooks**: Intercept and control operations at PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact
173
- **Permissions**: Runtime control via modes, callbacks, and hooks
174
175
**Bundled CLI**: Claude Code CLI is automatically included—no separate installation needed. The SDK manages the CLI lifecycle transparently.
176
177
## Capabilities
178
179
### Core Query Interface
180
181
The `query()` function for simple, stateless interactions and `ClaudeSDKClient` for bidirectional streaming with state management.
182
183
```python { .api }
184
async def query(
185
*,
186
prompt: str | AsyncIterable[dict[str, Any]],
187
options: ClaudeAgentOptions | None = None,
188
transport: Transport | None = None,
189
) -> AsyncIterator[Message]:
190
"""
191
One-shot or unidirectional streaming query to Claude.
192
193
Args:
194
prompt: User message string or async iterable of message dicts
195
options: Configuration options (default: ClaudeAgentOptions())
196
transport: Custom transport implementation (default: subprocess CLI)
197
198
Returns:
199
AsyncIterator yielding Message objects
200
"""
201
```
202
203
```python { .api }
204
class ClaudeSDKClient:
205
"""Bidirectional streaming client for interactive conversations."""
206
207
def __init__(
208
self,
209
options: ClaudeAgentOptions | None = None,
210
transport: Transport | None = None,
211
):
212
"""
213
Initialize client.
214
215
Args:
216
options: Configuration options
217
transport: Custom transport implementation
218
"""
219
220
async def connect(
221
self, prompt: str | AsyncIterable[dict[str, Any]] | None = None
222
) -> None:
223
"""Establish connection and optionally send initial prompt."""
224
225
async def query(
226
self, prompt: str | AsyncIterable[dict[str, Any]], session_id: str = "default"
227
) -> None:
228
"""Send a message to Claude."""
229
230
async def receive_messages(self) -> AsyncIterator[Message]:
231
"""Receive all messages until connection closes."""
232
233
async def receive_response(self) -> AsyncIterator[Message]:
234
"""Receive messages until ResultMessage is received."""
235
236
async def interrupt(self) -> None:
237
"""Send interrupt signal to stop current operation."""
238
239
async def set_permission_mode(self, mode: str) -> None:
240
"""Change permission mode during conversation."""
241
242
async def set_model(self, model: str | None = None) -> None:
243
"""Switch AI model during conversation."""
244
245
async def get_server_info(self) -> dict[str, Any] | None:
246
"""Get server capabilities and information."""
247
248
async def disconnect(self) -> None:
249
"""Close connection and clean up resources."""
250
```
251
252
[Core Query Interface](./core-query-interface.md)
253
254
### Messages and Content
255
256
Message types for communication between user and Claude, and content blocks that compose messages.
257
258
```python { .api }
259
@dataclass
260
class UserMessage:
261
"""Message from user to Claude."""
262
content: str | list[ContentBlock]
263
parent_tool_use_id: str | None = None
264
265
@dataclass
266
class AssistantMessage:
267
"""Message from Claude to user."""
268
content: list[ContentBlock]
269
model: str
270
parent_tool_use_id: str | None = None
271
272
@dataclass
273
class SystemMessage:
274
"""System-level message."""
275
subtype: str
276
data: dict[str, Any]
277
278
@dataclass
279
class ResultMessage:
280
"""Final result of conversation turn."""
281
subtype: str
282
duration_ms: int
283
duration_api_ms: int
284
is_error: bool
285
num_turns: int
286
session_id: str
287
total_cost_usd: float | None = None
288
usage: dict[str, Any] | None = None
289
result: str | None = None
290
structured_output: Any = None
291
292
@dataclass
293
class StreamEvent:
294
"""Raw stream event from Anthropic API."""
295
uuid: str
296
session_id: str
297
event: dict[str, Any]
298
parent_tool_use_id: str | None = None
299
300
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEvent
301
```
302
303
```python { .api }
304
@dataclass
305
class TextBlock:
306
"""Plain text content."""
307
text: str
308
309
@dataclass
310
class ThinkingBlock:
311
"""Extended thinking content."""
312
thinking: str
313
signature: str
314
315
@dataclass
316
class ToolUseBlock:
317
"""Tool invocation request."""
318
id: str
319
name: str
320
input: dict[str, Any]
321
322
@dataclass
323
class ToolResultBlock:
324
"""Tool execution result."""
325
tool_use_id: str
326
content: str | list[dict[str, Any]] | None = None
327
is_error: bool | None = None
328
329
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
330
```
331
332
[Messages and Content](./messages-and-content.md)
333
334
### Configuration Options
335
336
Comprehensive configuration via `ClaudeAgentOptions` dataclass covering tools, prompts, permissions, budgets, models, and more.
337
338
```python { .api }
339
@dataclass
340
class ClaudeAgentOptions:
341
"""Configuration options for Claude SDK."""
342
343
# Tool configuration
344
allowed_tools: list[str] = field(default_factory=list)
345
disallowed_tools: list[str] = field(default_factory=list)
346
347
# Prompt configuration
348
system_prompt: str | SystemPromptPreset | None = None
349
350
# MCP server configuration
351
mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
352
353
# Permission configuration
354
permission_mode: PermissionMode | None = None
355
permission_prompt_tool_name: str | None = None
356
can_use_tool: CanUseTool | None = None
357
358
# Session configuration
359
continue_conversation: bool = False
360
resume: str | None = None
361
fork_session: bool = False
362
363
# Budget and limits
364
max_turns: int | None = None
365
max_budget_usd: float | None = None
366
max_thinking_tokens: int | None = None
367
368
# Model configuration
369
model: str | None = None
370
fallback_model: str | None = None
371
372
# Working directory and CLI
373
cwd: str | Path | None = None
374
cli_path: str | Path | None = None
375
add_dirs: list[str | Path] = field(default_factory=list)
376
377
# Settings and sources
378
settings: str | None = None
379
setting_sources: list[SettingSource] | None = None
380
381
# Environment
382
env: dict[str, str] = field(default_factory=dict)
383
extra_args: dict[str, str | None] = field(default_factory=dict)
384
385
# Callbacks
386
stderr: Callable[[str], None] | None = None
387
debug_stderr: Any = sys.stderr # Deprecated: Use stderr callback instead
388
389
# Hooks and plugins
390
hooks: dict[HookEvent, list[HookMatcher]] | None = None
391
agents: dict[str, AgentDefinition] | None = None
392
plugins: list[SdkPluginConfig] = field(default_factory=list)
393
394
# Advanced features
395
include_partial_messages: bool = False
396
output_format: dict[str, Any] | None = None
397
max_buffer_size: int | None = None
398
user: str | None = None
399
```
400
401
[Configuration Options](./configuration-options.md)
402
403
### Custom Tools (In-Process MCP Servers)
404
405
Define custom tools as Python functions that Claude can invoke, running in the same process without subprocess overhead.
406
407
```python { .api }
408
def tool(
409
name: str,
410
description: str,
411
input_schema: type | dict[str, Any]
412
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]:
413
"""
414
Decorator for creating custom tools.
415
416
Args:
417
name: Tool name
418
description: Tool description for Claude
419
input_schema: Input schema as type or dict
420
421
Returns:
422
Decorator that wraps handler function
423
"""
424
425
def create_sdk_mcp_server(
426
name: str,
427
version: str = "1.0.0",
428
tools: list[SdkMcpTool[Any]] | None = None
429
) -> McpSdkServerConfig:
430
"""
431
Create an in-process MCP server with custom tools.
432
433
Args:
434
name: Server name
435
version: Server version
436
tools: List of tool definitions
437
438
Returns:
439
Server configuration for use in ClaudeAgentOptions.mcp_servers
440
"""
441
442
@dataclass
443
class SdkMcpTool(Generic[T]):
444
"""Custom tool definition."""
445
name: str
446
description: str
447
input_schema: type[T] | dict[str, Any]
448
handler: Callable[[T], Awaitable[dict[str, Any]]]
449
```
450
451
[Custom Tools (MCP Servers)](./custom-tools.md)
452
453
### Hook System
454
455
Intercept and control Claude's operations at specific points in the agent loop with PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, and PreCompact hooks.
456
457
```python { .api }
458
HookEvent = Literal[
459
"PreToolUse",
460
"PostToolUse",
461
"UserPromptSubmit",
462
"Stop",
463
"SubagentStop",
464
"PreCompact"
465
]
466
467
@dataclass
468
class HookMatcher:
469
"""Hook configuration with pattern matching."""
470
matcher: str | None = None # Tool name pattern (e.g., "Bash", "Write|Edit")
471
hooks: list[HookCallback] = field(default_factory=list)
472
473
HookCallback = Callable[
474
[HookInput, str | None, HookContext],
475
Awaitable[HookJSONOutput],
476
]
477
478
class HookContext(TypedDict):
479
"""Context provided to hooks."""
480
signal: Any | None
481
```
482
483
[Hook System](./hook-system.md)
484
485
### Permission Control
486
487
Runtime permission control via modes, programmatic callbacks, and permission updates.
488
489
```python { .api }
490
PermissionMode = Literal["default", "acceptEdits", "plan", "bypassPermissions"]
491
492
CanUseTool = Callable[
493
[str, dict[str, Any], ToolPermissionContext],
494
Awaitable[PermissionResult]
495
]
496
497
@dataclass
498
class ToolPermissionContext:
499
"""Context for permission callbacks."""
500
signal: Any | None = None
501
suggestions: list[PermissionUpdate] = field(default_factory=list)
502
503
@dataclass
504
class PermissionResultAllow:
505
"""Allow permission decision."""
506
behavior: Literal["allow"] = "allow"
507
updated_input: dict[str, Any] | None = None
508
updated_permissions: list[PermissionUpdate] | None = None
509
510
@dataclass
511
class PermissionResultDeny:
512
"""Deny permission decision."""
513
behavior: Literal["deny"] = "deny"
514
message: str = ""
515
interrupt: bool = False
516
517
PermissionResult = PermissionResultAllow | PermissionResultDeny
518
519
@dataclass
520
class PermissionUpdate:
521
"""Permission configuration update."""
522
type: Literal[
523
"addRules", "replaceRules", "removeRules",
524
"setMode", "addDirectories", "removeDirectories"
525
]
526
rules: list[PermissionRuleValue] | None = None
527
behavior: PermissionBehavior | None = None
528
mode: PermissionMode | None = None
529
directories: list[str] | None = None
530
destination: PermissionUpdateDestination | None = None
531
532
def to_dict(self) -> dict[str, Any]:
533
"""Convert to dictionary for CLI."""
534
```
535
536
[Permission Control](./permission-control.md)
537
538
### Agent Definitions
539
540
Define custom agents with specific tools, prompts, and models for specialized tasks.
541
542
```python { .api }
543
@dataclass
544
class AgentDefinition:
545
"""Custom agent configuration."""
546
description: str
547
prompt: str
548
tools: list[str] | None = None
549
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None
550
551
SettingSource = Literal["user", "project", "local"]
552
```
553
554
[Agent Definitions](./agent-definitions.md)
555
556
### MCP Server Configuration
557
558
Configure external MCP servers via stdio, SSE, or HTTP transports, in addition to in-process SDK servers.
559
560
```python { .api }
561
class McpStdioServerConfig(TypedDict):
562
"""Subprocess MCP server via stdio."""
563
type: NotRequired[Literal["stdio"]]
564
command: str
565
args: NotRequired[list[str]]
566
env: NotRequired[dict[str, str]]
567
568
class McpSSEServerConfig(TypedDict):
569
"""MCP server via Server-Sent Events."""
570
type: Literal["sse"]
571
url: str
572
headers: NotRequired[dict[str, str]]
573
574
class McpHttpServerConfig(TypedDict):
575
"""MCP server via HTTP."""
576
type: Literal["http"]
577
url: str
578
headers: NotRequired[dict[str, str]]
579
580
class McpSdkServerConfig(TypedDict):
581
"""In-process SDK MCP server."""
582
type: Literal["sdk"]
583
name: str
584
instance: McpServer
585
586
McpServerConfig = (
587
McpStdioServerConfig | McpSSEServerConfig |
588
McpHttpServerConfig | McpSdkServerConfig
589
)
590
```
591
592
[MCP Server Configuration](./mcp-server-configuration.md)
593
594
### Error Handling
595
596
Exception types for handling SDK errors including connection issues, process failures, and parsing errors.
597
598
```python { .api }
599
class ClaudeSDKError(Exception):
600
"""Base exception for all Claude SDK errors."""
601
602
class CLIConnectionError(ClaudeSDKError):
603
"""Raised when unable to connect to Claude Code."""
604
605
class CLINotFoundError(CLIConnectionError):
606
"""Raised when Claude Code is not found or not installed."""
607
608
def __init__(
609
self,
610
message: str = "Claude Code not found",
611
cli_path: str | None = None
612
):
613
...
614
615
cli_path: str | None
616
617
class ProcessError(ClaudeSDKError):
618
"""Raised when the CLI process fails."""
619
620
def __init__(
621
self,
622
message: str,
623
exit_code: int | None = None,
624
stderr: str | None = None
625
):
626
...
627
628
exit_code: int | None
629
stderr: str | None
630
631
class CLIJSONDecodeError(ClaudeSDKError):
632
"""Raised when unable to decode JSON from CLI output."""
633
634
def __init__(self, line: str, original_error: Exception):
635
...
636
637
line: str
638
original_error: Exception
639
```
640
641
[Error Handling](./error-handling.md)
642
643
### Version Information
644
645
```python { .api }
646
__version__: str # Current SDK version (e.g., "0.1.8")
647
```
648
649
## System Prompt Configuration
650
651
Configure Claude's system prompt using presets or custom text:
652
653
```python { .api }
654
class SystemPromptPreset(TypedDict):
655
"""System prompt preset configuration."""
656
type: Literal["preset"]
657
preset: Literal["claude_code"]
658
append: NotRequired[str]
659
```
660
661
**Usage**:
662
663
```python
664
# Use Claude Code's default system prompt
665
options = ClaudeAgentOptions(
666
system_prompt={"type": "preset", "preset": "claude_code"}
667
)
668
669
# Append additional instructions
670
options = ClaudeAgentOptions(
671
system_prompt={
672
"type": "preset",
673
"preset": "claude_code",
674
"append": "Always explain your reasoning step by step."
675
}
676
)
677
678
# Use custom system prompt
679
options = ClaudeAgentOptions(
680
system_prompt="You are a helpful Python expert specializing in async programming."
681
)
682
```
683
684
## Plugin Support
685
686
Load external plugins to extend SDK functionality:
687
688
```python { .api }
689
class SdkPluginConfig(TypedDict):
690
"""Plugin configuration."""
691
type: Literal["local"]
692
path: str
693
```
694
695
**Usage**:
696
697
```python
698
options = ClaudeAgentOptions(
699
plugins=[
700
{"type": "local", "path": "/path/to/plugin"}
701
]
702
)
703
```
704