Build an A2A Agent with PydanticAI and FastA2A in 5 Minutes
Step-by-step guide to building a production-ready A2A agent using PydanticAI and FastA2A — the fastest way to go from zero to a working A2A server in Python.
PydanticAI is a Python agent framework from the Pydantic team. It brings the same developer experience as FastAPI -- type hints, dependency injection, structured outputs -- to building LLM-powered agents. FastA2A is its built-in module that turns any PydanticAI agent into a full A2A-compliant server with one method call.
No boilerplate JSON-RPC handlers. No manual Agent Card construction. One line: agent.to_a2a().
Install
You need Python 3.10+ and an LLM API key (OpenAI, Google, Anthropic, or any supported provider).
mkdir my-a2a-agent && cd my-a2a-agent
python -m venv .venv
source .venv/bin/activate
pip install "pydantic-ai[a2a]" uvicorn
The [a2a] extra pulls in the fasta2a module and its dependencies.
export OPENAI_API_KEY="sk-..."
Create the Agent
Build a PydanticAI agent in a file called agent.py:
# agent.py
from pydantic_ai import Agent
agent = Agent(
"openai:gpt-4o",
instructions=(
"You are a code review assistant. When given code, analyze it for "
"bugs, security issues, and style problems. Return a structured "
"review with severity levels (critical, warning, info) for each finding."
),
)
That is a working agent. You can test it locally before exposing it over A2A:
result = agent.run_sync("Review this Python: eval(input())")
print(result.output)
Expose as A2A Server
Add one line to turn it into an A2A server:
# agent.py
from pydantic_ai import Agent
agent = Agent(
"openai:gpt-4o",
instructions=(
"You are a code review assistant. When given code, analyze it for "
"bugs, security issues, and style problems. Return a structured "
"review with severity levels (critical, warning, info) for each finding."
),
)
app = agent.to_a2a(
name="Code Review Agent",
description="Analyzes code for bugs, security vulnerabilities, and style issues",
version="1.0.0",
)
to_a2a() returns an ASGI application. It automatically generates an Agent Card, creates JSON-RPC endpoints for message/send and message/stream, and serves the card at /.well-known/agent-card.json.
Run the Server
uvicorn agent:app --host 0.0.0.0 --port 8000
Your A2A agent is live. Verify by fetching the Agent Card:
curl -s http://localhost:8000/.well-known/agent-card.json | python -m json.tool
{
"name": "Code Review Agent",
"description": "Analyzes code for bugs, security vulnerabilities, and style issues",
"version": "1.0.0",
"url": "http://localhost:8000",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"defaultInputModes": ["text"],
"defaultOutputModes": ["text"],
"skills": []
}
Test with curl
Send a task via the A2A JSON-RPC protocol:
curl -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "test-1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [
{
"type": "text",
"text": "Review this code:\n\ndef login(user, pwd):\n query = f\"SELECT * FROM users WHERE name={user} AND pass={pwd}\"\n db.execute(query)\n return True"
}
]
}
}
}'
The response follows the A2A task lifecycle -- the agent returns a completed task with its review as an artifact.
Add Skills to the Agent Card
Skills make your agent discoverable. Other agents can match tasks to your agent based on skill descriptions and tags:
from fasta2a import Skill
app = agent.to_a2a(
name="Code Review Agent",
description="Analyzes code for bugs, security vulnerabilities, and style issues",
version="1.0.0",
skills=[
Skill(
id="security-review",
name="Security Review",
description="Identifies SQL injection, XSS, CSRF, and other security vulnerabilities in code",
tags=["security", "vulnerabilities", "owasp"],
),
Skill(
id="style-review",
name="Style Review",
description="Checks code style, naming conventions, and adherence to PEP 8 or language-specific standards",
tags=["style", "linting", "best-practices"],
),
],
)
Add Tools
PydanticAI agents get powerful when you give them tools. Tools are plain Python functions decorated with @agent.tool or @agent.tool_plain:
from pydantic_ai import Agent
agent = Agent(
"openai:gpt-4o",
instructions=(
"You are a code review assistant. Use the available tools to "
"look up documentation and check for known vulnerabilities."
),
)
@agent.tool_plain
def check_cve_database(package_name: str, version: str) -> str:
"""Check if a package version has known CVEs.
Args:
package_name: The name of the package (e.g., 'django', 'flask').
version: The version string (e.g., '3.2.1').
Returns:
A summary of known vulnerabilities, or 'No known CVEs' if clean.
"""
# In production, call the NVD API or OSV.dev
return f"No known CVEs for {package_name}@{version}"
@agent.tool_plain
def get_language_style_guide(language: str) -> str:
"""Get the official style guide recommendations for a programming language.
Args:
language: The programming language (e.g., 'python', 'javascript').
Returns:
Key style guide rules and conventions.
"""
guides = {
"python": "Follow PEP 8: 4-space indent, snake_case, max 79 chars per line",
"javascript": "Follow Airbnb style: const over let, arrow functions, semicolons",
}
return guides.get(language.lower(), f"No style guide found for {language}")
app = agent.to_a2a(
name="Code Review Agent",
description="Code review agent with CVE checking and style guide lookup",
version="1.1.0",
)
The LLM decides when to call these tools based on the function name, docstring, and parameter types. PydanticAI extracts the schema automatically from the type hints.
Add Dependencies
PydanticAI's dependency injection system lets you pass runtime context (database connections, API clients, user sessions) into tool functions without globals:
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
import httpx
@dataclass
class ReviewDeps:
http_client: httpx.AsyncClient
repo_url: str
agent = Agent(
"openai:gpt-4o",
deps_type=ReviewDeps,
instructions="You are a code review assistant.",
)
@agent.tool
async def fetch_file_from_repo(ctx: RunContext[ReviewDeps], file_path: str) -> str:
"""Fetch a file from the repository for additional context.
Args:
file_path: Path to the file in the repository.
Returns:
The file contents.
"""
url = f"{ctx.deps.repo_url}/raw/main/{file_path}"
resp = await ctx.deps.http_client.get(url)
resp.raise_for_status()
return resp.text
app = agent.to_a2a(
name="Code Review Agent",
description="Code review agent with repo file access",
version="1.2.0",
)
Tools that accept RunContext[ReviewDeps] as their first argument automatically receive the dependencies at runtime.
Structured Output
For agents that need to return structured data, set output_type to a Pydantic model:
from pydantic import BaseModel
from pydantic_ai import Agent
class ReviewResult(BaseModel):
findings: list[dict]
overall_risk: str # "low", "medium", "high", "critical"
summary: str
agent = Agent(
"openai:gpt-4o",
output_type=ReviewResult,
instructions="Review code and return structured findings.",
)
app = agent.to_a2a(
name="Structured Code Review Agent",
description="Returns structured JSON code review results",
version="2.0.0",
)
The A2A response artifacts will contain the JSON-serialized ReviewResult.
Deploy to Production
For production, use gunicorn with uvicorn workers:
pip install gunicorn
gunicorn agent:app -w 4 -k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
Set the url parameter in to_a2a() to your production domain so the Agent Card has the correct URL:
app = agent.to_a2a(
name="Code Review Agent",
description="Code review agent",
version="1.0.0",
url="https://code-review.your-domain.com",
)
With Docker:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY agent.py .
CMD ["gunicorn", "agent:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
PydanticAI plus FastA2A gets you from zero to a production A2A agent faster than any other Python stack. The to_a2a() method handles the protocol plumbing so you can focus on what your agent actually does.
Related Stacks
Related posts
Google ADK Tutorial: Build and Deploy A2A Agents
Comprehensive Google ADK tutorial for 2026. Installation, agent definition, tools, sub-agents, A2A exposure, custom Agent Cards, deployment, and consuming remote agents.
A2A Python SDK: Complete Guide with Examples
Everything you need to build A2A agents and clients in Python. Server setup, client usage, streaming, push notifications, error handling, testing, and Google ADK integration.
A2A Protocol Tutorial: Your First Agent in 15 Minutes
Build your first A2A agent from scratch. Install the SDK, create an agent, expose it over A2A, test with curl, and consume it from another agent — all working code.