Pydantic AI model adapter for Claude Code CLI
Project description
claude-code-model
Pydantic AI model adapter for Claude Code CLI — type-safe agents on your Max subscription.
Use Pydantic AI's powerful agent framework with Claude Code CLI instead of API calls. Get structured outputs, tool calling, and full type safety without per-token costs.
Why Use This?
| Approach | Cost | Type Safety | Structured Output | Tool Calling |
|---|---|---|---|---|
Raw claude -p |
$0 | ❌ | ❌ | ❌ |
| Pydantic AI + API | $$$ per token | ✅ | ✅ | ✅ |
| claude-code-model | $0 | ✅ | ✅ | ✅ |
If you have a Claude Max subscription ($100-200/month), you get unlimited Claude access via CLI. This adapter lets you use that access with Pydantic AI's excellent developer experience.
Installation
# 1. Install Claude Code CLI (if not already installed)
npm install -g @anthropic-ai/claude-code
# 2. Authenticate
claude auth
# 3. Install this package
pip install claude-code-model
Quick Start
Structured Output
Get type-safe responses with automatic validation:
from pydantic import BaseModel
from pydantic_ai import Agent
from claude_code_model import ClaudeCodeModel
class ReviewResult(BaseModel):
verdict: str # APPROVE, REQUEST_CHANGES, COMMENT
issues: list[str]
suggestions: list[str]
agent = Agent(
ClaudeCodeModel(),
result_type=ReviewResult,
system_prompt="You are a code reviewer. Return structured JSON."
)
result = agent.run_sync("Review this code: def add(a,b): return a+b")
# Result is fully typed!
print(result.data.verdict) # IDE autocomplete works
print(result.data.issues) # Type checking works
Tool Calling
Give your agent functions it can call:
from pydantic_ai import Agent
from claude_code_model import ClaudeCodeModel
agent = Agent(ClaudeCodeModel())
@agent.tool_plain
def read_file(path: str) -> str:
"""Read a file from disk."""
return Path(path).read_text()
@agent.tool_plain
def list_files(directory: str = ".") -> list[str]:
"""List files in a directory."""
return [f.name for f in Path(directory).iterdir()]
result = agent.run_sync("What Python files are in the current directory?")
# Agent automatically calls list_files() and uses the results
Multi-Agent Systems
Different models for different tasks:
from claude_code_model import ClaudeCodeModel
# Fast agent for quick tasks
researcher = Agent(
ClaudeCodeModel(model="haiku"),
system_prompt="Quick research"
)
# Powerful agent for analysis
analyst = Agent(
ClaudeCodeModel(model="sonnet"),
system_prompt="Deep analysis"
)
# Analyst can delegate to researcher
@analyst.tool_plain
async def research(topic: str) -> str:
result = await researcher.run(f"Research {topic}")
return result.data
Features
- Zero API Costs: Uses your Claude Max subscription
- Full Pydantic AI Compatibility: Structured outputs, tools, deps, async
- Type Safety: Full IDE autocomplete and type checking
- Multiple Models: sonnet (default), opus, haiku
- Tool Calling: Give agents functions to call
- Async Support: Full async/await support
- Simple: <500 lines of code, easy to audit
Configuration
from claude_code_model import ClaudeCodeModel
from pathlib import Path
model = ClaudeCodeModel(
model="sonnet", # "sonnet" | "opus" | "haiku"
timeout=30, # seconds per CLI request (default: 30)
cwd=Path("/path"), # working directory for CLI
verbose=False, # enable debug logging
)
Debug Mode
Enable verbose logging to see exactly what's happening:
model = ClaudeCodeModel(verbose=True)
This shows:
- Request/response timing (e.g.,
CLAUDE CLI RESPONSE (took 3.7s)) - Full prompts being sent
- Tool calls detected
- JSON extraction status
- Request count and total time
Examples
The examples/ directory contains working examples:
simple.py: Structured output for code reviewwith_tools.py: File system tools with interactive chatmulti_agent.py: Multi-agent delegation pattern
Run them:
uv run python examples/simple.py
uv run python examples/with_tools.py
How It Works
┌─────────────────────────────────────────┐
│ Your Code │
│ agent = Agent(ClaudeCodeModel()) │
│ result = agent.run_sync("prompt") │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Pydantic AI │
│ - Manages conversation state │
│ - Handles tool calls and retries │
│ - Validates output against schema │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ claude-code-model │
│ - Converts messages → prompt string │
│ - Adds JSON schema instructions │
│ - Calls CLI wrapper │
│ - Parses response → ModelResponse │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Claude Code CLI │
│ $ claude -p "prompt" --model sonnet │
│ > {"verdict": "APPROVE", "issues": []} │
└─────────────────────────────────────────┘
The adapter translates between Pydantic AI's message format and Claude CLI's prompt-response model. It handles:
- System prompts, user messages, tool calls, tool results
- JSON extraction from various response formats
- Tool call detection and parsing
- Structured output validation
API Reference
ClaudeCodeModel
Main model class implementing Pydantic AI's Model interface.
@dataclass
class ClaudeCodeModel(Model):
model: Literal["sonnet", "opus", "haiku"] = "sonnet"
timeout: int = 30 # seconds per request
cwd: Path | None = None
verbose: bool = False # enable debug logging
Properties (read-only, for monitoring):
request_count: int- Number of CLI requests madetotal_time: float- Total seconds spent in CLI requests
Methods:
reset_stats()- Reset request_count and total_time to zero
Exceptions
from claude_code_model import (
ClaudeCodeError, # Base exception
ClaudeCodeNotFoundError, # CLI not installed
ClaudeCodeTimeoutError, # Command timed out
ClaudeCodeExecutionError, # Non-zero exit code
)
Limitations
- No streaming: CLI doesn't support streaming well
- No token counting: CLI doesn't report usage (returns 0)
- No concurrent calls: Run one request at a time
- Text-only: No image inputs (CLI limitation)
- Rate limits: Subject to Claude Max rate limits
Known Behaviors
Tool Calling is Prompt-Based
Unlike the API, Claude CLI doesn't have native tool calling. This adapter simulates tools by:
- Including tool definitions in the prompt with format instructions
- Asking Claude to output
TOOL_CALL: name({"arg": "value"}) - Parsing the response for this pattern
This works well but Claude occasionally:
- Ignores tools and answers directly
- Outputs the tool call but continues talking
- Returns placeholder JSON instead of real content
Mitigation: Use retries=5 on your Agent to handle occasional misbehavior:
agent = Agent(
ClaudeCodeModel(),
output_type=MyResult,
retries=5, # Allow retries for output validation
)
Response Timing
Each CLI invocation takes 2-10 seconds depending on:
- Model (haiku is fastest, opus slowest)
- Prompt length
- Claude's current load
Multi-agent workflows with tool calls require multiple CLI invocations, so expect 15-30+ seconds for complex tasks.
IDE Compatibility
The adapter works in both terminal and IDE environments. It uses stdin=DEVNULL to prevent hangs when running without a TTY (common in IDEs like PyCharm).
Development
# Setup
git clone https://github.com/yourusername/claude-code-model.git
cd claude-code-model
uv sync
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=claude_code_model
# Type check
uv run mypy src/
# Lint
uv run ruff check src/ tests/
uv run ruff format src/ tests/
# Run example
uv run python examples/simple.py
Requirements
- Python 3.11+
- Claude Code CLI installed and authenticated
- Claude Max subscription (for unlimited CLI access)
- pydantic-ai >= 0.1.0
- pydantic >= 2.0.0
Contributing
Contributions welcome! Please:
- Keep changes focused and small (<200 lines)
- Add tests for new functionality
- Ensure
pytest,mypy, andruffall pass - Update README if adding features
See CLAUDE.md for development guide.
License
MIT License - see LICENSE file for details.
Credits
Built with:
- Pydantic AI - The agent framework
- Claude Code - Anthropic's CLI tool
- Pydantic - Data validation
Disclaimer
This is an unofficial adapter, not affiliated with Anthropic. Use in accordance with Claude's Terms of Service. You are responsible for ensuring your usage complies with Anthropic's policies.
Support
- Issues: GitHub Issues
- Dev Guide: CLAUDE.md for contributors
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_code_model-0.3.0.tar.gz.
File metadata
- Download URL: claude_code_model-0.3.0.tar.gz
- Upload date:
- Size: 222.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82aa1657242f618c881edd413233af3e95cc16884c48e3765fc12a99c9af3f26
|
|
| MD5 |
0aed25827c9001774c9ffc3e75e7d479
|
|
| BLAKE2b-256 |
7fd5c7d25c3ddac9798ecfd2b0db5a907cf358fbb4fb3d88735cb2870c225f5a
|
Provenance
The following attestation bundles were made for claude_code_model-0.3.0.tar.gz:
Publisher:
publish.yml on aleksandr-bogdanov/claude-code-model
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_code_model-0.3.0.tar.gz -
Subject digest:
82aa1657242f618c881edd413233af3e95cc16884c48e3765fc12a99c9af3f26 - Sigstore transparency entry: 867505862
- Sigstore integration time:
-
Permalink:
aleksandr-bogdanov/claude-code-model@a967df0769af8dafc2d260ab94a548bf4c364b87 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/aleksandr-bogdanov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a967df0769af8dafc2d260ab94a548bf4c364b87 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file claude_code_model-0.3.0-py3-none-any.whl.
File metadata
- Download URL: claude_code_model-0.3.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3efb10645f68a5b855234e4b70eb294f6a7099dae3d944b0b5d6c6381b09c6e8
|
|
| MD5 |
f5f3ae95f4c94f1a11ee5db1ff02ea52
|
|
| BLAKE2b-256 |
46644e20d0fc52148118cc505b6862cee9523ef022e99aca21158f2267b4898a
|
Provenance
The following attestation bundles were made for claude_code_model-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on aleksandr-bogdanov/claude-code-model
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_code_model-0.3.0-py3-none-any.whl -
Subject digest:
3efb10645f68a5b855234e4b70eb294f6a7099dae3d944b0b5d6c6381b09c6e8 - Sigstore transparency entry: 867505869
- Sigstore integration time:
-
Permalink:
aleksandr-bogdanov/claude-code-model@a967df0769af8dafc2d260ab94a548bf4c364b87 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/aleksandr-bogdanov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a967df0769af8dafc2d260ab94a548bf4c364b87 -
Trigger Event:
workflow_dispatch
-
Statement type: