Skip to main content

tap_mcp/tools/
received_tools.rs

1//! Tools for working with received messages
2
3use crate::error::Result;
4use crate::mcp::protocol::{CallToolResult, Tool};
5use crate::tap_integration::TapIntegration;
6use crate::tools::{default_limit, error_text_response, ToolContent, ToolHandler};
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use serde_json::{json, Value};
10use std::sync::Arc;
11use tap_node::storage::{ReceivedStatus, SourceType};
12use tracing::{debug, error};
13
14/// Input for listing received messages
15#[derive(Debug, Deserialize)]
16pub struct ListReceivedInput {
17    /// The DID of the agent whose received messages to list
18    pub agent_did: String,
19    /// Maximum number of messages to return
20    #[serde(default = "default_limit")]
21    pub limit: u32,
22    /// Number of messages to skip for pagination
23    #[serde(default)]
24    pub offset: u32,
25    /// Filter by source type
26    pub source_type: Option<String>,
27    /// Filter by status
28    pub status: Option<String>,
29}
30
31/// Output for received message listing
32#[derive(Debug, Serialize)]
33pub struct ReceivedMessage {
34    /// Unique ID of the received record
35    pub id: i64,
36    /// Message ID extracted from the raw message (if available)
37    pub message_id: Option<String>,
38    /// Source type (https, internal, websocket, return_path, pickup)
39    pub source_type: String,
40    /// Source identifier (URL, agent DID, etc.)
41    pub source_identifier: Option<String>,
42    /// Processing status (pending, processed, failed)
43    pub status: String,
44    /// Error message if processing failed
45    pub error_message: Option<String>,
46    /// When the message was received
47    pub received_at: String,
48    /// When the message was processed
49    pub processed_at: Option<String>,
50    /// ID of the processed message in messages table
51    pub processed_message_id: Option<String>,
52}
53
54/// Tool for listing received messages
55pub struct ListReceivedTool {
56    tap_integration: Arc<TapIntegration>,
57}
58
59impl ListReceivedTool {
60    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
61        Self { tap_integration }
62    }
63}
64
65#[async_trait]
66impl ToolHandler for ListReceivedTool {
67    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
68        let input: ListReceivedInput = match arguments {
69            Some(args) => serde_json::from_value(args)?,
70            None => {
71                return Ok(error_text_response(
72                    "Missing required arguments".to_string(),
73                ));
74            }
75        };
76
77        debug!("Listing received messages for agent: {}", input.agent_did);
78
79        // Parse source type if provided
80        let source_type = input.source_type.as_ref().and_then(|s| match s.as_str() {
81            "https" => Some(SourceType::Https),
82            "internal" => Some(SourceType::Internal),
83            "websocket" => Some(SourceType::WebSocket),
84            "return_path" => Some(SourceType::ReturnPath),
85            "pickup" => Some(SourceType::Pickup),
86            _ => None,
87        });
88
89        // Parse status if provided
90        let status = input.status.as_ref().and_then(|s| match s.as_str() {
91            "pending" => Some(ReceivedStatus::Pending),
92            "processed" => Some(ReceivedStatus::Processed),
93            "failed" => Some(ReceivedStatus::Failed),
94            _ => None,
95        });
96
97        // Get agent storage
98        let storage = match self
99            .tap_integration
100            .storage_for_agent(&input.agent_did)
101            .await
102        {
103            Ok(s) => s,
104            Err(e) => {
105                error!("Failed to get agent storage: {}", e);
106                return Ok(error_text_response(format!(
107                    "Failed to get storage for agent {}: {}",
108                    input.agent_did, e
109                )));
110            }
111        };
112
113        // List received messages
114        let messages = match storage
115            .list_received(input.limit, input.offset, source_type, status)
116            .await
117        {
118            Ok(msgs) => msgs,
119            Err(e) => {
120                error!("Failed to list received messages: {}", e);
121                return Ok(error_text_response(format!(
122                    "Failed to list received messages: {}",
123                    e
124                )));
125            }
126        };
127
128        let received_messages: Vec<ReceivedMessage> = messages
129            .into_iter()
130            .map(|m| ReceivedMessage {
131                id: m.id,
132                message_id: m.message_id,
133                source_type: format!("{:?}", m.source_type).to_lowercase(),
134                source_identifier: m.source_identifier,
135                status: format!("{:?}", m.status).to_lowercase(),
136                error_message: m.error_message,
137                received_at: m.received_at,
138                processed_at: m.processed_at,
139                processed_message_id: m.processed_message_id,
140            })
141            .collect();
142
143        let text = format!(
144            "Found {} received messages for agent {}",
145            received_messages.len(),
146            input.agent_did
147        );
148
149        Ok(CallToolResult {
150            content: vec![
151                ToolContent::Text { text },
152                ToolContent::Text {
153                    text: serde_json::to_string_pretty(&json!({
154                        "messages": received_messages,
155                        "total": received_messages.len(),
156                        "agent_did": input.agent_did,
157                        "limit": input.limit,
158                        "offset": input.offset,
159                        "filters": {
160                            "source_type": input.source_type,
161                            "status": input.status
162                        }
163                    }))
164                    .unwrap_or_else(|_| "Failed to serialize JSON".to_string()),
165                },
166            ],
167            is_error: Some(false),
168        })
169    }
170
171    fn get_definition(&self) -> Tool {
172        Tool {
173            name: "tap_list_received".to_string(),
174            description: "Lists raw received messages with filtering and pagination support. Shows all incoming messages (JWE, JWS, or plain) before processing.".to_string(),
175            input_schema: json!({
176                "type": "object",
177                "required": ["agent_did"],
178                "properties": {
179                    "agent_did": {
180                        "type": "string",
181                        "description": "The DID of the agent whose received messages to list"
182                    },
183                    "limit": {
184                        "type": "number",
185                        "description": "Maximum number of messages to return",
186                        "default": 50,
187                        "minimum": 1,
188                        "maximum": 1000
189                    },
190                    "offset": {
191                        "type": "number",
192                        "description": "Number of messages to skip for pagination",
193                        "default": 0,
194                        "minimum": 0
195                    },
196                    "source_type": {
197                        "type": "string",
198                        "description": "Filter by source type",
199                        "enum": ["https", "internal", "websocket", "return_path", "pickup"]
200                    },
201                    "status": {
202                        "type": "string",
203                        "description": "Filter by processing status",
204                        "enum": ["pending", "processed", "failed"]
205                    }
206                },
207                "additionalProperties": false
208            }),
209        }
210    }
211}
212
213/// Input for getting pending received messages
214#[derive(Debug, Deserialize)]
215pub struct GetPendingReceivedInput {
216    /// The DID of the agent whose pending messages to get
217    pub agent_did: String,
218    /// Maximum number of messages to return
219    #[serde(default = "default_limit")]
220    pub limit: u32,
221}
222
223/// Tool for getting pending received messages
224pub struct GetPendingReceivedTool {
225    tap_integration: Arc<TapIntegration>,
226}
227
228impl GetPendingReceivedTool {
229    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
230        Self { tap_integration }
231    }
232}
233
234#[async_trait]
235impl ToolHandler for GetPendingReceivedTool {
236    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
237        let input: GetPendingReceivedInput = match arguments {
238            Some(args) => serde_json::from_value(args)?,
239            None => {
240                return Ok(error_text_response(
241                    "Missing required arguments".to_string(),
242                ));
243            }
244        };
245
246        debug!(
247            "Getting pending received messages for agent: {}",
248            input.agent_did
249        );
250
251        // Get agent storage
252        let storage = match self
253            .tap_integration
254            .storage_for_agent(&input.agent_did)
255            .await
256        {
257            Ok(s) => s,
258            Err(e) => {
259                error!("Failed to get agent storage: {}", e);
260                return Ok(error_text_response(format!(
261                    "Failed to get storage for agent {}: {}",
262                    input.agent_did, e
263                )));
264            }
265        };
266
267        // Get pending messages
268        let messages = match storage.get_pending_received(input.limit).await {
269            Ok(msgs) => msgs,
270            Err(e) => {
271                error!("Failed to get pending received messages: {}", e);
272                return Ok(error_text_response(format!(
273                    "Failed to get pending received messages: {}",
274                    e
275                )));
276            }
277        };
278
279        let received_messages: Vec<ReceivedMessage> = messages
280            .into_iter()
281            .map(|m| ReceivedMessage {
282                id: m.id,
283                message_id: m.message_id,
284                source_type: format!("{:?}", m.source_type).to_lowercase(),
285                source_identifier: m.source_identifier,
286                status: format!("{:?}", m.status).to_lowercase(),
287                error_message: m.error_message,
288                received_at: m.received_at,
289                processed_at: m.processed_at,
290                processed_message_id: m.processed_message_id,
291            })
292            .collect();
293
294        let text = format!(
295            "Found {} pending messages for agent {}",
296            received_messages.len(),
297            input.agent_did
298        );
299
300        Ok(CallToolResult {
301            content: vec![
302                ToolContent::Text { text },
303                ToolContent::Text {
304                    text: serde_json::to_string_pretty(&json!({
305                        "messages": received_messages,
306                        "total": received_messages.len(),
307                        "agent_did": input.agent_did
308                    }))
309                    .unwrap_or_else(|_| "Failed to serialize JSON".to_string()),
310                },
311            ],
312            is_error: Some(false),
313        })
314    }
315
316    fn get_definition(&self) -> Tool {
317        Tool {
318            name: "tap_get_pending_received".to_string(),
319            description: "Gets pending received messages that haven't been processed yet. Useful for debugging message processing issues.".to_string(),
320            input_schema: json!({
321                "type": "object",
322                "required": ["agent_did"],
323                "properties": {
324                    "agent_did": {
325                        "type": "string",
326                        "description": "The DID of the agent whose pending messages to get"
327                    },
328                    "limit": {
329                        "type": "number",
330                        "description": "Maximum number of messages to return",
331                        "default": 50,
332                        "minimum": 1,
333                        "maximum": 1000
334                    }
335                },
336                "additionalProperties": false
337            }),
338        }
339    }
340}
341
342/// Input for viewing raw received message
343#[derive(Debug, Deserialize)]
344pub struct ViewRawReceivedInput {
345    /// The DID of the agent who owns the received message
346    pub agent_did: String,
347    /// The ID of the received record
348    pub received_id: i64,
349}
350
351/// Tool for viewing raw received message content
352pub struct ViewRawReceivedTool {
353    tap_integration: Arc<TapIntegration>,
354}
355
356impl ViewRawReceivedTool {
357    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
358        Self { tap_integration }
359    }
360}
361
362#[async_trait]
363impl ToolHandler for ViewRawReceivedTool {
364    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
365        let input: ViewRawReceivedInput = match arguments {
366            Some(args) => serde_json::from_value(args)?,
367            None => {
368                return Ok(error_text_response(
369                    "Missing required arguments".to_string(),
370                ));
371            }
372        };
373
374        debug!(
375            "Viewing raw received message {} for agent: {}",
376            input.received_id, input.agent_did
377        );
378
379        // Get agent storage
380        let storage = match self
381            .tap_integration
382            .storage_for_agent(&input.agent_did)
383            .await
384        {
385            Ok(s) => s,
386            Err(e) => {
387                error!("Failed to get agent storage: {}", e);
388                return Ok(error_text_response(format!(
389                    "Failed to get storage for agent {}: {}",
390                    input.agent_did, e
391                )));
392            }
393        };
394
395        // Get the received record
396        let received = match storage.get_received_by_id(input.received_id).await {
397            Ok(Some(r)) => r,
398            Ok(None) => {
399                return Ok(error_text_response(format!(
400                    "Received message {} not found",
401                    input.received_id
402                )));
403            }
404            Err(e) => {
405                error!("Failed to get received message: {}", e);
406                return Ok(error_text_response(format!(
407                    "Failed to get received message: {}",
408                    e
409                )));
410            }
411        };
412
413        // Parse the raw message as JSON if possible
414        let raw_json = serde_json::from_str::<Value>(&received.raw_message).ok();
415
416        let text = format!(
417            "Received message {} (status: {:?})",
418            input.received_id, received.status
419        );
420
421        Ok(CallToolResult {
422            content: vec![
423                ToolContent::Text { text },
424                ToolContent::Text {
425                    text: serde_json::to_string_pretty(&json!({
426                        "id": received.id,
427                        "message_id": received.message_id,
428                        "source_type": format!("{:?}", received.source_type).to_lowercase(),
429                        "source_identifier": received.source_identifier,
430                        "status": format!("{:?}", received.status).to_lowercase(),
431                        "error_message": received.error_message,
432                        "received_at": received.received_at,
433                        "processed_at": received.processed_at,
434                        "processed_message_id": received.processed_message_id,
435                        "raw_message": received.raw_message,
436                        "raw_message_json": raw_json
437                    }))
438                    .unwrap_or_else(|_| "Failed to serialize JSON".to_string()),
439                },
440            ],
441            is_error: Some(false),
442        })
443    }
444
445    fn get_definition(&self) -> Tool {
446        Tool {
447            name: "tap_view_raw_received".to_string(),
448            description: "Views the raw content of a received message. Shows the complete raw message as received (JWE, JWS, or plain JSON).".to_string(),
449            input_schema: json!({
450                "type": "object",
451                "required": ["agent_did", "received_id"],
452                "properties": {
453                    "agent_did": {
454                        "type": "string",
455                        "description": "The DID of the agent who owns the received message"
456                    },
457                    "received_id": {
458                        "type": "number",
459                        "description": "The ID of the received record"
460                    }
461                },
462                "additionalProperties": false
463            }),
464        }
465    }
466}