systemprompt_agent/models/web/
create_agent.rs1use crate::models::a2a::{AgentCapabilities, AgentCard, AgentInterface, TransportProtocol};
12use serde::{Deserialize, Serialize};
13
14use super::card_input::AgentCardInput;
15use super::validation::{is_valid_version, list_available_mcp_servers};
16
17#[derive(Debug, Clone, Deserialize)]
18pub struct CreateAgentRequestRaw {
19 pub card: AgentCardInput,
20 pub is_active: Option<bool>,
21 pub system_prompt: Option<String>,
22 pub mcp_servers: Option<Vec<String>>,
23}
24
25#[derive(Debug, Clone, Serialize)]
26pub struct CreateAgentRequest {
27 pub card: AgentCard,
28 pub is_active: Option<bool>,
29 pub system_prompt: Option<String>,
30 pub mcp_servers: Option<Vec<String>>,
31}
32
33impl<'de> Deserialize<'de> for CreateAgentRequest {
34 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35 where
36 D: serde::Deserializer<'de>,
37 {
38 let raw = CreateAgentRequestRaw::deserialize(deserializer)?;
39
40 let url = raw
41 .card
42 .url
43 .unwrap_or_else(|| format!("http://placeholder/api/v1/agents/{}", raw.card.name));
44
45 let card = AgentCard {
46 name: raw.card.name,
47 description: raw.card.description,
48 supported_interfaces: vec![AgentInterface {
49 url,
50 protocol_binding: raw
51 .card
52 .preferred_transport
53 .unwrap_or(TransportProtocol::JsonRpc),
54 protocol_version: raw.card.protocol_version,
55 }],
56 version: raw.card.version,
57 icon_url: None,
58 provider: None,
59 documentation_url: None,
60 capabilities: raw.card.capabilities.normalize(),
61 security_schemes: raw.card.security_schemes,
62 security: raw.card.security,
63 default_input_modes: if raw.card.default_input_modes.is_empty() {
64 vec!["text/plain".to_owned()]
65 } else {
66 raw.card.default_input_modes
67 },
68 default_output_modes: if raw.card.default_output_modes.is_empty() {
69 vec!["text/plain".to_owned()]
70 } else {
71 raw.card.default_output_modes
72 },
73 skills: raw.card.skills,
74 supports_authenticated_extended_card: None,
75 signatures: None,
76 };
77
78 Ok(Self {
79 card,
80 is_active: raw.is_active,
81 system_prompt: raw.system_prompt,
82 mcp_servers: raw.mcp_servers,
83 })
84 }
85}
86
87impl CreateAgentRequest {
88 pub fn from_raw(raw: CreateAgentRequestRaw, api_server_url: &str) -> Self {
89 let url = raw
90 .card
91 .url
92 .unwrap_or_else(|| format!("{}/api/v1/agents/{}", api_server_url, raw.card.name));
93
94 let card = AgentCard {
95 name: raw.card.name,
96 description: raw.card.description,
97 supported_interfaces: vec![AgentInterface {
98 url,
99 protocol_binding: raw
100 .card
101 .preferred_transport
102 .unwrap_or(TransportProtocol::JsonRpc),
103 protocol_version: raw.card.protocol_version,
104 }],
105 version: raw.card.version,
106 icon_url: None,
107 provider: None,
108 documentation_url: None,
109 capabilities: raw.card.capabilities.normalize(),
110 security_schemes: raw.card.security_schemes,
111 security: raw.card.security,
112 default_input_modes: if raw.card.default_input_modes.is_empty() {
113 vec!["text/plain".to_owned()]
114 } else {
115 raw.card.default_input_modes
116 },
117 default_output_modes: if raw.card.default_output_modes.is_empty() {
118 vec!["text/plain".to_owned()]
119 } else {
120 raw.card.default_output_modes
121 },
122 skills: raw.card.skills,
123 supports_authenticated_extended_card: None,
124 signatures: None,
125 };
126
127 Self {
128 card,
129 is_active: raw.is_active,
130 system_prompt: raw.system_prompt,
131 mcp_servers: raw.mcp_servers,
132 }
133 }
134
135 pub async fn validate(&self) -> Result<(), String> {
136 if self.card.name.trim().is_empty() {
137 return Err("Name is required".to_owned());
138 }
139
140 let card_url = self.card.url().unwrap_or("");
141 if !card_url.starts_with("http://") && !card_url.starts_with("https://") {
142 return Err("URL must be a valid HTTP or HTTPS URL".to_owned());
143 }
144
145 if !is_valid_version(&self.card.version) {
146 return Err("Version must be in semantic version format (e.g., 1.0.0)".to_owned());
147 }
148
149 if let Some(ref mcp_servers) = self.mcp_servers
150 && !mcp_servers.is_empty()
151 {
152 let available_servers = list_available_mcp_servers().await?;
153 let mut invalid_servers = Vec::new();
154
155 for server in mcp_servers {
156 if !available_servers.contains(server) {
157 invalid_servers.push(server.clone());
158 }
159 }
160
161 if !invalid_servers.is_empty() {
162 return Err(format!(
163 "Invalid MCP server(s): {}. Available servers: {}",
164 invalid_servers.join(", "),
165 if available_servers.is_empty() {
166 "(none)".to_owned()
167 } else {
168 available_servers.join(", ")
169 }
170 ));
171 }
172 }
173
174 Ok(())
175 }
176
177 pub fn get_version(&self) -> String {
178 self.card.version.clone()
179 }
180
181 pub fn is_active(&self) -> bool {
182 self.is_active.unwrap_or(true)
183 }
184
185 pub fn extract_port(&self) -> u16 {
186 self.card
187 .url()
188 .and_then(super::validation::extract_port_from_url)
189 .unwrap_or(80)
190 }
191
192 pub const fn get_capabilities(&self) -> &AgentCapabilities {
193 &self.card.capabilities
194 }
195}