Skip to main content

Python wrapper around the Claude CLI — call Claude from code using your existing subscription, no API key needed.

Project description

Claude Subprocess SDK

A Python wrapper around the claude CLI that lets you call Claude from your code using your existing Claude subscription — no API key needed.

It works by spawning claude -p <prompt> --output-format json as a subprocess and parsing the response.

Installation

pip install claude-subprocess-sdk

Or install from GitHub:

pip install git+https://github.com/rmnjaat/claude_sdk.git

Or install from source:

git clone https://github.com/rmnjaat/claude_sdk.git
cd claude_sdk
pip install .

For development (editable install):

pip install -e .

Prerequisite: The Claude CLI must be installed and authenticated.

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

Quick Start

from claude_sdk import Claude

# Safe by default — read-only, $1 budget cap
client = Claude()
response = client.ask("Explain Python decorators")
print(response.result)
print(response.cost_usd)

Features

  • One-shot queriesclient.ask("prompt")
  • Streamingclient.stream("prompt", on_event=callback) or client.stream_iter("prompt")
  • Multi-turn conversationsConversation class with automatic session tracking
  • Session management — resume, fork, name sessions, and resume from PRs
  • Structured output — pass a JSON schema, get parsed dicts back
  • Custom agents — define and use your own agent types
  • Safety presets"safe", "careful", "full" with sane defaults
  • Fine-grained tool control — allow/disallow specific tools or tool patterns
  • Directory sandboxing — restrict Claude's file access to specific directories
  • Budget protection — per-call spending caps and turn limits
  • No external dependencies — pure Python stdlib

Safety Presets

Preset Permission Mode Tools Max Turns Budget
safe (default) plan Read, Glob, Grep 1 $1
careful acceptEdits + Edit, Write 3 $5
full auto All (including Bash, Agent) 10 $20
# Read-only (default)
client = Claude()

# Can edit files but not run shell commands
client = Claude(safety="careful")

# Full access — use with caution
client = Claude(safety="full")

Any explicit parameter overrides the preset:

# Start from "careful" but also allow Bash for git commands only
client = Claude(safety="careful", allowed_tools=["Bash(git:*)"])

Usage Examples

Choose a model

client = Claude(model="sonnet")
# Also: "opus", "haiku", or a full model ID like "claude-sonnet-4-6"

One-shot query

response = client.ask("Explain Python decorators")
print(response.result)

Continue a conversation

response = client.ask("What is recursion?")
response = client.ask("Show me an example", continue_session=True)

Resume a specific session

response = client.ask("Where were we?", resume="5b81a84a-c6d3-...")

Fork a session (branch the conversation)

# Creates a NEW session that starts with full history of the original
response = client.ask("Try a different approach", resume="abc-123", fork=True)
# response.session_id is now a new ID; the original session is untouched

Named sessions

response = client.ask("Start my code review", session_name="code-review-pr-42")

Custom session ID

import uuid
my_id = str(uuid.uuid4())
response = client.ask("Hello", session_id=my_id)

Resume from a PR

response = client.ask("What's the status?", from_pr="123")
response = client.ask("Review this", from_pr="https://github.com/org/repo/pull/123")

Multi-turn with Conversation helper

from claude_sdk import Claude, Conversation

client = Claude(model="sonnet")
conv = Conversation(client, system_prompt="You are a helpful tutor")

r1 = conv.say("What is recursion?")
r2 = conv.say("Show me a Python example")  # auto-continues
r3 = conv.say("Now explain the base case")  # still same session

print(conv.session_id)       # session UUID
print(conv.total_cost_usd)   # cumulative cost
print(conv.turn_count)       # 3
print(conv.history)          # list of (prompt, response) tuples

# Fork the conversation (try a different direction)
conv2 = conv.fork()
r4 = conv2.say("Try a tree analogy instead")
# conv2 has a new session; conv is untouched

Streaming (callback-based)

def on_event(event):
    if event.get("type") == "assistant":
        for block in event["message"]["content"]:
            if block["type"] == "text":
                print(block["text"], end="", flush=True)

response = client.stream("Write a poem about code", on_event=on_event)

Streaming (iterator-based)

for event in client.stream_iter("Write a poem about code"):
    if event.get("type") == "assistant":
        for block in event["message"]["content"]:
            if block["type"] == "text":
                print(block["text"], end="", flush=True)

Streaming with partial messages

response = client.stream(
    "Write a story",
    on_event=on_event,
    include_partial_messages=True  # get token-by-token chunks
)

Structured output

response = client.ask(
    "Extract name and age from: John is 30",
    json_schema={
        "type": "object",
        "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
        "required": ["name", "age"]
    }
)
print(response.parsed)  # {"name": "John", "age": 30}

Custom agents

client = Claude(
    safety="full",
    agents={
        "reviewer": {
            "description": "Reviews code for bugs and style",
            "prompt": "You are a senior code reviewer."
        },
        "security": {
            "description": "Security audit agent",
            "prompt": "You are a security expert. Find vulnerabilities."
        }
    }
)
response = client.ask("Check auth.py", agent="reviewer")

Tool Control

Fine-grained control over what Claude can do:

# Only allow read operations (default in "safe" preset)
client = Claude(tools=["Read", "Glob", "Grep"])

# Allow Bash but ONLY for git commands
client = Claude(allowed_tools=["Bash(git:*)"])

# Allow everything EXCEPT file deletion
client = Claude(disallowed_tools=["Bash(rm:*)", "Bash(rmdir:*)"])

# No tools at all (pure text Q&A, like a chatbot)
client = Claude(tools=[])

Directory Sandboxing

Restrict which directories Claude can access:

# Claude can only see/touch files in /tmp/sandbox
client = Claude(cwd="/tmp/sandbox")

# Claude can access /tmp/sandbox + an additional directory
client = Claude(cwd="/tmp/sandbox", add_dirs=["/data/inputs"])

Budget Protection

Prevent runaway costs:

client = Claude(
    max_budget_usd=5.0,  # hard spending cap per call
    max_turns=3,          # max agentic turns per call
)

Dangerous Mode

Certain configurations require explicit opt-in:

# This RAISES ClaudeSafetyError:
client = Claude(permission_mode="bypassPermissions")

# This works (explicit acknowledgement):
client = Claude(permission_mode="bypassPermissions", confirm_unsafe=True)

Rules enforced:

  • permission_mode="bypassPermissions" requires confirm_unsafe=True
  • dangerously_skip_permissions=True requires confirm_unsafe=True
  • allow_dangerously_skip_permissions=True requires confirm_unsafe=True
  • max_turns > 20 prints a warning about runaway cost

MCP & Plugins

# Use an MCP config file
client = Claude(mcp_config="/path/to/mcp-config.json")

# Multiple MCP configs
client = Claude(mcp_config=["/path/to/config1.json", "/path/to/config2.json"])

# Strict MCP (only use explicitly configured MCP servers)
client = Claude(mcp_config="/path/to/config.json", strict_mcp_config=True)

# Plugin directories
client = Claude(plugin_dirs=["/path/to/plugins"])

Advanced Options

client = Claude(
    system_prompt="You are ...",              # replace default system prompt
    append_system_prompt="Also do ...",       # add to default system prompt
    effort="high",                            # "low", "medium", "high", "max"
    fallback_model="haiku",                   # fallback model on overload
    no_session_persistence=True,              # ephemeral sessions (not saved to disk)
    bare=True,                                # minimal mode (no hooks, LSP, plugins)
    verbose=True,                             # verbose output
    debug=True,                               # debug logging
    debug_file="/tmp/claude-debug.log",       # write debug logs to file
    files=["file_abc:doc.txt"],               # file resources
    worktree=True,                            # create git worktree for isolation
    brief=True,                               # enable SendUserMessage tool
    settings="/path/to/settings.json",        # custom settings file
    setting_sources=["user", "project"],      # which setting sources to use
    betas=["beta-feature"],                   # beta feature flags
    disable_slash_commands=True,              # disable all skills/slash commands
    exclude_dynamic_prompt=True,              # exclude dynamic system prompt sections
)

Project Structure

claude_sdk/
├── pyproject.toml     # Package configuration (pip installable)
├── claude_sdk/
│   ├── __init__.py        # Public API exports
│   ├── client.py          # Core Claude class — builds CLI commands, runs subprocess, parses output
│   ├── models.py          # ClaudeResponse dataclass
│   ├── exceptions.py      # Custom exceptions (CLINotFound, CLIError, SafetyError, etc.)
│   ├── conversation.py    # Multi-turn Conversation helper with session tracking
│   └── streaming.py       # Streaming response handler (stream-json parsing + callbacks)

No external dependencies — pure Python stdlib.

Response Object

response = client.ask("Hello")

response.result              # The text response
response.is_error            # Whether the CLI reported an error
response.cost_usd            # Cost of the call
response.duration_ms         # Wall clock time
response.duration_api_ms     # API-only time
response.num_turns           # Conversation turns
response.session_id          # Session UUID for resuming later
response.stop_reason         # "end_turn", "max_turns", etc.
response.usage               # Raw token usage dict
response.model_usage         # Per-model breakdown (tokens, cost, context window)
response.permission_denials  # Tools blocked by permissions
response.parsed              # Structured output dict (if json_schema used)
response.raw                 # Full raw JSON dict from the CLI

Error Handling

from claude_sdk.exceptions import (
    ClaudeSDKError,         # Base exception for all SDK errors
    ClaudeCLINotFound,      # claude binary not in PATH
    ClaudeCLIError,         # CLI returned an error (auth issues, invalid flags, etc.)
    ClaudeSessionNotFound,  # Invalid session ID for resume/continue
    ClaudeSafetyError,      # Unsafe config without confirm_unsafe=True
)

try:
    response = client.ask("Hello")
except ClaudeCLINotFound:
    print("Install Claude CLI: npm install -g @anthropic-ai/claude-code")
except ClaudeSessionNotFound as e:
    print(f"Session {e.session_id} not found")
except ClaudeSafetyError as e:
    print(f"Unsafe setting: {e.unsafe_setting}")
except ClaudeCLIError as e:
    print(f"CLI error (exit {e.exit_code}): {e.message}")

CLI Flags Reference

Every claude CLI flag has a corresponding Python parameter:

Python Parameter CLI Flag Type
model --model str
system_prompt --system-prompt str
append_system_prompt --append-system-prompt str
effort --effort str
fallback_model --fallback-model str
allowed_tools --allowedTools list[str]
disallowed_tools --disallowedTools list[str]
tools --tools list[str]
permission_mode --permission-mode str
dangerously_skip_permissions --dangerously-skip-permissions bool
max_budget_usd --max-budget-usd float
json_schema --json-schema dict
mcp_config --mcp-config str or list[str]
cwd subprocess working directory str
add_dirs --add-dir list[str]
agent --agent str
agents --agents dict
verbose --verbose bool
debug --debug bool or str
bare --bare bool
files --file list[str]
worktree --worktree bool or str

See the full parameter list in claude_sdk/client.py.

License

MIT

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_subprocess_sdk-0.1.0.tar.gz (18.0 kB view details)

Uploaded Source

Built Distribution

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

claude_subprocess_sdk-0.1.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file claude_subprocess_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: claude_subprocess_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 18.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for claude_subprocess_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eb264113b31cefefe2313d688df5ae145275dabd5b7b5070b3a42192e35e0f2a
MD5 7b7df539cab657fdd6639e4404e35fc9
BLAKE2b-256 361bb5507cfbf19f87adc2753b5e1b92b6d1990e4e201cdc2d6117ec4fddf0f1

See more details on using hashes here.

File details

Details for the file claude_subprocess_sdk-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_subprocess_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd5ad383253a46fee839b0935f71bb3995a63513125a538cbfaabba2b0a1a0fb
MD5 f6d05dd15dcc82fbdf736c760e009344
BLAKE2b-256 9ce4872bd3ce637b497a4a0f56c194fa768c9fcb8ef13ef41ce23f0031098cdd

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