a2a_protocol_client/config.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Tom F.
3
4//! Client configuration types.
5//!
6//! [`ClientConfig`] controls how the client connects to agents: which transport
7//! to prefer, what content types to accept, timeouts, and TLS settings.
8
9use std::time::Duration;
10
11// ── ProtocolBinding ─────────────────────────────────────────────────────────
12
13/// Protocol binding identifier.
14///
15/// In v1.0, protocol bindings are free-form strings (`"JSONRPC"`, `"REST"`,
16/// `"GRPC"`) rather than a fixed enum.
17pub const BINDING_JSONRPC: &str = "JSONRPC";
18
19/// HTTP+JSON protocol binding (spec name for the REST transport).
20pub const BINDING_HTTP_JSON: &str = "HTTP+JSON";
21
22/// REST protocol binding (legacy alias for [`BINDING_HTTP_JSON`]).
23pub const BINDING_REST: &str = "REST";
24
25/// gRPC protocol binding.
26pub const BINDING_GRPC: &str = "GRPC";
27
28// ── TlsConfig ────────────────────────────────────────────────────────────────
29
30/// TLS configuration for the HTTP client.
31///
32/// When TLS is disabled, the client only supports plain HTTP (`http://` URLs).
33/// Enable the `tls-rustls` feature to support HTTPS.
34#[derive(Debug, Clone)]
35pub enum TlsConfig {
36 /// Plain HTTP only; HTTPS connections will fail.
37 Disabled,
38 /// Enable TLS using the system's default configuration.
39 ///
40 /// Requires the `tls-rustls` feature.
41 #[cfg(feature = "tls-rustls")]
42 Rustls,
43}
44
45#[allow(clippy::derivable_impls)]
46impl Default for TlsConfig {
47 fn default() -> Self {
48 #[cfg(feature = "tls-rustls")]
49 {
50 Self::Rustls
51 }
52 #[cfg(not(feature = "tls-rustls"))]
53 {
54 Self::Disabled
55 }
56 }
57}
58
59// ── ClientConfig ──────────────────────────────────────────────────────────────
60
61/// Configuration for an [`crate::A2aClient`] instance.
62///
63/// Build via [`crate::ClientBuilder`]. Reasonable defaults are provided for all
64/// fields; most users only need to set the agent URL.
65#[derive(Debug, Clone)]
66pub struct ClientConfig {
67 /// Ordered list of preferred protocol bindings.
68 ///
69 /// The client tries each in order, selecting the first one supported by the
70 /// target agent's card. Defaults to `["JSONRPC"]`.
71 pub preferred_bindings: Vec<String>,
72
73 /// MIME types the client will advertise in `acceptedOutputModes`.
74 ///
75 /// Defaults to `["text/plain", "application/json"]`.
76 pub accepted_output_modes: Vec<String>,
77
78 /// Number of historical messages to include in task responses.
79 ///
80 /// `None` means use the agent's default.
81 pub history_length: Option<u32>,
82
83 /// If `true`, `send_message` returns immediately with the submitted task
84 /// rather than waiting for completion.
85 pub return_immediately: bool,
86
87 /// Per-request timeout for non-streaming calls.
88 ///
89 /// Defaults to 30 seconds.
90 pub request_timeout: Duration,
91
92 /// Per-request timeout for establishing the SSE stream.
93 ///
94 /// Once the stream is established this timeout no longer applies.
95 /// Defaults to 30 seconds.
96 pub stream_connect_timeout: Duration,
97
98 /// TCP connection timeout (DNS + handshake).
99 ///
100 /// Prevents the client from hanging for the OS default (~2 minutes)
101 /// when the server is unreachable. Defaults to 10 seconds.
102 pub connection_timeout: Duration,
103
104 /// TLS configuration.
105 pub tls: TlsConfig,
106}
107
108impl ClientConfig {
109 /// Returns the default configuration suitable for connecting to a local
110 /// or well-known agent over plain HTTP.
111 #[must_use]
112 pub fn default_http() -> Self {
113 Self {
114 preferred_bindings: vec![BINDING_JSONRPC.into()],
115 accepted_output_modes: vec!["text/plain".into(), "application/json".into()],
116 history_length: None,
117 return_immediately: false,
118 request_timeout: Duration::from_secs(30),
119 stream_connect_timeout: Duration::from_secs(30),
120 connection_timeout: Duration::from_secs(10),
121 tls: TlsConfig::Disabled,
122 }
123 }
124}
125
126impl Default for ClientConfig {
127 fn default() -> Self {
128 Self {
129 preferred_bindings: vec![BINDING_JSONRPC.into()],
130 accepted_output_modes: vec!["text/plain".into(), "application/json".into()],
131 history_length: None,
132 return_immediately: false,
133 request_timeout: Duration::from_secs(30),
134 stream_connect_timeout: Duration::from_secs(30),
135 connection_timeout: Duration::from_secs(10),
136 tls: TlsConfig::default(),
137 }
138 }
139}
140
141// ── Tests ─────────────────────────────────────────────────────────────────────
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn default_config_has_jsonrpc_binding() {
149 let cfg = ClientConfig::default();
150 assert_eq!(cfg.preferred_bindings, vec![BINDING_JSONRPC]);
151 }
152
153 #[test]
154 fn default_config_timeout() {
155 let cfg = ClientConfig::default();
156 assert_eq!(cfg.request_timeout, Duration::from_secs(30));
157 }
158
159 #[test]
160 fn default_http_config_is_disabled_tls() {
161 let cfg = ClientConfig::default_http();
162 assert!(matches!(cfg.tls, TlsConfig::Disabled));
163 }
164}