Skip to content

feat: Add GetExtendedAgentCard Support to RequestHandlers#919

Open
sokoliva wants to merge 6 commits intoa2aproject:1.0-devfrom
sokoliva:GetExtendedAgentCard
Open

feat: Add GetExtendedAgentCard Support to RequestHandlers#919
sokoliva wants to merge 6 commits intoa2aproject:1.0-devfrom
sokoliva:GetExtendedAgentCard

Conversation

@sokoliva
Copy link
Copy Markdown
Member

@sokoliva sokoliva commented Apr 1, 2026

Description

The GetExtendedAgentCard capability was defined in the spec but not implemented in the request_handler.py.

Changes

  • Added on_get_extended_agent_card to the base RequestHandler and its child class DefaultRequestHandler.
  • Removed GetExtendedAgentCard method implementations from the Transport layer and consequently moved AgentCard informations from the Transport layer to the RequestHandlers.
  • moved validate logic from the transport layer to request handler

Fixes

  • Type checkers (like Pyright/mypy) were misinterpreting abstract streaming methods (like on_message_send_stream and on_subscribe_to_task) as standard coroutines because they were raising an exception instantly instead of yielding. Removed standard raise UnsupportedOperationError from the abstract definitions and replaced it with a dummy conditional yield.

Fixes #866 🦕

@sokoliva sokoliva requested a review from a team as a code owner April 1, 2026 10:11
@sokoliva sokoliva requested a review from guglielmo-san April 1, 2026 10:11
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the management of AgentCard and its modifiers by centralizing them within the RequestHandler architecture, removing the need to pass these objects individually to gRPC, REST, and JSON-RPC handlers. It also introduces centralized capability validation using an updated validate decorator and adds a specific error for missing push notification configurations. Feedback highlights a regression in the validate decorator which now fails to correctly wrap standard coroutine functions, as well as a violation of the style guide regarding the direct raising of exception classes.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🧪 Code Coverage (vs 1.0-dev)

⬇️ Download Full Report

Base PR Delta
src/a2a/client/transports/http_helpers.py 94.44% 90.28% 🔴 -4.17%
src/a2a/server/request_handlers/default_request_handler.py 96.66% 93.53% 🔴 -3.13%
src/a2a/server/request_handlers/grpc_handler.py 94.61% 94.34% 🔴 -0.27%
src/a2a/server/routes/jsonrpc_dispatcher.py 86.32% 86.84% 🟢 +0.53%
src/a2a/server/routes/jsonrpc_routes.py 66.67% 62.50% 🔴 -4.17%
src/a2a/server/routes/rest_dispatcher.py 92.43% 94.64% 🟢 +2.21%
src/a2a/utils/helpers.py 97.19% 97.46% 🟢 +0.27%
Total 91.58% 91.48% 🔴 -0.10%

Generated by coverage-comment.yml

Comment on lines +688 to +692
@validate(
lambda self: self.agent_card.capabilities.extended_agent_card,
error_message='The agent does not have an extended agent card configured',
error_type=ExtendedAgentCardNotConfiguredError,
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

According to spec, it should return ExtendedAgentCardNotConfiguredError if there is no extended_agent_card_defined.
If it is not supported the error is UnsupportedOperationError

Comment on lines -524 to -526
if not self._push_config_store:
raise PushNotificationNotSupportedError

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would leave this check here and below to avoid the ignore[union-attr]

Comment on lines +39 to +43
@property
@abstractmethod
def agent_card(self) -> AgentCard:
"""The core agent card to serve logic against."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why did you add this? I don't think it should be needed

Comment on lines +134 to +136
# This is needed for typechecker to recognise this method as an async generator.
if False:
yield
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's leave it as it was for now, and tackle this in a separate PR maybe

Comment on lines +191 to +193
# This is needed for typechecker to recognise the method as an async generator.
if False:
yield
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same as above

Comment on lines +64 to +71
normalized_path = rpc_url
if not rpc_url.startswith('/'):
logger.warning(
"The rpc_url '%s' does not start with a leading slash '/'. automatically prepending it.",
rpc_url,
)
normalized_path = f'/{rpc_url}'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's not modify the rpc_url, if the user passes one without a leading / the server will not start showing an error

context = self._build_call_context(request)
card_to_serve = await maybe_await(
self.extended_card_modifier(card_to_serve, context)
async def _handler(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add the version validation also on this method

return config

raise InternalError(message='Push notification config not found')
raise PushNotificationConfigNotFoundError
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment on lines +43 to +48
class PushNotificationConfigNotFoundError(A2AError):
"""Exception raised when a push notification configuration is not found."""

message = 'Push notification config not found'


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this is not defined anywhere in the specifications

finally:
await inner.aclose()

return async_gen_wrapper
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This might cause a FAIL-LATE error corrupting the code behaviour, let's discuss tomorrow what could be the best way to handle this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants