1use super::schema;
4use super::{error_text_response, success_text_response, ToolHandler};
5use crate::error::{Error, Result};
6use crate::mcp::protocol::{CallToolResult, Tool};
7use crate::tap_integration::TapIntegration;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::sync::Arc;
11use tap_msg::message::tap_message_trait::TapMessageBody;
12use tap_msg::message::{AddAgents, Agent, RemoveAgent, ReplaceAgent};
13use tracing::{debug, error};
14
15pub struct AddAgentsTool {
17 tap_integration: Arc<TapIntegration>,
18}
19
20#[derive(Debug, Deserialize)]
22struct AddAgentsParams {
23 agent_did: String, transaction_id: String,
25 agents: Vec<AgentInfo>,
26}
27
28#[derive(Debug, Deserialize)]
29struct AgentInfo {
30 #[serde(rename = "@id")]
31 id: String,
32 role: String,
33 #[serde(rename = "for")]
34 for_party: String,
35}
36
37#[derive(Debug, Serialize)]
39struct AddAgentsResponse {
40 transaction_id: String,
41 message_id: String,
42 status: String,
43 agents_added: usize,
44 added_at: String,
45}
46
47impl AddAgentsTool {
48 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
49 Self { tap_integration }
50 }
51
52 fn tap_integration(&self) -> &TapIntegration {
53 &self.tap_integration
54 }
55}
56
57#[async_trait::async_trait]
58impl ToolHandler for AddAgentsTool {
59 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
60 let params: AddAgentsParams = match arguments {
61 Some(args) => serde_json::from_value(args)
62 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
63 None => {
64 return Ok(error_text_response(
65 "Missing required parameters".to_string(),
66 ))
67 }
68 };
69
70 debug!(
71 "Adding {} agents to transaction: {}",
72 params.agents.len(),
73 params.transaction_id
74 );
75
76 let agents: Vec<Agent> = params
78 .agents
79 .iter()
80 .map(|agent_info| Agent::new(&agent_info.id, &agent_info.role, &agent_info.for_party))
81 .collect();
82
83 let add_agents = AddAgents::new(¶ms.transaction_id, agents);
85
86 if let Err(e) = add_agents.validate() {
88 return Ok(error_text_response(format!(
89 "AddAgents validation failed: {}",
90 e
91 )));
92 }
93
94 let didcomm_message = match add_agents.to_didcomm(¶ms.agent_did) {
96 Ok(msg) => msg,
97 Err(e) => {
98 return Ok(error_text_response(format!(
99 "Failed to create DIDComm message: {}",
100 e
101 )));
102 }
103 };
104
105 let recipient_did = if !didcomm_message.to.is_empty() {
107 didcomm_message.to[0].clone()
108 } else {
109 return Ok(error_text_response(
110 "No recipient found for add agents message".to_string(),
111 ));
112 };
113
114 debug!(
115 "Sending add agents from {} to {} for transaction: {}",
116 params.agent_did, recipient_did, params.transaction_id
117 );
118
119 match self
121 .tap_integration()
122 .node()
123 .send_message(params.agent_did.clone(), didcomm_message.clone())
124 .await
125 {
126 Ok(packed_message) => {
127 debug!(
128 "AddAgents message sent successfully to {}, packed message length: {}",
129 recipient_did,
130 packed_message.len()
131 );
132
133 let response = AddAgentsResponse {
134 transaction_id: params.transaction_id,
135 message_id: didcomm_message.id,
136 status: "sent".to_string(),
137 agents_added: add_agents.agents.len(),
138 added_at: chrono::Utc::now().to_rfc3339(),
139 };
140
141 let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
142 Error::tool_execution(format!("Failed to serialize response: {}", e))
143 })?;
144
145 Ok(success_text_response(response_json))
146 }
147 Err(e) => {
148 error!("Failed to send add agents message: {}", e);
149 Ok(error_text_response(format!(
150 "Failed to send add agents message: {}",
151 e
152 )))
153 }
154 }
155 }
156
157 fn get_definition(&self) -> Tool {
158 Tool {
159 name: "tap_add_agents".to_string(),
160 description: "Adds agents to a TAP transaction using the AddAgents message (TAIP-5)"
161 .to_string(),
162 input_schema: schema::add_agents_schema(),
163 }
164 }
165}
166
167pub struct RemoveAgentTool {
169 tap_integration: Arc<TapIntegration>,
170}
171
172#[derive(Debug, Deserialize)]
174struct RemoveAgentParams {
175 agent_did: String, transaction_id: String,
177 agent_to_remove: String,
178}
179
180#[derive(Debug, Serialize)]
182struct RemoveAgentResponse {
183 transaction_id: String,
184 message_id: String,
185 status: String,
186 removed_agent: String,
187 removed_at: String,
188}
189
190impl RemoveAgentTool {
191 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
192 Self { tap_integration }
193 }
194
195 fn tap_integration(&self) -> &TapIntegration {
196 &self.tap_integration
197 }
198}
199
200#[async_trait::async_trait]
201impl ToolHandler for RemoveAgentTool {
202 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
203 let params: RemoveAgentParams = match arguments {
204 Some(args) => serde_json::from_value(args)
205 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
206 None => {
207 return Ok(error_text_response(
208 "Missing required parameters".to_string(),
209 ))
210 }
211 };
212
213 debug!(
214 "Removing agent {} from transaction: {}",
215 params.agent_to_remove, params.transaction_id
216 );
217
218 let remove_agent = RemoveAgent::new(¶ms.transaction_id, ¶ms.agent_to_remove);
220
221 if let Err(e) = remove_agent.validate() {
223 return Ok(error_text_response(format!(
224 "RemoveAgent validation failed: {}",
225 e
226 )));
227 }
228
229 let didcomm_message = match remove_agent.to_didcomm(¶ms.agent_did) {
231 Ok(msg) => msg,
232 Err(e) => {
233 return Ok(error_text_response(format!(
234 "Failed to create DIDComm message: {}",
235 e
236 )));
237 }
238 };
239
240 let recipient_did = if !didcomm_message.to.is_empty() {
242 didcomm_message.to[0].clone()
243 } else {
244 return Ok(error_text_response(
245 "No recipient found for remove agent message".to_string(),
246 ));
247 };
248
249 debug!(
250 "Sending remove agent from {} to {} for transaction: {}",
251 params.agent_did, recipient_did, params.transaction_id
252 );
253
254 match self
256 .tap_integration()
257 .node()
258 .send_message(params.agent_did.clone(), didcomm_message.clone())
259 .await
260 {
261 Ok(packed_message) => {
262 debug!(
263 "RemoveAgent message sent successfully to {}, packed message length: {}",
264 recipient_did,
265 packed_message.len()
266 );
267
268 let response = RemoveAgentResponse {
269 transaction_id: params.transaction_id,
270 message_id: didcomm_message.id,
271 status: "sent".to_string(),
272 removed_agent: params.agent_to_remove,
273 removed_at: chrono::Utc::now().to_rfc3339(),
274 };
275
276 let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
277 Error::tool_execution(format!("Failed to serialize response: {}", e))
278 })?;
279
280 Ok(success_text_response(response_json))
281 }
282 Err(e) => {
283 error!("Failed to send remove agent message: {}", e);
284 Ok(error_text_response(format!(
285 "Failed to send remove agent message: {}",
286 e
287 )))
288 }
289 }
290 }
291
292 fn get_definition(&self) -> Tool {
293 Tool {
294 name: "tap_remove_agent".to_string(),
295 description:
296 "Removes an agent from a TAP transaction using the RemoveAgent message (TAIP-5)"
297 .to_string(),
298 input_schema: schema::remove_agent_schema(),
299 }
300 }
301}
302
303pub struct ReplaceAgentTool {
305 tap_integration: Arc<TapIntegration>,
306}
307
308#[derive(Debug, Deserialize)]
310struct ReplaceAgentParams {
311 agent_did: String, transaction_id: String,
313 original_agent: String,
314 new_agent: AgentInfo,
315}
316
317#[derive(Debug, Serialize)]
319struct ReplaceAgentResponse {
320 transaction_id: String,
321 message_id: String,
322 status: String,
323 old_agent: String,
324 new_agent: String,
325 replaced_at: String,
326}
327
328impl ReplaceAgentTool {
329 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
330 Self { tap_integration }
331 }
332
333 fn tap_integration(&self) -> &TapIntegration {
334 &self.tap_integration
335 }
336}
337
338#[async_trait::async_trait]
339impl ToolHandler for ReplaceAgentTool {
340 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
341 let params: ReplaceAgentParams = match arguments {
342 Some(args) => serde_json::from_value(args)
343 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
344 None => {
345 return Ok(error_text_response(
346 "Missing required parameters".to_string(),
347 ))
348 }
349 };
350
351 debug!(
352 "Replacing agent {} with {} in transaction: {}",
353 params.original_agent, params.new_agent.id, params.transaction_id
354 );
355
356 let replacement_agent = Agent::new(
358 ¶ms.new_agent.id,
359 ¶ms.new_agent.role,
360 ¶ms.new_agent.for_party,
361 );
362
363 let replace_agent = ReplaceAgent::new(
365 ¶ms.transaction_id,
366 ¶ms.original_agent,
367 replacement_agent,
368 );
369
370 if let Err(e) = replace_agent.validate() {
372 return Ok(error_text_response(format!(
373 "ReplaceAgent validation failed: {}",
374 e
375 )));
376 }
377
378 let didcomm_message = match replace_agent.to_didcomm(¶ms.agent_did) {
380 Ok(msg) => msg,
381 Err(e) => {
382 return Ok(error_text_response(format!(
383 "Failed to create DIDComm message: {}",
384 e
385 )));
386 }
387 };
388
389 let recipient_did = if !didcomm_message.to.is_empty() {
391 didcomm_message.to[0].clone()
392 } else {
393 return Ok(error_text_response(
394 "No recipient found for replace agent message".to_string(),
395 ));
396 };
397
398 debug!(
399 "Sending replace agent from {} to {} for transaction: {}",
400 params.agent_did, recipient_did, params.transaction_id
401 );
402
403 match self
405 .tap_integration()
406 .node()
407 .send_message(params.agent_did.clone(), didcomm_message.clone())
408 .await
409 {
410 Ok(packed_message) => {
411 debug!(
412 "ReplaceAgent message sent successfully to {}, packed message length: {}",
413 recipient_did,
414 packed_message.len()
415 );
416
417 let response = ReplaceAgentResponse {
418 transaction_id: params.transaction_id,
419 message_id: didcomm_message.id,
420 status: "sent".to_string(),
421 old_agent: params.original_agent,
422 new_agent: params.new_agent.id,
423 replaced_at: chrono::Utc::now().to_rfc3339(),
424 };
425
426 let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
427 Error::tool_execution(format!("Failed to serialize response: {}", e))
428 })?;
429
430 Ok(success_text_response(response_json))
431 }
432 Err(e) => {
433 error!("Failed to send replace agent message: {}", e);
434 Ok(error_text_response(format!(
435 "Failed to send replace agent message: {}",
436 e
437 )))
438 }
439 }
440 }
441
442 fn get_definition(&self) -> Tool {
443 Tool {
444 name: "tap_replace_agent".to_string(),
445 description:
446 "Replaces an agent in a TAP transaction using the ReplaceAgent message (TAIP-5)"
447 .to_string(),
448 input_schema: schema::replace_agent_schema(),
449 }
450 }
451}