Python SDK for Claude Code
Project description
Claude Agent SDK for Python
An improved fork of the official Claude Agent SDK with stronger type safety and additional features. (API still quite similar to the official one)
Installation
uv add clawd-code-sdk
Prerequisites:
- Python 3.13+
Quick Start
import anyio
from clawd_code_sdk import ClaudeSDKClient
async def main():
async for message in ClaudeSDKClient.one_shot("What is 2 + 2?"):
print(message)
anyio.run(main)
Basic Usage
One-shot queries
ClaudeSDKClient.one_shot() is the simplest way to query Claude Code.
It handles connection lifecycle automatically and yields response messages:
from clawd_code_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
# Simple query
async for message in ClaudeSDKClient.one_shot("Hello Claude"):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
# With options
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant",
max_turns=1,
)
async for message in ClaudeSDKClient.one_shot("Tell me a joke", options=options):
print(message)
Interactive sessions
ClaudeSDKClient supports bidirectional, interactive conversations with Claude
Code. Unlike one_shot(), it additionally enables custom tools, hooks,
and multi-turn conversations within the same session.
from clawd_code_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
options = ClaudeAgentOptions(max_turns=3)
async with ClaudeSDKClient(options=options) as client:
# First turn
await client.query("What is 2 + 2?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
# Follow-up in the same session
await client.query("Now multiply that by 3")
async for message in client.receive_response():
print(message)
Message Sequence
When you call client.query() and iterate client.receive_response(),
messages arrive in this order:
SessionStateChangedMessage state=running # session starts processing
InitSystemMessage subtype=init # session metadata (tools, model, cwd)
StreamEvent # raw Anthropic SSE events (deltas)
StreamEvent # ...
AssistantMessage # complete assistant turn (text, thinking, tool use)
StreamEvent # more streaming if multi-turn
AssistantMessage # another complete turn
ResultSuccessMessage subtype=success # final result with usage/cost
SessionStateChangedMessage state=idle # session fully done — iterator stops here
Key points:
receive_response()automatically terminates when the session transitions toidle— the authoritative signal that the CLI has fully finished (held-back results flushed, background agents exited).StreamEventwraps raw Anthropic streaming events (message_start,content_block_delta, etc.). Useful for real-time UI updates.AssistantMessagecontains complete content blocks (TextBlock,ThinkingBlock,ToolUseBlock) — no need to reassemble from deltas.- Hook messages (
HookStartedSystemMessage,HookResponseSystemMessage) may appear at any point if hooks are configured. ResultSuccessMessage/ResultErrorMessagecarry token usage and cost information.
Configuration
Tools
options = ClaudeAgentOptions(
tools=["Read", "Write", "Bash"], # tools available to the agent
allowed_tools=["Read", "Write", "Bash"], # auto-approved (no permission prompt)
disallowed_tools=["WebFetch"], # completely removed from context
permission_mode="acceptEdits", # auto-accept file edits
)
Working Directory
from pathlib import Path
options = ClaudeAgentOptions(
cwd="/path/to/project", # or Path("/path/to/project")
add_dirs=["/other/project"], # additional working directories
)
Model and Thinking
from clawd_code_sdk import ClaudeAgentOptions, ThinkingConfigEnabled
options = ClaudeAgentOptions(
model="sonnet",
fallback_model="haiku",
thinking=ThinkingConfigEnabled(budget_tokens=10_000),
effort="high",
)
Session Management
from clawd_code_sdk import ClaudeAgentOptions, NewSession, ResumeSession, ContinueLatest
# Fresh session (default)
options = ClaudeAgentOptions(session=NewSession())
# Resume by ID
options = ClaudeAgentOptions(session=ResumeSession(session_id="abc-123"))
# Or shorthand
options = ClaudeAgentOptions(session="abc-123")
# Continue most recent
options = ClaudeAgentOptions(session=ContinueLatest())
Structured Output
from pydantic import BaseModel
class Joke(BaseModel):
setup: str
punchline: str
options = ClaudeAgentOptions(
output_schema=Joke, # accepts Pydantic models, dataclasses, TypedDicts, or raw dicts
)
MCP Servers
In-Process SDK Servers
Define tools as Python functions that run in-process — no subprocess management or IPC overhead:
from clawd_code_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient
@tool("greet", "Greet a user", {"name": str})
async def greet_user(args):
return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}
server = create_sdk_mcp_server(
name="my-tools",
version="1.0.0",
tools=[greet_user],
)
options = ClaudeAgentOptions(
mcp_servers={"tools": server},
allowed_tools=["mcp__tools__greet"],
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Greet Alice")
async for msg in client.receive_response():
print(msg)
External Servers
from clawd_code_sdk import McpStdioServerConfig, McpHttpServerConfig
options = ClaudeAgentOptions(
mcp_servers={
# Subprocess (stdio)
"git": McpStdioServerConfig(command="uvx", args=["mcp-server-git"]),
# HTTP
"remote": McpHttpServerConfig(url="https://mcp.example.com/sse"),
}
)
Mixed Servers
SDK and external servers can be used together:
options = ClaudeAgentOptions(
mcp_servers={
"internal": sdk_server, # in-process
"git": McpStdioServerConfig(command="uvx", args=["mcp-server-git"]), # subprocess
}
)
Hooks
Hooks are Python callbacks that the Claude Code CLI invokes at specific points of the agent loop. They enable deterministic processing and automated feedback.
Each hook event has strongly-typed input/output types:
from clawd_code_sdk import (
ClaudeAgentOptions,
ClaudeSDKClient,
HookMatcher,
PreToolUseHookInput,
HookContext,
)
async def check_bash_command(
input_data: PreToolUseHookInput,
tool_use_id: str | None,
context: HookContext,
):
tool_input = input_data["tool_input"]
command = tool_input.get("command", "")
if "rm -rf" in command:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command blocked",
}
}
return {}
options = ClaudeAgentOptions(
allowed_tools=["Bash"],
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[check_bash_command]),
],
},
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Run echo hello")
async for msg in client.receive_response():
print(msg)
Available Hook Events
| Event | When it fires |
|---|---|
PreToolUse |
Before a tool executes (can deny/modify) |
PostToolUse |
After a tool succeeds |
PostToolUseFailure |
After a tool fails |
UserPromptSubmit |
When a user prompt is submitted |
Stop / StopFailure |
When the agent stops or fails to stop |
SubagentStart / SubagentStop |
Subagent lifecycle |
SessionStart / SessionEnd |
Session lifecycle |
Notification |
Agent notifications |
PermissionRequest / PermissionDenied |
Permission events |
PreCompact / PostCompact |
Context compaction |
Elicitation / ElicitationResult |
MCP elicitation |
TaskCreated / TaskCompleted |
Task lifecycle |
FileChanged / CwdChanged |
Filesystem events |
See the Claude Code Hooks Reference for full documentation.
Multimodal Prompts
Send images, PDFs, and text together using typed prompt classes:
from clawd_code_sdk import (
ClaudeSDKClient,
UserImageURLPrompt,
UserDocumentPrompt,
UserPlainTextDocumentPrompt,
)
async with ClaudeSDKClient() as client:
await client.query(
UserImageURLPrompt(url="https://example.com/chart.png"),
"What does this chart show?",
)
async for msg in client.receive_response():
print(msg)
Subagents
Define subagents with their own prompts, tools, and MCP servers:
from clawd_code_sdk import ClaudeAgentOptions, ClaudeSDKClient, AgentDefinition, McpStdioServerConfig
options = ClaudeAgentOptions(
agents={
"researcher": AgentDefinition(
description="A research agent with web access",
prompt="You are a research assistant.",
tools=["WebFetch", "Read"],
),
"git-agent": AgentDefinition(
description="An agent with git tools",
prompt="You are a git helper.",
mcp_servers={"git": McpStdioServerConfig(command="uvx", args=["mcp-server-git"])},
),
},
max_turns=10,
)
Types
Message Types
| Type | Description |
|---|---|
AssistantMessage |
Claude's response (text, thinking, tool use) |
UserMessage |
Echoed user messages |
StreamEvent |
Raw Anthropic SSE streaming events |
InitSystemMessage |
Session metadata (tools, model, cwd) |
ResultSuccessMessage |
Successful completion with usage |
ResultErrorMessage |
Error completion |
SessionStateChangedMessage |
Session state transitions |
HookStartedSystemMessage |
Hook execution started |
HookResponseSystemMessage |
Hook execution completed |
Content Block Types
| Type | Appears in |
|---|---|
TextBlock |
Assistant and user messages |
ThinkingBlock |
Assistant messages (when thinking enabled) |
ToolUseBlock |
Assistant messages |
ToolResultBlock |
User messages |
ImageBlock |
User messages |
The SDK provides role-specific narrowed types:
AssistantContentBlock=TextBlock | ThinkingBlock | ToolUseBlockUserContentBlock=TextBlock | ToolResultBlock | ImageBlockContentBlock= full 5-type union (for storage/serialization)
See src/clawd_code_sdk/models/content_blocks.py for complete type definitions.
Error Handling
from clawd_code_sdk import (
ClaudeSDKError, # Base error
CLINotFoundError, # Claude Code not installed
CLIConnectionError, # Connection issues
ProcessError, # Process failed
CLIJSONDecodeError, # JSON parsing issues
BillingError, # Insufficient credits
RateLimitError, # Rate limited
AuthenticationError, # Invalid API key
)
try:
async for message in ClaudeSDKClient.one_shot("Hello"):
pass
except CLINotFoundError:
print("Please install Claude Code")
except ProcessError as e:
print(f"Process failed with exit code: {e.exit_code}")
See src/clawd_code_sdk/_errors.py for all error types.
Available Tools
See the Claude Code documentation for a complete list of available tools.
Migrating from Claude Code SDK
If you're upgrading from the Claude Code SDK (versions < 0.1.0), please see the CHANGELOG.md for details on breaking changes and new features, including:
ClaudeCodeOptions→ClaudeAgentOptionsrename- Merged system prompt configuration
- Settings isolation and explicit control
- New programmatic subagents and session forking features
Development
If you're contributing to this project, run the initial setup script to install git hooks:
./scripts/initial-setup.sh
This installs a pre-push hook that runs lint checks before pushing, matching the CI workflow. To skip the hook temporarily, use git push --no-verify.
Building Wheels Locally
To build wheels with the bundled Claude Code CLI:
# Install build dependencies
pip install build twine
# Build wheel with bundled CLI
python scripts/build_wheel.py
# Build with specific version
python scripts/build_wheel.py --version 0.1.4
# Build with specific CLI version
python scripts/build_wheel.py --cli-version 2.0.0
# Clean bundled CLI after building
python scripts/build_wheel.py --clean
# Skip CLI download (use existing)
python scripts/build_wheel.py --skip-download
The build script:
- Downloads Claude Code CLI for your platform
- Bundles it in the wheel
- Builds both wheel and source distribution
- Checks the package with twine
See python scripts/build_wheel.py --help for all options.
Release Workflow
The package is published to PyPI via the GitHub Actions workflow in .github/workflows/publish.yml. To create a new release:
-
Trigger the workflow manually from the Actions tab with two inputs:
version: The package version to publish (e.g.,0.1.5)claude_code_version: The Claude Code CLI version to bundle (e.g.,2.0.0orlatest)
-
The workflow will:
- Build platform-specific wheels for macOS, Linux, and Windows
- Bundle the specified Claude Code CLI version in each wheel
- Build a source distribution
- Publish all artifacts to PyPI
- Create a release branch with version updates
- Open a PR to main with:
- Updated
pyproject.tomlversion - Updated
src/clawd_code_sdk/_version.py - Updated
src/clawd_code_sdk/_cli_version.pywith bundled CLI version - Auto-generated
CHANGELOG.mdentry
- Updated
-
Review and merge the release PR to update main with the new version information
The workflow tracks both the package version and the bundled CLI version separately, allowing you to release a new package version with an updated CLI without code changes.
License and terms
Use of this SDK is governed by Anthropic's Commercial Terms of Service, including when you use it to power products and services that you make available to your own customers and end users, except to the extent a specific component or dependency is covered by a different license as indicated in that component's LICENSE file.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file clawd_code_sdk-1.0.28.tar.gz.
File metadata
- Download URL: clawd_code_sdk-1.0.28.tar.gz
- Upload date:
- Size: 167.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baf371cccb9ec9ccae13586203c4008a5f73927456b52e9fdc69a0d6e753519c
|
|
| MD5 |
0b95b6c1d72a0a3eec9776a86ac3b4c2
|
|
| BLAKE2b-256 |
49bafe23b2f95f114f09e7515e3ec2dc03a3360dbfe5dcda9ace1f9764e1fba0
|
File details
Details for the file clawd_code_sdk-1.0.28-py3-none-any.whl.
File metadata
- Download URL: clawd_code_sdk-1.0.28-py3-none-any.whl
- Upload date:
- Size: 140.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e80059661d1623e960a30ff450f2e9b9c886cd8b9bcdb522449e32269490b740
|
|
| MD5 |
84f551b2ebaf83cd23be12a99a807838
|
|
| BLAKE2b-256 |
ec5d6f9a5b49220b1af00653acab444784d1702541a12cf6359d3477cfd547ca
|