name: using-claude-cli-wrapper description: Integrates the claude-cli-wrapper Python package to invoke Claude Code CLI programmatically. Covers one-shot prompts via run(), multi-turn sessions via claude_session(), structured output with JSON schemas or Pydantic models, tool control, custom agents, and error handling. Use when the project needs to call Claude from Python code.
Using claude-cli-wrapper
Prerequisites
- Claude Code CLI installed (
npm install -g @anthropic-ai/claude-code) and authenticated (runclaudeonce interactively to log in) - Python 3.10+
Installation
uv add git+https://github.com/rpakishore/claude-cli-wrapper.git
For Pydantic structured output, add pydantic>=2.0 separately:
uv add git+https://github.com/rpakishore/claude-cli-wrapper.git pydantic
The package has zero runtime dependencies.
Quick Reference
One-shot prompt
from claude_cli_wrapper import run
response = run("Explain Python generators in 2 sentences")
print(response.text)
Multi-turn session
from claude_cli_wrapper import claude_session
with claude_session() as session:
r1 = session.run("What is the capital of France?")
r2 = session.run("What is its population?") # remembers context
saved_id = session.session_id # save for later
Structured output (Pydantic)
from pydantic import BaseModel
from claude_cli_wrapper import run
class Person(BaseModel):
name: str
age: int
response = run("Extract: 'Jane is 25 years old'", response_model=Person)
person = response.parsed # typed Person instance
run() Parameters
run(prompt, *, ...) is the main entry point. All keyword arguments are keyword-only.
Model
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
str | None |
None |
"sonnet", "opus", "haiku", or full model name |
fallback_model |
str | None |
None |
Fallback when primary model is overloaded |
System prompt
| Parameter | Type | Default | Description |
|---|---|---|---|
system_prompt |
str | None |
None |
Replace the default system prompt entirely |
append_system_prompt |
str | None |
None |
Append text to the default system prompt |
Execution
| Parameter | Type | Default | Description |
|---|---|---|---|
working_dir |
str | Path | None |
cwd |
Working directory for CLI execution |
timeout |
float | None |
None |
Timeout in seconds (None = no timeout) |
max_turns |
int | None |
None |
Maximum number of agentic turns |
Output
| Parameter | Type | Default | Description |
|---|---|---|---|
output_format |
str | None |
None |
"text", "json", or "stream-json" |
json_schema |
dict | None |
None |
JSON schema for structured output validation |
response_model |
type | None |
None |
Pydantic BaseModel subclass for response parsing |
Tools
| Parameter | Type | Default | Description |
|---|---|---|---|
allowed_tools |
list[str] | None |
None |
Whitelist of tools, e.g. ["Read", "Bash(git:*)"] |
disallowed_tools |
list[str] | None |
None |
Blacklist of tools to disallow |
tools |
list[str] | None |
None |
Restrict available tools to exactly this list |
Permissions
| Parameter | Type | Default | Description |
|---|---|---|---|
permission_mode |
str | None |
None |
"default", "plan", "acceptEdits", "bypassPermissions", "dontAsk", "delegate" |
dangerously_skip_permissions |
bool |
True |
Skip all permission checks (default True for non-interactive use) |
Session
| Parameter | Type | Default | Description |
|---|---|---|---|
session_id |
str | None |
None |
Use a specific session ID |
continue_session |
bool |
False |
Continue the most recent conversation |
resume |
str | None |
None |
Resume a specific session by ID |
fork_session |
bool |
False |
Fork instead of continuing when resuming |
no_session_persistence |
bool |
False |
Disable session persistence |
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
cli_path |
str | None |
None |
Path to Claude CLI (auto-discovered if None) |
add_dirs |
list[str | Path] | None |
None |
Additional directories to allow tool access |
mcp_config |
str | dict | None |
None |
Path to MCP config file or inline config dict |
agents |
list[Agent] | None |
None |
Custom Agent objects for subagents |
files |
dict[str, str] | None |
None |
Dict mapping file_id to relative path |
betas |
list[str] | None |
None |
Beta features to enable |
Debugging
| Parameter | Type | Default | Description |
|---|---|---|---|
verbose |
bool |
False |
Enable verbose CLI output |
ClaudeResponse
Returned by run() and Session.run(). Supports str(response) which returns response.text.
| Attribute | Type | Description |
|---|---|---|
text |
str |
The response text from Claude |
exit_code |
int |
CLI process exit code (0 = success) |
stderr |
str |
Any stderr output from the CLI |
command |
list[str] |
The full command that was executed |
working_dir |
str |
Working directory used for execution |
duration |
float |
Execution time in seconds |
metadata |
dict | None |
Property. CLI envelope metadata (session_id, total_cost_usd, usage, num_turns, duration_ms, etc.). Returns None unless output_format="json" was used. |
json |
dict | None |
Property. Parsed JSON; returns None unless json_schema or response_model was provided. Prefers structured_output from CLI envelope when available. Raises ValueError on invalid JSON. |
parsed |
Any | None |
Property. Validated Pydantic model instance; returns None unless response_model was provided. Raises ImportError if pydantic is not installed, ValueError on validation failure. |
Sessions
claude_session() is a context manager that yields a Session object.
claude_session(
*,
resume: str | None = None, # session ID to resume
fork: bool = False, # fork from resumed session
working_dir: str | Path | None = None,
cli_path: str | None = None,
) -> Generator[Session, None, None]
Session.run() accepts the same keyword arguments as the top-level run() except for session_id, continue_session, resume, fork_session, and no_session_persistence—session state is managed automatically.
Resume a session
with claude_session(resume="abc-123") as session:
r = session.run("Continue our conversation")
Fork a session
with claude_session(resume="abc-123", fork=True) as session:
r = session.run("Try a different approach")
# original session "abc-123" is unchanged
Structured Output
JSON schema (no extra dependencies)
response = run(
"Extract the person's name and age from: 'John is 30 years old'",
json_schema={
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"required": ["name", "age"],
},
)
data = response.json # {"name": "John", "age": 30}
Pydantic model (requires pydantic extra)
from pydantic import BaseModel
from claude_cli_wrapper import run
class Person(BaseModel):
name: str
age: int
response = run("Extract: 'Jane is 25'", response_model=Person)
person = response.parsed # Person(name='Jane', age=25)
print(person.name) # "Jane"
When response_model is provided, the wrapper automatically sets output_format="json" and generates the JSON schema from the model. You do not need to set json_schema separately.
JSON schema limitations
Schemas passed to json_schema (or generated from response_model) must follow Anthropic's structured output constraints.
Supported: basic types (object, array, string, integer, number, boolean, null), enum (primitives only), const, anyOf/allOf, $ref/$def/definitions (no external $ref), default, required, additionalProperties: false (required on all objects), string formats (date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid), minItems (0 or 1 only).
Not supported: recursive schemas, complex types in enums, external $ref, numerical constraints (minimum, maximum, multipleOf), string constraints (minLength, maxLength), array constraints beyond minItems 0/1, additionalProperties set to anything other than false.
Tool Control
# Whitelist specific tools
response = run("Analyze this codebase", allowed_tools=["Read", "Grep", "Glob"])
# Blacklist specific tools
response = run("Suggest improvements", disallowed_tools=["Edit", "Write", "Bash"])
# Granular Bash permissions
response = run("Check git status", allowed_tools=["Bash(git:*)", "Read"])
# Restrict to exactly these tools (nothing else available)
response = run("Read the README", tools=["Read"])
Custom Agents
from claude_cli_wrapper import run, Agent
reviewer = Agent(
name="code-reviewer",
description="Expert code reviewer for Python projects",
prompt="You are a senior Python developer. Focus on code quality and security.",
tools=["Read", "Grep", "Glob"], # None = inherit all tools
model="sonnet", # None = use default model
)
response = run("Review the authentication module", agents=[reviewer])
Agent is a dataclass with fields: name (str, required), description (str, required), prompt (str, required), tools (list[str] | None), model (str | None).
Error Handling
Exception hierarchy:
ClaudeError # base; has .message
├── CLINotFoundError # has .cli_path
├── AuthenticationError # (no extra attrs)
├── InvalidArgumentError # has .argument, .value (both optional)
├── TimeoutError # has .timeout, .command (optional)
└── ExecutionError # has .exit_code, .stderr, .command (all optional except exit_code)
Usage:
from claude_cli_wrapper import (
run, ClaudeError, CLINotFoundError, AuthenticationError,
InvalidArgumentError, TimeoutError, ExecutionError,
)
try:
response = run("Hello", timeout=30)
except CLINotFoundError as e:
print(f"CLI not found at: {e.cli_path}")
except AuthenticationError:
print("Run 'claude' interactively to log in")
except TimeoutError as e:
print(f"Timed out after {e.timeout}s")
except InvalidArgumentError as e:
print(f"Bad argument: {e.argument}={e.value}")
except ExecutionError as e:
print(f"Exit code {e.exit_code}: {e.stderr}")
except ClaudeError as e:
print(f"Unexpected: {e.message}")
Key Caveats
dangerously_skip_permissions=Trueby default — the wrapper is designed for non-interactive use. Set toFalseonly if you can handle interactive permission prompts.- Prompts are sent via stdin, not as CLI arguments. This avoids shell escaping issues and argument length limits.
--printflag is always added — the CLI runs in non-interactive print mode. Do not expect interactive behavior.- CLI path resolution order: explicit
cli_pathparameter, thenCLAUDE_CLI_PATHenvironment variable, then systemPATHlookup viashutil.which. - Cross-platform: on Windows the CLI is discovered as
claude.cmd; on Unix asclaude. .jsonreturnsNoneunlessjson_schemaorresponse_modelwas provided torun()..parsedreturnsNoneunlessresponse_modelwas provided. Requires thepydanticextra to be installed.response_modelmust be a PydanticBaseModelsubclass — passing a plain dataclass or dict raisesInvalidArgumentError.- All
run()keyword arguments are keyword-only — positional usage beyondpromptis not supported. Model,PermissionMode,OutputFormatenums arestrenums — you can pass either the enum member or its string value (e.g.,Model.OPUSor"opus").