Python SDK for Claude Code providing simple query functions and advanced bidirectional interactive conversations with custom tool support
Rich message system with structured content blocks supporting text, tool usage, tool results, thinking blocks, and streaming events. Includes complete type definitions for all message and content types used in Claude Code SDK communications.
All possible message types that can be received from Claude Code.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEventMessages representing user input and prompts.
@dataclass
class UserMessage:
"""User message."""
content: str | list[ContentBlock]
parent_tool_use_id: str | None = NoneFields:
content: User input as string or structured content blocksparent_tool_use_id: Optional reference to parent tool use operationMessages from Claude containing responses with rich content blocks.
@dataclass
class AssistantMessage:
"""Assistant message with content blocks."""
content: list[ContentBlock]
model: str
parent_tool_use_id: str | None = NoneFields:
content: List of content blocks (text, tool use, etc.)model: Claude model identifier used for the responseparent_tool_use_id: Optional reference to parent tool use operationInternal system messages with metadata and notifications.
@dataclass
class SystemMessage:
"""System message with metadata."""
subtype: str
data: dict[str, Any]Fields:
subtype: Type of system message (status, notification, etc.)data: Structured data payload for the system messageFinal result messages containing cost, usage, and completion information.
@dataclass
class ResultMessage:
"""Result message with cost and usage information."""
subtype: str
duration_ms: int
duration_api_ms: int
is_error: bool
num_turns: int
session_id: str
total_cost_usd: float | None = None
usage: dict[str, Any] | None = None
result: str | None = NoneFields:
subtype: Result type identifierduration_ms: Total duration in millisecondsduration_api_ms: API call duration in millisecondsis_error: Whether the interaction resulted in an errornum_turns: Number of conversation turnssession_id: Session identifiertotal_cost_usd: Total cost in USD (if available)usage: Token usage and other metricsresult: Final result summaryStreaming updates during partial message processing.
@dataclass
class StreamEvent:
"""Stream event for partial message updates during streaming."""
uuid: str
session_id: str
event: dict[str, Any] # The raw Anthropic API stream event
parent_tool_use_id: str | None = NoneFields:
uuid: Unique identifier for the stream eventsession_id: Session identifierevent: Raw Anthropic API stream event dataparent_tool_use_id: Optional reference to parent tool use operationAll possible content block types within messages.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockPlain text content blocks containing textual responses.
@dataclass
class TextBlock:
"""Text content block."""
text: strFields:
text: The text contentClaude's internal reasoning and thought process.
@dataclass
class ThinkingBlock:
"""Thinking content block."""
thinking: str
signature: strFields:
thinking: Claude's internal reasoning textsignature: Cryptographic signature for thinking contentBlocks representing Claude's intention to use a tool.
@dataclass
class ToolUseBlock:
"""Tool use content block."""
id: str
name: str
input: dict[str, Any]Fields:
id: Unique identifier for the tool usename: Name of the tool to be usedinput: Input parameters for the toolBlocks containing the results of tool execution.
@dataclass
class ToolResultBlock:
"""Tool result content block."""
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneFields:
tool_use_id: Reference to the tool use that generated this resultcontent: Tool execution result contentis_error: Whether the tool execution resulted in an errorfrom claude_code_sdk import query, AssistantMessage, TextBlock, ToolUseBlock, ToolResultBlock
async def main():
async for message in query(prompt="Create a Python file and run it"):
if isinstance(message, AssistantMessage):
print(f"Response from {message.model}:")
for block in message.content:
if isinstance(block, TextBlock):
print(f"Text: {block.text}")
elif isinstance(block, ToolUseBlock):
print(f"Tool use: {block.name} (ID: {block.id})")
print(f"Input: {block.input}")
elif isinstance(block, ToolResultBlock):
print(f"Tool result for {block.tool_use_id}:")
if block.is_error:
print(f"Error: {block.content}")
else:
print(f"Result: {block.content}")
elif isinstance(block, ThinkingBlock):
print(f"Claude's thinking: {block.thinking[:100]}...")from claude_code_sdk import (
query, UserMessage, AssistantMessage, SystemMessage,
ResultMessage, StreamEvent
)
async def main():
async for message in query(prompt="What is the capital of France?"):
if isinstance(message, UserMessage):
print(f"User: {message.content}")
elif isinstance(message, AssistantMessage):
print(f"Assistant ({message.model}): Processing {len(message.content)} content blocks")
elif isinstance(message, SystemMessage):
print(f"System ({message.subtype}): {message.data}")
elif isinstance(message, ResultMessage):
print(f"Result: {message.num_turns} turns, {message.duration_ms}ms")
if message.total_cost_usd:
print(f"Cost: ${message.total_cost_usd:.4f}")
elif isinstance(message, StreamEvent):
print(f"Stream event: {message.uuid}")from claude_code_sdk import ClaudeSDKClient, AssistantMessage, ResultMessage, TextBlock
async def main():
async with ClaudeSDKClient() as client:
await client.query("Explain quantum computing")
complete_response = []
async for msg in client.receive_response():
complete_response.append(msg)
if isinstance(msg, ResultMessage):
# Response is complete
break
# Process collected messages
for msg in complete_response:
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)from claude_code_sdk import (
query, ClaudeCodeOptions, AssistantMessage,
ToolUseBlock, ToolResultBlock
)
async def main():
options = ClaudeCodeOptions(
allowed_tools=["Read", "Write", "Bash"],
permission_mode="acceptEdits"
)
tool_uses = []
tool_results = []
async for message in query(
prompt="Create a hello.py file and run it",
options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
tool_uses.append(block)
print(f"Claude wants to use {block.name}:")
print(f" ID: {block.id}")
print(f" Input: {block.input}")
elif isinstance(block, ToolResultBlock):
tool_results.append(block)
print(f"Tool result for {block.tool_use_id}:")
if block.is_error:
print(f" Error: {block.content}")
else:
print(f" Success: {block.content}")
print(f"Total tool uses: {len(tool_uses)}")
print(f"Total tool results: {len(tool_results)}")from claude_code_sdk import ClaudeCodeOptions, query, StreamEvent
async def main():
options = ClaudeCodeOptions(
include_partial_messages=True # Enable streaming events
)
async for message in query(
prompt="Write a long story about space exploration",
options=options
):
if isinstance(message, StreamEvent):
event_type = message.event.get("type", "unknown")
print(f"Stream event ({event_type}): {message.uuid}")
# Handle different stream event types
if event_type == "content_block_start":
print(" Starting content block")
elif event_type == "content_block_delta":
delta = message.event.get("delta", {})
if "text" in delta:
print(f" Text delta: {delta['text']}")
elif event_type == "content_block_stop":
print(" Finished content block")from claude_code_sdk import query, ResultMessage, ToolResultBlock
async def main():
async for message in query(prompt="Run an invalid command"):
if isinstance(message, ResultMessage):
if message.is_error:
print(f"Conversation ended with error: {message.result}")
else:
print(f"Conversation completed successfully")
# Check for tool errors in content blocks
if hasattr(message, 'content'):
for block in getattr(message, 'content', []):
if isinstance(block, ToolResultBlock) and block.is_error:
print(f"Tool error in {block.tool_use_id}: {block.content}")from claude_code_sdk import query, AssistantMessage, TextBlock
from collections import Counter
async def main():
message_types = Counter()
content_types = Counter()
total_text_length = 0
async for message in query(prompt="Analyze this codebase and suggest improvements"):
message_types[type(message).__name__] += 1
if isinstance(message, AssistantMessage):
for block in message.content:
content_types[type(block).__name__] += 1
if isinstance(block, TextBlock):
total_text_length += len(block.text)
print("Message type distribution:")
for msg_type, count in message_types.items():
print(f" {msg_type}: {count}")
print("Content block distribution:")
for content_type, count in content_types.items():
print(f" {content_type}: {count}")
print(f"Total text content length: {total_text_length} characters")UserMessage: Initial user promptAssistantMessage: Claude's response with content blocksToolUseBlock: Claude decides to use a toolToolResultBlock: Tool execution resultAssistantMessage: Claude's response to tool resultResultMessage: Final result with costs and usageWhen include_partial_messages=True:
UserMessage: User promptStreamEvent: Content block startStreamEvent: Multiple text deltasStreamEvent: Content block stopAssistantMessage: Complete assembled messageResultMessage: Final resultUserMessage: User promptAssistantMessage: Partial responseToolUseBlock: Tool attemptToolResultBlock: Tool error (is_error=True)AssistantMessage: Error handling responseResultMessage: Error result (is_error=True)All message types are dataclasses with type hints, enabling:
For configuration options related to message handling, see Configuration and Options.
Install with Tessl CLI
npx tessl i tessl/pypi-claude-code-sdk