Pydantic AI V2 went stable on June 23, 2026, and it changes how Python agents are built. After seven betas, the release’s central contribution is a new primitive called a capability — a composable unit that bundles tools, lifecycle hooks, instructions, and model settings into one object. If you’ve been putting off the upgrade, the architecture is genuinely better and the migration is shorter than you expect.
What Is a Capability?
In V1, you wired an agent by passing a growing list of keyword arguments to the Agent constructor: tools=, mcp_servers=, instrument=, prepare_tools=, history_processors=. In V2, all of that lives inside capabilities passed as a list. Each capability bundles everything a feature needs: its tools, the instructions that tell the model when to use them, any lifecycle hooks, and model settings — one composable object.
The practical payoff is reusability. Build a memory capability once, drop it into any agent that needs it. Set defer_loading=True and the capability collapses to a single catalog entry — the model loads the full bundle only when it decides to use it, reducing token overhead on agents with large tool sets.
from pydantic_ai import Agent
from pydantic_ai.capabilities import Capability, Thinking, WebSearch
from pydantic_ai.mcp import MCPToolset
agent = Agent(
'anthropic:claude-sonnet-5',
instructions='Research thoroughly and cite sources.',
capabilities=[
Thinking(effort='high'),
WebSearch(),
Capability(
id='github',
description='Look up GitHub issues, PRs, and code.',
instructions='Use GitHub tools when questions involve repositories.',
toolset=MCPToolset('https://mcp.example.com/github'),
defer_loading=True,
),
],
)
The Pydantic AI Harness
The Harness is a separate package — pydantic-ai-harness — that ships capabilities not yet ready for core. The split is deliberate: core stays small and stable; the Harness iterates quickly. When a capability matures and proves broadly useful, it graduates into core. The Harness currently ships memory systems, guardrails, context management, file system access, and the feature most worth your attention: CodeMode.
CodeMode: Fewer Round-Trips, Same Tools
CodeMode wraps your existing tools into a single run_code tool. Instead of one model round-trip per tool call, the model writes Python code that orchestrates your tools — with asyncio.gather for parallel calls, loops, and conditionals — inside a single execution. An agent that previously needed 11 model calls to fetch and process 10 items now does it in roughly 2.
The runtime is Monty, a minimal Python interpreter written in Rust. It runs a safe subset: standard library modules including asyncio, json, re, math, and pathlib are available. No third-party imports, no timing primitives — sandboxed by design.
from pydantic_ai import Agent
from pydantic_ai_harness import CodeMode
# uv add "pydantic-ai-harness[code-mode]"
agent = Agent('anthropic:claude-sonnet-5', capabilities=[CodeMode()])
@agent.tool_plain
def get_weather(city: str) -> dict:
return {'city': city, 'temp_f': 72, 'condition': 'sunny'}
@agent.tool_plain
def convert_temp(fahrenheit: float) -> float:
return round((fahrenheit - 32) * 5 / 9, 1)
result = agent.run_sync("Weather in Paris and Tokyo, in Celsius?")
The model receives one run_code tool instead of two separate ones. It calls both tools for both cities inside a single code block using asyncio.gather. Pydantic’s docs note CodeMode is “an early candidate to graduate into core” — meaning this won’t always require the Harness package.
Five Breaking Changes to Fix Before You Upgrade
The V1-to-V2 migration path is: upgrade to the latest V1 first, fix every deprecation warning, then jump to V2. That handles roughly 80% of the work. The remaining 20% is a short list of behavior changes that warnings couldn’t catch:
- The
openai:prefix changed silently. It now routes to the Responses API instead of Chat Completions. If your code usesopenai:and expects Chat Completions behavior, switch toopenai-chat:. This is the most dangerous change — it produces no error, just different behavior. - Provider extras are now opt-in. Bedrock, Groq, Mistral, Cohere, and others are no longer installed with a bare
pip install pydantic-ai. Add them explicitly:pip install 'pydantic-ai[bedrock,groq]'. - The MCP server classes are gone.
MCPServerStdio,MCPServerSSE, andMCPServerStreamableHTTPare removed. UseMCPToolsetfrompydantic_ai.mcpinstead, and replaceagent.run_mcp_servers()withasync with agent:. - WebSearch and WebFetch default to native-only. If your model doesn’t support native search, requests will raise instead of falling back silently. Restore fallback behavior:
WebSearch(local='duckduckgo')orWebFetch(local=True). - Agent constructor kwargs migrated to capabilities. The kwargs
instrument=,mcp_servers=,prepare_tools=, andhistory_processors=are removed fromAgent(). PassInstrumentation(...),PrepareTools(...), andProcessHistory(...)as capabilities instead.
One thing that does not break: message history. V1-serialized messages continue to deserialize in V2, so persistent conversations survive the upgrade intact.
Upgrade Now
V1 is not deprecated, but the capability pattern is cleaner architecture for anything beyond a single-tool agent. If you run multi-tool agents — especially with MCP servers — the migration pays off immediately: less constructor configuration, reusable capabilities, and CodeMode to cut tool round-trips. The version policy also changed: breaking change windows shrink from six months to three months between majors, so the sooner you’re on V2, the more runway you have before the next transition.
Start with the upgrade guide, review the V2 announcement for the architectural overview, and explore the CodeMode docs for any agent doing repeated tool calls.













