pydantic-claude-code
Use your Claude Code subscription from a plain pydantic-ai Agent, with full
pydantic-ai tool support. No API key, no separate billing: if Claude Code works
from your terminal, this wheel works too.
The repo is mpfaffenberger/pydantic-ai-claude-code and the import is
pydantic_ai_claude_code; the PyPI project is pydantic-claude-code
(pip install pydantic-claude-code).
Why
pydantic-ai gained a Codex OAuth path where openai-codex:gpt-6-astra just
works against a ChatGPT subscription. This wheel brings the same experience to
Claude: authenticate once, then run pydantic-ai agents against your Claude
subscription, using claude-fable-5-1, claude-sonnet-5, claude-opus-5, or
whatever model you subscribe to.
Two deliberate design choices distinguish this from a fork of pydantic-ai:
- pydantic-ai owns the loop. We don't hand the whole agent loop to the
Claude Code CLI. pydantic-ai's own
Agentmachinery drives the conversation, executes your tools, and validates structured output. The wheel is a model- provider, not a second agent fighting for control.
- Object-only resolution. Instead of a
claude-code:model-name string (which would require patching pydantic-ai's internals), pass the model object you build from the provider.
Quick start
import asyncio
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeCodeProvider, login
async def main() -> None:
# One-time: opens your browser, mints tokens, stores them
# (only needs to run again when tokens are revoked).
await login()
provider = ClaudeCodeProvider() # loads the stored tokens
agent = Agent(provider.model('claude-fable-5-1'))
result = await agent.run('Say hi in three words.')
print(result.data)
asyncio.run(main())
With tools
from pydantic_ai import Agent
provider = ClaudeCodeProvider()
agent = Agent(provider.model('claude-fable-5-1'))
@agent.tool_plain
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
Tools defined on the agent are passed to the API as standard Anthropic tool
definitions, and structured output works the same way as with the built-in
anthropic provider. That's the whole point of the wheel.
Structured output
from pydantic import BaseModel
from pydantic_ai import Agent
class Weather(BaseModel):
city: str
temperature_c: float
agent = Agent(ClaudeCodeProvider().model('claude-fable-5-1'), output_type=Weather)
result = await agent.run('Weather in Paris right now?')
assert result.output.city == 'Paris'
Streaming
from pydantic_ai import Agent
agent = Agent(ClaudeCodeProvider().model('claude-fable-5-1'))
async with agent.run_stream('Count from 1 to 3.') as stream:
async for chunk in stream.stream_text():
print(chunk, end='', flush=True)
How auth works
The flow uses the same shared OAuth client the Claude Code CLI uses:
- Authorization URL:
https://claude.ai/oauth/authorize - Token URL:
https://platform.claude.com/v1/oauth/token - Scopes:
org:create_api_key user:profile user:inference
Tokens are saved to the OS keyring by default: the Keychain on macOS, Credential
Manager on Windows, and Secret Service on Linux. On machines without a keychain,
credentials fall back to a JSON file (overridable via CLAUDE_CODE_AUTH_FILE):
~/.local/share/pydantic-ai-claude-code/auth.json
The fallback file is written with 0644 permissions and only ever contains what
the issuer gave us. We never read the CLI's own credential files.
Force a backend with the CLAUDE_CODE_CREDENTIALS env var: keyring or file.
Refreshes happen automatically in the background: the auth shim refreshes before expiry and retries once on a 401, exactly like the codex provider does.
Requests identify as Claude Code: "You are Claude Code, Anthropic's official CLI for Claude." is prepended to the system context (position 0), the same
persona the CLI sends. The subscription backend expects it and rate-gates
premium models without it.
Security and scope
This is a plain Anthropic Messages API client authenticated by your Claude subscription tokens. It does not run the Claude Code CLI in a subprocess, so it does not inherit Claude Code's sandboxing, permission prompts, or hooks. Treat it like any code-executing agent: only give it tools you trust.
Projects using this are responsible for following Anthropic's rules for using Claude Code credentials in their own products.
Development
uv sync --extra dev # or: source .venv/bin/activate && pip install -e ".[dev]"
ruff check src tests
pytest
Prior art
The OAuth mechanics (shared client id, PKCE, token storage and refresh) are
lifted from the claude_code_oauth plugin in
code_puppy_core_plugins,
cleaned up and reshaped around the provider pattern in pydantic-ai.
Metadata
Release files for pydantic-claude-code 0.2.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 | |
|---|---|---|---|
| pydantic_claude_code-0.2.0.tar.gz | 16.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pydantic_claude_code-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.8 kB
Release files / pydantic_claude_code-0.2.0.tar.gz
| Download URL | pydantic_claude_code-0.2.0.tar.gz |
|---|---|
| Size | 16.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bccbc90c409d25680cb1f785e52b0031c939474cf99bd552b5b8d4b0328b77b2
|
|
BLAKE2b-256 checksum How to use checksums |
16a80dea28ec190d3abbba3c7fda6d14b2792a3b0afd13bca48fdb4c15db64d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pydantic_claude_code-0.2.0-py3-none-any.whl
| Download URL | pydantic_claude_code-0.2.0-py3-none-any.whl |
|---|---|
| Size | 16.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dde0352f988d876755d53fc8bb68c4cdf23476df48cf59be64b3031d88130823
|
|
BLAKE2b-256 checksum How to use checksums |
d33416251a2dee585fae1efe5a8d7f03683c233628b91fd869162dbdba79ee27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|