Add A2A agent card discovery - #1480
Conversation
Signed-off-by: Rajesh Digambar Bagul <102693488+Rajesh270712@users.noreply.github.com>
6884c56 to
ffd4f28
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces an A2A (Agent-to-Agent) discovery adapter for the core agent service, adding Pydantic schemas, API endpoints for retrieving the agent card, and corresponding unit tests. The review feedback highlights several schema alignment issues with the A2A specification: the A2AAPIKeySecurityScheme model should use in_ (aliased to in) and include a type field, and the A2ASecurityScheme union fields should be optional. Corresponding updates are also required in the agent card builder and test assertions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| class A2AAPIKeySecurityScheme(A2ABaseModel): | ||
| """A2A API key security scheme.""" | ||
|
|
||
| description: str = "" | ||
| location: Literal["query", "header", "cookie"] | ||
| name: str | ||
|
|
There was a problem hiding this comment.
The A2AAPIKeySecurityScheme model defines the field location instead of in. According to the A2A specification (which aligns with OpenAPI 3.x security schemes), the field name must be in (e.g., "in": "header"). Additionally, the scheme must include a type field set to "apiKey".
Since in is a reserved keyword in Python, you should define the field as in_ with alias="in".
| class A2AAPIKeySecurityScheme(A2ABaseModel): | |
| """A2A API key security scheme.""" | |
| description: str = "" | |
| location: Literal["query", "header", "cookie"] | |
| name: str | |
| class A2AAPIKeySecurityScheme(A2ABaseModel): | |
| """A2A API key security scheme.""" | |
| type: Literal["apiKey"] = "apiKey" | |
| description: str = "" | |
| in_: Literal["query", "header", "cookie"] = Field(alias="in") | |
| name: str |
There was a problem hiding this comment.
Fixed in head 07a3f6e: A2AAPIKeySecurityScheme now includes type="apiKey" and uses in_ with alias "in", so the serialized scheme emits "in": "header" and no "location" key.
Validated with python3 -m pytest core/agent/tests/test_a2a.py -q -> 2 passed, 1 warning.
| class A2ASecurityScheme(A2ABaseModel): | ||
| """A2A security scheme union.""" | ||
|
|
||
| api_key_security_scheme: A2AAPIKeySecurityScheme = Field( | ||
| alias="apiKeySecurityScheme" | ||
| ) |
There was a problem hiding this comment.
The A2ASecurityScheme model represents a union of different security schemes. Currently, api_key_security_scheme is a required field with no default value. This violates the union semantics of the A2A specification, where exactly one of the schemes should be populated. To allow extensibility and correct union behavior, the field should be marked as Optional and default to None.
| class A2ASecurityScheme(A2ABaseModel): | |
| """A2A security scheme union.""" | |
| api_key_security_scheme: A2AAPIKeySecurityScheme = Field( | |
| alias="apiKeySecurityScheme" | |
| ) | |
| class A2ASecurityScheme(A2ABaseModel): | |
| """A2A security scheme union.""" | |
| api_key_security_scheme: Optional[A2AAPIKeySecurityScheme] = Field( | |
| default=None, alias="apiKeySecurityScheme" | |
| ) |
There was a problem hiding this comment.
Fixed in head 07a3f6e: A2ASecurityScheme.apiKeySecurityScheme is now optional with default None, so an empty security scheme serializes with apiKeySecurityScheme=None instead of requiring the API key branch.
Validated with python3 -m pytest core/agent/tests/test_a2a.py -q -> 2 passed, 1 warning.
| securitySchemes={ | ||
| "astronConsumer": A2ASecurityScheme( | ||
| apiKeySecurityScheme=A2AAPIKeySecurityScheme( | ||
| description="Astron gateway consumer header.", | ||
| location="header", | ||
| name="x-consumer-username", | ||
| ) | ||
| ) | ||
| }, |
There was a problem hiding this comment.
Update the instantiation of A2AAPIKeySecurityScheme to use the corrected in_ field instead of location to match the A2A specification.
| securitySchemes={ | |
| "astronConsumer": A2ASecurityScheme( | |
| apiKeySecurityScheme=A2AAPIKeySecurityScheme( | |
| description="Astron gateway consumer header.", | |
| location="header", | |
| name="x-consumer-username", | |
| ) | |
| ) | |
| }, | |
| securitySchemes={ | |
| "astronConsumer": A2ASecurityScheme( | |
| apiKeySecurityScheme=A2AAPIKeySecurityScheme( | |
| description="Astron gateway consumer header.", | |
| in_="header", | |
| name="x-consumer-username", | |
| ) | |
| ) | |
| }, |
There was a problem hiding this comment.
Fixed in head 07a3f6e: the A2A agent card builder now constructs A2AAPIKeySecurityScheme with in_="header" for x-consumer-username.
Validated with python3 -m pytest core/agent/tests/test_a2a.py -q -> 2 passed, 1 warning.
| assert dumped["securitySchemes"]["astronConsumer"]["apiKeySecurityScheme"] == { | ||
| "description": "Astron gateway consumer header.", | ||
| "location": "header", | ||
| "name": "x-consumer-username", | ||
| } |
There was a problem hiding this comment.
Update the test assertions to match the corrected A2A specification schema, verifying that the serialized JSON contains "in": "header" and "type": "apiKey" instead of "location": "header".
assert dumped["securitySchemes"]["astronConsumer"]["apiKeySecurityScheme"] == {
"type": "apiKey",
"description": "Astron gateway consumer header.",
"in": "header",
"name": "x-consumer-username",
}There was a problem hiding this comment.
Fixed in head 07a3f6e: the A2A test assertion now expects the spec-shaped API key scheme with type="apiKey" and "in": "header", with no "location" key.
Validated with python3 -m pytest core/agent/tests/test_a2a.py -q -> 2 passed, 1 warning.
|
Thanks @Rajesh270712 |
Signed-off-by: Rajesh Digambar Bagul <102693488+Rajesh270712@users.noreply.github.com> Co-Authored-By: Paperclip <noreply@paperclip.ing>
5296293 to
07a3f6e
Compare
Summary
Part 1/3 of the split requested on PR #1456.
Stack
Part 2 and 3 are stacked in my fork to keep their diffs focused until this upstream base lands.
Validation
uv run pytest tests/test_a2a.py -quv run isort --profile black --check-only api/schemas/a2a.py api/v1/a2a.py tests/test_a2a.pyuv run black --check api/schemas/a2a.py api/v1/a2a.py tests/test_a2a.pyuv run flake8 --extend-ignore=E501 api/schemas/a2a.py api/v1/a2a.py tests/test_a2a.py