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