From 7dd561f6aff0807e207d91155908b3416b02fa88 Mon Sep 17 00:00:00 2001 From: rajeshvelicheti Date: Thu, 28 Aug 2025 14:22:01 -0700 Subject: [PATCH 1/4] bug: Sync jsonrpc and rest implementation of authenticated agent card --- src/a2a/server/request_handlers/jsonrpc_handler.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/a2a/server/request_handlers/jsonrpc_handler.py b/src/a2a/server/request_handlers/jsonrpc_handler.py index 3beb4e4f6..f5af170ef 100644 --- a/src/a2a/server/request_handlers/jsonrpc_handler.py +++ b/src/a2a/server/request_handlers/jsonrpc_handler.py @@ -1,5 +1,4 @@ import logging - from collections.abc import AsyncIterable, Callable from a2a.server.context import ServerCallContext @@ -49,7 +48,6 @@ from a2a.utils.helpers import validate from a2a.utils.telemetry import SpanKind, trace_class - logger = logging.getLogger(__name__) @@ -425,14 +423,10 @@ async def get_authenticated_extended_card( Returns: A `GetAuthenticatedExtendedCardResponse` object containing the config or a JSON-RPC error. """ - if ( - self.extended_agent_card is None - and self.extended_card_modifier is None - ): - return GetAuthenticatedExtendedCardResponse( - root=JSONRPCErrorResponse( - id=request.id, - error=AuthenticatedExtendedCardNotConfiguredError(), + if not self.agent_card.supports_authenticated_extended_card: + raise ServerError( + error=AuthenticatedExtendedCardNotConfiguredError( + message='Authenticated card not supported' ) ) From f85f2495177c10755b4f69b2db5e42cbe7157e3d Mon Sep 17 00:00:00 2001 From: rajeshvelicheti Date: Thu, 28 Aug 2025 15:12:13 -0700 Subject: [PATCH 2/4] fix: Sync jsonrpc and rest implementation of authenticated agent card --- src/a2a/server/apps/rest/rest_adapter.py | 6 +++--- src/a2a/server/request_handlers/jsonrpc_handler.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/a2a/server/apps/rest/rest_adapter.py b/src/a2a/server/apps/rest/rest_adapter.py index 40a4aacbc..cdf86ab14 100644 --- a/src/a2a/server/apps/rest/rest_adapter.py +++ b/src/a2a/server/apps/rest/rest_adapter.py @@ -182,9 +182,9 @@ async def handle_authenticated_agent_card( if self.extended_card_modifier: context = self._context_builder.build(request) - # If no base extended card is provided, pass the public card to the modifier - base_card = card_to_serve if card_to_serve else self.agent_card - card_to_serve = self.extended_card_modifier(base_card, context) + card_to_serve = self.extended_card_modifier(card_to_serve, context) + elif self.card_modifier: + card_to_serve = self.card_modifier(card_to_serve) return card_to_serve.model_dump(mode='json', exclude_none=True) diff --git a/src/a2a/server/request_handlers/jsonrpc_handler.py b/src/a2a/server/request_handlers/jsonrpc_handler.py index f5af170ef..d79b54336 100644 --- a/src/a2a/server/request_handlers/jsonrpc_handler.py +++ b/src/a2a/server/request_handlers/jsonrpc_handler.py @@ -1,4 +1,5 @@ import logging + from collections.abc import AsyncIterable, Callable from a2a.server.context import ServerCallContext @@ -48,6 +49,7 @@ from a2a.utils.helpers import validate from a2a.utils.telemetry import SpanKind, trace_class + logger = logging.getLogger(__name__) @@ -64,6 +66,7 @@ def __init__( [AgentCard, ServerCallContext], AgentCard ] | None = None, + card_modifier: Callable[[AgentCard], AgentCard] | None = None, ): """Initializes the JSONRPCHandler. @@ -74,11 +77,14 @@ def __init__( extended_card_modifier: An optional callback to dynamically modify the extended agent card before it is served. It receives the call context. + card_modifier: An optional callback to dynamically modify the public + agent card before it is served. """ self.agent_card = agent_card self.request_handler = request_handler self.extended_agent_card = extended_agent_card self.extended_card_modifier = extended_card_modifier + self.card_modifer = card_modifier async def on_message_send( self, @@ -437,6 +443,8 @@ async def get_authenticated_extended_card( card_to_serve = base_card if self.extended_card_modifier and context: card_to_serve = self.extended_card_modifier(base_card, context) + elif self.card_modifer: + card_to_serve = self.card_modifer(base_card) return GetAuthenticatedExtendedCardResponse( root=GetAuthenticatedExtendedCardSuccessResponse( From 6b969c83be54adab7f038cd40de7507fed94c343 Mon Sep 17 00:00:00 2001 From: rajeshvelicheti Date: Thu, 28 Aug 2025 22:04:07 -0700 Subject: [PATCH 3/4] Updated unit tests --- .../request_handlers/test_jsonrpc_handler.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/server/request_handlers/test_jsonrpc_handler.py b/tests/server/request_handlers/test_jsonrpc_handler.py index b460b2f33..19cf8be06 100644 --- a/tests/server/request_handlers/test_jsonrpc_handler.py +++ b/tests/server/request_handlers/test_jsonrpc_handler.py @@ -1,6 +1,5 @@ import unittest import unittest.async_case - from collections.abc import AsyncGenerator from typing import Any, NoReturn from unittest.mock import AsyncMock, MagicMock, call, patch @@ -75,7 +74,6 @@ ) from a2a.utils.errors import ServerError - MINIMAL_TASK: dict[str, Any] = { 'id': 'task_123', 'contextId': 'session-xyz', @@ -93,7 +91,9 @@ class TestJSONRPCtHandler(unittest.async_case.IsolatedAsyncioTestCase): @pytest.fixture(autouse=True) def init_fixtures(self) -> None: self.mock_agent_card = MagicMock( - spec=AgentCard, url='http://agent.example.com/api' + spec=AgentCard, + url='http://agent.example.com/api', + supports_authenticated_extended_card=True, ) async def test_on_get_task_success(self) -> None: @@ -1233,6 +1233,7 @@ async def test_get_authenticated_extended_card_not_configured(self) -> None: """Test error when authenticated extended agent card is not configured.""" # Arrange mock_request_handler = AsyncMock(spec=DefaultRequestHandler) + self.mock_agent_card.supports_extended_card = True handler = JSONRPCHandler( self.mock_agent_card, mock_request_handler, @@ -1248,11 +1249,12 @@ async def test_get_authenticated_extended_card_not_configured(self) -> None: ) # Assert - self.assertIsInstance(response.root, JSONRPCErrorResponse) - self.assertEqual(response.root.id, 'ext-card-req-2') + # Authenticated Extended Card flag is set with no extended card, + # returns base card in this case. self.assertIsInstance( - response.root.error, AuthenticatedExtendedCardNotConfiguredError + response.root, GetAuthenticatedExtendedCardSuccessResponse ) + self.assertEqual(response.root.id, 'ext-card-req-2') async def test_get_authenticated_extended_card_with_modifier(self) -> None: """Test successful retrieval of a dynamically modified extended agent card.""" From 138a1c45b52aa42e7a0282a51b43bdf341ce7ca1 Mon Sep 17 00:00:00 2001 From: rajeshvelicheti Date: Thu, 28 Aug 2025 22:08:25 -0700 Subject: [PATCH 4/4] Updated unit tests --- src/a2a/server/request_handlers/jsonrpc_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/a2a/server/request_handlers/jsonrpc_handler.py b/src/a2a/server/request_handlers/jsonrpc_handler.py index d79b54336..2cee937f4 100644 --- a/src/a2a/server/request_handlers/jsonrpc_handler.py +++ b/src/a2a/server/request_handlers/jsonrpc_handler.py @@ -84,7 +84,7 @@ def __init__( self.request_handler = request_handler self.extended_agent_card = extended_agent_card self.extended_card_modifier = extended_card_modifier - self.card_modifer = card_modifier + self.card_modifier = card_modifier async def on_message_send( self, @@ -443,8 +443,8 @@ async def get_authenticated_extended_card( card_to_serve = base_card if self.extended_card_modifier and context: card_to_serve = self.extended_card_modifier(base_card, context) - elif self.card_modifer: - card_to_serve = self.card_modifer(base_card) + elif self.card_modifier: + card_to_serve = self.card_modifier(base_card) return GetAuthenticatedExtendedCardResponse( root=GetAuthenticatedExtendedCardSuccessResponse(