Skip to content

Commit 39a430d

Browse files
committed
Merge branch 'main' into restful
2 parents 0891716 + b567e80 commit 39a430d

File tree

12 files changed

+471
-24
lines changed

12 files changed

+471
-24
lines changed

src/a2a/server/apps/jsonrpc/fastapi_app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
AGENT_CARD_WELL_KNOWN_PATH,
1717
DEFAULT_RPC_URL,
1818
EXTENDED_AGENT_CARD_PATH,
19+
PREV_AGENT_CARD_WELL_KNOWN_PATH,
1920
)
2021

2122

@@ -89,6 +90,13 @@ def add_routes_to_app(
8990
)(self._handle_requests)
9091
app.get(agent_card_url)(self._handle_get_agent_card)
9192

93+
# add deprecated path only if the agent_card_url uses default well-known path
94+
if agent_card_url == AGENT_CARD_WELL_KNOWN_PATH:
95+
app.get(PREV_AGENT_CARD_WELL_KNOWN_PATH, include_in_schema=False)(
96+
self.handle_deprecated_agent_card_path
97+
)
98+
99+
# TODO: deprecated endpoint to be removed in a future release
92100
if self.agent_card.supports_authenticated_extended_card:
93101
app.get(extended_agent_card_url)(
94102
self._handle_get_authenticated_extended_agent_card

src/a2a/server/apps/jsonrpc/jsonrpc_app.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
AgentCard,
3333
CancelTaskRequest,
3434
DeleteTaskPushNotificationConfigRequest,
35+
GetAuthenticatedExtendedCardRequest,
3536
GetTaskPushNotificationConfigRequest,
3637
GetTaskRequest,
3738
InternalError,
3839
InvalidRequestError,
3940
JSONParseError,
4041
JSONRPCError,
4142
JSONRPCErrorResponse,
43+
JSONRPCRequest,
4244
JSONRPCResponse,
4345
ListTaskPushNotificationConfigRequest,
4446
SendMessageRequest,
@@ -52,6 +54,7 @@
5254
AGENT_CARD_WELL_KNOWN_PATH,
5355
DEFAULT_RPC_URL,
5456
EXTENDED_AGENT_CARD_PATH,
57+
PREV_AGENT_CARD_WELL_KNOWN_PATH,
5558
)
5659
from a2a.utils.errors import MethodNotImplementedError
5760

@@ -142,7 +145,9 @@ def __init__(
142145
self.agent_card = agent_card
143146
self.extended_agent_card = extended_agent_card
144147
self.handler = JSONRPCHandler(
145-
agent_card=agent_card, request_handler=http_handler
148+
agent_card=agent_card,
149+
request_handler=http_handler,
150+
extended_agent_card=extended_agent_card,
146151
)
147152
if (
148153
self.agent_card.supports_authenticated_extended_card
@@ -212,7 +217,16 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
212217

213218
try:
214219
body = await request.json()
220+
if isinstance(body, dict):
221+
request_id = body.get('id')
222+
223+
# First, validate the basic JSON-RPC structure. This is crucial
224+
# because the A2ARequest model is a discriminated union where some
225+
# request types have default values for the 'method' field
226+
JSONRPCRequest.model_validate(body)
227+
215228
a2a_request = A2ARequest.model_validate(body)
229+
216230
call_context = self._context_builder.build(request)
217231

218232
request_id = a2a_request.root.id
@@ -352,6 +366,13 @@ async def _process_non_streaming_request(
352366
context,
353367
)
354368
)
369+
case GetAuthenticatedExtendedCardRequest():
370+
handler_result = (
371+
await self.handler.get_authenticated_extended_card(
372+
request_obj,
373+
context,
374+
)
375+
)
355376
case _:
356377
logger.error(
357378
f'Unhandled validated request type: {type(request_obj)}'
@@ -436,10 +457,23 @@ async def _handle_get_agent_card(self, request: Request) -> JSONResponse:
436457
)
437458
)
438459

460+
async def handle_deprecated_agent_card_path(
461+
self, request: Request
462+
) -> JSONResponse:
463+
"""Handles GET requests for the deprecated agent card endpoint."""
464+
logger.warning(
465+
f"Deprecated agent card endpoint '{PREV_AGENT_CARD_WELL_KNOWN_PATH}' accessed. Please use '{AGENT_CARD_WELL_KNOWN_PATH}' instead. This endpoint will be removed in a future version."
466+
)
467+
return await self._handle_get_agent_card(request)
468+
439469
async def _handle_get_authenticated_extended_agent_card(
440470
self, request: Request
441471
) -> JSONResponse:
442472
"""Handles GET requests for the authenticated extended agent card."""
473+
logger.warning(
474+
'HTTP GET for authenticated extended card has been called by a client. '
475+
'This endpoint is deprecated in favor of agent/authenticatedExtendedCard JSON-RPC method and will be removed in a future release.'
476+
)
443477
if not self.agent_card.supports_authenticated_extended_card:
444478
return JSONResponse(
445479
{'error': 'Extended agent card not supported or not enabled.'},

src/a2a/server/apps/jsonrpc/starlette_app.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
AGENT_CARD_WELL_KNOWN_PATH,
1616
DEFAULT_RPC_URL,
1717
EXTENDED_AGENT_CARD_PATH,
18+
PREV_AGENT_CARD_WELL_KNOWN_PATH,
1819
)
1920

2021

@@ -86,6 +87,18 @@ def routes(
8687
),
8788
]
8889

90+
# add deprecated path only if the agent_card_url uses default well-known path
91+
if agent_card_url == AGENT_CARD_WELL_KNOWN_PATH:
92+
app_routes.append(
93+
Route(
94+
PREV_AGENT_CARD_WELL_KNOWN_PATH,
95+
self.handle_deprecated_agent_card_path,
96+
methods=['GET'],
97+
name='agent_card_path_deprecated',
98+
)
99+
)
100+
101+
# TODO: deprecated endpoint to be removed in a future release
89102
if self.agent_card.supports_authenticated_extended_card:
90103
app_routes.append(
91104
Route(

src/a2a/server/request_handlers/jsonrpc_handler.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
from a2a.server.request_handlers.response_helpers import prepare_response_object
88
from a2a.types import (
99
AgentCard,
10+
AuthenticatedExtendedCardNotConfiguredError,
1011
CancelTaskRequest,
1112
CancelTaskResponse,
1213
CancelTaskSuccessResponse,
1314
DeleteTaskPushNotificationConfigRequest,
1415
DeleteTaskPushNotificationConfigResponse,
1516
DeleteTaskPushNotificationConfigSuccessResponse,
17+
GetAuthenticatedExtendedCardRequest,
18+
GetAuthenticatedExtendedCardResponse,
19+
GetAuthenticatedExtendedCardSuccessResponse,
1620
GetTaskPushNotificationConfigRequest,
1721
GetTaskPushNotificationConfigResponse,
1822
GetTaskPushNotificationConfigSuccessResponse,
@@ -57,15 +61,18 @@ def __init__(
5761
self,
5862
agent_card: AgentCard,
5963
request_handler: RequestHandler,
64+
extended_agent_card: AgentCard | None = None,
6065
):
6166
"""Initializes the JSONRPCHandler.
6267
6368
Args:
6469
agent_card: The AgentCard describing the agent's capabilities.
6570
request_handler: The underlying `RequestHandler` instance to delegate requests to.
71+
extended_agent_card: An optional, distinct Extended AgentCard to be served
6672
"""
6773
self.agent_card = agent_card
6874
self.request_handler = request_handler
75+
self.extended_agent_card = extended_agent_card
6976

7077
async def on_message_send(
7178
self,
@@ -395,3 +402,31 @@ async def delete_push_notification_config(
395402
id=request.id, error=e.error if e.error else InternalError()
396403
)
397404
)
405+
406+
async def get_authenticated_extended_card(
407+
self,
408+
request: GetAuthenticatedExtendedCardRequest,
409+
context: ServerCallContext | None = None,
410+
) -> GetAuthenticatedExtendedCardResponse:
411+
"""Handles the 'agent/authenticatedExtendedCard' JSON-RPC method.
412+
413+
Args:
414+
request: The incoming `GetAuthenticatedExtendedCardRequest` object.
415+
context: Context provided by the server.
416+
417+
Returns:
418+
A `GetAuthenticatedExtendedCardResponse` object containing the config or a JSON-RPC error.
419+
"""
420+
if self.extended_agent_card is None:
421+
return GetAuthenticatedExtendedCardResponse(
422+
root=JSONRPCErrorResponse(
423+
id=request.id,
424+
error=AuthenticatedExtendedCardNotConfiguredError(),
425+
)
426+
)
427+
428+
return GetAuthenticatedExtendedCardResponse(
429+
root=GetAuthenticatedExtendedCardSuccessResponse(
430+
id=request.id, result=self.extended_agent_card
431+
)
432+
)

src/a2a/types.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,26 @@ class AgentSkill(A2ABaseModel):
172172
"""
173173

174174

175+
class AuthenticatedExtendedCardNotConfiguredError(A2ABaseModel):
176+
"""
177+
An A2A-specific error indicating that the agent does not have an Authenticated Extended Card configured
178+
"""
179+
180+
code: Literal[-32007] = -32007
181+
"""
182+
The error code for when an authenticated extended card is not configured.
183+
"""
184+
data: Any | None = None
185+
"""
186+
A primitive or structured value containing additional information about the error.
187+
This may be omitted.
188+
"""
189+
message: str | None = 'Authenticated Extended Card is not configured'
190+
"""
191+
The error message.
192+
"""
193+
194+
175195
class AuthorizationCodeOAuthFlow(A2ABaseModel):
176196
"""
177197
Defines configuration details for the OAuth 2.0 Authorization Code flow.
@@ -375,6 +395,27 @@ class FileWithUri(A2ABaseModel):
375395
"""
376396

377397

398+
class GetAuthenticatedExtendedCardRequest(A2ABaseModel):
399+
"""
400+
Represents a JSON-RPC request for the `agent/getAuthenticatedExtendedCard` method.
401+
"""
402+
403+
id: str | int
404+
"""
405+
The identifier for this request.
406+
"""
407+
jsonrpc: Literal['2.0'] = '2.0'
408+
"""
409+
The version of the JSON-RPC protocol. MUST be exactly "2.0".
410+
"""
411+
method: Literal['agent/getAuthenticatedExtendedCard'] = (
412+
'agent/getAuthenticatedExtendedCard'
413+
)
414+
"""
415+
The method name. Must be 'agent/getAuthenticatedExtendedCard'.
416+
"""
417+
418+
378419
class GetTaskPushNotificationConfigParams(A2ABaseModel):
379420
"""
380421
Defines parameters for fetching a specific push notification configuration for a task.
@@ -999,6 +1040,7 @@ class A2AError(
9991040
| UnsupportedOperationError
10001041
| ContentTypeNotSupportedError
10011042
| InvalidAgentResponseError
1043+
| AuthenticatedExtendedCardNotConfiguredError
10021044
]
10031045
):
10041046
root: (
@@ -1013,6 +1055,7 @@ class A2AError(
10131055
| UnsupportedOperationError
10141056
| ContentTypeNotSupportedError
10151057
| InvalidAgentResponseError
1058+
| AuthenticatedExtendedCardNotConfiguredError
10161059
)
10171060
"""
10181061
A discriminated union of all standard JSON-RPC and A2A-specific error types.
@@ -1170,6 +1213,7 @@ class JSONRPCErrorResponse(A2ABaseModel):
11701213
| UnsupportedOperationError
11711214
| ContentTypeNotSupportedError
11721215
| InvalidAgentResponseError
1216+
| AuthenticatedExtendedCardNotConfiguredError
11731217
)
11741218
"""
11751219
An object describing the error that occurred.
@@ -1625,6 +1669,7 @@ class A2ARequest(
16251669
| TaskResubscriptionRequest
16261670
| ListTaskPushNotificationConfigRequest
16271671
| DeleteTaskPushNotificationConfigRequest
1672+
| GetAuthenticatedExtendedCardRequest
16281673
]
16291674
):
16301675
root: (
@@ -1637,6 +1682,7 @@ class A2ARequest(
16371682
| TaskResubscriptionRequest
16381683
| ListTaskPushNotificationConfigRequest
16391684
| DeleteTaskPushNotificationConfigRequest
1685+
| GetAuthenticatedExtendedCardRequest
16401686
)
16411687
"""
16421688
A discriminated union representing all possible JSON-RPC 2.0 requests supported by the A2A specification.
@@ -1750,6 +1796,25 @@ class AgentCard(A2ABaseModel):
17501796
"""
17511797

17521798

1799+
class GetAuthenticatedExtendedCardSuccessResponse(A2ABaseModel):
1800+
"""
1801+
Represents a successful JSON-RPC response for the `agent/getAuthenticatedExtendedCard` method.
1802+
"""
1803+
1804+
id: str | int | None = None
1805+
"""
1806+
The identifier established by the client.
1807+
"""
1808+
jsonrpc: Literal['2.0'] = '2.0'
1809+
"""
1810+
The version of the JSON-RPC protocol. MUST be exactly "2.0".
1811+
"""
1812+
result: AgentCard
1813+
"""
1814+
The result is an Agent Card object.
1815+
"""
1816+
1817+
17531818
class Task(A2ABaseModel):
17541819
"""
17551820
Represents a single, stateful operation or conversation between a client and an agent.
@@ -1769,7 +1834,7 @@ class Task(A2ABaseModel):
17691834
"""
17701835
id: str
17711836
"""
1772-
A unique identifier for the task, generated by the client for a new task or provided by the agent.
1837+
A unique identifier for the task, generated by the server for a new task.
17731838
"""
17741839
kind: Literal['task'] = 'task'
17751840
"""
@@ -1804,6 +1869,17 @@ class CancelTaskSuccessResponse(A2ABaseModel):
18041869
"""
18051870

18061871

1872+
class GetAuthenticatedExtendedCardResponse(
1873+
RootModel[
1874+
JSONRPCErrorResponse | GetAuthenticatedExtendedCardSuccessResponse
1875+
]
1876+
):
1877+
root: JSONRPCErrorResponse | GetAuthenticatedExtendedCardSuccessResponse
1878+
"""
1879+
Represents a JSON-RPC response for the `agent/getAuthenticatedExtendedCard` method.
1880+
"""
1881+
1882+
18071883
class GetTaskSuccessResponse(A2ABaseModel):
18081884
"""
18091885
Represents a successful JSON-RPC response for the `tasks/get` method.
@@ -1889,6 +1965,7 @@ class JSONRPCResponse(
18891965
| GetTaskPushNotificationConfigSuccessResponse
18901966
| ListTaskPushNotificationConfigSuccessResponse
18911967
| DeleteTaskPushNotificationConfigSuccessResponse
1968+
| GetAuthenticatedExtendedCardSuccessResponse
18921969
]
18931970
):
18941971
root: (
@@ -1901,6 +1978,7 @@ class JSONRPCResponse(
19011978
| GetTaskPushNotificationConfigSuccessResponse
19021979
| ListTaskPushNotificationConfigSuccessResponse
19031980
| DeleteTaskPushNotificationConfigSuccessResponse
1981+
| GetAuthenticatedExtendedCardSuccessResponse
19041982
)
19051983
"""
19061984
A discriminated union representing all possible JSON-RPC 2.0 responses

src/a2a/utils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
AGENT_CARD_WELL_KNOWN_PATH,
1010
DEFAULT_RPC_URL,
1111
EXTENDED_AGENT_CARD_PATH,
12+
PREV_AGENT_CARD_WELL_KNOWN_PATH,
1213
)
1314
from a2a.utils.helpers import (
1415
append_artifact_to_task,
@@ -34,6 +35,7 @@
3435
'AGENT_CARD_WELL_KNOWN_PATH',
3536
'DEFAULT_RPC_URL',
3637
'EXTENDED_AGENT_CARD_PATH',
38+
'PREV_AGENT_CARD_WELL_KNOWN_PATH',
3739
'append_artifact_to_task',
3840
'are_modalities_compatible',
3941
'build_text_artifact',

src/a2a/utils/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Constants for well-known URIs used throughout the A2A Python SDK."""
22

3-
AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent.json'
3+
AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent-card.json'
4+
PREV_AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent.json'
45
EXTENDED_AGENT_CARD_PATH = '/agent/authenticatedExtendedCard'
56
DEFAULT_RPC_URL = '/'

0 commit comments

Comments
 (0)