Hire AI Developers Need An Online Store? Client Contracts & NDAs Grow Your Sales Funnel
Hire AI Developers Grow Your Sales Funnel

A2A Python SDK: Build Your First A2A Agent in Python

Updated July 2026
The A2A Python SDK (a2a-sdk) is the most mature implementation of the A2A protocol, providing everything you need to build A2A servers and clients. This guide covers the SDK's architecture, key classes, server configuration, client usage, streaming implementation, and integration with existing agent frameworks like LangGraph and CrewAI.

The Python SDK is maintained by the A2A project team and published on PyPI. It targets Python 3.10+ and uses modern Python patterns including async/await, type hints, and Pydantic models for data validation. The SDK handles all protocol-level concerns (JSON-RPC parsing, Agent Card serving, SSE streaming, task state management) so you can focus on your agent's actual capabilities.

Set Up the Project

Start by creating a new Python project and installing the SDK. A clean virtual environment is recommended to avoid dependency conflicts.

Run pip install a2a-sdk to install the core package. The SDK depends on httpx for HTTP client operations, uvicorn for the ASGI server, starlette for HTTP routing, and pydantic for data validation. These dependencies are installed automatically.

A typical project structure separates the server and client into different modules. Create a server.py for your A2A server and a client.py for testing. As your agent grows, you will likely add modules for the actual agent logic (LLM calls, tool use, data processing) separate from the A2A protocol layer.

The SDK's main imports are: AgentCard and AgentSkill for defining your agent's capabilities, A2AServer for running the server, A2AClient for connecting to other agents, and the message/task types (Task, Message, TextPart, DataPart, FilePart, Artifact) for constructing requests and parsing responses.

Build the Agent Card with the SDK

The SDK provides Pydantic models for constructing Agent Cards programmatically. Use the AgentCard class to build a type-safe, validated card instead of manually crafting JSON.

Create an AgentCard instance with the required fields. The name should be a clear, descriptive title. The description should explain what your agent does in enough detail for both humans and LLMs to evaluate its suitability. The url is the endpoint where the agent accepts A2A requests. For local development, use http://localhost:8000.

Define skills using the AgentSkill class. Each skill gets an id (lowercase with hyphens, like "analyze-data"), name (human-readable), and description (detailed capability explanation). Optionally add inputSchema and outputSchema as JSON Schema dictionaries for structured validation.

Set defaultInputModes and defaultOutputModes to declare content type support. Common values include text for natural language, data for structured JSON, and file for binary content. An agent that accepts text and returns text plus data would set input modes to ["text"] and output modes to ["text", "data"].

The SDK validates the Agent Card at construction time. If required fields are missing or field values are invalid, Pydantic raises a clear validation error before the server starts. This catches configuration mistakes early rather than at runtime when a client tries to fetch the card.

Implement the Server with a Task Handler

The server's core component is the task handler, a class or function that receives incoming tasks and produces results. The SDK defines a handler interface that your implementation must follow.

The standard approach is to subclass the SDK's AgentExecutor (or similar base class, depending on SDK version). Your subclass implements the execute method, which receives the incoming Task object and returns an updated task with results. Inside this method, you extract the client's message from the task, process it using your agent's logic, and construct artifacts containing the output.

Extracting input from the task follows a consistent pattern. Access task.messages to get the list of messages, take the last message (which is the most recent client input), and iterate over its parts to find the content. Check the part type: TextPart for text input, DataPart for structured JSON, FilePart for file content.

Constructing the response follows the reverse pattern. Create an Artifact with a list of parts containing your output. Wrap it in the task update along with the completed status. The SDK handles serializing this into the correct JSON-RPC response format.

For agents that need clarification, return the task with input-required status and a message asking the client what you need to know. When the client responds, the SDK calls your handler again with the updated task containing the new message. Check the message history to distinguish initial requests from follow-up responses.

Wire your handler to the server by passing it to the A2AServer constructor along with the Agent Card. Call server.start() to launch. The server automatically configures routes for the Agent Card endpoint and the A2A JSON-RPC endpoint.

Build the Client

The SDK's A2AClient class simplifies connecting to A2A servers. Initialize it with the server's base URL, and the client handles Agent Card fetching, JSON-RPC request formatting, and response parsing.

Fetching the Agent Card is the first step. Call client.get_agent_card() to retrieve and parse the server's Agent Card. Examine the returned AgentCard object to verify the server has the skills you need. In orchestration scenarios, you might fetch cards from multiple servers and choose the best one based on skill descriptions.

Submitting a task requires constructing a Task or SendTaskRequest with a unique task ID and a message containing your instructions. The SDK provides helper functions for generating task IDs. Add parts to your message: a TextPart with natural language instructions, a DataPart with structured parameters, or a FilePart with file content. Call client.send_task() to submit and wait for the response.

Processing the response means examining the returned task object. Check the status field: completed means results are in the artifacts, input-required means the server needs more information, and failed means something went wrong. For completed tasks, iterate over task.artifacts and extract the parts from each artifact.

The SDK's client is async-first, using Python's asyncio. All network operations return coroutines that must be awaited. For simple scripts, wrap your client code in an async def main() function and run it with asyncio.run(main()).

Add Streaming and Advanced Features

Streaming replaces the single-response pattern with a continuous event stream. On the client side, use client.send_task_subscribe() instead of send_task(). This returns an async iterator that yields events as the server produces them.

Each event has a type. TaskStatusUpdateEvent carries a new task status and optional message (useful for progress updates). TaskArtifactUpdateEvent carries a new or updated artifact (useful for partial results). Your client code iterates over these events and handles each type appropriately.

On the server side, streaming requires modifying your handler to yield events instead of returning a single result. The SDK provides a streaming handler interface where your implementation is an async generator. Yield status updates as you work, yield partial artifacts when portions of the output are ready, and yield the final completed status with the full artifact when done.

For long-running tasks, implement push notification support. The SDK's server automatically handles the tasks/pushNotification/set and tasks/pushNotification/get methods. When a client registers a webhook URL, the server stores it and sends HTTP POST notifications to that URL whenever the task state changes. Your handler code does not need to manage notifications directly, the SDK handles them based on your handler's status updates and artifact yields.

Additional SDK features include: middleware support for adding logging, metrics, or authentication checks to all incoming requests; error handling utilities for constructing protocol-compliant error responses; and task storage adapters for persisting tasks to databases instead of the default in-memory store.

Integrating with Agent Frameworks

If you already have agents built on LangGraph, CrewAI, Google ADK, or another framework, you do not need to rewrite them. The A2A ecosystem provides adapter packages that wrap existing framework agents as A2A servers.

For LangGraph, the adapter wraps your compiled graph as an A2A task handler. Incoming A2A tasks are converted to LangGraph inputs, the graph runs normally, and the output is converted back to A2A artifacts. The adapter handles streaming by mapping LangGraph's streaming callbacks to A2A SSE events.

For CrewAI, the adapter maps A2A skills to CrewAI tasks and A2A messages to CrewAI inputs. Your crew configuration remains unchanged, and the adapter layer translates between CrewAI's internal representation and the A2A protocol. Crew outputs become A2A artifacts.

For Google ADK (Agent Development Kit), integration is built into the ADK itself since both A2A and ADK are Google projects. ADK agents can expose themselves as A2A servers through built-in configuration options without any separate adapter package.

The general pattern for any framework is: build an adapter that translates incoming A2A tasks to your framework's input format, invokes your existing agent logic, and translates the output back to A2A artifacts. The A2A SDK provides base classes and utilities that make this translation layer straightforward to implement.

Error Handling Patterns

Robust error handling separates production agents from prototypes. The SDK provides utilities for constructing protocol-compliant errors, but you need to decide what errors to handle and how.

Protocol errors (malformed JSON-RPC, unknown methods, invalid parameters) are handled automatically by the SDK. Your handler never sees these because they are caught and responded to before dispatch. The SDK returns standard JSON-RPC error codes that clients understand.

Task-level errors (your handler cannot complete the work) should be returned as failed tasks with descriptive error messages. Include enough context in the error message for the client agent to understand what went wrong and potentially retry with different parameters. "Failed to analyze data: input file contains no numeric columns" is actionable. "Internal error" is not.

Transient errors (temporary network failures, rate limits, service unavailability) should be retried within your handler before failing the task. The SDK does not retry automatically because retry logic depends on what your handler is doing. A database query might be safe to retry, but a financial transaction might not. Implement retry logic that matches your domain's requirements.

Testing Your A2A Agent

The SDK includes testing utilities for verifying your agent's A2A compliance without running a full HTTP server. You can invoke your handler directly with constructed Task objects and verify the responses match your expectations. This is faster and more reliable than end-to-end HTTP tests for unit-level verification.

For integration testing, run the server in a test fixture and use the SDK's client to send real HTTP requests. Verify Agent Card accessibility, task submission and completion, streaming event delivery, error responses for invalid inputs, and the input-required flow for multi-turn interactions. The SDK's async architecture works well with pytest and pytest-asyncio for async test execution.

Key Takeaway

The A2A Python SDK handles all protocol complexity. You define an Agent Card (what your agent can do), implement a task handler (how it does the work), and the SDK handles JSON-RPC, SSE streaming, Agent Card serving, and task state management. Framework adapters let existing LangGraph, CrewAI, and ADK agents participate in A2A without rewrites.