1use 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#[derive(Debug, Deserialize)]
16pub struct ListReceivedInput {
17 pub agent_did: String,
19 #[serde(default = "default_limit")]
21 pub limit: u32,
22 #[serde(default)]
24 pub offset: u32,
25 pub source_type: Option<String>,
27 pub status: Option<String>,
29}
30
31#[derive(Debug, Serialize)]
33pub struct ReceivedMessage {
34 pub id: i64,
36 pub message_id: Option<String>,
38 pub source_type: String,
40 pub source_identifier: Option<String>,
42 pub status: String,
44 pub error_message: Option<String>,
46 pub received_at: String,
48 pub processed_at: Option<String>,
50 pub processed_message_id: Option<String>,
52}
53
54pub 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 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 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 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 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#[derive(Debug, Deserialize)]
215pub struct GetPendingReceivedInput {
216 pub agent_did: String,
218 #[serde(default = "default_limit")]
220 pub limit: u32,
221}
222
223pub 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 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 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#[derive(Debug, Deserialize)]
344pub struct ViewRawReceivedInput {
345 pub agent_did: String,
347 pub received_id: i64,
349}
350
351pub 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 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 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 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}