Skip to content

Commit 7a6397e

Browse files
author
Fankouzu
committed
feat(security,performance): implement TLS support, JSON Schema validation, and uvloop optimization
1 parent d2d3860 commit 7a6397e

9 files changed

Lines changed: 1006 additions & 7 deletions

File tree

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies = [
1313
"pydantic>=2.11.3",
1414
"protobuf>=5.29.5",
1515
"google-api-core>=1.26.0",
16+
"jsonschema>=4.23.0",
1617
]
1718

1819
classifiers = [
@@ -37,6 +38,8 @@ postgresql = ["sqlalchemy[asyncio,postgresql-asyncpg]>=2.0.0"]
3738
mysql = ["sqlalchemy[asyncio,aiomysql]>=2.0.0"]
3839
signing = ["PyJWT>=2.0.0"]
3940
sqlite = ["sqlalchemy[asyncio,aiosqlite]>=2.0.0"]
41+
performance = ["uvloop>=0.21.0"]
42+
tls = ["cryptography>=43.0.0"]
4043

4144
sql = ["a2a-sdk[postgresql,mysql,sqlite]"]
4245

@@ -47,6 +50,8 @@ all = [
4750
"a2a-sdk[grpc]",
4851
"a2a-sdk[telemetry]",
4952
"a2a-sdk[signing]",
53+
"a2a-sdk[performance]",
54+
"a2a-sdk[tls]",
5055
]
5156

5257
[project.urls]

src/a2a/__init__.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,32 @@
1-
"""The A2A Python SDK."""
1+
"""The A2A Python SDK.
2+
3+
This SDK provides tools for building A2A (Agent-to-Agent) protocol clients
4+
and servers with support for:
5+
6+
- TLS/SSL secure communication (see a2a.client.TLSConfig)
7+
- JSON Schema message validation (see a2a.validation)
8+
- High-performance async operations via uvloop (see a2a.performance)
9+
10+
Example usage:
11+
12+
from a2a.client import Client, ClientConfig, TLSConfig
13+
from a2a.validation import validate_message
14+
from a2a.performance import install_uvloop
15+
16+
# Enable uvloop for better performance
17+
install_uvloop()
18+
19+
# Configure TLS
20+
tls = TLSConfig(
21+
enabled=True,
22+
verify='/path/to/ca.pem',
23+
cert=('/path/to/client.pem', '/path/to/key.pem'),
24+
)
25+
26+
config = ClientConfig(tls_config=tls)
27+
"""
28+
29+
from a2a import client, performance, types, validation
30+
31+
32+
__all__ = ['client', 'performance', 'types', 'validation']

src/a2a/client/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
from a2a.client.helpers import create_text_message_object
2121
from a2a.client.legacy import A2AClient
2222
from a2a.client.middleware import ClientCallContext, ClientCallInterceptor
23+
from a2a.client.tls import (
24+
TLSConfig,
25+
create_grpc_channel_factory,
26+
create_server_ssl_context,
27+
)
2328

2429

2530
logger = logging.getLogger(__name__)
@@ -62,6 +67,9 @@ def __init__(self, *args, **kwargs):
6267
'Consumer',
6368
'CredentialService',
6469
'InMemoryContextCredentialStore',
70+
'TLSConfig',
71+
'create_grpc_channel_factory',
72+
'create_server_ssl_context',
6573
'create_text_message_object',
6674
'minimal_agent_card',
6775
]

src/a2a/client/client.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from abc import ABC, abstractmethod
55
from collections.abc import AsyncIterator, Callable, Coroutine
6-
from typing import Any
6+
from typing import TYPE_CHECKING, Any
77

88
import httpx
99

@@ -24,6 +24,10 @@
2424
)
2525

2626

27+
if TYPE_CHECKING:
28+
from a2a.client.tls import TLSConfig
29+
30+
2731
logger = logging.getLogger(__name__)
2832

2933

@@ -70,6 +74,46 @@ class ClientConfig:
7074
extensions: list[str] = dataclasses.field(default_factory=list)
7175
"""A list of extension URIs the client supports."""
7276

77+
tls_config: 'TLSConfig | None' = None
78+
"""TLS/SSL configuration for secure communication. If provided, this
79+
will be used to configure secure connections for HTTP and gRPC
80+
transports. Ignored if httpx_client or grpc_channel_factory is
81+
explicitly provided."""
82+
83+
validate_messages: bool = False
84+
"""Whether to validate messages against JSON Schema before sending
85+
and after receiving. Useful for protocol compliance testing."""
86+
87+
def get_httpx_client(self) -> httpx.AsyncClient:
88+
"""Get or create an httpx client with TLS configuration.
89+
90+
Returns:
91+
Configured httpx.AsyncClient instance.
92+
"""
93+
if self.httpx_client is not None:
94+
return self.httpx_client
95+
96+
if self.tls_config is not None:
97+
return self.tls_config.create_httpx_client()
98+
99+
return httpx.AsyncClient()
100+
101+
def get_grpc_channel_factory(self) -> Callable[[str], Channel] | None:
102+
"""Get or create a gRPC channel factory with TLS configuration.
103+
104+
Returns:
105+
A callable that creates gRPC channels, or None.
106+
"""
107+
if self.grpc_channel_factory is not None:
108+
return self.grpc_channel_factory
109+
110+
if self.tls_config is not None:
111+
from a2a.client.tls import create_grpc_channel_factory
112+
113+
return create_grpc_channel_factory(self.tls_config)
114+
115+
return None
116+
73117

74118
UpdateEvent = TaskStatusUpdateEvent | TaskArtifactUpdateEvent | None
75119
# Alias for emitted events from client

src/a2a/client/client_factory.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,17 @@ def __init__(
7171
def _register_defaults(
7272
self, supported: list[str | TransportProtocol]
7373
) -> None:
74-
# Empty support list implies JSON-RPC only.
74+
httpx_client = self._config.httpx_client
75+
if httpx_client is None and self._config.tls_config is not None:
76+
httpx_client = self._config.tls_config.create_httpx_client()
77+
elif httpx_client is None:
78+
httpx_client = httpx.AsyncClient()
79+
7580
if TransportProtocol.jsonrpc in supported or not supported:
7681
self.register(
7782
TransportProtocol.jsonrpc,
7883
lambda card, url, config, interceptors: JsonRpcTransport(
79-
config.httpx_client or httpx.AsyncClient(),
84+
httpx_client,
8085
card,
8186
url,
8287
interceptors,
@@ -87,7 +92,7 @@ def _register_defaults(
8792
self.register(
8893
TransportProtocol.http_json,
8994
lambda card, url, config, interceptors: RestTransport(
90-
config.httpx_client or httpx.AsyncClient(),
95+
httpx_client,
9196
card,
9297
url,
9398
interceptors,

0 commit comments

Comments
 (0)