From e7618d2ba29595916767aa18ea27fb7f452f4b47 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 21 Apr 2026 13:40:34 +0200 Subject: [PATCH 01/16] docs: update v1.0 migration guide --- docs/migrations/v1_0/README.md | 127 ++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 51 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 34b2f1bed..fb08a813f 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -6,6 +6,11 @@ Beyond protocol support, `v1.0` enhances the developer experience by introducing This documentation details the technical upgrades and architectural modifications introduced in A2A Python SDK v1.0. For developers using the database persistence layer, please refer to the [Database Migration Guide](database/) for specific update instructions. +> ### **Why Upgrade to v1.0?** +> * **Protocol v1.0 Compliance**: Full alignment with the latest A2A industry standard for cross-agent interoperability. +> * **Reduced Boilerplate**: Unified helper utilities that simplify common tasks like message and task creation. +> * **Architectural Flexibility**: Direct Starlette/FastAPI integration allows you to mount A2A routes into existing applications with full control over middleware. + --- ## Table of Contents @@ -55,9 +60,9 @@ pip install --upgrade a2a-sdk Types have migrated from Pydantic models to Protobuf-based classes. -### Enum values: snake_case → SCREAMING_SNAKE_CASE +### Enum values: `snake_case` → `SCREAMING_SNAKE_CASE` -All the enum values are now standardized from snake_case to **SCREAMING_SNAKE_CASE** format. +All the enum values are now [standardised](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format. This affects every enum in the SDK: `TaskState`, `Role`. @@ -131,14 +136,14 @@ message = Message( ### AgentCard Structure -The new `AgentCard` can supports multiple transport bindings using `AgentInterface` class. - -Key differences: -- `url` is gone; use `supported_interfaces` with one or more `AgentInterface` entries -- `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed; use `AgentCard.default_input_modes` / `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` / `AgentSkill.output_modes` for per-skill overrides -- `supports_authenticated_extended_card` is no longer a top-level `AgentCard` field; it has moved into `AgentCapabilities` and is renamed to `extended_agent_card` -- `AgentInterface.protocol_binding` accepted values: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'` -- `examples` field was removed; set it per `AgentSkill` instead +Key changes: +- Added `AgentInterface` class to support multiple transport bindings via the `supported_interfaces` field in AgentCard. +- The `url` parameter in `AgentCapabilities` is removed and is now part of `AgentInterface`. +- Accepted values for `AgentInterface.protocol_binding`: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'` +- The `AgentCard.capabilities` field is renamed to `AgentCard.agent_capabilities`. +- The `AgentCard.supports_authenticated_extended_card` field is renamed to `AgentCapabilities.extended_agent_card`. +- The `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` fields are removed; use `AgentCard.default_input_modes` / `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` / `AgentSkill.output_modes` for per-skill overrides. +- The `examples` parameter in `AgentCard` is removed and is now part of `AgentSkill`. **Before (v0.3):** ```python @@ -164,7 +169,7 @@ agent_card = AgentCard( **After (v1.0):** ```python -from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill, +from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill agent_card = AgentCard( name='My Agent', @@ -232,10 +237,13 @@ The wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2A from a2a.server.apps import A2AStarletteApplication import uvicorn +# Create application using A2AStarletteApplication wrapper class server = A2AStarletteApplication( agent_card=agent_card, http_handler=request_handler, ) + +# Start the server uvicorn.run(server.build(), host=host, port=port) ``` @@ -245,26 +253,19 @@ from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes from starlette.applications import Starlette import uvicorn -routes = [] -routes.extend(create_agent_card_routes(agent_card)) -routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/')) - -app = Starlette(routes=routes) -uvicorn.run(app, host=host, port=port) -``` - -If you need REST transport in addition to JSON-RPC: -```python -from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes, create_rest_routes -from starlette.applications import Starlette -import uvicorn +# Define routes for transports as per AgentCard routes = [] routes.extend(create_agent_card_routes(agent_card)) -routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/')) -routes.extend(create_rest_routes(request_handler)) +routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/api/v1/jsonrpc/')) + +# Optional: Add routes for other transports +# routes.extend(create_rest_routes(request_handler, path_prefix='/api/v1/rest/')) +# Create application using routes app = Starlette(routes=routes) + +# Start the server uvicorn.run(app, host=host, port=port) ``` @@ -297,19 +298,18 @@ create_rest_routes(request_handler, enable_v0_3_compat=True) ## 6. Client: Creating a Client -New `create_client()` `ClientFactory` function that creates a client for the agent. +In `v1.0`, use `a2a.client.create_client()` helper function to create a `Client` for the agent. -> **Note**: The legacy `A2AClient` class has been removed. **Before (v0.3):** ```python from a2a.client import ClientFactory -# From URL +# Option 1: Using Agent Server URL factory = ClientFactory() client = factory.create_client('http://localhost:9999/') -# From an already-resolved AgentCard +# Option 2: Using AgentCard factory = ClientFactory() client = factory.create_client(agent_card) ``` @@ -318,10 +318,10 @@ client = factory.create_client(agent_card) ```python from a2a.client import create_client -# From URL — resolves the agent card automatically +# Option 1: Using Agent Server URL client = await create_client('http://localhost:9999/') -# From an already-resolved AgentCard +# Option 2: Using AgentCard client = await create_client(agent_card) ``` @@ -332,9 +332,9 @@ client = await create_client(agent_card) ## 7. Client: Send Message -The `BaseClient.send_message()` return type is standardised from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`. +The `BaseClient.send_message()` return type is standardised from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`. -Each `StreamResponse` yields exactly one of: `task`, `message`, `status_update`, or `artifact_update`. Use `HasField()` to check which field is set. +Each `StreamResponse` yields exactly one of: (`task`, `message`, `status_update`, or `artifact_update`). Use `HasField()` to check which field is set. **Before (v0.3):** @@ -368,6 +368,7 @@ async for chunk in client.send_message(request): `ClientConfig.push_notification_config` is now **singular** (a single `TaskPushNotificationConfig` or `None`), not a list. + **Before (v0.3):** ```python config = ClientConfig( @@ -386,31 +387,55 @@ config = ClientConfig( ## 9. Helper Utilities -A new `a2a.helpers` module consolidates helper functions into a single import. Most were previously available under `a2a.utils.*`; a few are new in v1.0. +To improve the developer experience, we have consolidated helper functions into a single import. In v0.3, these helper functions were scattered across different modules; In v1.0, they are all available under `a2a.helpers`. + +| Helper Function | Description | +|---|---| +| `display_agent_card` | Prints a human-readable summary of an `AgentCard` to stdout. | +| `get_artifact_text` | Joins all text parts of an `Artifact` into a single string (using `\n` as delimiter). | +| `get_message_text` | Joins all text parts of a `Message` into a single string (using `\n` as delimiter). | +| `get_stream_response_text` | Extracts text from a `StreamResponse` protobuf message. | +| `get_text_parts` | Returns a list of raw text strings from a sequence of `Part` objects, skipping non-text parts. | +| `new_artifact` | Creates an `Artifact` from a list of `Part` objects, a name, and an optional description and ID. | +| `new_message` | Creates a `Message` from a list of `Part` objects with a role (defaults to `ROLE_AGENT`), and optional task/context IDs. | +| `new_task` | Creates a `Task` with an explicit task ID, context ID, and state. | +| `new_task_from_user_message` | Creates a `TASK_STATE_SUBMITTED` `Task` from a user `Message`. Raises an error if the role is not `ROLE_USER` or if parts are empty. | +| `new_text_artifact` | Creates an `Artifact` with a single text `Part`, a name, and an optional description and ID. | +| `new_text_artifact_update_event` | Creates a `TaskArtifactUpdateEvent` with a text artifact. | +| `new_text_message` | Creates a `Message` with a single text `Part`; role defaults to `ROLE_AGENT`. | +| `new_text_status_update_event` | Creates a `TaskStatusUpdateEvent` with a text message. | + +Example Usage: + +**1. Create a user message** ```python -from a2a.helpers import ( - display_agent_card, # print a human-readable summary of an AgentCard to stdout - get_artifact_text, # join all text parts of an Artifact into a single string (delimiter='\n') - get_message_text, # join all text parts of a Message into a single string (delimiter='\n') - get_stream_response_text, # extract text from a StreamResponse proto message - get_text_parts, # return a list of raw text strings from a sequence of Parts (skips non-text parts) - new_artifact, # create an Artifact from a list of Parts, name, optional description and artifact_id - new_message, # create a Message from a list of Parts with role (default ROLE_AGENT), optional task_id/context_id - new_task, # create a Task with explicit task_id, context_id, and state - new_task_from_user_message, # create a TASK_STATE_SUBMITTED Task from a user Message; raises if role != ROLE_USER or parts are empty - new_text_artifact, # create an Artifact with a single text Part, name, optional description and artifact_id - new_text_artifact_update_event, # create a TaskArtifactUpdateEvent with a text artifact - new_text_message, # create a Message with a single text Part; role defaults to ROLE_AGENT - new_text_status_update_event, # create a TaskStatusUpdateEvent with a text message -) +from a2a.helpers import new_text_message +from a2a.types import Role + +# Create a user message +user_message = new_text_message("What's the weather?", role=Role.ROLE_USER) + +# Create an agent response message +response_message = new_text_message("It is sunny today!") +``` + +**2. Extract the text out of a message** + +```python +from a2a.helpers import get_message_text + +# Get text from a message +text = get_message_text(response_message) +print(text) ``` --- ## 10. Summary of Key Changes in v1.0 -- **Standardisation to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `kebab-case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification. +- **Migration to Protobuf** — Core types have migrated from Pydantic models to Protobuf-based classes. Protobuf objects do not support arbitrary attribute assignment. Use `MessageToDict` from `google.protobuf.json_format` to convert objects to dictionaries, and `HasField('field_name')` to check for optional fields. +- **Standardisation to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `snake_case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification. - **`AgentCard`** — Significantly restructured to support multiple transport interfaces. - **`AgentInterface`** — The top-level `url` field is replaced by `supported_interfaces`, a list of `AgentInterface` objects. Each entry describes a single transport endpoint carrying `protocol_binding`, `protocol_version`, and `url`. - **Input and output modes** — `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed and now live directly on `AgentCard` as `default_input_modes` and `default_output_modes`. Individual skills can override these with their own `input_modes` and `output_modes`. From 93e6d745a88ad2aa18d8186e07990a5728a8a105 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 21 Apr 2026 13:56:02 +0200 Subject: [PATCH 02/16] docs: update v1.0 migration guide --- docs/migrations/v1_0/README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index fb08a813f..56e100306 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -230,7 +230,7 @@ request_handler = DefaultRequestHandler( ## 4. Server: Application Setup -The wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) are now removed. The Server setup now uses Starlette route factory functions directly, giving you full control over the routing. +The application wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) are now removed. The Server setup now uses Starlette route factory functions directly, giving you better control over the routing, middleware, authentication, logging and other aspects of the server. **Before (v0.3):** ```python @@ -248,19 +248,28 @@ uvicorn.run(server.build(), host=host, port=port) ``` **After (v1.0):** + +Define routes for each supported transport as per AgentCard. + ```python from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes -from starlette.applications import Starlette -import uvicorn - # Define routes for transports as per AgentCard routes = [] +# A2A Agent Card routes routes.extend(create_agent_card_routes(agent_card)) +# JSON-RPC routes routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/api/v1/jsonrpc/')) -# Optional: Add routes for other transports +# Optional: Add routes for REST/HTTP transports # routes.extend(create_rest_routes(request_handler, path_prefix='/api/v1/rest/')) +``` + +Add the routes to the application: + +```python +from starlette.applications import Starlette +import uvicorn # Create application using routes app = Starlette(routes=routes) @@ -269,6 +278,19 @@ app = Starlette(routes=routes) uvicorn.run(app, host=host, port=port) ``` +If you prefer FastAPI for your server application: + +```python +from fastapi import FastAPI +import uvicorn + +# Create application using routes +app = FastAPI(routes=routes) + +# Start the server +uvicorn.run(app, host=host, port=port) +``` + > **Example**: [`a2a-mcp-without-framework/server/__main__.py` in PR #509](https://github.com/a2aproject/a2a-samples/pull/509/files#diff-d15d39ae64c3d4e3a36cc6fb442302caf4e32a6dbd858792e7a4bed180a625ac) --- @@ -407,7 +429,7 @@ To improve the developer experience, we have consolidated helper functions into Example Usage: -**1. Create a user message** +**1. Create text based message** ```python from a2a.helpers import new_text_message From affa5a8fa5209047cc5e931520a804f866d8ac59 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 21 Apr 2026 14:07:06 +0200 Subject: [PATCH 03/16] docs: update Readme --- docs/migrations/v1_0/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 56e100306..94f7ada4a 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -62,7 +62,7 @@ Types have migrated from Pydantic models to Protobuf-based classes. ### Enum values: `snake_case` → `SCREAMING_SNAKE_CASE` -All the enum values are now [standardised](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format. +All the enum values are now [standardized](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format. This affects every enum in the SDK: `TaskState`, `Role`. @@ -354,7 +354,7 @@ client = await create_client(agent_card) ## 7. Client: Send Message -The `BaseClient.send_message()` return type is standardised from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`. +The `BaseClient.send_message()` return type is standardized from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`. Each `StreamResponse` yields exactly one of: (`task`, `message`, `status_update`, or `artifact_update`). Use `HasField()` to check which field is set. @@ -409,7 +409,7 @@ config = ClientConfig( ## 9. Helper Utilities -To improve the developer experience, we have consolidated helper functions into a single import. In v0.3, these helper functions were scattered across different modules; In v1.0, they are all available under `a2a.helpers`. +To improve the developer experience, we have consolidated helper functions into a single import. In v0.3, these helper functions were scattered across different modules. In v1.0, they are all available under `a2a.helpers`. | Helper Function | Description | |---|---| @@ -457,7 +457,7 @@ print(text) ## 10. Summary of Key Changes in v1.0 - **Migration to Protobuf** — Core types have migrated from Pydantic models to Protobuf-based classes. Protobuf objects do not support arbitrary attribute assignment. Use `MessageToDict` from `google.protobuf.json_format` to convert objects to dictionaries, and `HasField('field_name')` to check for optional fields. -- **Standardisation to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `snake_case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification. +- **Standardization to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `snake_case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification. - **`AgentCard`** — Significantly restructured to support multiple transport interfaces. - **`AgentInterface`** — The top-level `url` field is replaced by `supported_interfaces`, a list of `AgentInterface` objects. Each entry describes a single transport endpoint carrying `protocol_binding`, `protocol_version`, and `url`. - **Input and output modes** — `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed and now live directly on `AgentCard` as `default_input_modes` and `default_output_modes`. Individual skills can override these with their own `input_modes` and `output_modes`. @@ -486,4 +486,3 @@ uv run python samples/cli.py Then type a message like `hello` and press Enter. See [`samples/README.md`](../../../samples/README.md) for full details. For more examples see the [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/python). - From 58eb30df7b243c62937c0908ab85ab92b787b0f8 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 21 Apr 2026 14:23:50 +0200 Subject: [PATCH 04/16] docs: update v1.0 migration guide to clarify enum mappings and AgentCard field changes --- docs/migrations/v1_0/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 94f7ada4a..59e18614b 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -68,7 +68,6 @@ This affects every enum in the SDK: `TaskState`, `Role`. | Enum | v0.3 | v1.0 | |---|---|---| -| `TaskState` | *(no equivalent — protobuf default)* | `TaskState.TASK_STATE_UNSPECIFIED` | | `TaskState` | `TaskState.submitted` | `TaskState.TASK_STATE_SUBMITTED` | | `TaskState` | `TaskState.working` | `TaskState.TASK_STATE_WORKING` | | `TaskState` | `TaskState.completed` | `TaskState.TASK_STATE_COMPLETED` | @@ -77,10 +76,11 @@ This affects every enum in the SDK: `TaskState`, `Role`. | `TaskState` | `TaskState.input_required` | `TaskState.TASK_STATE_INPUT_REQUIRED` | | `TaskState` | `TaskState.auth_required` | `TaskState.TASK_STATE_AUTH_REQUIRED` | | `TaskState` | `TaskState.rejected` | `TaskState.TASK_STATE_REJECTED` | +| `TaskState` | | `TaskState.TASK_STATE_UNSPECIFIED` (new) | ||| -| `Role` | *(no equivalent — protobuf default)* | `Role.ROLE_UNSPECIFIED` | | `Role` | `Role.user` | `Role.ROLE_USER` | | `Role` | `Role.agent` | `Role.ROLE_AGENT` | +| `Role` | | `Role.ROLE_UNSPECIFIED` (new) | > **Example**: [`a2a-mcp-without-framework/server/agent_executor.py` in PR #509](https://github.com/a2aproject/a2a-samples/pull/509/changes#diff-1f9b098f9f82ee40666ee61db56dc2246281423c445bcf017079c53a0a05954f) @@ -88,7 +88,7 @@ This affects every enum in the SDK: `TaskState`, `Role`. Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, `Part` is a single unified message — set the content type directly on it and the wrapper types are gone entirely. -Key differences: +Key changes: - `Part(TextPart(text=...))` → `Part(text=...)` (flat union field) - `Role.user` → `Role.ROLE_USER`, `Role.agent` → `Role.ROLE_AGENT` @@ -115,7 +115,6 @@ from a2a.types import Role # Use the helper function to create `Hello` message message = new_text_message(text="Hello", role=Role.ROLE_USER) - ``` Without helper utils, you can still construct directly @@ -137,12 +136,12 @@ message = Message( ### AgentCard Structure Key changes: -- Added `AgentInterface` class to support multiple transport bindings via the `supported_interfaces` field in AgentCard. -- The `url` parameter in `AgentCapabilities` is removed and is now part of `AgentInterface`. +- Added `AgentInterface` class to support multiple transport bindings via the newly added `supported_interfaces` field in AgentCard. +- The `url` parameter in `AgentCard` is removed and is now part of `AgentInterface`. - Accepted values for `AgentInterface.protocol_binding`: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'` - The `AgentCard.capabilities` field is renamed to `AgentCard.agent_capabilities`. - The `AgentCard.supports_authenticated_extended_card` field is renamed to `AgentCapabilities.extended_agent_card`. -- The `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` fields are removed; use `AgentCard.default_input_modes` / `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` / `AgentSkill.output_modes` for per-skill overrides. +- The `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` fields are removed; use `AgentCard.default_input_modes` and `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` and `AgentSkill.output_modes` for per-skill overrides. - The `examples` parameter in `AgentCard` is removed and is now part of `AgentSkill`. **Before (v0.3):** From 0bfa2e3c1dcd5f47f7142cece300f77e38ae7273 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 21 Apr 2026 14:27:25 +0200 Subject: [PATCH 05/16] docs: update TaskState and Role migration table --- docs/migrations/v1_0/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 59e18614b..c135822a4 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -76,11 +76,11 @@ This affects every enum in the SDK: `TaskState`, `Role`. | `TaskState` | `TaskState.input_required` | `TaskState.TASK_STATE_INPUT_REQUIRED` | | `TaskState` | `TaskState.auth_required` | `TaskState.TASK_STATE_AUTH_REQUIRED` | | `TaskState` | `TaskState.rejected` | `TaskState.TASK_STATE_REJECTED` | -| `TaskState` | | `TaskState.TASK_STATE_UNSPECIFIED` (new) | +| `TaskState` | | 🆕 `TaskState.TASK_STATE_UNSPECIFIED` | ||| | `Role` | `Role.user` | `Role.ROLE_USER` | | `Role` | `Role.agent` | `Role.ROLE_AGENT` | -| `Role` | | `Role.ROLE_UNSPECIFIED` (new) | +| `Role` | | 🆕 `Role.ROLE_UNSPECIFIED` | > **Example**: [`a2a-mcp-without-framework/server/agent_executor.py` in PR #509](https://github.com/a2aproject/a2a-samples/pull/509/changes#diff-1f9b098f9f82ee40666ee61db56dc2246281423c445bcf017079c53a0a05954f) From be8d9c68d3c92bb212634eddf7c3b4fa5ddd1c7a Mon Sep 17 00:00:00 2001 From: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:50:38 +0200 Subject: [PATCH 06/16] Fix link reference in README for v1.0 migration --- docs/migrations/v1_0/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index c135822a4..a5733c8e2 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -107,7 +107,7 @@ message = Message( **After (v1.0):** -Using [A2A helper utilities](#helper-utilities) +Using [A2A helper utilities](#9-helper-utilities) ```python from a2a.helpers import new_text_message From c9f7cf96702e7fba94b1ff311e0f58d424b242c5 Mon Sep 17 00:00:00 2001 From: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:54:05 +0200 Subject: [PATCH 07/16] Update README with link to new types implementation --- docs/migrations/v1_0/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index a5733c8e2..181990c6d 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -57,7 +57,7 @@ pip install --upgrade a2a-sdk ## 2. Types -Types have migrated from Pydantic models to Protobuf-based classes. +[Types](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/types/a2a_pb2.pyi) have migrated from Pydantic models to Protobuf-based classes. ### Enum values: `snake_case` → `SCREAMING_SNAKE_CASE` From 5180e4cb89655d86c1bd27f8f641fe8e605a587f Mon Sep 17 00:00:00 2001 From: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:11:53 +0200 Subject: [PATCH 08/16] Update README with details on types migration Expanded explanation of types migration to include alignment with A2A spec and ProtoJSON standard. --- docs/migrations/v1_0/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 181990c6d..3e3c61458 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -57,7 +57,7 @@ pip install --upgrade a2a-sdk ## 2. Types -[Types](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/types/a2a_pb2.pyi) have migrated from Pydantic models to Protobuf-based classes. +[Types](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/types/a2a_pb2.pyi) have migrated from Pydantic models to Protobuf-based classes to align with the A2A spec's proto-first design and adopt ProtoJSON as the canonical JSON serialization standard, ensuring consistent cross-implementation interoperability. ### Enum values: `snake_case` → `SCREAMING_SNAKE_CASE` From b8d01e876045308edf21e2db2047307de1a765a5 Mon Sep 17 00:00:00 2001 From: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:20:38 +0200 Subject: [PATCH 09/16] Update README to clarify message construction in v1.0 --- docs/migrations/v1_0/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 3e3c61458..d81b64b7b 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -86,7 +86,7 @@ This affects every enum in the SDK: `TaskState`, `Role`. ### Message and Part construction -Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, `Part` is a single unified message — set the content type directly on it and the wrapper types are gone entirely. +Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and content types are set directly on the `Part`. Key changes: - `Part(TextPart(text=...))` → `Part(text=...)` (flat union field) From b70018ebd26a4e2a3859fcb35d0e7390d495eab8 Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 08:36:27 +0000 Subject: [PATCH 10/16] docs: expand Part construction examples for all part types in v1.0 migration guide --- docs/migrations/v1_0/README.md | 89 +++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index d81b64b7b..d86dabfca 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -86,20 +86,50 @@ This affects every enum in the SDK: `TaskState`, `Role`. ### Message and Part construction -Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and content types are set directly on the `Part`. +Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and all content fields are set directly on the unified `Part` message. -Key changes: -- `Part(TextPart(text=...))` → `Part(text=...)` (flat union field) -- `Role.user` → `Role.ROLE_USER`, `Role.agent` → `Role.ROLE_AGENT` +| Part type | v0.3 | v1.0 | +|---|---|---| +| Text | `Part(TextPart(text=...))` | `Part(text=...)` | +| File (bytes) | `Part(FilePart(file=FileWithBytes(bytes=..., mime_type=..., name=...)))` | `Part(raw=..., media_type=..., filename=...)` | +| File (URI) | `Part(FilePart(file=FileWithUri(uri=..., mime_type=..., name=...)))` | `Part(url=..., media_type=..., filename=...)` | +| Structured data | `Part(DataPart(data={...}))` | `Part(data=json_format.ParseDict({...}, Value()))` | + +> **Note on file bytes**: In v0.3 `FileWithBytes.bytes` was a **base64-encoded string**. In v1.0 `Part.raw` is raw **`bytes`** — no base64 encoding needed. + +> **Note on structured data**: In v0.3 `DataPart.data` was a plain `dict`. In v1.0 `Part.data` is a `google.protobuf.Value`, so use `json_format.ParseDict` to convert from a Python dict. **Before (v0.3):** ```python -from a2a.types import Message, Part, Role, TextPart +import base64 from uuid import uuid4 +from a2a.types import Message, Part, Role, TextPart, FilePart, DataPart, FileWithBytes, FileWithUri + +# Text part +text_part = Part(TextPart(text="What's the weather in Paris?")) + +# File part — base64-encoded bytes (e.g. an image) +with open("photo.png", "rb") as f: + image_b64 = base64.b64encode(f.read()).decode() +file_bytes_part = Part(FilePart(file=FileWithBytes( + bytes=image_b64, + mime_type="image/png", + name="photo.png", +))) + +# File part — URI pointing to a remote file +file_uri_part = Part(FilePart(file=FileWithUri( + uri="https://example.com/report.pdf", + mime_type="application/pdf", + name="report.pdf", +))) + +# Data part — structured JSON payload +data_part = Part(DataPart(data={"city": "Paris", "temperature_c": 18})) message = Message( role=Role.user, - parts=[Part(TextPart(text="Hello"))], + parts=[text_part, file_bytes_part, file_uri_part, data_part], message_id=uuid4().hex, task_id=uuid4().hex, ) @@ -107,30 +137,53 @@ message = Message( **After (v1.0):** -Using [A2A helper utilities](#9-helper-utilities) - ```python -from a2a.helpers import new_text_message -from a2a.types import Role +from uuid import uuid4 +from google.protobuf import json_format +from google.protobuf.struct_pb2 import Value +from a2a.types import Message, Part, Role -# Use the helper function to create `Hello` message -message = new_text_message(text="Hello", role=Role.ROLE_USER) -``` +# Text part +text_part = Part(text="What's the weather in Paris?") -Without helper utils, you can still construct directly +# File part — raw bytes (e.g. an image); no base64 encoding required +with open("photo.png", "rb") as f: + image_bytes = f.read() +file_bytes_part = Part( + raw=image_bytes, + media_type="image/png", + filename="photo.png", +) -```python -from a2a.types import Message, Part, Role -from uuid import uuid4 +# File part — URI pointing to a remote file +file_uri_part = Part( + url="https://example.com/report.pdf", + media_type="application/pdf", + filename="report.pdf", +) + +# Data part — use json_format.ParseDict to convert a Python dict to a protobuf Value +data_part = Part( + data=json_format.ParseDict({"city": "Paris", "temperature_c": 18}, Value()), +) message = Message( role=Role.ROLE_USER, - parts=[Part(text="Hello")], + parts=[text_part, file_bytes_part, file_uri_part, data_part], message_id=uuid4().hex, task_id=uuid4().hex, ) ``` +For text-only messages, use the [A2A helper utilities](#9-helper-utilities) to reduce boilerplate: + +```python +from a2a.helpers import new_text_message +from a2a.types import Role + +message = new_text_message(text="What's the weather in Paris?", role=Role.ROLE_USER) +``` + > **Example**: [`helloworld/test_client.py` in PR #474](https://github.com/a2aproject/a2a-samples/pull/474/files#diff-f62c07d3b00364a3100b7effb3e2a1cca0624277d3e40da1bdb07bb46b6a8cef) ### AgentCard Structure From 05e8272db6afa9bba3591ee93044f906b600afdd Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 08:37:22 +0000 Subject: [PATCH 11/16] Revert "docs: expand Part construction examples for all part types in v1.0 migration guide" This reverts commit b70018ebd26a4e2a3859fcb35d0e7390d495eab8. --- docs/migrations/v1_0/README.md | 89 +++++++--------------------------- 1 file changed, 18 insertions(+), 71 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index d86dabfca..d81b64b7b 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -86,50 +86,20 @@ This affects every enum in the SDK: `TaskState`, `Role`. ### Message and Part construction -Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and all content fields are set directly on the unified `Part` message. +Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and content types are set directly on the `Part`. -| Part type | v0.3 | v1.0 | -|---|---|---| -| Text | `Part(TextPart(text=...))` | `Part(text=...)` | -| File (bytes) | `Part(FilePart(file=FileWithBytes(bytes=..., mime_type=..., name=...)))` | `Part(raw=..., media_type=..., filename=...)` | -| File (URI) | `Part(FilePart(file=FileWithUri(uri=..., mime_type=..., name=...)))` | `Part(url=..., media_type=..., filename=...)` | -| Structured data | `Part(DataPart(data={...}))` | `Part(data=json_format.ParseDict({...}, Value()))` | - -> **Note on file bytes**: In v0.3 `FileWithBytes.bytes` was a **base64-encoded string**. In v1.0 `Part.raw` is raw **`bytes`** — no base64 encoding needed. - -> **Note on structured data**: In v0.3 `DataPart.data` was a plain `dict`. In v1.0 `Part.data` is a `google.protobuf.Value`, so use `json_format.ParseDict` to convert from a Python dict. +Key changes: +- `Part(TextPart(text=...))` → `Part(text=...)` (flat union field) +- `Role.user` → `Role.ROLE_USER`, `Role.agent` → `Role.ROLE_AGENT` **Before (v0.3):** ```python -import base64 +from a2a.types import Message, Part, Role, TextPart from uuid import uuid4 -from a2a.types import Message, Part, Role, TextPart, FilePart, DataPart, FileWithBytes, FileWithUri - -# Text part -text_part = Part(TextPart(text="What's the weather in Paris?")) - -# File part — base64-encoded bytes (e.g. an image) -with open("photo.png", "rb") as f: - image_b64 = base64.b64encode(f.read()).decode() -file_bytes_part = Part(FilePart(file=FileWithBytes( - bytes=image_b64, - mime_type="image/png", - name="photo.png", -))) - -# File part — URI pointing to a remote file -file_uri_part = Part(FilePart(file=FileWithUri( - uri="https://example.com/report.pdf", - mime_type="application/pdf", - name="report.pdf", -))) - -# Data part — structured JSON payload -data_part = Part(DataPart(data={"city": "Paris", "temperature_c": 18})) message = Message( role=Role.user, - parts=[text_part, file_bytes_part, file_uri_part, data_part], + parts=[Part(TextPart(text="Hello"))], message_id=uuid4().hex, task_id=uuid4().hex, ) @@ -137,53 +107,30 @@ message = Message( **After (v1.0):** -```python -from uuid import uuid4 -from google.protobuf import json_format -from google.protobuf.struct_pb2 import Value -from a2a.types import Message, Part, Role +Using [A2A helper utilities](#9-helper-utilities) -# Text part -text_part = Part(text="What's the weather in Paris?") +```python +from a2a.helpers import new_text_message +from a2a.types import Role -# File part — raw bytes (e.g. an image); no base64 encoding required -with open("photo.png", "rb") as f: - image_bytes = f.read() -file_bytes_part = Part( - raw=image_bytes, - media_type="image/png", - filename="photo.png", -) +# Use the helper function to create `Hello` message +message = new_text_message(text="Hello", role=Role.ROLE_USER) +``` -# File part — URI pointing to a remote file -file_uri_part = Part( - url="https://example.com/report.pdf", - media_type="application/pdf", - filename="report.pdf", -) +Without helper utils, you can still construct directly -# Data part — use json_format.ParseDict to convert a Python dict to a protobuf Value -data_part = Part( - data=json_format.ParseDict({"city": "Paris", "temperature_c": 18}, Value()), -) +```python +from a2a.types import Message, Part, Role +from uuid import uuid4 message = Message( role=Role.ROLE_USER, - parts=[text_part, file_bytes_part, file_uri_part, data_part], + parts=[Part(text="Hello")], message_id=uuid4().hex, task_id=uuid4().hex, ) ``` -For text-only messages, use the [A2A helper utilities](#9-helper-utilities) to reduce boilerplate: - -```python -from a2a.helpers import new_text_message -from a2a.types import Role - -message = new_text_message(text="What's the weather in Paris?", role=Role.ROLE_USER) -``` - > **Example**: [`helloworld/test_client.py` in PR #474](https://github.com/a2aproject/a2a-samples/pull/474/files#diff-f62c07d3b00364a3100b7effb3e2a1cca0624277d3e40da1bdb07bb46b6a8cef) ### AgentCard Structure From 2a0ea562e356c85e910ecbd7bb4c310964ca671c Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 08:58:09 +0000 Subject: [PATCH 12/16] update Part construction --- docs/migrations/v1_0/README.md | 89 +++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index d81b64b7b..5c3c11975 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -86,20 +86,50 @@ This affects every enum in the SDK: `TaskState`, `Role`. ### Message and Part construction -Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and content types are set directly on the `Part`. +Constructing messages is simplified in v1.0. The old API required wrapping content in an intermediate type (`TextPart`, `FilePart`, `DataPart`) before placing it inside a `Part`. In v1.0, the wrapper types are removed and all content fields are set directly on the unified `Part` message. -Key changes: -- `Part(TextPart(text=...))` → `Part(text=...)` (flat union field) -- `Role.user` → `Role.ROLE_USER`, `Role.agent` → `Role.ROLE_AGENT` +| Part type | v0.3 | v1.0 | +|---|---|---| +| Text | `Part(TextPart(text=..., ...))` | `Part(text=..., ...)` | +| File (bytes) | `Part(FilePart(file=FileWithBytes(bytes=..., ...)))` | `Part(raw=..., ...)` | +| File (URI) | `Part(FilePart(file=FileWithUri(uri=..., ...)))` | `Part(url=..., ...)` | +| Structured data | `Part(DataPart(data=..., ...))` | `Part(data=..., ...))` | + +> **Note on file bytes**: In v0.3 `FileWithBytes.bytes` was a **base64-encoded string**. In v1.0 `Part.raw` is raw **`bytes`** — no base64 encoding needed. + +> **Note on structured data**: In v0.3 `DataPart.data` was a plain `dict`. In v1.0 `Part.data` is a `google.protobuf.Value`, so use `ParseDict` to convert from a Python dict. **Before (v0.3):** ```python -from a2a.types import Message, Part, Role, TextPart +import base64 from uuid import uuid4 +from a2a.types import Message, Part, Role, TextPart, FilePart, DataPart, FileWithBytes, FileWithUri + +# Text part +text_part = Part(TextPart(text="What's the weather in Warsaw?")) + +# File part — base64-encoded bytes (e.g. an image) +with open("photo.png", "rb") as f: + image_b64 = base64.b64encode(f.read()).decode() +file_bytes_part = Part(FilePart(file=FileWithBytes( + bytes=image_b64, + mime_type="image/png", + name="photo.png", +))) + +# File part — URI pointing to a remote file +file_uri_part = Part(FilePart(file=FileWithUri( + uri="https://example.com/report.pdf", + mime_type="application/pdf", + name="report.pdf", +))) + +# Data part — structured JSON payload +data_part = Part(DataPart(data={"city": "Warsaw", "temperature_c": 18})) message = Message( role=Role.user, - parts=[Part(TextPart(text="Hello"))], + parts=[text_part, file_bytes_part, file_uri_part, data_part], message_id=uuid4().hex, task_id=uuid4().hex, ) @@ -107,30 +137,53 @@ message = Message( **After (v1.0):** -Using [A2A helper utilities](#9-helper-utilities) - ```python -from a2a.helpers import new_text_message -from a2a.types import Role +from uuid import uuid4 +from google.protobuf.json_format import ParseDict +from google.protobuf.struct_pb2 import Value +from a2a.types import Message, Part, Role -# Use the helper function to create `Hello` message -message = new_text_message(text="Hello", role=Role.ROLE_USER) -``` +# Text part +text_part = Part(text="What's the weather in Warsaw?") -Without helper utils, you can still construct directly +# File part — raw bytes (e.g. an image); no base64 encoding required +with open("photo.png", "rb") as f: + image_bytes = f.read() +file_bytes_part = Part( + raw=image_bytes, + media_type="image/png", + filename="photo.png", +) -```python -from a2a.types import Message, Part, Role -from uuid import uuid4 +# File part — URI pointing to a remote file +file_uri_part = Part( + url="https://example.com/report.pdf", + media_type="application/pdf", + filename="report.pdf", +) + +# Data part — use ParseDict to convert a Python dict to a protobuf Value +data_part = Part( + data=.ParseDict({"city": "Warsaw", "temperature_c": 18}, Value()), +) message = Message( role=Role.ROLE_USER, - parts=[Part(text="Hello")], + parts=[text_part, file_bytes_part, file_uri_part, data_part], message_id=uuid4().hex, task_id=uuid4().hex, ) ``` +For text-only messages, use the [A2A helper utilities](#9-helper-utilities) to reduce boilerplate: + +```python +from a2a.helpers import new_text_message +from a2a.types import Role + +message = new_text_message(text="What's the weather in Warsaw?", role=Role.ROLE_USER) +``` + > **Example**: [`helloworld/test_client.py` in PR #474](https://github.com/a2aproject/a2a-samples/pull/474/files#diff-f62c07d3b00364a3100b7effb3e2a1cca0624277d3e40da1bdb07bb46b6a8cef) ### AgentCard Structure From 6df414e2754723e4404ae64f4bf4ac7156710397 Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 09:02:55 +0000 Subject: [PATCH 13/16] fix --- docs/migrations/v1_0/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 5c3c11975..c2c836991 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -93,7 +93,7 @@ Constructing messages is simplified in v1.0. The old API required wrapping conte | Text | `Part(TextPart(text=..., ...))` | `Part(text=..., ...)` | | File (bytes) | `Part(FilePart(file=FileWithBytes(bytes=..., ...)))` | `Part(raw=..., ...)` | | File (URI) | `Part(FilePart(file=FileWithUri(uri=..., ...)))` | `Part(url=..., ...)` | -| Structured data | `Part(DataPart(data=..., ...))` | `Part(data=..., ...))` | +| Structured data | `Part(DataPart(data=..., ...))` | `Part(data=..., ...)` | > **Note on file bytes**: In v0.3 `FileWithBytes.bytes` was a **base64-encoded string**. In v1.0 `Part.raw` is raw **`bytes`** — no base64 encoding needed. @@ -164,7 +164,7 @@ file_uri_part = Part( # Data part — use ParseDict to convert a Python dict to a protobuf Value data_part = Part( - data=.ParseDict({"city": "Warsaw", "temperature_c": 18}, Value()), + data=ParseDict({"city": "Warsaw", "temperature_c": 18}, Value()), ) message = Message( @@ -192,7 +192,6 @@ Key changes: - Added `AgentInterface` class to support multiple transport bindings via the newly added `supported_interfaces` field in AgentCard. - The `url` parameter in `AgentCard` is removed and is now part of `AgentInterface`. - Accepted values for `AgentInterface.protocol_binding`: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'` -- The `AgentCard.capabilities` field is renamed to `AgentCard.agent_capabilities`. - The `AgentCard.supports_authenticated_extended_card` field is renamed to `AgentCapabilities.extended_agent_card`. - The `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` fields are removed; use `AgentCard.default_input_modes` and `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` and `AgentSkill.output_modes` for per-skill overrides. - The `examples` parameter in `AgentCard` is removed and is now part of `AgentSkill`. From 7cebbc5d13617b5e59831db6820cbe2268f34352 Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 09:16:56 +0000 Subject: [PATCH 14/16] update AgentCard sample --- docs/migrations/v1_0/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index c2c836991..35b92160b 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -201,10 +201,10 @@ Key changes: from a2a.types import AgentCard, AgentCapabilities, AgentSkill agent_card = AgentCard( - name='My Agent', - description='...', + name='Hello World Agent', + description='Returns Hello, World!', url='http://localhost:9999/', - version='1.0.0', + version='0.0.1', default_input_modes=['text/plain'], default_output_modes=['text/plain'], supports_authenticated_extended_card=True, @@ -214,7 +214,7 @@ agent_card = AgentCard( streaming=True, ), skills=[skill], - examples=['example'], + examples=['Hello, World!'], ) ``` @@ -223,8 +223,8 @@ agent_card = AgentCard( from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill agent_card = AgentCard( - name='My Agent', - description='...', + name='Hello World Agent', + description='Returns Hello, World!', supported_interfaces=[ # JSON-RPC AgentInterface( @@ -237,7 +237,7 @@ agent_card = AgentCard( url='http://localhost:50051/a2a/grpc/', ) ], - version='1.0.0', + version='0.0.1', default_input_modes=['text/plain'], default_output_modes=['text/plain'], capabilities=AgentCapabilities( From 942c850dec00747612c019853a7a5962cd41b30f Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 10:04:11 +0000 Subject: [PATCH 15/16] added AgentSkill --- docs/migrations/v1_0/README.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 35b92160b..744136b70 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -57,12 +57,12 @@ pip install --upgrade a2a-sdk ## 2. Types -[Types](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/types/a2a_pb2.pyi) have migrated from Pydantic models to Protobuf-based classes to align with the A2A spec's proto-first design and adopt ProtoJSON as the canonical JSON serialization standard, ensuring consistent cross-implementation interoperability. +[Types](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/types/a2a_pb2.pyi) have migrated from Pydantic models to Protobuf-based classes to align with the A2A spec's proto-first design and to adopt ProtoJSON as the canonical JSON serialization standard, ensuring consistent cross-implementation interoperability. ### Enum values: `snake_case` → `SCREAMING_SNAKE_CASE` -All the enum values are now [standardized](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format. +All enum values are now [standardized](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format. This affects every enum in the SDK: `TaskState`, `Role`. @@ -95,9 +95,9 @@ Constructing messages is simplified in v1.0. The old API required wrapping conte | File (URI) | `Part(FilePart(file=FileWithUri(uri=..., ...)))` | `Part(url=..., ...)` | | Structured data | `Part(DataPart(data=..., ...))` | `Part(data=..., ...)` | -> **Note on file bytes**: In v0.3 `FileWithBytes.bytes` was a **base64-encoded string**. In v1.0 `Part.raw` is raw **`bytes`** — no base64 encoding needed. - -> **Note on structured data**: In v0.3 `DataPart.data` was a plain `dict`. In v1.0 `Part.data` is a `google.protobuf.Value`, so use `ParseDict` to convert from a Python dict. +**Note**: +* When using `File (bytes)` in v1.0, the data serialisatinon (via base64 encoding) is not required as A2A now uses Protobuf that automatically does it for you. +* In v1.0, `Part.DataPart.data` is renamed to `Part.data` and is of type `google.protobuf.Value`. Use `ParseDict` to convert a Python dict into a suitable value. Check the examples below for better understanding. **Before (v0.3):** ```python @@ -189,7 +189,7 @@ message = new_text_message(text="What's the weather in Warsaw?", role=Role.ROLE_ ### AgentCard Structure Key changes: -- Added `AgentInterface` class to support multiple transport bindings via the newly added `supported_interfaces` field in AgentCard. +- Added an `AgentInterface` class to support multiple transport bindings via the newly added `supported_interfaces` field in AgentCard. - The `url` parameter in `AgentCard` is removed and is now part of `AgentInterface`. - Accepted values for `AgentInterface.protocol_binding`: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'` - The `AgentCard.supports_authenticated_extended_card` field is renamed to `AgentCapabilities.extended_agent_card`. @@ -200,6 +200,16 @@ Key changes: ```python from a2a.types import AgentCard, AgentCapabilities, 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'], +) + agent_card = AgentCard( name='Hello World Agent', description='Returns Hello, World!', @@ -222,6 +232,16 @@ agent_card = AgentCard( ```python 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', description='Returns Hello, World!', From 4803e3562a105c383af9dc278b18fd09f5928bd6 Mon Sep 17 00:00:00 2001 From: sokoliva Date: Wed, 22 Apr 2026 10:09:09 +0000 Subject: [PATCH 16/16] grammar fix --- docs/migrations/v1_0/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/migrations/v1_0/README.md b/docs/migrations/v1_0/README.md index 744136b70..b0d71c8cc 100644 --- a/docs/migrations/v1_0/README.md +++ b/docs/migrations/v1_0/README.md @@ -31,7 +31,7 @@ This documentation details the technical upgrades and architectural modification ## 1. Update Dependencies -(UV users) To upgrade to the latest version of the `a2a-sdk`, update the dependencies section in your `pyproject.toml` file. +For UV users: To upgrade to the latest version of the `a2a-sdk`, update the dependencies section in your `pyproject.toml` file. | File | Before (`v0.3`) | After (`v1.0`) | |------------------|-----------------------------------|-----------------------------------| @@ -97,7 +97,7 @@ Constructing messages is simplified in v1.0. The old API required wrapping conte **Note**: * When using `File (bytes)` in v1.0, the data serialisatinon (via base64 encoding) is not required as A2A now uses Protobuf that automatically does it for you. -* In v1.0, `Part.DataPart.data` is renamed to `Part.data` and is of type `google.protobuf.Value`. Use `ParseDict` to convert a Python dict into a suitable value. Check the examples below for better understanding. +* In v1.0, `Part.DataPart.data` is renamed to `Part.data` and is of type `google.protobuf.Value`. Use `ParseDict` to convert a Python dict into a suitable value. See the examples below for more details. **Before (v0.3):** ```python @@ -301,7 +301,7 @@ request_handler = DefaultRequestHandler( ## 4. Server: Application Setup -The application wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) are now removed. The Server setup now uses Starlette route factory functions directly, giving you better control over the routing, middleware, authentication, logging and other aspects of the server. +The application wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) have been removed. The server setup now uses Starlette route factory functions directly, giving you better control over the routing, middleware, authentication, logging, and other aspects of the server. **Before (v0.3):** ```python @@ -320,12 +320,12 @@ uvicorn.run(server.build(), host=host, port=port) **After (v1.0):** -Define routes for each supported transport as per AgentCard. +Define routes for each supported transport as defined in the `AgentCard`. ```python from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes -# Define routes for transports as per AgentCard +# Define routes for transports as defined in the AgentCard routes = [] # A2A Agent Card routes routes.extend(create_agent_card_routes(agent_card)) @@ -391,7 +391,7 @@ create_rest_routes(request_handler, enable_v0_3_compat=True) ## 6. Client: Creating a Client -In `v1.0`, use `a2a.client.create_client()` helper function to create a `Client` for the agent. +In `v1.0`, use the `a2a.client.create_client()` helper function to create a `Client` for the agent. **Before (v0.3):** @@ -427,7 +427,7 @@ client = await create_client(agent_card) The `BaseClient.send_message()` return type is standardized from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`. -Each `StreamResponse` yields exactly one of: (`task`, `message`, `status_update`, or `artifact_update`). Use `HasField()` to check which field is set. +Each `StreamResponse` contains exactly one of: `task`, `message`, `status_update`, or `artifact_update`. Use `HasField()` to check which field is set. **Before (v0.3):** @@ -498,9 +498,9 @@ To improve the developer experience, we have consolidated helper functions into | `new_text_message` | Creates a `Message` with a single text `Part`; role defaults to `ROLE_AGENT`. | | `new_text_status_update_event` | Creates a `TaskStatusUpdateEvent` with a text message. | -Example Usage: +**Example usage:** -**1. Create text based message** +**1. Create a text-based message** ```python from a2a.helpers import new_text_message @@ -513,7 +513,7 @@ user_message = new_text_message("What's the weather?", role=Role.ROLE_USER) response_message = new_text_message("It is sunny today!") ``` -**2. Extract the text out of a message** +**2. Extract text from a message** ```python from a2a.helpers import get_message_text @@ -530,9 +530,9 @@ print(text) - **Migration to Protobuf** — Core types have migrated from Pydantic models to Protobuf-based classes. Protobuf objects do not support arbitrary attribute assignment. Use `MessageToDict` from `google.protobuf.json_format` to convert objects to dictionaries, and `HasField('field_name')` to check for optional fields. - **Standardization to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `snake_case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification. - **`AgentCard`** — Significantly restructured to support multiple transport interfaces. - - **`AgentInterface`** — The top-level `url` field is replaced by `supported_interfaces`, a list of `AgentInterface` objects. Each entry describes a single transport endpoint carrying `protocol_binding`, `protocol_version`, and `url`. + - **`AgentInterface`** — The top-level `url` field is replaced by `supported_interfaces`, a list of `AgentInterface` objects. Each entry describes a single transport endpoint with fields for `protocol_binding`, `protocol_version`, and `url`. - **Input and output modes** — `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed and now live directly on `AgentCard` as `default_input_modes` and `default_output_modes`. Individual skills can override these with their own `input_modes` and `output_modes`. -- **Application setup** — The wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) are now removed. Server setup now uses route factory functions `create_jsonrpc_routes()`, `create_rest_routes()`, `create_agent_card_routes()` composed directly into a Starlette or FastAPI app. +- **Application setup** — The wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2ARESTFastApiApplication`) have been removed. Server setup now uses route factory functions — `create_jsonrpc_routes()`, `create_rest_routes()`, and `create_agent_card_routes()` — composed directly into a Starlette or FastAPI app. - **Helper utilities** — A new `a2a.helpers` module consolidates all helper functions under a single import, replacing the scattered `a2a.utils.*` modules and adding new helpers for constructing and reading v1.0 proto types. ---