1use crate::error::{Error, Result};
4use crate::mcp::protocol::{CallToolResult, Tool};
5use crate::tap_integration::TapIntegration;
6use crate::tools::{error_text_response, success_text_response, ToolHandler};
7use async_trait::async_trait;
8use serde_json::{json, Value};
9use std::collections::HashMap;
10use std::sync::Arc;
11use tap_msg::didcomm::PlainMessage;
12use tap_msg::message::tap_message_trait::TapMessageBody;
13use tap_msg::message::{BasicMessage, TrustPing};
14use tracing::{debug, error};
15
16pub struct TrustPingTool {
18 tap_integration: Arc<TapIntegration>,
19}
20
21impl TrustPingTool {
22 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
23 Self { tap_integration }
24 }
25}
26
27#[async_trait]
28impl ToolHandler for TrustPingTool {
29 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
30 let args =
31 arguments.ok_or_else(|| Error::tool_execution("Arguments required".to_string()))?;
32
33 let from_did = args
35 .get("from_did")
36 .and_then(|v| v.as_str())
37 .ok_or_else(|| Error::tool_execution("from_did is required".to_string()))?;
38
39 let to_did = args
40 .get("to_did")
41 .and_then(|v| v.as_str())
42 .ok_or_else(|| Error::tool_execution("to_did is required".to_string()))?;
43
44 let response_requested = args
45 .get("response_requested")
46 .and_then(|v| v.as_bool())
47 .unwrap_or(true);
48
49 let comment = args
50 .get("comment")
51 .and_then(|v| v.as_str())
52 .map(|s| s.to_string());
53
54 debug!(
55 "Sending Trust Ping from {} to {}, response_requested: {}",
56 from_did, to_did, response_requested
57 );
58
59 let mut ping = TrustPing::new().response_requested(response_requested);
61
62 if let Some(comment_text) = comment {
63 ping = TrustPing::with_comment(comment_text);
64 ping = ping.response_requested(response_requested);
65 }
66
67 let ping_message = PlainMessage {
69 id: uuid::Uuid::new_v4().to_string(),
70 typ: "application/didcomm-plain+json".to_string(),
71 type_: TrustPing::message_type().to_string(),
72 body: serde_json::to_value(&ping).map_err(|e| {
73 Error::tool_execution(format!("Failed to serialize Trust Ping: {}", e))
74 })?,
75 from: from_did.to_string(),
76 to: vec![to_did.to_string()],
77 thid: None,
78 pthid: None,
79 extra_headers: HashMap::new(),
80 attachments: None,
81 created_time: Some(chrono::Utc::now().timestamp_millis() as u64),
82 expires_time: None,
83 from_prior: None,
84 };
85
86 match self
88 .tap_integration
89 .node()
90 .send_message(from_did.to_string(), ping_message)
91 .await
92 {
93 Ok(message_id) => {
94 let response_text = if response_requested {
95 format!("Trust Ping sent successfully with ID: {}. Response requested from recipient.", message_id)
96 } else {
97 format!(
98 "Trust Ping sent successfully with ID: {}. No response expected.",
99 message_id
100 )
101 };
102 Ok(success_text_response(response_text))
103 }
104 Err(e) => {
105 error!("Failed to send Trust Ping: {}", e);
106 Ok(error_text_response(format!(
107 "Failed to send Trust Ping: {}",
108 e
109 )))
110 }
111 }
112 }
113
114 fn get_definition(&self) -> Tool {
115 Tool {
116 name: "tap_trust_ping".to_string(),
117 description: "Send a Trust Ping message to test connectivity with another agent"
118 .to_string(),
119 input_schema: json!({
120 "type": "object",
121 "properties": {
122 "from_did": {
123 "type": "string",
124 "description": "The DID of the sender agent"
125 },
126 "to_did": {
127 "type": "string",
128 "description": "The DID of the recipient agent"
129 },
130 "response_requested": {
131 "type": "boolean",
132 "description": "Whether a response is requested (default: true)",
133 "default": true
134 },
135 "comment": {
136 "type": "string",
137 "description": "Optional comment to include with the ping"
138 }
139 },
140 "required": ["from_did", "to_did"],
141 "additionalProperties": false
142 }),
143 }
144 }
145}
146
147pub struct BasicMessageTool {
149 tap_integration: Arc<TapIntegration>,
150}
151
152impl BasicMessageTool {
153 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
154 Self { tap_integration }
155 }
156}
157
158#[async_trait]
159impl ToolHandler for BasicMessageTool {
160 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
161 let args =
162 arguments.ok_or_else(|| Error::tool_execution("Arguments required".to_string()))?;
163
164 let from_did = args
166 .get("from_did")
167 .and_then(|v| v.as_str())
168 .ok_or_else(|| Error::tool_execution("from_did is required".to_string()))?;
169
170 let to_did = args
171 .get("to_did")
172 .and_then(|v| v.as_str())
173 .ok_or_else(|| Error::tool_execution("to_did is required".to_string()))?;
174
175 let content = args
176 .get("content")
177 .and_then(|v| v.as_str())
178 .ok_or_else(|| Error::tool_execution("content is required".to_string()))?;
179
180 let locale = args
181 .get("locale")
182 .and_then(|v| v.as_str())
183 .map(|s| s.to_string());
184
185 debug!("Sending Basic Message from {} to {}", from_did, to_did);
186
187 let basic_message = BasicMessage {
189 content: content.to_string(),
190 locale,
191 sent_time: Some(chrono::Utc::now().timestamp_millis() as u64),
192 metadata: HashMap::new(),
193 };
194
195 let message = PlainMessage {
197 id: uuid::Uuid::new_v4().to_string(),
198 typ: "application/didcomm-plain+json".to_string(),
199 type_: BasicMessage::message_type().to_string(),
200 body: serde_json::to_value(&basic_message).map_err(|e| {
201 Error::tool_execution(format!("Failed to serialize Basic Message: {}", e))
202 })?,
203 from: from_did.to_string(),
204 to: vec![to_did.to_string()],
205 thid: None,
206 pthid: None,
207 extra_headers: HashMap::new(),
208 attachments: None,
209 created_time: Some(chrono::Utc::now().timestamp_millis() as u64),
210 expires_time: None,
211 from_prior: None,
212 };
213
214 match self
216 .tap_integration
217 .node()
218 .send_message(from_did.to_string(), message)
219 .await
220 {
221 Ok(message_id) => Ok(success_text_response(format!(
222 "Basic Message sent successfully with ID: {}. Content: \"{}\"",
223 message_id, content
224 ))),
225 Err(e) => {
226 error!("Failed to send Basic Message: {}", e);
227 Ok(error_text_response(format!(
228 "Failed to send Basic Message: {}",
229 e
230 )))
231 }
232 }
233 }
234
235 fn get_definition(&self) -> Tool {
236 Tool {
237 name: "tap_basic_message".to_string(),
238 description: "Send a basic text message to another agent".to_string(),
239 input_schema: json!({
240 "type": "object",
241 "properties": {
242 "from_did": {
243 "type": "string",
244 "description": "The DID of the sender agent"
245 },
246 "to_did": {
247 "type": "string",
248 "description": "The DID of the recipient agent"
249 },
250 "content": {
251 "type": "string",
252 "description": "The text content of the message"
253 },
254 "locale": {
255 "type": "string",
256 "description": "Optional locale/language code (e.g., 'en-US')"
257 }
258 },
259 "required": ["from_did", "to_did", "content"],
260 "additionalProperties": false
261 }),
262 }
263 }
264}