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 queries —
client.ask("prompt") - Streaming —
client.stream("prompt", on_event=callback)orclient.stream_iter("prompt") - Multi-turn conversations —
Conversationclass 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"requiresconfirm_unsafe=Truedangerously_skip_permissions=Truerequiresconfirm_unsafe=Trueallow_dangerously_skip_permissions=Truerequiresconfirm_unsafe=Truemax_turns > 20prints 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
Metadata
Release files for claude-subprocess-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claude_subprocess_sdk-0.1.0.tar.gz | 18.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claude_subprocess_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.8 kB
Release files / claude_subprocess_sdk-0.1.0.tar.gz
| Download URL | claude_subprocess_sdk-0.1.0.tar.gz |
|---|---|
| Size | 18.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eb264113b31cefefe2313d688df5ae145275dabd5b7b5070b3a42192e35e0f2a
|
|
BLAKE2b-256 checksum How to use checksums |
361bb5507cfbf19f87adc2753b5e1b92b6d1990e4e201cdc2d6117ec4fddf0f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / claude_subprocess_sdk-0.1.0-py3-none-any.whl
| Download URL | claude_subprocess_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd5ad383253a46fee839b0935f71bb3995a63513125a538cbfaabba2b0a1a0fb
|
|
BLAKE2b-256 checksum How to use checksums |
9ce4872bd3ce637b497a4a0f56c194fa768c9fcb8ef13ef41ce23f0031098cdd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|