Skip to main content

pydantic-claude-code

Use your Claude Code subscription from a plain pydantic-ai Agent or from CLAI2, with full pydantic-ai tool support. No API key, no separate billing: if Claude Code works from your terminal, this 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).

Models

Model ID
Claude Opus 5.5 claude-opus-5-5
Claude Sonnet 5.5 claude-sonnet-5-5
Claude Fable 5.1 claude-fable-5-1
Claude Haiku 4.5 claude-haiku-4-5

These are the current models, the ones CLAI2's menus offer (config.MODELS). They were checked on 2026-09-30 against Anthropic's models overview, against the anthropic SDK's model list that pydantic-ai's KnownModelName is built from, and live against a Claude subscription. Any other model ID your subscription serves, such as claude-opus-4-8, works too; it just isn't listed.

Use it in CLAI2

The plugin adds claude-code:MODEL models to CLAI2, next to its own providers, so the stock agent, Coder, and your other plugins all keep working.

Install

One line installs the latest release of the plugin, and running it again updates it:

curl -fsSL https://raw.githubusercontent.com/mpfaffenberger/pydantic-ai-claude-code/main/install.sh | sh

To pin a release, pass its tag (... | sh -s -- v0.6.0), or pass main for unreleased changes. The script downloads that version of this repo and copies the src/pydantic_ai_claude_code folder into CLAI2's plugins folder as claude_code, replacing any older copy. Nothing to pip install: everything the plugin imports (pydantic-ai with Anthropic support, httpx2, keyring) already ships with CLAI2.

The plugins folder is $XDG_CONFIG_HOME/pydantic-clai2/plugins/, which is ~/.config/pydantic-clai2/plugins/ by default on macOS and Linux: the folder next to CLAI2's config.db. To install by hand, copy the folder there yourself:

git clone --depth 1 https://github.com/mpfaffenberger/pydantic-ai-claude-code /tmp/claude-code-plugin
mkdir -p ~/.config/pydantic-clai2/plugins
cp -R /tmp/claude-code-plugin/src/pydantic_ai_claude_code ~/.config/pydantic-clai2/plugins/claude_code

To hack on the plugin, symlink the folder instead of copying it: ln -s "$PWD/src/pydantic_ai_claude_code" ~/.config/pydantic-clai2/plugins/claude_code.

Dropped-in plugins load when CLAI2 starts. Inside CLAI2, /plugins lists it as claude_code, where Space turns it off and on. (The shell's clai2 plugins list shows only plugins added by name, so it won't appear there.)

Updates: each time CLAI2 starts, the plugin checks PyPI in the background. When a newer release is out, it says so once, after your first reply, with the one-liner to update. Being offline is silent. Set CLAUDE_CODE_NO_UPDATE_CHECK=1 to turn the check off.

CLAI2 version: the plugin is a class-based CLAI2 Plugin (pydantic/pydantic-ai#9493, merged), so it needs a CLAI2 built from pydantic-ai main at or after that commit; no pydantic-clai2 release has it yet (0.52.0 is the latest). Plugin 0.5.0 was the last activate(host) plugin, for CLAI2 main before #9493. Until a release is out, run CLAI2 from pydantic-ai's main:

git clone https://github.com/pydantic/pydantic-ai
cd pydantic-ai
uv run clai2

Or install it as a package instead, into CLAI2's environment, and point CLAI2 at it: uv tool install pydantic-clai2 --with pydantic-claude-code, then /plugins add claude_code pydantic_ai_claude_code.

Sign in

Run /login claude. Your browser opens Claude's sign-in page, and CLAI2 prints the URL in case it doesn't. Or open the settings menu with /claude_code (or C on the plugin in /plugins), choose Sign-in, and press Enter.

Pick a model

/login claude adds every model in the table above to your model list, so /model offers them right away, and /model_settings sets thinking mode, effort, and the rest for each. On an older CLAI2, or for a model that isn't listed, open /add_model and choose the claude-code provider, or type one directly:

/add_model claude-code:claude-opus-5-5
/model claude-code:claude-sonnet-5-5

Headless runs work the same way: clai2 -p "Summarize this repo" -m claude-code:claude-fable-5-1.

Settings menu

/plugins configure claude_code, C in /plugins, or a bare /claude_code opens it. Every change is saved right away and applies from the next run.

Row What it does Stored in
Sign-in Enter signs in through the browser; R signs out the token store below, never in plugin settings
Credential storage auto (CLAUDE_CODE_CREDENTIALS, else keyring, else a file), keyring, or file plugin settings (credentials)

/claude_code logout and /claude_code status do the same without the menu.

Where the sign-in lives

The sign-in is an OAuth token pair, not an API key, so it does not go in /keys. It is kept where this package keeps it outside CLAI2, which means one sign-in serves both CLAI2 and your own agents: the OS keyring (service pydantic-ai-claude-code), or, without a keychain, a file readable only by you. CLAI2's plugin settings are plaintext SQLite, so they hold only the storage choice. Tokens are refreshed automatically and the refreshed pair is saved back. The Credential storage row's auto follows the CLAUDE_CODE_CREDENTIALS variable described below when it is set; keyring and file override it.

Signing out deletes the stored tokens; they stay valid at Anthropic until they expire. Switching storage does not move an existing sign-in, so sign in again after switching.

If a run says Sign in to Claude Code first or Your Claude Code sign-in has expired, run /login claude. If the plugin fails to load with cannot import name 'Plugin' from 'pydantic_clai2.plugins', your CLAI2 predates class-based plugins: upgrade it as described under Install, or pin plugin 0.5.0.

Use it from Python

pydantic-ai owns the loop: its Agent drives the conversation, runs your tools, and validates structured output. This package is just a model plus a provider. Instead of a claude-code: model string, construct ClaudeCodeModel('claude-fable-5-1') directly; it connects itself to a ClaudeCodeProvider by default.

Quick start

import asyncio

from pydantic_ai import Agent

from pydantic_ai_claude_code import ClaudeCodeModel, default_store, login


async def main() -> None:
    # One-time: opens your browser, mints tokens, stores them
    # (it only needs to run again when the tokens are revoked).
    if default_store().load() is None:
        await login()

    agent = Agent(ClaudeCodeModel('claude-opus-5-5'))

    result = await agent.run('Say hi in three words.')
    print(result.output)


asyncio.run(main())

You can also sign in from the shell: python -m pydantic_ai_claude_code login.

With tools

from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeCodeModel

agent = Agent(ClaudeCodeModel('claude-sonnet-5-5'))

@agent.tool_plain
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

Tools defined on the agent are sent to the API as standard Anthropic tool definitions. Structured output works the same way as with the built-in anthropic provider.

Structured output

from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeCodeModel

class Weather(BaseModel):
    city: str
    temperature_c: float

agent = Agent(ClaudeCodeModel('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
from pydantic_ai_claude_code import ClaudeCodeModel

agent = Agent(ClaudeCodeModel('claude-haiku-4-5'))
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 public OAuth client that 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 (including headless Linux, where keyring reports its fail backend), credentials go to a JSON file instead, created 0600. You can override its path with CLAUDE_CODE_AUTH_FILE:

~/.local/share/pydantic-ai-claude-code/auth.json

The file 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), or pass one to default_store('file').

Refreshes happen automatically: the auth shim refreshes before expiry and retries once on a 401, exactly like pydantic-ai's Codex provider. When the refresh token itself is rejected, runs raise ClaudeCodeSignInExpiredError (a UserError) telling you to sign in again.

Requests identify as Claude Code 2.1.285. The persona "You are Claude Code, Anthropic's official CLI for Claude." is prepended to the system context (position 0), as the CLI sends it. The client version matters: the subscription backend refuses newer models to older CLI versions (Opus 5.5 needs 2.1.280 or newer).

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 agent that runs code: 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 --extra clai2
uv run ruff check src tests && uv run ruff format --check src tests
uv run pyright
uv run pytest

To release, bump version in pyproject.toml and __version__ in src/pydantic_ai_claude_code/__init__.py (a test checks they match), merge, and push a matching tag (git tag v0.6.0 && git push origin v0.6.0). The Publish workflow tests, builds, and uploads it to PyPI with the PYPI_API_TOKEN repository secret.

The tests never touch your real keychain or token file. They run against a local Messages API stub, including a full CLAI2 turn through the plugin.

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.6.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.6.0
File Size Uploaded
pydantic_claude_code-0.6.0.tar.gz 145.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pydantic-claude-code 0.6.0
File Interpreter ABI Platform
pydantic_claude_code-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 172.0 kB

Release files / pydantic_claude_code-0.6.0.tar.gz

Download URL pydantic_claude_code-0.6.0.tar.gz
Size 145.6 kB
Tags Source
SHA-256 checksum
How to use checksums
0a81f3c4156940e65efa17007946e320d12e504c8ad77ff19da4d3d8edc2a0dc
BLAKE2b-256 checksum
How to use checksums
ad0aebdf24f0af33516fec897f502c361f7c9cdc59ebc8aa20b6d0ab8209d14e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pydantic_claude_code-0.6.0-py3-none-any.whl

Download URL pydantic_claude_code-0.6.0-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e9cb53914ce0df1abc607af2e3e69637848b8d65bb1d6bcf9b442e5f1efac4d7
BLAKE2b-256 checksum
How to use checksums
1548b356ca759fc3a6197949fb08c7ff8fdc696b5e309e23e3c3c8305a01eded
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

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