Skip to main content

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:

  1. pydantic-ai owns the loop. We don't hand the whole agent loop to the Claude Code CLI. pydantic-ai's own Agent machinery drives the conversation, executes your tools, and validates structured output. The wheel is a model
    • provider, not a second agent fighting for control.
  2. 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)

Source distribution for pydantic-claude-code 0.2.0
File Size Uploaded
pydantic_claude_code-0.2.0.tar.gz 16.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pydantic-claude-code 0.2.0
File Interpreter ABI Platform
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}

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page