Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fc31d03
fix: Ensure metadata propagation for `Task` `ToProto` and `FromProto`…
sokoliva Nov 21, 2025
dbc73e8
chore(main): release 0.3.16 (#562)
a2a-bot Nov 21, 2025
53bbf7a
feat(client): allow specifying `history_length` via call-site `Messag…
TadakiAsechi Nov 24, 2025
7e121e0
chore(main): release 0.3.17 (#565)
a2a-bot Nov 24, 2025
0ce239e
fix: return updated `agent_card` in `JsonRpcTransport.get_card()` (#552)
ShishirRmc Nov 24, 2025
213d9f8
chore(main): release 0.3.18 (#567)
a2a-bot Nov 25, 2025
847f18e
fix(jsonrpc, rest): `extensions` support in `get_card` methods in `js…
sokoliva Nov 25, 2025
3bfbea9
chore(main): release 0.3.19 (#568)
a2a-bot Nov 25, 2025
7ea7475
fix: Improve streaming errors handling (#576)
lkawka Dec 3, 2025
174d58d
chore(main): release 0.3.20 (#577)
a2a-bot Dec 3, 2025
5fea21f
docs: Fixing typos (#586)
didier-durand Dec 12, 2025
8a76730
feat: Implement Agent Card Signing and Verification per Spec (#581)
sokoliva Dec 12, 2025
090ca9c
chore: Fixing typos (final round) (#588)
didier-durand Dec 12, 2025
03fa4c2
chore(main): release 0.3.21 (#587)
a2a-bot Dec 12, 2025
04bcafc
feat: Add custom ID generators to SimpleRequestContextBuilder (#594)
chenweiyang0204 Dec 16, 2025
e12ca42
test: adding 2 additional tests to user.py (#595)
didier-durand Dec 16, 2025
3deecc4
test: adding 21 tests for client/card_resolver.py (#592)
didier-durand Dec 16, 2025
6fa6a6c
refactor: Move agent card signature verification into `A2ACardResolve…
sokoliva Dec 16, 2025
86c6759
chore(main): release 0.3.22 (#599)
a2a-bot Dec 16, 2025
df78a94
test: adding 13 tests for id_generator.py (#591)
didier-durand Jan 5, 2026
cb7cdb3
chore(deps): bump the github-actions group across 1 directory with 4 …
dependabot[bot] Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,14 @@ initdb
inmemory
INR
isready
jku
JPY
JSONRPCt
jwk
jwks
JWS
jws
kid
kwarg
langgraph
lifecycles
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
if: github.repository == 'a2aproject/a2a-python'
steps:
- name: Checkout Code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand All @@ -26,7 +26,7 @@ jobs:
run: uv build

- name: Upload distributions
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: release-dists
path: dist/
Expand All @@ -40,7 +40,7 @@ jobs:

steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: release-dists
path: dist/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name: Mark stale issues and pull requests

on:
schedule:
# Scheduled to run at 10.30PM UTC everyday (1530PDT/1430PST)
# Scheduled to run at 10.30PM UTC every day (1530PDT/1430PST)
- cron: "30 22 * * *"
workflow_dispatch:

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
python-version: ['3.10', '3.13']
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up test environment variables
run: |
echo "POSTGRES_TEST_DSN=postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test" >> $GITHUB_ENV
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/update-a2a-types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down Expand Up @@ -42,7 +42,7 @@ jobs:
uv run scripts/grpc_gen_post_processor.py
echo "Buf generate finished."
- name: Create Pull Request with Updates
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.A2A_BOT_PAT }}
committer: a2a-bot <[email protected]>
Expand Down
62 changes: 58 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,58 @@
# Changelog
# Changelog

## [0.3.22](https://github.com/a2aproject/a2a-python/compare/v0.3.21...v0.3.22) (2025-12-16)


### Features

* Add custom ID generators to SimpleRequestContextBuilder ([#594](https://github.com/a2aproject/a2a-python/issues/594)) ([04bcafc](https://github.com/a2aproject/a2a-python/commit/04bcafc737cf426d9975c76e346335ff992363e2))


### Code Refactoring

* Move agent card signature verification into `A2ACardResolver` ([6fa6a6c](https://github.com/a2aproject/a2a-python/commit/6fa6a6cf3875bdf7bfc51fb1a541a3f3e8381dc0))

## [0.3.21](https://github.com/a2aproject/a2a-python/compare/v0.3.20...v0.3.21) (2025-12-12)


### Documentation

* Fixing typos ([#586](https://github.com/a2aproject/a2a-python/issues/586)) ([5fea21f](https://github.com/a2aproject/a2a-python/commit/5fea21fb34ecea55e588eb10139b5d47020a76cb))

## [0.3.20](https://github.com/a2aproject/a2a-python/compare/v0.3.19...v0.3.20) (2025-12-03)


### Bug Fixes

* Improve streaming errors handling ([#576](https://github.com/a2aproject/a2a-python/issues/576)) ([7ea7475](https://github.com/a2aproject/a2a-python/commit/7ea7475091df2ee40d3035ef1bc34ee2f86524ee))

## [0.3.19](https://github.com/a2aproject/a2a-python/compare/v0.3.18...v0.3.19) (2025-11-25)


### Bug Fixes

* **jsonrpc, rest:** `extensions` support in `get_card` methods in `json-rpc` and `rest` transports ([#564](https://github.com/a2aproject/a2a-python/issues/564)) ([847f18e](https://github.com/a2aproject/a2a-python/commit/847f18eff59985f447c39a8e5efde87818b68d15))

## [0.3.18](https://github.com/a2aproject/a2a-python/compare/v0.3.17...v0.3.18) (2025-11-24)


### Bug Fixes

* return updated `agent_card` in `JsonRpcTransport.get_card()` ([#552](https://github.com/a2aproject/a2a-python/issues/552)) ([0ce239e](https://github.com/a2aproject/a2a-python/commit/0ce239e98f67ccbf154f2edcdbcee43f3b080ead))

## [0.3.17](https://github.com/a2aproject/a2a-python/compare/v0.3.16...v0.3.17) (2025-11-24)


### Features

* **client:** allow specifying `history_length` via call-site `MessageSendConfiguration` in `BaseClient.send_message` ([53bbf7a](https://github.com/a2aproject/a2a-python/commit/53bbf7ae3ad58fb0c10b14da05cf07c0a7bd9651))

## [0.3.16](https://github.com/a2aproject/a2a-python/compare/v0.3.15...v0.3.16) (2025-11-21)


### Bug Fixes

* Ensure metadata propagation for `Task` `ToProto` and `FromProto` conversion ([#557](https://github.com/a2aproject/a2a-python/issues/557)) ([fc31d03](https://github.com/a2aproject/a2a-python/commit/fc31d03e8c6acb68660f6d1924262e16933c5d50))

## [0.3.15](https://github.com/a2aproject/a2a-python/compare/v0.3.14...v0.3.15) (2025-11-19)

Expand Down Expand Up @@ -66,7 +120,7 @@
### Bug Fixes

* apply `history_length` for `message/send` requests ([#498](https://github.com/a2aproject/a2a-python/issues/498)) ([a49f94e](https://github.com/a2aproject/a2a-python/commit/a49f94ef23d81b8375e409b1c1e51afaf1da1956))
* **client:** `A2ACardResolver.get_agent_card` will auto-populate with `agent_card_path` when `relative_card_path` is empty ([#508](https://github.com/a2aproject/a2a-python/issues/508)) ([ba24ead](https://github.com/a2aproject/a2a-python/commit/ba24eadb5b6fcd056a008e4cbcef03b3f72a37c3))
* **client:** `A2ACardResolver.get_agent_card` will autopopulate with `agent_card_path` when `relative_card_path` is empty ([#508](https://github.com/a2aproject/a2a-python/issues/508)) ([ba24ead](https://github.com/a2aproject/a2a-python/commit/ba24eadb5b6fcd056a008e4cbcef03b3f72a37c3))


### Documentation
Expand Down Expand Up @@ -403,8 +457,8 @@
* Event consumer should stop on input_required ([#167](https://github.com/a2aproject/a2a-python/issues/167)) ([51c2d8a](https://github.com/a2aproject/a2a-python/commit/51c2d8addf9e89a86a6834e16deb9f4ac0e05cc3))
* Fix Release Version ([#161](https://github.com/a2aproject/a2a-python/issues/161)) ([011d632](https://github.com/a2aproject/a2a-python/commit/011d632b27b201193813ce24cf25e28d1335d18e))
* generate StrEnum types for enums ([#134](https://github.com/a2aproject/a2a-python/issues/134)) ([0c49dab](https://github.com/a2aproject/a2a-python/commit/0c49dabcdb9d62de49fda53d7ce5c691b8c1591c))
* library should released as 0.2.6 ([d8187e8](https://github.com/a2aproject/a2a-python/commit/d8187e812d6ac01caedf61d4edaca522e583d7da))
* remove error types from enqueable events ([#138](https://github.com/a2aproject/a2a-python/issues/138)) ([511992f](https://github.com/a2aproject/a2a-python/commit/511992fe585bd15e956921daeab4046dc4a50a0a))
* library should be released as 0.2.6 ([d8187e8](https://github.com/a2aproject/a2a-python/commit/d8187e812d6ac01caedf61d4edaca522e583d7da))
* remove error types from enqueueable events ([#138](https://github.com/a2aproject/a2a-python/issues/138)) ([511992f](https://github.com/a2aproject/a2a-python/commit/511992fe585bd15e956921daeab4046dc4a50a0a))
* **stream:** don't block event loop in EventQueue ([#151](https://github.com/a2aproject/a2a-python/issues/151)) ([efd9080](https://github.com/a2aproject/a2a-python/commit/efd9080b917c51d6e945572fd123b07f20974a64))
* **task_updater:** fix potential duplicate artifact_id from default v… ([#156](https://github.com/a2aproject/a2a-python/issues/156)) ([1f0a769](https://github.com/a2aproject/a2a-python/commit/1f0a769c1027797b2f252e4c894352f9f78257ca))

Expand Down
2 changes: 1 addition & 1 deletion Gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- uv as package manager

## How to run all tests
1. If dependencies are not installed install them using following command
1. If dependencies are not installed, install them using the following command
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This change adds a comma for better readability, which is a good grammatical correction.

```
uv sync --all-extras
```
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ grpc = ["grpcio>=1.60", "grpcio-tools>=1.60", "grpcio_reflection>=1.7.0"]
telemetry = ["opentelemetry-api>=1.33.0", "opentelemetry-sdk>=1.33.0"]
postgresql = ["sqlalchemy[asyncio,postgresql-asyncpg]>=2.0.0"]
mysql = ["sqlalchemy[asyncio,aiomysql]>=2.0.0"]
signing = ["PyJWT>=2.0.0"]
sqlite = ["sqlalchemy[asyncio,aiosqlite]>=2.0.0"]

sql = ["a2a-sdk[postgresql,mysql,sqlite]"]
Expand All @@ -45,6 +46,7 @@ all = [
"a2a-sdk[encryption]",
"a2a-sdk[grpc]",
"a2a-sdk[telemetry]",
"a2a-sdk[signing]",
]

[project.urls]
Expand Down Expand Up @@ -86,6 +88,7 @@ style = "pep440"
dev = [
"datamodel-code-generator>=0.30.0",
"mypy>=1.15.0",
"PyJWT>=2.0.0",
"pytest>=8.3.5",
"pytest-asyncio>=0.26.0",
"pytest-cov>=6.1.1",
Expand Down
21 changes: 18 additions & 3 deletions src/a2a/client/base_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from collections.abc import AsyncIterator
from collections.abc import AsyncIterator, Callable
from typing import Any

from a2a.client.client import (
Expand Down Expand Up @@ -47,6 +47,7 @@ async def send_message(
self,
request: Message,
*,
configuration: MessageSendConfiguration | None = None,
context: ClientCallContext | None = None,
request_metadata: dict[str, Any] | None = None,
extensions: list[str] | None = None,
Expand All @@ -59,14 +60,15 @@ async def send_message(

Args:
request: The message to send to the agent.
configuration: Optional per-call overrides for message sending behavior.
context: The client call context.
request_metadata: Extensions Metadata attached to the request.
extensions: List of extensions to be activated.

Yields:
An async iterator of `ClientEvent` or a final `Message` response.
"""
config = MessageSendConfiguration(
base_config = MessageSendConfiguration(
accepted_output_modes=self._config.accepted_output_modes,
blocking=not self._config.polling,
push_notification_config=(
Expand All @@ -75,6 +77,15 @@ async def send_message(
else None
),
)
if configuration is not None:
update_data = configuration.model_dump(
exclude_unset=True,
by_alias=False,
)
config = base_config.model_copy(update=update_data)
else:
config = base_config

params = MessageSendParams(
message=request, configuration=config, metadata=request_metadata
)
Expand Down Expand Up @@ -250,6 +261,7 @@ async def get_card(
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> AgentCard:
"""Retrieves the agent's card.

Expand All @@ -259,12 +271,15 @@ async def get_card(
Args:
context: The client call context.
extensions: List of extensions to be activated.
signature_verifier: A callable used to verify the agent card's signatures.

Returns:
The `AgentCard` for the agent.
"""
card = await self._transport.get_card(
context=context, extensions=extensions
context=context,
extensions=extensions,
signature_verifier=signature_verifier,
)
self._card = card
return card
Expand Down
5 changes: 5 additions & 0 deletions src/a2a/client/card_resolver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import logging

from collections.abc import Callable
from typing import Any

import httpx
Expand Down Expand Up @@ -44,6 +45,7 @@ async def get_agent_card(
self,
relative_card_path: str | None = None,
http_kwargs: dict[str, Any] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> AgentCard:
"""Fetches an agent card from a specified path relative to the base_url.

Expand All @@ -56,6 +58,7 @@ async def get_agent_card(
agent card path. Use `'/'` for an empty path.
http_kwargs: Optional dictionary of keyword arguments to pass to the
underlying httpx.get request.
signature_verifier: A callable used to verify the agent card's signatures.

Returns:
An `AgentCard` object representing the agent's capabilities.
Expand Down Expand Up @@ -86,6 +89,8 @@ async def get_agent_card(
agent_card_data,
)
agent_card = AgentCard.model_validate(agent_card_data)
if signature_verifier:
signature_verifier(agent_card)
except httpx.HTTPStatusError as e:
raise A2AClientHTTPError(
e.response.status_code,
Expand Down
1 change: 1 addition & 0 deletions src/a2a/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ async def get_card(
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> AgentCard:
"""Retrieves the agent's card."""

Expand Down
6 changes: 5 additions & 1 deletion src/a2a/client/client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ async def connect( # noqa: PLR0913
resolver_http_kwargs: dict[str, Any] | None = None,
extra_transports: dict[str, TransportProducer] | None = None,
extensions: list[str] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> Client:
"""Convenience method for constructing a client.

Expand Down Expand Up @@ -146,6 +147,7 @@ async def connect( # noqa: PLR0913
extra_transports: Additional transport protocols to enable when
constructing the client.
extensions: List of extensions to be activated.
signature_verifier: A callable used to verify the agent card's signatures.

Returns:
A `Client` object.
Expand All @@ -158,12 +160,14 @@ async def connect( # noqa: PLR0913
card = await resolver.get_agent_card(
relative_card_path=relative_card_path,
http_kwargs=resolver_http_kwargs,
signature_verifier=signature_verifier,
)
else:
resolver = A2ACardResolver(client_config.httpx_client, agent)
card = await resolver.get_agent_card(
relative_card_path=relative_card_path,
http_kwargs=resolver_http_kwargs,
signature_verifier=signature_verifier,
)
else:
card = agent
Expand Down Expand Up @@ -256,7 +260,7 @@ def minimal_agent_card(
"""Generates a minimal card to simplify bootstrapping client creation.

This minimal card is not viable itself to interact with the remote agent.
Instead this is a short hand way to take a known url and transport option
Instead this is a shorthand way to take a known url and transport option
and interact with the get card endpoint of the agent server to get the
correct agent card. This pattern is necessary for gRPC based card access
as typically these servers won't expose a well known path card.
Expand Down
3 changes: 2 additions & 1 deletion src/a2a/client/transports/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Callable

from a2a.client.middleware import ClientCallContext
from a2a.types import (
Expand Down Expand Up @@ -103,6 +103,7 @@ async def get_card(
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> AgentCard:
"""Retrieves the AgentCard."""

Expand Down
6 changes: 5 additions & 1 deletion src/a2a/client/transports/grpc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging

from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Callable


try:
Expand Down Expand Up @@ -223,6 +223,7 @@ async def get_card(
*,
context: ClientCallContext | None = None,
extensions: list[str] | None = None,
signature_verifier: Callable[[AgentCard], None] | None = None,
) -> AgentCard:
"""Retrieves the agent's card."""
card = self.agent_card
Expand All @@ -236,6 +237,9 @@ async def get_card(
metadata=self._get_grpc_metadata(extensions),
)
card = proto_utils.FromProto.agent_card(card_pb)
if signature_verifier:
signature_verifier(card)

self.agent_card = card
self._needs_extended_card = False
return card
Expand Down
Loading
Loading