t# Pydantic AI Claude Code
Use your local Claude Code CLI as a Pydantic AI model provider.
This package provides a Pydantic AI-compatible model implementation that wraps the local Claude CLI, enabling you to use Claude locally with all Pydantic AI features including structured responses, tool calling, streaming, and multi-turn conversations.
Features
- Full Pydantic AI Compatibility: Drop-in replacement for any Pydantic AI model
- Structured Responses: Get validated, typed responses using Pydantic models
- Custom Tool Calling: Use your own Python functions as tools
- True Streaming: Real-time response streaming via Claude CLI's stream-json mode
- Local Execution: All processing happens locally on your machine
- Session Persistence: Maintain conversation context across multiple requests
- Additional Files: Provide local files for Claude to read and analyze
- Automatic Response Saving: Raw prompts and responses saved for debugging
- Configurable: Fine-tune permissions, working directories, and tool access
Installation
# Using uv (recommended)
uv add pydantic-ai-claude-code
# Using pip
pip install pydantic-ai-claude-code
Prerequisites:
- Claude Code CLI must be installed and authenticated
- Sandbox Runtime (required):
npm install -g @anthropic-ai/sandbox-runtime
Sandbox Mode (Enabled by Default)
New in v0.8.0: All Claude Code execution now runs in a secure sandbox by default using Anthropic's sandbox-runtime.
What This Means
- OS-Level Isolation: Uses bubblewrap (Linux) or sandbox-exec (macOS) for kernel-level security
- Filesystem Access: Full read/write access to
/tmponly - everything else is read-only - Network Access: Restricted to
api.anthropic.comfor Claude API calls - Autonomous Execution:
IS_SANDBOX=1environment variable enables Claude to run without permission prompts - Zero Configuration: Works automatically after installing
sandbox-runtime
Installation
# Install sandbox-runtime globally via npm
npm install -g @anthropic-ai/sandbox-runtime
# Verify installation
srt --version
Disabling Sandbox (Not Recommended)
If you need to disable sandbox mode for debugging:
from pydantic_ai_claude_code import ClaudeCodeProvider
from pydantic_ai import Agent
provider = ClaudeCodeProvider({"use_sandbox_runtime": False})
agent = Agent('claude-code:sonnet')
⚠️ Warning: Disabling sandbox removes security isolation. Only do this in trusted environments.
Quick Start
Basic Usage (String Format - Simplest!)
import pydantic_ai_claude_code # Register the provider
from pydantic_ai import Agent
# Just use the string format - easiest way!
agent = Agent('claude-code:sonnet')
# Run a simple query
result = agent.run_sync("What is 2+2?")
print(result.output) # Output: 4
Structured Responses
import pydantic_ai_claude_code
from pydantic import BaseModel
from pydantic_ai import Agent
class Analysis(BaseModel):
complexity: int # 1-10
maintainability: str
suggestions: list[str]
# String format works with all Pydantic AI features
agent = Agent('claude-code:sonnet', output_type=Analysis)
result = agent.run_sync("Analyze this code: def foo(): pass")
print(f"Complexity: {result.output.complexity}")
Custom Tools
import pydantic_ai_claude_code
from pydantic_ai import Agent
def get_weather(city: str) -> str:
"""Get weather for a city."""
return f"Weather in {city}: Sunny, 22°C"
agent = Agent(
'claude-code:sonnet',
tools=[get_weather],
)
result = agent.run_sync("What's the weather in Paris?")
print(result.output)
Streaming
import pydantic_ai_claude_code
from pydantic_ai import Agent
agent = Agent('claude-code:sonnet')
async with agent.run_stream('Write a haiku about code') as result:
async for text in result.stream_text():
print(text, end='', flush=True)
Advanced Configuration
When you need fine-grained control, use the explicit model and provider:
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider
# Custom provider with specific settings
provider = ClaudeCodeProvider(
working_directory="/path/to/project",
allowed_tools=["Read", "Edit", "Grep"], # Restrict tool access
permission_mode="acceptEdits",
use_temp_workspace=False, # Use specific directory instead of /tmp
)
model = ClaudeCodeModel("sonnet", provider=provider)
agent = Agent(model)
result = agent.run_sync("Analyze the codebase structure")
Temporary Workspace
from pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider
# Create isolated workspace that auto-cleans
with ClaudeCodeProvider(use_temp_workspace=True) as provider:
model = ClaudeCodeModel("sonnet", provider=provider)
agent = Agent(model)
result = agent.run_sync("Create a test file")
# Workspace automatically cleaned up
Providing Additional Files
Provide local files for Claude to read and analyze:
from pathlib import Path
from pydantic_ai import Agent
agent = Agent('claude-code:sonnet')
result = agent.run_sync(
"Read utils.py and config.json. Summarize what they configure.",
model_settings={
"additional_files": {
"utils.py": Path("src/utils.py"), # Copy single file
"config.json": Path("config/prod.json"), # From different location
"docs/spec.md": Path("specs/feature.md"), # Into subdirectory
}
}
)
Files are copied into the working directory before execution, and Claude can reference them directly:
"Read utils.py""Read config.json""Read docs/spec.md"
Each execution gets its own numbered subdirectory with isolated file copies.
Binary Content (Standard Interface)
Use Pydantic AI's standard BinaryContent interface to attach images, PDFs, and other binary files. This works identically across all model providers:
from pathlib import Path
from pydantic_ai import Agent, BinaryContent
agent = Agent('claude-code:sonnet')
# Send an image from file
image_data = Path('photo.jpg').read_bytes()
result = agent.run_sync(
[
'What is in this image?',
BinaryContent(data=image_data, media_type='image/jpeg'),
]
)
# Send a PDF document
pdf_data = Path('document.pdf').read_bytes()
result = agent.run_sync(
[
'Summarize this document:',
BinaryContent(data=pdf_data, media_type='application/pdf'),
]
)
# Compare multiple images
result = agent.run_sync(
[
'Compare these images:',
BinaryContent(data=image1_data, media_type='image/png'),
'and',
BinaryContent(data=image2_data, media_type='image/jpeg'),
]
)
How it works:
- Binary content is automatically written to files in the working directory
- Files are referenced in the prompt (e.g.,
[Image: filename.png]) - Claude Code CLI can then read the files directly
- No Claude Code-specific code needed - this is standard Pydantic AI!
Supported formats:
- Images: PNG, JPEG, GIF, WebP, etc.
- Documents: PDF, TXT, JSON, etc.
- Audio: MP3, WAV, etc.
- Video: MP4, WebM, etc.
Benefits:
- Portable code - works with OpenAI, Anthropic, Google, and Claude Code
- Standard interface - same
BinaryContentclass for all providers - No provider lock-in - switch between cloud and local with one line change
Error Handling
OAuth Token Expiration
For long-running processes (>7 hours), OAuth tokens may expire. Handle gracefully with ClaudeOAuthError:
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeOAuthError
agent = Agent('claude-code:sonnet')
try:
result = agent.run_sync("Process large dataset")
except ClaudeOAuthError as e:
print(f"Authentication expired: {e}")
print(f"Please run: {e.reauth_instruction}") # "Please run /login"
# Prompt user to re-authenticate, then retry
except RuntimeError as e:
# Handle other CLI errors
print(f"CLI error: {e}")
For batch processing with automatic retry:
from pydantic_ai_claude_code import ClaudeOAuthError
import time
def process_batch_with_retry(items, max_auth_retries=3):
"""Process items with OAuth re-authentication support."""
results = []
for item in items:
auth_retries = 0
while auth_retries < max_auth_retries:
try:
result = agent.run_sync(f"Process: {item}")
results.append(result.output)
break # Success
except ClaudeOAuthError as e:
auth_retries += 1
print(f"\n{'='*60}")
print(f"OAuth token expired: {e.reauth_instruction}")
print(f"{'='*60}\n")
if auth_retries >= max_auth_retries:
raise # Give up after max retries
input("Press Enter after running /login to continue...")
time.sleep(2) # Brief pause before retry
return results
Logging
The package uses Python's standard logging module. To enable debug logging in your application:
import logging
# Enable debug logging for pydantic-ai-claude-code
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('pydantic_ai_claude_code').setLevel(logging.DEBUG)
# Or just for specific components
logging.getLogger('pydantic_ai_claude_code.model').setLevel(logging.DEBUG)
logging.getLogger('pydantic_ai_claude_code.utils').setLevel(logging.INFO)
This will log:
- Model initialization and configuration
- CLI command execution and responses
- Message formatting and conversion
- Tool call parsing and execution
- Structured output handling
- Streaming events and completion
Available Models
claude-code:sonnet- Claude 3.5 Sonnet (default, recommended)claude-code:opus- Claude 3 Opus (most capable)claude-code:haiku- Claude 3.5 Haiku (fastest)
Or use full model names like claude-code:claude-sonnet-4-5-20250929
Integration with Existing Projects
Replace your current LLM calls with Claude Code:
Before:
agent = Agent('openai:gpt-4o')
# or
agent = Agent('anthropic:claude-3-5-sonnet-latest')
After:
import pydantic_ai_claude_code # Add this import
agent = Agent('claude-code:sonnet') # Change this line
Everything else stays the same! All your tools, structured outputs, dependencies, and streaming code works identically.
Key Differences from Cloud Providers
| Aspect | Cloud Providers | Claude Code Local |
|---|---|---|
| Execution | Remote API calls | Local on your machine |
| Cost | Per-token pricing | Uses Claude desktop subscription |
| Data Privacy | Data sent to cloud | Data stays local |
| Speed | Network latency | Local execution |
| API Key | Required | Not needed (uses local auth) |
Examples
See the examples/ directory for more demonstrations:
basic_example.py- Simple queries and usage trackingstructured_example.py- Structured output with Pydantic modelsasync_example.py- Async/await usage patternsadvanced_example.py- Custom provider configurationstools_and_streaming.py- Custom tools and streaming responsessandbox_example.py- Sandbox-runtime integration for production securitybinary_content_example.py- Standard interface for images, PDFs, and binary filesadditional_files_example.py- Providing local files for analysis (Claude Code-specific)
License
MIT License
Metadata
Release files for pydantic-ai-claude-code 0.8.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pydantic_ai_claude_code-0.8.1.tar.gz | 47.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pydantic_ai_claude_code-0.8.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 102.2 kB
Release files / pydantic_ai_claude_code-0.8.1.tar.gz
| Download URL | pydantic_ai_claude_code-0.8.1.tar.gz |
|---|---|
| Size | 47.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1633acea1301c02c7292ad84067aea4813fb6d311c5249279f576d2dfdd38d0b
|
|
BLAKE2b-256 checksum How to use checksums |
c2cca0b89a28aa6718ca3bd6e9f6330af4dc7193f9273d221d553cd94210993b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.14
|
Release files / pydantic_ai_claude_code-0.8.1-py3-none-any.whl
| Download URL | pydantic_ai_claude_code-0.8.1-py3-none-any.whl |
|---|---|
| Size | 54.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1882eb8b08d1192b98e1f09e41aa7449fbc16298ee86541aba618a8ea8bd70c2
|
|
BLAKE2b-256 checksum How to use checksums |
adde1541b052531fbc89b728c45364f33a53f8cdde18faebe4b7b1d3b0fb4b3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.14
|
Security for pydantic-ai-claude-code 0.8.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.