A2A Python SDK

repository·main·Indexed 24 days ago

https://github.com/a2aproject/a2a-python

A library for building and running agentic applications that function as A2A Servers, adhering to the Agent2Agent (A2A) Protocol. The SDK provides tools for implementing AgentExecutors, managing AgentCards, and creating clients to interact with A2A servers. Version 1.0 introduces Protobuf-based JSON serialization, a redesigned AgentCard structure for multiple transport bindings, and strict enforcement of A2A specification rules for streaming patterns.

Tokens
15.5K
Snippets
33
Records
71
Agent score
84%

What's inside a2a-sdk

  1. A2A Protocol Compatibility and Spec Versions

    main

    The SDK implements the A2A Protocol Specification 1.0 and includes a compatibility mode for 0.3.

    Supported transports for both versions include:

    • JSON-RPC
    • HTTP+JSON/REST
    • gRPC

    Both Clients and Servers are supported across these versions and transports.

  2. Migrate AgentCard structure to v1.0

    main

    The AgentCard structure has been redesigned in v1.0 to support multiple transport bindings and improved capability definitions.

    Major Changes:

    • Transport Bindings: The url parameter is removed from AgentCard. Instead, use the supported_interfaces field, which contains a list of AgentInterface objects. Each AgentInterface defines a protocol_binding ('JSONRPC', 'HTTP+JSON', or 'GRPC') and its own url.
    • Capabilities: AgentCapabilities.supports_authenticated_extended_card is renamed to AgentCapabilities.extended_agent_card. The input_modes and output_modes fields were removed from AgentCapabilities; use AgentCard.default_input_modes and AgentCard.default_output_modes for card-level defaults.
    • Skill Examples: The examples parameter was removed from AgentCard and moved to AgentSkill.
    from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill
    
    skill = AgentSkill(
        id='hello_world',
        name='Hello World',
        description='Returns a Hello World message.',
        tags=['hello', 'world'],
        input_modes=['text/plain'],
        output_modes=['text/plain'],
        examples=['hello world', 'Hello, World!'],  # moved from AgentCard.examples
    )
    
    agent_card = AgentCard(
        name='Hello World Agent',
        supported_interfaces=[
            # JSON-RPC
            AgentInterface(
                protocol_binding='JSONRPC',
                url='http://localhost:41241/a2a/jsonrpc/',
            ),
            # GRPC
            AgentInterface(
                protocol_binding='GRPC',
                url='http://localhost:50051/a2a/grpc/',
            )
        ],
        version='0.0.1',
        default_input_modes=['text/plain'],
        default_output_modes=['text/plain'],
        capabilities=AgentCapabilities(
            streaming=True,
            extended_agent_card=True,
        ),
        skills=[skill],
    )
  3. How DatabaseStore version-aware read/write works

    main

    The A2A DatabaseStore classes (such as DatabaseTaskStore and DatabasePushNotificationConfigStore) use a version-aware pattern to support multiple protocol versions simultaneously:

    • Write Logic:
      • If core_to_model_conversion is provided during initialization, the store uses that function to write data in the legacy format.
      • If core_to_model_conversion is NOT provided, the store defaults to the modern v1.0 Protobuf JSON format.
    • Read Logic:
      • The store inspects the protocol_version column for every row.
      • If protocol_version is NULL or 0.3, it uses the internal v0.3 legacy parser.
      • If protocol_version is 1.0, it uses the modern Protobuf parser.

    This mechanism allows v1.0 instances to read all existing data regardless of when it was written.

  4. How A2A v0.3 backward compatibility works

    main

    To enable modern v1.0 clients and servers to interoperate with legacy v0.3 A2A systems, the SDK uses a phased transformation process. This process bridges the gap between three distinct data representations:

    1. Legacy v0.3 Pydantic Models (types.py): Acts as the 'pivot' format. Legacy JSON-RPC and REST implementations use these models. They serve as the intermediary between old wire formats and the modern SDK.
    2. Legacy v0.3 Protobuf Bindings (a2a_v0_3_pb2.py): Native Protobuf bindings for the legacy v0.3 gRPC protocol, used to decode incoming bytes from legacy gRPC clients or encode outbound bytes to legacy gRPC servers.
    3. Current v1.0 Protobuf Bindings (a2a.types.a2a_pb2): The central source of truth for the modern SDK. All legacy payloads must be translated into these core objects to be processed by the modern AgentExecutor.

    The transformation flow follows this path: Legacy gRPC Bytes $\rightarrow$ Legacy Pydantic Model $\rightarrow$ Modern v1.0 Protobuf.

  5. Implement AgentExecutor streaming patterns in v1.0

    main

    In v1.0, the server strictly enforces A2A spec rules for SendStreamingMessage. AgentExecutor implementations must follow exactly one of two mutually exclusive streaming patterns. Mixing these patterns or violating the order of events will raise an InvalidAgentResponseError.

    Supported Patterns

    1. Message-only stream: Enqueue exactly one Message and then stop.
    2. Task lifecycle stream: Enqueue a Task first, followed by zero or more TaskStatusUpdateEvent or TaskArtifactUpdateEvent objects until a terminal state is reached.

    Common Violations (Raises InvalidAgentResponseError)

    • Enqueueing a Message after a Task (mixing modes).
    • Enqueueing more than one Message.
    • Enqueueing a Task or update event after a Message.
    • Enqueueing a TaskStatusUpdateEvent before the initial Task.
    # Pattern A: Message-only stream — one Message, then done.
    class GreetingExecutor(AgentExecutor):
        async def execute(self, context, event_queue):
            await event_queue.enqueue_event(
                new_text_message('Hello!', role=Role.ROLE_AGENT)
            )
    
    # Pattern B: Task lifecycle stream — Task first, then updates.
    class WorkflowExecutor(AgentExecutor):
        def __init__(self, agent):
            self._agent = agent
    
        async def execute(self, context, event_queue):
            task = context.current_task or new_task_from_user_message(context.message)
            await event_queue.enqueue_event(task)  # ✅ Task MUST be first
    
            await event_queue.enqueue_event(
                new_text_status_update_event(
                    task_id=task.id,
                    context_id=task.context_id,
                    state=TaskState.TASK_STATE_WORKING,
                    text='Processing...',
                )
            )
    
            result = await self._agent.invoke(context.message)
            await event_queue.enqueue_event(
                new_text_artifact_update_event(
                    task_id=task.id,
                    context_id=task.context_id,
                    name='result',
                    text=result,
                )
            )
    
            await event_queue.enqueue_event(
                new_text_status_update_event(
                    task_id=task.id,
                    context_id=task.context_id,
                    state=TaskState.TASK_STATE_COMPLETED,
                    text='Done!',
                )
            )
  6. Install the a2a-db migration tool

    main

    To use the a2a-db CLI tool for database schema updates, you must install the a2a-sdk package with the db-cli extra. You can also install all extras using the all option.

    # Using uv
    uv add "a2a-sdk[db-cli]"
    uv add "a2a-sdk[all]"
    
    # Using pip
    pip install "a2a-sdk[db-cli]"
    pip install "a2a-sdk[all]"
  7. Run ITK tests locally

    main

    To execute ITK tests, you must first specify the target revision of the a2a-itk repository using the A2A_ITK_REVISION environment variable (this can be a branch name, tag, or commit hash). Then, execute the local test script.

    The ./run_itk.sh script performs the following:

    1. Clones a2a-itk (if not present).
    2. Checks out the specified revision.
    3. Builds the ITK service Docker image.
    4. Runs the tests and outputs results.
    export A2A_ITK_REVISION=main
    ./run_itk.sh
  8. Run A2A v1.0 samples

    main

    To see a working implementation of the v1.0 SDK, you can run the provided samples using uv.

    1. Start the Agent: Run the server which exposes JSON-RPC, REST, and gRPC (with v0.3 compatibility enabled).
    2. Connect with the CLI: Run the interactive terminal client to communicate with the agent.

    Ensure you have uv installed before running these commands.

    # In one terminal — start the agent:
    uv run python samples/hello_world_agent.py
    
    # In another terminal — connect with the CLI:
    uv run python samples/cli.py
  9. Configure the Database URL for migrations

    main

    The a2a-db tool requires a DATABASE_URL environment variable using an async-compatible driver. You can set this globally via export DATABASE_URL or pass it directly to a command using the --database-url flag.

    Supported driver examples:

    • SQLite: sqlite+aiosqlite://...
    • PostgreSQL: postgresql+asyncpg://...
    • MySQL: mysql+aiomysql://...
  10. Perform zero-downtime migration from v0.3 to v1.0

    main

    To migrate an Agent application from A2A protocol v0.3 to v1.0 without service interruption in a distributed environment, follow the 'Expand, Migrate, Contract' strategy.

    Warning: Always back up your database before proceeding.

    Step 1: Apply Schema Updates

    Run the a2a-db tool to add new columns (owner, protocol_version, last_updated). These columns are nullable, so existing v0.3 code will continue to function.

    uv run a2a-db --database-url "your-database-url"

    Verify the schema version with:

    uv run a2a-db current

    Step 2: Rolling Deployment in Compatibility Mode

    Deploy v1.0 SDK code but configure it to write in the legacy v0.3 format. This allows v0.3 instances still in the cluster to read data produced by new v1.0 instances.

    To do this, pass the appropriate conversion functions to your Store constructors (see Compatibility Conversions for details).

    Step 3: Transition to v1.0 Mode

    Critical: Only perform this step once 100% of your instances are running v1.0 code. v0.3 code cannot read v1.0 native entries.

    To transition, remove the core_to_model_conversion arguments from your Store constructors to revert to native v1.0 write behavior.

  11. Migrate A2A database from v0.3 to v1.0

    main

    For single-instance applications or non-critical services that can tolerate short downtime, follow these steps to migrate from A2A protocol v0.3 to v1.0:

    1. Backup your database before proceeding.
    2. Apply Schema Updates: Run the a2a-db tool against your target database. This adds new columns (owner, protocol_version, last_updated) while preserving existing v0.3 data. The v1.0 schema is backward compatible; v1.0 code can read v0.3 entries using a built-in legacy parser.
    3. Verify the Migration: Check that the schema is at the correct version using the current command.
    4. Update Application Code: Upgrade your application to use the v1.0 SDK.
  12. Key changes in A2A Python SDK v1.0

    main

    The transition from v0.3 to v1.0 introduces several breaking changes and architectural shifts:

    • Protobuf Migration: Core types are now Protobuf-based instead of Pydantic. You cannot assign arbitrary attributes to these objects. Use google.protobuf.json_format.MessageToDict for dictionary conversion and .HasField('field_name') to check for optional fields.
    • Enum Naming: All enum values have changed from snake_case to SCREAMING_SNAKE_CASE to comply with ProtoJSON.
    • AgentCard Restructuring: The url field is replaced by supported_interfaces (a list of AgentInterface objects containing protocol_binding, protocol_version, and url). Input/output modes now live directly on AgentCard as default_input_modes and default_output_modes.
    • AgentExecutor Streaming: Servers strictly enforce the A2A spec. An executor must enqueue either a single Message OR a Task followed by update events. Mixing these or sending updates before the initial Task results in InvalidAgentResponseError.
    • Application Setup: Wrapper classes like A2AStarletteApplication have been removed. Use route factory functions (create_jsonrpc_routes(), create_rest_routes(), and create_agent_card_routes()) to compose routes into Starlette or FastAPI apps.
    • Helper Consolidation: All utilities are now located in a2a.helpers.