You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
#895 mentions lack of agent card validation on the client which may be used to trigger calls to private URLs when agent card is coming from an untrusted source.
Currently no hooks are provided for such validation, which is especially important for functions like create_client and create_from_url which support URL fetch.
Describe the solution you'd like
The SDK should not define the rules of validation and private URLs should not be forbidden as it can be a perfectly valid use case.
"""Built-in validators for agent cards."""from __future__ importannotationsimportipaddressfromcollections.abcimportCallablefromurllib.parseimporturlparsefroma2a.typesimportAgentCard# Type alias: a validator raises on invalid, returns None on successCardValidator=Callable[[AgentCard], None]
classInvalidAgentCardError(ValueError):
"""Raised when an agent card fails validation."""defreject_non_https_urls(card: AgentCard) ->None:
"""Rejects cards whose primary URL is not https://."""parsed=urlparse(card.url)
ifparsed.scheme!="https":
raiseInvalidAgentCardError(
f"Agent card URL must use https, got: {card.url!r}"
)
# Also validate any interface URLs if presentforifaceingetattr(card, "additional_interfaces", None) or []:
ifurlparse(iface.url).scheme!="https":
raiseInvalidAgentCardError(
f"Additional interface URL must use https, got: {iface.url!r}"
)
defreject_private_urls(card: AgentCard) ->None:
"""Rejects cards that point to private/loopback/link-local addresses."""_check_not_private(card.url)
forifaceingetattr(card, "additional_interfaces", None) or []:
_check_not_private(iface.url)
def_check_not_private(url: str) ->None:
parsed=urlparse(url)
host=parsed.hostnameifhostisNone:
raiseInvalidAgentCardError(f"URL has no hostname: {url!r}")
try:
ip=ipaddress.ip_address(host)
exceptValueError:
# Not a raw IP - skip (DNS-level SSRF protection needs separate handling)returnifip.is_privateorip.is_loopbackorip.is_link_localorip.is_reserved:
raiseInvalidAgentCardError(
f"URL points to private/loopback address: {url!r}"
)
Activity
mtsmyassin commentedon Apr 17, 2026
Proposed Implementation
Here's a concrete sketch of the agent card validation hook:
1. Validator types (
src/a2a/client/card_validators.py)2. Client factory integration (
src/a2a/client/client_factory.py)