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