Skip to main content

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}