Skip to main content

A lightweight Python SDK for Claude Code CLI using subprocess

Project description

Claude SDK Lite

A lightweight Python SDK for Claude Code CLI using subprocess.

๐Ÿ“‘ Table of Contents

๐Ÿš€ Features

  • ๐Ÿชถ Lightweight - Only depends on Pydantic, uses your installed claudecode CLI
  • โœ… Type-safe - Full Pydantic model validation
  • ๐Ÿ”ง Complete Coverage - Supports all Claude Code CLI parameters
  • ๐Ÿ“ Easy to Use - Simple, sync and async API compatible with official claude-agent-sdk
  • ๐Ÿ”„ Message Types - Full support for AssistantMessage, TextBlock, and more
  • ๐ŸŽฏ Event-Driven - Real-time message handling via MessageEventListener callbacks
  • ๐Ÿ”„ Session-Based - Multi-turn conversations with context retention

๐Ÿ“ฆ Installation

Prerequisites

Install Claude Code CLI:

npm install -g @anthropic-ai/claude-code

Install the SDK

pip install claude-sdk-lite

๐Ÿ”ง Comparison with Official SDK

Feature claude-sdk-lite claude-agent-sdk
Package Size ~50KB ~100MB+
Dependencies Pydantic only anyio, anthropic, mcp, ...
CLI User installed Bundled
API Sync + Async Async only
Message Types Full support Full support
Use Case Projects with pre-installed CLI Standalone deployment

๐Ÿ’ก When to Use claude-sdk-lite vs Official SDK

Choose claude-sdk-lite if you:

  • โœ… Need sync API support - Use in synchronous contexts without asyncio overhead (official SDK is async-only)
  • โœ… Already have Claude Code CLI installed - Want to avoid downloading bundled CLI (~100MB)
  • โœ… Need multi-turn conversations - Session-based client with event-driven message handling
  • โœ… Care about package size and dependencies - Projects with strict dependency requirements
  • โœ… Don't need custom MCP servers - No need for in-process tools
  • โœ… Don't need hooks system - No need to intercept tool calls
  • โœ… Lightweight deployment - CI/CD, containerized environments

Typical use cases:

# Simple code generation
for message in query(prompt="Write a Python function to parse JSON"):
    print(message)

# Batch processing
for prompt in prompts:
    response = query_text(prompt=prompt)
    process(response)

# Event-driven multi-turn conversations
from claude_sdk_lite import ClaudeClient, DefaultMessageHandler

handler = DefaultMessageHandler()
with ClaudeClient(message_handler=handler) as client:
    client.send_request("First question")
    handler.wait_for_completion()

    client.send_request("Follow-up question")  # Same session
    handler.wait_for_completion()

# Scripts and automation
result = query_text(prompt="Analyze this code", options=ClaudeOptions(model="haiku"))

Choose official claude-agent-sdk if you:

  • ๐Ÿ”ง Need custom MCP servers - Create in-process tools with direct app state access
  • ๐Ÿ”ง Need hooks system - Intercept and modify tool calls, implement permission controls
  • ๐Ÿ”ง Need advanced tool features - In-process tools with direct app state, tool callbacks
  • ๐Ÿ”ง Deploy without pre-installed CLI - Need to distribute standalone application
  • ๐Ÿ”ง Need comprehensive error handling - Built-in retry, connection management, flow control
  • ๐Ÿ”ง Need tool permission callbacks - Dynamic permission decisions
  • ๐Ÿ”ง Need plugin system - Extend SDK functionality

Typical use cases:

# Custom MCP tools (official SDK only)
@tool("greet", "Greet a user", {"name": str})
async def greet(args):
    return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

server = create_sdk_mcp_server("my-tools", tools=[greet])

# Hooks system (official SDK only)
async def check_bash_command(input_data, tool_use_id, context):
    if "dangerous" in input_data["tool_input"].get("command", ""):
        return {"permissionDecision": "deny"}
    return {}

options = ClaudeAgentOptions(
    hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[check_bash_command])]}
)

# Advanced tool callbacks (official SDK only)
async with ClaudeSDKClient(options=options) as client:
    await client.query("First question")
    async for msg in client.receive_response():
        print(msg)
    await client.query("Follow-up question")  # Continue same session

Feature Comparison Table

Feature claude-sdk-lite claude-agent-sdk
Basic query โœ… โœ…
Sync API โœ… โŒ
Async API โœ… โœ…
Event-driven messages โœ… โœ…
Session-based conversation โœ… โœ…
Custom MCP servers โŒ โœ…
Hooks system โŒ โœ…
Tool permission callbacks โŒ โœ…
Bundled CLI โŒ โœ…
Package size ~50KB ~100MB+
Dependencies 1 (Pydantic) 3+

๐ŸŽฏ Quick Start

Basic Usage

from claude_sdk_lite import query, AssistantMessage, TextBlock

for message in query(prompt="What is the capital of France?"):
    if isinstance(message, AssistantMessage):
        for block in message.content:
            if isinstance(block, TextBlock):
                print(block.text)

Async Usage

import asyncio
from claude_sdk_lite import async_query, AssistantMessage, TextBlock

async def main():
    async for message in async_query(prompt="What is the capital of France?"):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

asyncio.run(main())

Event-Driven Multi-Turn Conversations

The session-based client with event-driven message handling enables real-time message processing and multi-turn conversations:

from claude_sdk_lite import ClaudeClient, DefaultMessageHandler, ClaudeOptions

# Create handler for message callbacks
handler = DefaultMessageHandler()

# Use context manager for automatic cleanup
with ClaudeClient(
    message_handler=handler,
    options=ClaudeOptions(model="sonnet")
) as client:
    # Send first request
    client.send_request("What is the capital of France?")
    handler.wait_for_completion(timeout=30.0)

    # Access all messages from the conversation
    for message in handler.get_messages():
        print(message)

    # Send follow-up in the same session
    client.send_request("What about Germany?")
    handler.wait_for_completion(timeout=30.0)

    # Context is automatically maintained across queries

Custom Message Handlers

Create custom handlers by implementing MessageEventListener:

from claude_sdk_lite import (
    ClaudeClient,
    MessageEventListener,
    AssistantMessage,
    TextBlock,
    ThinkingBlock,
)

class ChatHandler(MessageEventListener):
    def __init__(self):
        self.response_text = []

    def on_query_start(self, prompt: str):
        print(f"\n๐Ÿค” Query: {prompt}")

    def on_message(self, message):
        # Process messages in real-time
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text, end="", flush=True)
                    self.response_text.append(block.text)
                elif isinstance(block, ThinkingBlock):
                    print("\n๐Ÿคฏ Thinking...", end="", flush=True)

    def on_query_complete(self, messages):
        print("\nโœ… Query complete!")

handler = ChatHandler()

with ClaudeClient(message_handler=handler) as client:
    client.send_request("Explain recursion in simple terms")
    handler.wait_for_completion()

Async Event-Driven Conversations

import asyncio
from claude_sdk_lite import (
    AsyncClaudeClient,
    AsyncMessageEventListener,
    AsyncDefaultMessageHandler,
)

async def chat_example():
    # Use default async handler
    handler = AsyncDefaultMessageHandler()

    async with AsyncClaudeClient(message_handler=handler) as client:
        await client.send_request("First question")
        await handler.wait_for_completion(timeout=30.0)

        # Continue conversation
        await client.send_request("Follow-up question")
        await handler.wait_for_completion(timeout=30.0)

        messages = await handler.get_messages()
        print(f"Received {len(messages)} messages")

asyncio.run(chat_example())

Simplified Text Response

from claude_sdk_lite import query_text

response = query_text(prompt="What is 2 + 2?")
print(response)  # "2 + 2 equals 4."

With Options

from claude_sdk_lite import query, ClaudeOptions

options = ClaudeOptions(
    model="haiku",
    system_prompt="You are a helpful math tutor",
    max_turns=1,
)

for message in query(
    prompt="Explain calculus in simple terms",
    options=options
):
    print(message)

## ๐Ÿ”„ Migration from Official SDK

```python
# Official SDK (async only)
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(model="sonnet")
    async for message in query(prompt="Hello", options=options):
        print(message)

asyncio.run(main())

# claude-sdk-lite - use async_query for async code
import asyncio
from claude_sdk_lite import async_query, ClaudeOptions

async def main():
    options = ClaudeOptions(model="sonnet")
    async for message in async_query(prompt="Hello", options=options):
        print(message)

asyncio.run(main())

# OR use sync API (claude-sdk-lite exclusive!)
from claude_sdk_lite import query, ClaudeOptions

options = ClaudeOptions(model="sonnet")
for message in query(prompt="Hello", options=options):
    print(message)

Key difference: Official SDK only supports async API, while claude-sdk-lite supports both sync and async APIs.

๐Ÿ“– API Documentation

Session-Based Client API

ClaudeClient(message_handler, options=None)

Synchronous client for multi-turn conversations with event-driven message handling.

Parameters:

  • message_handler (MessageEventListener): Required handler for message callbacks
  • options (ClaudeOptions | None): Optional configuration

Methods:

  • connect() - Start the persistent subprocess and listener
  • disconnect() - Stop the subprocess and cleanup
  • send_request(prompt) - Send a query to Claude
  • interrupt() - Interrupt the current query

Properties:

  • is_connected - Check if client is connected
  • message_handler - Get the message handler (read-only)
  • session_id - The session identifier
  • stderr_output - Get captured stderr output

Example:

from claude_sdk_lite import ClaudeClient, DefaultMessageHandler

handler = DefaultMessageHandler()
with ClaudeClient(message_handler=handler) as client:
    client.send_request("Hello!")
    handler.wait_for_completion(timeout=30.0)

    # Continue conversation
    client.send_request("Tell me more")
    handler.wait_for_completion(timeout=30.0)

AsyncClaudeClient(message_handler, options=None)

Async version of ClaudeClient for async/await patterns.

Example:

import asyncio
from claude_sdk_lite import AsyncClaudeClient, AsyncDefaultMessageHandler

async def main():
    handler = AsyncDefaultMessageHandler()
    async with AsyncClaudeClient(message_handler=handler) as client:
        await client.send_request("Hello!")
        await handler.wait_for_completion(timeout=30.0)

asyncio.run(main())

Message Event Listeners

MessageEventListener

Base class for handling message events in real-time.

Callback Methods:

  • on_message(message) - Called when any message is received
  • on_query_start(prompt) - Called when a query starts
  • on_query_complete(messages) - Called when a query completes
  • on_stream_start() - Called when streaming starts
  • on_stream_end() - Called when streaming ends
  • on_error(error) - Called when an error occurs

Example:

from claude_sdk_lite import MessageEventListener

class MyHandler(MessageEventListener):
    def on_message(self, message):
        print(f"Received: {type(message).__name__}")

    def on_query_complete(self, messages):
        print(f"Complete! Got {len(messages)} messages")

AsyncMessageEventListener

Async version of MessageEventListener with async callback methods.

DefaultMessageHandler

Default implementation that buffers messages and provides synchronization helpers.

Methods:

  • get_messages() - Get all buffered messages for current query
  • wait_for_completion(timeout=60.0) - Wait for query to complete
  • is_complete() - Check if current query is complete

AsyncDefaultMessageHandler

Async version of DefaultMessageHandler with async methods.

Simple Query API

query(prompt, options=None)

Query Claude Code (sync version), returning a generator of messages.

Parameters:

  • prompt (str): The prompt to send to Claude
  • options (ClaudeOptions | None): Optional configuration

Yields:

  • Message: Messages from the conversation (AssistantMessage, SystemMessage, ResultMessage)

Example:

for message in query(prompt="Hello"):
    print(message)

async_query(prompt, options=None)

Query Claude Code (async version), returning an async iterator of messages.

Parameters:

  • prompt (str): The prompt to send to Claude
  • options (ClaudeOptions | None): Optional configuration

Yields:

  • Message: Messages from the conversation (AssistantMessage, SystemMessage, ResultMessage)

Example:

async for message in async_query(prompt="Hello"):
    print(message)

query_text(prompt, options=None) -> str

Convenience function that returns only the text response.

Parameters:

  • prompt (str): The prompt to send to Claude
  • options (ClaudeOptions | None): Optional configuration

Returns:

  • str: The concatenated text response

ClaudeOptions

Configuration options class using Pydantic for validation.

Core Options

ClaudeOptions(
    model="sonnet",  # Model: sonnet, opus, haiku
    agent="custom-agent",  # Agent to use
)

System Prompt

ClaudeOptions(
    system_prompt="You are a helpful assistant",
    append_system_prompt="Always be concise",
)

Tools

ClaudeOptions(
    allowed_tools=["Bash(git:*)", "Read", "Edit"],
    disallowed_tools=["WebFetch"],
    tools=["Bash", "Read", "Write"],
)

Session Management

ClaudeOptions(
    continue_conversation=True,  # Continue recent conversation
    resume="session-id",  # Resume specific session
    session_id="uuid",  # Use specific session ID
)

Permission Mode

ClaudeOptions(
    permission_mode="acceptEdits",  # Auto-accept file edits
    # Other options: default, plan, bypassPermissions, delegate, dontAsk
)

Budget Limits

ClaudeOptions(
    max_budget_usd=0.50,  # Maximum spend in USD
    max_turns=10,  # Maximum conversation turns
)

Custom Agents

ClaudeOptions(
    agents={
        "reviewer": {
            "description": "Code reviewer",
            "prompt": "You are an expert code reviewer",
            "model": "sonnet"
        }
    },
    agent="reviewer"
)

MCP Servers

ClaudeOptions(
    mcp_config={
        "mcpServers": {
            "filesystem": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"]
            }
        }
    }
)

๐Ÿ“ Message Types

AssistantMessage

Claude's response message with content blocks.

class AssistantMessage(BaseModel):
    content: list[ContentBlock]  # List of content blocks
    model: str  # Model used
    parent_tool_use_id: str | None
    error: str | None

TextBlock

Text content block.

class TextBlock(BaseModel):
    text: str
    type: str = "text"

ToolUseBlock

Tool usage block.

class ToolUseBlock(BaseModel):
    id: str
    name: str  # Tool name
    input: dict[str, Any]  # Tool input
    type: str = "tool_use"

ResultMessage

Result message with cost and usage information.

class ResultMessage(BaseModel):
    subtype: str
    duration_ms: int
    is_error: bool
    num_turns: int
    session_id: str
    total_cost_usd: float | None
    usage: dict[str, Any] | None
    result: str | None

๐Ÿ”— Examples

Check out the examples directory for complete working examples:

Run examples:

# Sync chat
python examples/simple_chat.py

# Async chat
python examples/simple_async_chat.py

# With debug mode
CLAUDE_SDK_DEBUG=true python examples/simple_chat.py

๐Ÿ“„ Release Notes

[0.2.0] - 2025-02-10

๐ŸŽ‰ Major Update: Event-Driven Architecture & Multi-Turn Conversations

New Features

  • Session-Based Clients - Maintain conversation context across multiple queries

    • ClaudeClient - Synchronous client with persistent subprocess
    • AsyncClaudeClient - Async version for async/await patterns
    • Automatic session management and context retention
  • Event-Driven Message Handling - Real-time message processing via callbacks

    • MessageEventListener - Base class for custom message handlers
    • AsyncMessageEventListener - Async version with async callbacks
    • DefaultMessageHandler - Built-in handler with message buffering
    • AsyncDefaultMessageHandler - Async default handler

Key Benefits

  • โœ… Multi-turn conversations - Send multiple queries in the same session
  • โœ… Real-time streaming - Process messages as they arrive via callbacks
  • โœ… Flexible handlers - Create custom handlers for your use case
  • โœ… Thread-safe - Safe for concurrent operations
  • โœ… Better control - Interrupt queries, track completion, access buffered messages

See examples/ for complete working examples.

[0.1.0] - Initial Release

Basic query functions with full Claude Code CLI parameter support.

๐Ÿ“„ License

MIT License - see LICENSE file for details.

๐Ÿ™ Acknowledgments

This SDK is inspired by Anthropic's official claude-agent-sdk and provides a lightweight alternative with compatible API.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_sdk_lite-0.2.0.tar.gz (69.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_sdk_lite-0.2.0-py3-none-any.whl (40.2 kB view details)

Uploaded Python 3

File details

Details for the file claude_sdk_lite-0.2.0.tar.gz.

File metadata

  • Download URL: claude_sdk_lite-0.2.0.tar.gz
  • Upload date:
  • Size: 69.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for claude_sdk_lite-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3f950508bdff981fa9107cc6b38cdfbc954b0adaea0462816e6c1a421a496efc
MD5 7abf50645a31ed3e3c8e14a0304f18b1
BLAKE2b-256 05723f14451f35bb677164c82d710465d63f824990f277c91512e6e12f3287e5

See more details on using hashes here.

File details

Details for the file claude_sdk_lite-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_sdk_lite-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 578382780b069922ec9000e63518cf33ae82068a02615f4d400e967a0ac9510e
MD5 2e7dc486420a02fae5f632d86ec76259
BLAKE2b-256 2238cafb3c3ea5ab4ae0ee9ce316aeeac1a9a04b6cc8ba2300e90c15959c1db6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page