Skip to content

Commit 6086f96

Browse files
authored
refactor: cleanup legacy agent card paths (#750)
- Remove `/agent/authenticatedExtendedCard` and `/.well-known/agent.json` support. - Remove `A2AException` - it doesn't exist on `main` and it was some intermediate `1.0-dev` state during development. Re #701
1 parent e25ba7b commit 6086f96

11 files changed

Lines changed: 22 additions & 369 deletions

File tree

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

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
from a2a.utils.constants import (
3131
AGENT_CARD_WELL_KNOWN_PATH,
3232
DEFAULT_RPC_URL,
33-
EXTENDED_AGENT_CARD_PATH,
34-
PREV_AGENT_CARD_WELL_KNOWN_PATH,
3533
)
3634

3735

@@ -137,15 +135,13 @@ def add_routes_to_app(
137135
app: FastAPI,
138136
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
139137
rpc_url: str = DEFAULT_RPC_URL,
140-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
141138
) -> None:
142139
"""Adds the routes to the FastAPI application.
143140
144141
Args:
145142
app: The FastAPI application to add the routes to.
146143
agent_card_url: The URL for the agent card endpoint.
147144
rpc_url: The URL for the A2A JSON-RPC endpoint.
148-
extended_agent_card_url: The URL for the authenticated extended agent card endpoint.
149145
"""
150146
app.post(
151147
rpc_url,
@@ -165,40 +161,24 @@ def add_routes_to_app(
165161
)(self._handle_requests)
166162
app.get(agent_card_url)(self._handle_get_agent_card)
167163

168-
if agent_card_url == AGENT_CARD_WELL_KNOWN_PATH:
169-
# For backward compatibility, serve the agent card at the deprecated path as well.
170-
# TODO: remove in a future release
171-
app.get(PREV_AGENT_CARD_WELL_KNOWN_PATH)(
172-
self._handle_get_agent_card
173-
)
174-
175-
if self.agent_card.capabilities.extended_agent_card:
176-
app.get(extended_agent_card_url)(
177-
self._handle_get_authenticated_extended_agent_card
178-
)
179-
180164
def build(
181165
self,
182166
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
183167
rpc_url: str = DEFAULT_RPC_URL,
184-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
185168
**kwargs: Any,
186169
) -> FastAPI:
187170
"""Builds and returns the FastAPI application instance.
188171
189172
Args:
190173
agent_card_url: The URL for the agent card endpoint.
191174
rpc_url: The URL for the A2A JSON-RPC endpoint.
192-
extended_agent_card_url: The URL for the authenticated extended agent card endpoint.
193175
**kwargs: Additional keyword arguments to pass to the FastAPI constructor.
194176
195177
Returns:
196178
A configured FastAPI application instance.
197179
"""
198180
app = A2AFastAPI(**kwargs)
199181

200-
self.add_routes_to_app(
201-
app, agent_card_url, rpc_url, extended_agent_card_url
202-
)
182+
self.add_routes_to_app(app, agent_card_url, rpc_url)
203183

204184
return app

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

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@
4747
from a2a.utils.constants import (
4848
AGENT_CARD_WELL_KNOWN_PATH,
4949
DEFAULT_RPC_URL,
50-
EXTENDED_AGENT_CARD_PATH,
51-
PREV_AGENT_CARD_WELL_KNOWN_PATH,
5250
)
5351
from a2a.utils.errors import (
54-
A2AException,
52+
A2AError,
5553
MethodNotImplementedError,
5654
UnsupportedOperationError,
5755
)
@@ -247,7 +245,7 @@ def __init__( # noqa: PLR0913
247245
def _generate_error_response(
248246
self,
249247
request_id: str | int | None,
250-
error: Exception | JSONRPCError | A2AException,
248+
error: Exception | JSONRPCError | A2AError,
251249
) -> JSONResponse:
252250
"""Creates a Starlette JSONResponse for a JSON-RPC error.
253251
@@ -260,7 +258,7 @@ def _generate_error_response(
260258
Returns:
261259
A `JSONResponse` object formatted as a JSON-RPC error response.
262260
"""
263-
if not isinstance(error, A2AException | JSONRPCError):
261+
if not isinstance(error, A2AError | JSONRPCError):
264262
error = InternalError(message=str(error))
265263

266264
response_data = build_error_response(request_id, error)
@@ -578,14 +576,6 @@ async def _handle_get_agent_card(self, request: Request) -> JSONResponse:
578576
Returns:
579577
A JSONResponse containing the agent card data.
580578
"""
581-
if request.url.path == PREV_AGENT_CARD_WELL_KNOWN_PATH:
582-
logger.warning(
583-
"Deprecated agent card endpoint '%s' accessed. "
584-
"Please use '%s' instead. This endpoint will be removed in a future version.",
585-
PREV_AGENT_CARD_WELL_KNOWN_PATH,
586-
AGENT_CARD_WELL_KNOWN_PATH,
587-
)
588-
589579
card_to_serve = self.agent_card
590580
if self.card_modifier:
591581
card_to_serve = await maybe_await(self.card_modifier(card_to_serve))
@@ -597,62 +587,18 @@ async def _handle_get_agent_card(self, request: Request) -> JSONResponse:
597587
)
598588
)
599589

600-
async def _handle_get_authenticated_extended_agent_card(
601-
self, request: Request
602-
) -> JSONResponse:
603-
"""Handles GET requests for the authenticated extended agent card."""
604-
logger.warning(
605-
'HTTP GET for authenticated extended card has been called by a client. '
606-
'This endpoint is deprecated in favor of agent/authenticatedExtendedCard JSON-RPC method and will be removed in a future release.'
607-
)
608-
if not self.agent_card.capabilities.extended_agent_card:
609-
return JSONResponse(
610-
{'error': 'Extended agent card not supported or not enabled.'},
611-
status_code=404,
612-
)
613-
614-
card_to_serve = self.extended_agent_card
615-
616-
if self.extended_card_modifier:
617-
context = self._context_builder.build(request)
618-
# If no base extended card is provided, pass the public card to the modifier
619-
base_card = card_to_serve if card_to_serve else self.agent_card
620-
card_to_serve = await maybe_await(
621-
self.extended_card_modifier(base_card, context)
622-
)
623-
624-
if card_to_serve:
625-
return JSONResponse(
626-
MessageToDict(
627-
card_to_serve,
628-
preserving_proto_field_name=False,
629-
)
630-
)
631-
# If capabilities.extended_agent_card is true, but no
632-
# extended_agent_card was provided, and no modifier produced a card,
633-
# return a 404.
634-
return JSONResponse(
635-
{
636-
'error': 'Authenticated extended agent card is supported but not configured on the server.'
637-
},
638-
status_code=404,
639-
)
640-
641590
@abstractmethod
642591
def build(
643592
self,
644593
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
645594
rpc_url: str = DEFAULT_RPC_URL,
646-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
647595
**kwargs: Any,
648596
) -> FastAPI | Starlette:
649597
"""Builds and returns the JSONRPC application instance.
650598
651599
Args:
652600
agent_card_url: The URL for the agent card endpoint.
653601
rpc_url: The URL for the A2A JSON-RPC endpoint.
654-
extended_agent_card_url: The URL for the authenticated extended
655-
agent card endpoint.
656602
**kwargs: Additional keyword arguments to pass to the FastAPI constructor.
657603
658604
Returns:

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

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
from a2a.utils.constants import (
3333
AGENT_CARD_WELL_KNOWN_PATH,
3434
DEFAULT_RPC_URL,
35-
EXTENDED_AGENT_CARD_PATH,
36-
PREV_AGENT_CARD_WELL_KNOWN_PATH,
3735
)
3836

3937

@@ -101,19 +99,17 @@ def routes(
10199
self,
102100
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
103101
rpc_url: str = DEFAULT_RPC_URL,
104-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
105102
) -> list[Route]:
106103
"""Returns the Starlette Routes for handling A2A requests.
107104
108105
Args:
109106
agent_card_url: The URL path for the agent card endpoint.
110107
rpc_url: The URL path for the A2A JSON-RPC endpoint (POST requests).
111-
extended_agent_card_url: The URL for the authenticated extended agent card endpoint.
112108
113109
Returns:
114110
A list of Starlette Route objects.
115111
"""
116-
app_routes = [
112+
return [
117113
Route(
118114
rpc_url,
119115
self._handle_requests,
@@ -128,74 +124,43 @@ def routes(
128124
),
129125
]
130126

131-
if agent_card_url == AGENT_CARD_WELL_KNOWN_PATH:
132-
# For backward compatibility, serve the agent card at the deprecated path as well.
133-
# TODO: remove in a future release
134-
app_routes.append(
135-
Route(
136-
PREV_AGENT_CARD_WELL_KNOWN_PATH,
137-
self._handle_get_agent_card,
138-
methods=['GET'],
139-
name='deprecated_agent_card',
140-
)
141-
)
142-
143-
# TODO: deprecated endpoint to be removed in a future release
144-
if self.agent_card.capabilities.extended_agent_card:
145-
app_routes.append(
146-
Route(
147-
extended_agent_card_url,
148-
self._handle_get_authenticated_extended_agent_card,
149-
methods=['GET'],
150-
name='authenticated_extended_agent_card',
151-
)
152-
)
153-
return app_routes
154-
155127
def add_routes_to_app(
156128
self,
157129
app: Starlette,
158130
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
159131
rpc_url: str = DEFAULT_RPC_URL,
160-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
161132
) -> None:
162133
"""Adds the routes to the Starlette application.
163134
164135
Args:
165136
app: The Starlette application to add the routes to.
166137
agent_card_url: The URL path for the agent card endpoint.
167138
rpc_url: The URL path for the A2A JSON-RPC endpoint (POST requests).
168-
extended_agent_card_url: The URL for the authenticated extended agent card endpoint.
169139
"""
170140
routes = self.routes(
171141
agent_card_url=agent_card_url,
172142
rpc_url=rpc_url,
173-
extended_agent_card_url=extended_agent_card_url,
174143
)
175144
app.routes.extend(routes)
176145

177146
def build(
178147
self,
179148
agent_card_url: str = AGENT_CARD_WELL_KNOWN_PATH,
180149
rpc_url: str = DEFAULT_RPC_URL,
181-
extended_agent_card_url: str = EXTENDED_AGENT_CARD_PATH,
182150
**kwargs: Any,
183151
) -> Starlette:
184152
"""Builds and returns the Starlette application instance.
185153
186154
Args:
187155
agent_card_url: The URL path for the agent card endpoint.
188156
rpc_url: The URL path for the A2A JSON-RPC endpoint (POST requests).
189-
extended_agent_card_url: The URL for the authenticated extended agent card endpoint.
190157
**kwargs: Additional keyword arguments to pass to the Starlette constructor.
191158
192159
Returns:
193160
A configured Starlette application instance.
194161
"""
195162
app = Starlette(**kwargs)
196163

197-
self.add_routes_to_app(
198-
app, agent_card_url, rpc_url, extended_agent_card_url
199-
)
164+
self.add_routes_to_app(app, agent_card_url, rpc_url)
200165

201166
return app

src/a2a/server/request_handlers/jsonrpc_handler.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
)
3434
from a2a.utils import proto_utils
3535
from a2a.utils.errors import (
36-
A2AException,
36+
A2AError,
3737
AuthenticatedExtendedCardNotConfiguredError,
3838
ContentTypeNotSupportedError,
3939
InternalError,
@@ -54,7 +54,7 @@
5454
logger = logging.getLogger(__name__)
5555

5656

57-
EXCEPTION_MAP: dict[type[A2AException], type[JSONRPCError]] = {
57+
EXCEPTION_MAP: dict[type[A2AError], type[JSONRPCError]] = {
5858
TaskNotFoundError: JSONRPCError,
5959
TaskNotCancelableError: JSONRPCError,
6060
PushNotificationNotSupportedError: JSONRPCError,
@@ -68,7 +68,7 @@
6868
MethodNotFoundError: JSONRPCError,
6969
}
7070

71-
ERROR_CODE_MAP: dict[type[A2AException], int] = {
71+
ERROR_CODE_MAP: dict[type[A2AError], int] = {
7272
TaskNotFoundError: -32001,
7373
TaskNotCancelableError: -32002,
7474
PushNotificationNotSupportedError: -32003,
@@ -94,7 +94,7 @@ def _build_error_response(
9494
) -> dict[str, Any]:
9595
"""Build a JSON-RPC error response dict."""
9696
jsonrpc_error: JSONRPCError
97-
if isinstance(error, A2AException):
97+
if isinstance(error, A2AError):
9898
error_type = type(error)
9999
model_class = EXCEPTION_MAP.get(error_type, JSONRPCInternalError)
100100
code = ERROR_CODE_MAP.get(error_type, -32603)

src/a2a/server/request_handlers/response_helpers.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
SendMessageResponse as SendMessageResponseProto,
2626
)
2727
from a2a.utils.errors import (
28-
A2AException,
28+
A2AError,
2929
AuthenticatedExtendedCardNotConfiguredError,
3030
ContentTypeNotSupportedError,
3131
InternalError,
@@ -40,7 +40,7 @@
4040
)
4141

4242

43-
EXCEPTION_MAP: dict[type[A2AException], type[JSONRPCError]] = {
43+
EXCEPTION_MAP: dict[type[A2AError], type[JSONRPCError]] = {
4444
TaskNotFoundError: JSONRPCError,
4545
TaskNotCancelableError: JSONRPCError,
4646
PushNotificationNotSupportedError: JSONRPCError,
@@ -54,7 +54,7 @@
5454
InternalError: JSONRPCInternalError,
5555
}
5656

57-
ERROR_CODE_MAP: dict[type[A2AException], int] = {
57+
ERROR_CODE_MAP: dict[type[A2AError], int] = {
5858
TaskNotFoundError: -32001,
5959
TaskNotCancelableError: -32002,
6060
PushNotificationNotSupportedError: -32003,
@@ -69,7 +69,7 @@
6969

7070

7171
# Tuple of all A2AError types for isinstance checks
72-
_A2A_ERROR_TYPES: tuple[type, ...] = (A2AException,)
72+
_A2A_ERROR_TYPES: tuple[type, ...] = (A2AError,)
7373

7474

7575
# Result types for handler responses
@@ -81,7 +81,7 @@
8181
| TaskPushNotificationConfig
8282
| StreamResponse
8383
| SendMessageResponseProto
84-
| A2AException
84+
| A2AError
8585
| JSONRPCError
8686
| list[TaskPushNotificationConfig]
8787
| ListTasksResponse
@@ -91,21 +91,21 @@
9191

9292
def build_error_response(
9393
request_id: str | int | None,
94-
error: A2AException | JSONRPCError,
94+
error: A2AError | JSONRPCError,
9595
) -> dict[str, Any]:
9696
"""Build a JSON-RPC error response dict.
9797
9898
Args:
9999
request_id: The ID of the request that caused the error.
100-
error: The A2AException or JSONRPCError object.
100+
error: The A2AError or JSONRPCError object.
101101
102102
Returns:
103103
A dict representing the JSON-RPC error response.
104104
"""
105105
jsonrpc_error: JSONRPCError
106106
if isinstance(error, JSONRPCError):
107107
jsonrpc_error = error
108-
elif isinstance(error, A2AException):
108+
elif isinstance(error, A2AError):
109109
error_type = type(error)
110110
model_class = EXCEPTION_MAP.get(error_type, JSONRPCInternalError)
111111
code = ERROR_CODE_MAP.get(error_type, -32603)
@@ -145,7 +145,7 @@ def prepare_response_object(
145145
result = MessageToDict(response, preserving_proto_field_name=False)
146146
return JSONRPC20Response(result=result, _id=request_id).data
147147

148-
if isinstance(response, A2AException | JSONRPCError):
148+
if isinstance(response, A2AError | JSONRPCError):
149149
return build_error_response(request_id, response)
150150

151151
# If response is not an expected success type and not an error,

0 commit comments

Comments
 (0)