Skip to content

Commit 13d0106

Browse files
refactor(client): move agent card signature verification to BaseClient (#793)
This PR refactors the signature verification logic for Extended Agent Cards to be handled at the client level (BaseClient) rather than being duplicated across each individual transport implementation (GrpcTransport, JsonRpcTransport, RestTransport). Changes: - BaseClient: Added signature verification step and state mutation logic (self._card = card) inside get_extended_agent_card(). - Transports (gRPC, JSON-RPC, REST, TenantDecorator): Removed the signature_verifier parameter from get_extended_agent_card(). - Removed internal caching mechanism and state (self.agent_card, self._needs_extended_card) from the get_extended_agent_card() flow.
1 parent 47a5959 commit 13d0106

8 files changed

Lines changed: 40 additions & 209 deletions

File tree

src/a2a/client/base_client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,10 @@ async def get_extended_agent_card(
303303
card = await self._transport.get_extended_agent_card(
304304
request,
305305
context=context,
306-
signature_verifier=signature_verifier,
307306
)
307+
if signature_verifier:
308+
signature_verifier(card)
309+
308310
self._card = card
309311
return card
310312

src/a2a/client/transports/base.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from collections.abc import AsyncGenerator, Callable
2+
from collections.abc import AsyncGenerator
33
from types import TracebackType
44

55
from typing_extensions import Self
@@ -141,7 +141,6 @@ async def get_extended_agent_card(
141141
request: GetExtendedAgentCardRequest,
142142
*,
143143
context: ClientCallContext | None = None,
144-
signature_verifier: Callable[[AgentCard], None] | None = None,
145144
) -> AgentCard:
146145
"""Retrieves the Extended AgentCard."""
147146

src/a2a/client/transports/grpc.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ def __init__(
105105
self.agent_card = agent_card
106106
self.channel = channel
107107
self.stub = a2a_pb2_grpc.A2AServiceStub(channel)
108-
self._needs_extended_card = (
109-
agent_card.capabilities.extended_agent_card if agent_card else True
110-
)
111108

112109
@classmethod
113110
def create(
@@ -270,22 +267,18 @@ async def get_extended_agent_card(
270267
request: GetExtendedAgentCardRequest,
271268
*,
272269
context: ClientCallContext | None = None,
273-
signature_verifier: Callable[[AgentCard], None] | None = None,
274270
) -> AgentCard:
275271
"""Retrieves the agent's card."""
276-
card = await self._call_grpc(
272+
card = self.agent_card
273+
if card and not card.capabilities.extended_agent_card:
274+
return card
275+
276+
return await self._call_grpc(
277277
self.stub.GetExtendedAgentCard,
278278
request,
279279
context,
280280
)
281281

282-
if signature_verifier:
283-
signature_verifier(card)
284-
285-
self.agent_card = card
286-
self._needs_extended_card = False
287-
return card
288-
289282
async def close(self) -> None:
290283
"""Closes the gRPC channel."""
291284
await self.channel.close()

src/a2a/client/transports/jsonrpc.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22

3-
from collections.abc import AsyncGenerator, Callable
3+
from collections.abc import AsyncGenerator
44
from typing import Any
55
from uuid import uuid4
66

@@ -62,7 +62,6 @@ def __init__(
6262
self.httpx_client = httpx_client
6363
self.agent_card = agent_card
6464
self.interceptors = interceptors or []
65-
self._needs_extended_card = agent_card.capabilities.extended_agent_card
6665

6766
async def send_message(
6867
self,
@@ -284,11 +283,9 @@ async def get_extended_agent_card(
284283
request: GetExtendedAgentCardRequest,
285284
*,
286285
context: ClientCallContext | None = None,
287-
signature_verifier: Callable[[AgentCard], None] | None = None,
288286
) -> AgentCard:
289287
"""Retrieves the agent's card."""
290288
card = self.agent_card
291-
292289
if not card.capabilities.extended_agent_card:
293290
return card
294291

@@ -312,11 +309,7 @@ async def get_extended_agent_card(
312309
response: AgentCard = json_format.ParseDict(
313310
json_rpc_response.result, AgentCard()
314311
)
315-
if signature_verifier:
316-
signature_verifier(response)
317312

318-
self.agent_card = response
319-
self._needs_extended_card = False
320313
return response
321314

322315
async def close(self) -> None:

src/a2a/client/transports/rest.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
import logging
33

4-
from collections.abc import AsyncGenerator, Callable
4+
from collections.abc import AsyncGenerator
55
from typing import Any, NoReturn
66

77
import httpx
@@ -61,7 +61,6 @@ def __init__(
6161
self.httpx_client = httpx_client
6262
self.agent_card = agent_card
6363
self.interceptors = interceptors or []
64-
self._needs_extended_card = agent_card.capabilities.extended_agent_card
6564

6665
async def send_message(
6766
self,
@@ -265,26 +264,17 @@ async def get_extended_agent_card(
265264
request: GetExtendedAgentCardRequest,
266265
*,
267266
context: ClientCallContext | None = None,
268-
signature_verifier: Callable[[AgentCard], None] | None = None,
269267
) -> AgentCard:
270268
"""Retrieves the Extended AgentCard."""
271269
card = self.agent_card
272-
273270
if not card.capabilities.extended_agent_card:
274271
return card
275272

276273
response_data = await self._execute_request(
277274
'GET', '/extendedAgentCard', request.tenant, context=context
278275
)
279-
response: AgentCard = ParseDict(response_data, AgentCard())
280-
281-
if signature_verifier:
282-
signature_verifier(response)
283276

284-
# Update the transport's agent_card
285-
self.agent_card = response
286-
self._needs_extended_card = False
287-
return response
277+
return ParseDict(response_data, AgentCard())
288278

289279
async def close(self) -> None:
290280
"""Closes the httpx client."""

src/a2a/client/transports/tenant_decorator.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from collections.abc import AsyncGenerator, Callable
1+
from collections.abc import AsyncGenerator
22

33
from a2a.client.middleware import ClientCallContext
44
from a2a.client.transports.base import ClientTransport
@@ -154,14 +154,12 @@ async def get_extended_agent_card(
154154
request: GetExtendedAgentCardRequest,
155155
*,
156156
context: ClientCallContext | None = None,
157-
signature_verifier: Callable[[AgentCard], None] | None = None,
158157
) -> AgentCard:
159158
"""Retrieves the Extended AgentCard."""
160159
request.tenant = self._resolve_tenant(request.tenant)
161160
return await self._base.get_extended_agent_card(
162161
request,
163162
context=context,
164-
signature_verifier=signature_verifier,
165163
)
166164

167165
async def close(self) -> None:

src/a2a/compat/v0_3/grpc_transport.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ async def get_extended_agent_card(
338338
request: a2a_pb2.GetExtendedAgentCardRequest,
339339
*,
340340
context: ClientCallContext | None = None,
341-
signature_verifier: Callable[[a2a_pb2.AgentCard], None] | None = None,
342341
) -> a2a_pb2.AgentCard:
343342
"""Retrieves the agent's card (v0.3)."""
344343
req_proto = a2a_v0_3_pb2.GetAgentCardRequest()
@@ -350,9 +349,6 @@ async def get_extended_agent_card(
350349
proto_utils.FromProto.agent_card(resp_proto)
351350
)
352351

353-
if signature_verifier:
354-
signature_verifier(card)
355-
356352
self.agent_card = card
357353
return card
358354

0 commit comments

Comments
 (0)