1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use super::model::{default_input_modes, default_output_modes, default_protocol_version};
5
6pub const A2A_VERSION_V101: &str = "1.0.1";
9
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "lowercase")]
14pub enum A2ATransport {
15 JsonRpc,
17 HttpJson,
19 Grpc,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
29pub struct AgentInterface {
30 #[serde(rename = "protocolVersion")]
32 pub protocol_version: String,
33 pub transport: A2ATransport,
35 pub url: String,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub tenant: Option<String>,
40}
41
42impl AgentInterface {
43 pub fn new(
45 protocol_version: impl Into<String>,
46 transport: A2ATransport,
47 url: impl Into<String>,
48 ) -> Self {
49 Self {
50 protocol_version: protocol_version.into(),
51 transport,
52 url: url.into(),
53 tenant: None,
54 }
55 }
56
57 pub fn with_tenant(mut self, tenant: impl Into<String>) -> Self {
59 self.tenant = Some(tenant.into());
60 self
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct AgentSkill {
70 pub id: String,
72 pub name: String,
74 pub description: String,
76}
77
78impl AgentSkill {
79 pub fn new(
81 id: impl Into<String>,
82 name: impl Into<String>,
83 description: impl Into<String>,
84 ) -> Self {
85 Self {
86 id: id.into(),
87 name: name.into(),
88 description: description.into(),
89 }
90 }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct AgentCard {
100 pub name: String,
102 pub description: String,
104 pub url: String,
106 #[serde(default)]
108 pub skills: Vec<AgentSkill>,
109 #[serde(default = "default_protocol_version", rename = "protocolVersion")]
111 pub protocol_version: String,
112 #[serde(skip_serializing_if = "Option::is_none", rename = "securitySchemes")]
114 pub security_schemes: Option<Value>,
115 #[serde(skip_serializing_if = "Option::is_none")]
120 pub interfaces: Option<Value>,
121 #[serde(
127 default,
128 skip_serializing_if = "Vec::is_empty",
129 rename = "supportedInterfaces"
130 )]
131 pub supported_interfaces: Vec<AgentInterface>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub provider: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub documentation_url: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub authentication: Option<Vec<String>>,
141 #[serde(default = "default_input_modes")]
143 pub default_input_modes: Vec<String>,
144 #[serde(default = "default_output_modes")]
146 pub default_output_modes: Vec<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
152 pub signature: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none", rename = "dataClass")]
156 pub data_class: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
160 pub jurisdiction: Option<String>,
161 #[serde(default, skip_serializing_if = "Vec::is_empty")]
167 pub capabilities: Vec<String>,
168}
169
170impl AgentCard {
171 pub fn new(
173 name: impl Into<String>,
174 description: impl Into<String>,
175 url: impl Into<String>,
176 ) -> Self {
177 Self {
178 name: name.into(),
179 description: description.into(),
180 url: url.into(),
181 skills: Vec::new(),
182 protocol_version: default_protocol_version(),
183 security_schemes: None,
184 interfaces: None,
185 supported_interfaces: Vec::new(),
186 provider: None,
187 documentation_url: None,
188 authentication: None,
189 default_input_modes: default_input_modes(),
190 default_output_modes: default_output_modes(),
191 signature: None,
192 data_class: None,
193 jurisdiction: None,
194 capabilities: Vec::new(),
195 }
196 }
197
198 pub fn with_skill(mut self, skill: AgentSkill) -> Self {
200 self.skills.push(skill);
201 self
202 }
203
204 pub fn with_protocol_version(mut self, version: impl Into<String>) -> Self {
206 self.protocol_version = version.into();
207 self
208 }
209
210 pub fn with_security_schemes(mut self, schemes: Value) -> Self {
212 self.security_schemes = Some(schemes);
213 self
214 }
215
216 pub fn with_interfaces(mut self, interfaces: Value) -> Self {
218 self.interfaces = Some(interfaces);
219 self
220 }
221
222 pub fn with_supported_interface(mut self, interface: AgentInterface) -> Self {
224 self.supported_interfaces.push(interface);
225 self
226 }
227
228 pub fn negotiate(
233 &self,
234 transport: A2ATransport,
235 client_versions: &[&str],
236 ) -> Result<AgentInterface, String> {
237 self.supported_interfaces
238 .iter()
239 .find(|i| {
240 i.transport == transport && client_versions.contains(&i.protocol_version.as_str())
241 })
242 .cloned()
243 .ok_or_else(|| {
244 format!(
245 "no mutually supported interface: transport={transport:?} client_versions={client_versions:?}"
246 )
247 })
248 }
249
250 pub fn is_v101(&self) -> bool {
252 !self.supported_interfaces.is_empty()
253 }
254
255 pub fn with_provider(mut self, provider: impl Into<String>) -> Self {
257 self.provider = Some(provider.into());
258 self
259 }
260
261 pub fn with_documentation_url(mut self, url: impl Into<String>) -> Self {
263 self.documentation_url = Some(url.into());
264 self
265 }
266
267 pub fn with_authentication(mut self, schemes: Vec<String>) -> Self {
269 self.authentication = Some(schemes);
270 self
271 }
272
273 pub fn with_signature(mut self, signature: impl Into<String>) -> Self {
275 self.signature = Some(signature.into());
276 self
277 }
278
279 pub fn with_data_class(mut self, class: impl Into<String>) -> Self {
281 self.data_class = Some(class.into());
282 self
283 }
284
285 pub fn with_jurisdiction(mut self, jurisdiction: impl Into<String>) -> Self {
287 self.jurisdiction = Some(jurisdiction.into());
288 self
289 }
290
291 pub fn with_capability(mut self, capability: impl Into<String>) -> Self {
293 self.capabilities.push(capability.into());
294 self
295 }
296}