Skip to main content

tap_mcp/tools/
transaction_tools.rs

1//! Transaction management tools
2
3use super::schema;
4use super::{default_limit, 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::collections::HashMap;
11use std::sync::Arc;
12use tap_caip::AssetId;
13use tap_msg::message::payment::InvoiceReference;
14use tap_msg::message::tap_message_trait::TapMessageBody;
15use tap_msg::message::transfer::TransactionValue;
16use tap_msg::message::{
17    Agent, Authorize, Cancel, Capture, Connect, ConnectionConstraints, Escrow, Exchange, Party,
18    Payment, Quote, Reject, Revert, Settle, TransactionLimits, Transfer,
19};
20use tap_msg::settlement_address::SettlementAddress;
21use tap_node::storage::models::SchemaType;
22use tap_node::storage::DecisionType;
23use tracing::{debug, error};
24
25/// Resolve decisions in the decision_log after a successful action.
26///
27/// When an action tool (authorize, reject, settle, cancel, revert) succeeds,
28/// this function resolves matching pending/delivered decisions in the shared
29/// database. This enables poll-mode architectures where external processes
30/// act on decisions and the decision_log is automatically cleaned up.
31async fn auto_resolve_decisions(
32    tap_integration: &TapIntegration,
33    agent_did: &str,
34    transaction_id: &str,
35    action: &str,
36    decision_type: Option<DecisionType>,
37) {
38    if let Ok(storage) = tap_integration.storage_for_agent(agent_did).await {
39        match storage
40            .resolve_decisions_for_transaction(transaction_id, action, decision_type)
41            .await
42        {
43            Ok(count) => {
44                if count > 0 {
45                    debug!(
46                        "Auto-resolved {} decisions for transaction {} with action: {}",
47                        count, transaction_id, action
48                    );
49                }
50            }
51            Err(e) => {
52                debug!(
53                    "Could not auto-resolve decisions for transaction {}: {}",
54                    transaction_id, e
55                );
56            }
57        }
58    }
59}
60
61/// Tool for creating transfer transactions
62pub struct CreateTransferTool {
63    tap_integration: Arc<TapIntegration>,
64}
65
66/// Parameters for creating a transfer
67#[derive(Debug, Deserialize)]
68struct CreateTransferParams {
69    agent_did: String, // The DID of the agent that will sign and send this message
70    asset: String,
71    amount: String,
72    originator: PartyInfo,
73    beneficiary: PartyInfo,
74    #[serde(default)]
75    agents: Vec<AgentInfo>,
76    #[serde(default)]
77    memo: Option<String>,
78    #[serde(default)]
79    expiry: Option<String>,
80    #[serde(default)]
81    transaction_value: Option<TransactionValueInfo>,
82    #[serde(default)]
83    metadata: Option<Value>,
84}
85
86#[derive(Debug, Deserialize)]
87struct TransactionValueInfo {
88    amount: String,
89    currency: String,
90}
91
92#[derive(Debug, Deserialize)]
93struct PartyInfo {
94    #[serde(rename = "@id")]
95    id: String,
96    #[serde(default)]
97    metadata: Option<Value>,
98}
99
100#[derive(Debug, Deserialize)]
101struct AgentInfo {
102    #[serde(rename = "@id")]
103    id: String,
104    role: String,
105    #[serde(rename = "for")]
106    for_party: String,
107}
108
109/// Response for creating a transfer
110#[derive(Debug, Serialize)]
111struct CreateTransferResponse {
112    transaction_id: String,
113    message_id: String,
114    status: String,
115    created_at: String,
116}
117
118impl CreateTransferTool {
119    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
120        Self { tap_integration }
121    }
122
123    fn tap_integration(&self) -> &TapIntegration {
124        &self.tap_integration
125    }
126}
127
128#[async_trait::async_trait]
129impl ToolHandler for CreateTransferTool {
130    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
131        let params: CreateTransferParams = match arguments {
132            Some(args) => serde_json::from_value(args)
133                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
134            None => {
135                return Ok(error_text_response(
136                    "Missing required parameters".to_string(),
137                ))
138            }
139        };
140
141        debug!(
142            "Creating transfer: asset={}, amount={}, originator={}, beneficiary={}",
143            params.asset, params.amount, params.originator.id, params.beneficiary.id
144        );
145
146        // Parse asset ID
147        let asset_id = params
148            .asset
149            .parse::<AssetId>()
150            .map_err(|e| Error::invalid_parameter(format!("Invalid asset ID: {}", e)))?;
151
152        // Get storage for the agent to look up customer metadata
153        let storage = match self
154            .tap_integration()
155            .storage_for_agent(&params.agent_did)
156            .await
157        {
158            Ok(storage) => storage,
159            Err(e) => {
160                error!(
161                    "Failed to get storage for agent {}: {}",
162                    params.agent_did, e
163                );
164                return Ok(error_text_response(format!(
165                    "Failed to get storage for agent {}: {}",
166                    params.agent_did, e
167                )));
168            }
169        };
170
171        // Create parties with metadata from customer database if available
172        let mut originator = Party::new(&params.originator.id);
173        if let Ok(Some(customer)) = storage.get_customer(&params.originator.id).await {
174            // Extract relevant metadata from customer profile
175            if let Some(_profile) = customer.profile.as_object() {
176                let mut metadata = HashMap::new();
177
178                match customer.schema_type {
179                    SchemaType::Person => {
180                        // For natural persons, use name hash instead of PII
181                        let full_name = match (&customer.given_name, &customer.family_name) {
182                            (Some(given), Some(family)) => format!("{} {}", given, family),
183                            (Some(given), None) => given.clone(),
184                            (None, Some(family)) => family.clone(),
185                            (None, None) => customer.display_name.clone().unwrap_or_default(),
186                        };
187
188                        if !full_name.is_empty() {
189                            // Add name hash according to TAIP-12
190                            originator = originator.with_name_hash(&full_name);
191                        }
192
193                        // Add address information if available (still needed for compliance)
194                        if let Some(country) = customer.address_country {
195                            metadata.insert(
196                                "addressCountry".to_string(),
197                                serde_json::Value::String(country),
198                            );
199                        }
200                    }
201                    SchemaType::Organization => {
202                        // For organizations, include LEI code if available
203                        if let Some(lei_code) = customer.lei_code {
204                            originator = originator.with_lei(&lei_code);
205                        }
206
207                        // Add legal name for organizations
208                        if let Some(legal_name) = customer.legal_name {
209                            metadata.insert(
210                                "legalName".to_string(),
211                                serde_json::Value::String(legal_name),
212                            );
213                        }
214
215                        // Add address information if available
216                        if let Some(country) = customer.address_country {
217                            metadata.insert(
218                                "addressCountry".to_string(),
219                                serde_json::Value::String(country),
220                            );
221                        }
222                    }
223                    SchemaType::Thing => {
224                        // For other entity types, include minimal metadata
225                        if let Some(display_name) = customer.display_name {
226                            metadata.insert(
227                                "name".to_string(),
228                                serde_json::Value::String(display_name),
229                            );
230                        }
231                    }
232                }
233
234                // Apply any additional metadata
235                if !metadata.is_empty() {
236                    originator = Party::with_metadata(&originator.id, metadata);
237                }
238            }
239        }
240        // Also merge any provided metadata
241        if let Some(provided_metadata) = params.originator.metadata {
242            if let Some(obj) = provided_metadata.as_object() {
243                let mut metadata = originator.metadata.clone();
244                for (k, v) in obj {
245                    metadata.insert(k.clone(), v.clone());
246                }
247                originator = Party::with_metadata(&originator.id, metadata);
248            }
249        }
250
251        let mut beneficiary = Party::new(&params.beneficiary.id);
252        if let Ok(Some(customer)) = storage.get_customer(&params.beneficiary.id).await {
253            // Extract relevant metadata from customer profile
254            if let Some(_profile) = customer.profile.as_object() {
255                let mut metadata = HashMap::new();
256
257                match customer.schema_type {
258                    SchemaType::Person => {
259                        // For natural persons, use name hash instead of PII
260                        let full_name = match (&customer.given_name, &customer.family_name) {
261                            (Some(given), Some(family)) => format!("{} {}", given, family),
262                            (Some(given), None) => given.clone(),
263                            (None, Some(family)) => family.clone(),
264                            (None, None) => customer.display_name.clone().unwrap_or_default(),
265                        };
266
267                        if !full_name.is_empty() {
268                            // Add name hash according to TAIP-12
269                            beneficiary = beneficiary.with_name_hash(&full_name);
270                        }
271
272                        // Add address information if available (still needed for compliance)
273                        if let Some(country) = customer.address_country {
274                            metadata.insert(
275                                "addressCountry".to_string(),
276                                serde_json::Value::String(country),
277                            );
278                        }
279                    }
280                    SchemaType::Organization => {
281                        // For organizations, include LEI code if available
282                        if let Some(lei_code) = customer.lei_code {
283                            beneficiary = beneficiary.with_lei(&lei_code);
284                        }
285
286                        // Add legal name for organizations
287                        if let Some(legal_name) = customer.legal_name {
288                            metadata.insert(
289                                "legalName".to_string(),
290                                serde_json::Value::String(legal_name),
291                            );
292                        }
293
294                        // Add address information if available
295                        if let Some(country) = customer.address_country {
296                            metadata.insert(
297                                "addressCountry".to_string(),
298                                serde_json::Value::String(country),
299                            );
300                        }
301                    }
302                    SchemaType::Thing => {
303                        // For other entity types, include minimal metadata
304                        if let Some(display_name) = customer.display_name {
305                            metadata.insert(
306                                "name".to_string(),
307                                serde_json::Value::String(display_name),
308                            );
309                        }
310                    }
311                }
312
313                // Apply any additional metadata
314                if !metadata.is_empty() {
315                    beneficiary = Party::with_metadata(&beneficiary.id, metadata);
316                }
317            }
318        }
319        // Also merge any provided metadata
320        if let Some(provided_metadata) = params.beneficiary.metadata {
321            if let Some(obj) = provided_metadata.as_object() {
322                let mut metadata = beneficiary.metadata.clone();
323                for (k, v) in obj {
324                    metadata.insert(k.clone(), v.clone());
325                }
326                beneficiary = Party::with_metadata(&beneficiary.id, metadata);
327            }
328        }
329
330        // Create agents
331        let agents: Vec<Agent> = params
332            .agents
333            .iter()
334            .map(|agent_info| Agent::new(&agent_info.id, &agent_info.role, &agent_info.for_party))
335            .collect();
336
337        // Create transfer message (transaction_id will be generated when creating DIDComm message)
338        let transfer = Transfer {
339            transaction_id: None,
340            asset: asset_id,
341            originator: Some(originator),
342            beneficiary: Some(beneficiary),
343            amount: params.amount,
344            agents,
345            memo: params.memo,
346            settlement_id: None,
347            expiry: params.expiry,
348            transaction_value: params.transaction_value.map(|tv| TransactionValue {
349                amount: tv.amount,
350                currency: tv.currency,
351            }),
352            connection_id: None,
353            metadata: params
354                .metadata
355                .and_then(|v| {
356                    v.as_object()
357                        .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
358                })
359                .unwrap_or_default(),
360        };
361
362        // Validate the transfer
363        if let Err(e) = transfer.validate() {
364            return Ok(error_text_response(format!(
365                "Transfer validation failed: {}",
366                e
367            )));
368        }
369
370        // Create DIDComm message using the specified agent DID
371        let didcomm_message = match transfer.to_didcomm(&params.agent_did) {
372            Ok(msg) => msg,
373            Err(e) => {
374                return Ok(error_text_response(format!(
375                    "Failed to create DIDComm message: {}",
376                    e
377                )));
378            }
379        };
380
381        // Determine recipient - use beneficiary if available, otherwise first recipient in the message
382        let recipient_did = if let Some(beneficiary) = &transfer.beneficiary {
383            beneficiary.id.clone()
384        } else if !didcomm_message.to.is_empty() {
385            didcomm_message.to[0].clone()
386        } else {
387            return Ok(error_text_response(
388                "No recipient found for transfer message".to_string(),
389            ));
390        };
391
392        debug!(
393            "Sending transfer from {} to {}",
394            params.agent_did, recipient_did
395        );
396
397        // Send the message through the TAP node (this will handle storage, logging, and delivery tracking)
398        match self
399            .tap_integration()
400            .node()
401            .send_message(params.agent_did.clone(), didcomm_message.clone())
402            .await
403        {
404            Ok(packed_message) => {
405                debug!(
406                    "Transfer message sent successfully to {}, packed message length: {}",
407                    recipient_did,
408                    packed_message.len()
409                );
410
411                let response = CreateTransferResponse {
412                    transaction_id: didcomm_message
413                        .thid
414                        .clone()
415                        .unwrap_or(didcomm_message.id.clone()),
416                    message_id: didcomm_message.id,
417                    status: "sent".to_string(),
418                    created_at: chrono::Utc::now().to_rfc3339(),
419                };
420
421                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
422                    Error::tool_execution(format!("Failed to serialize response: {}", e))
423                })?;
424
425                Ok(success_text_response(response_json))
426            }
427            Err(e) => {
428                error!("Failed to send transfer message: {}", e);
429                Ok(error_text_response(format!(
430                    "Failed to send transfer message: {}",
431                    e
432                )))
433            }
434        }
435    }
436
437    fn get_definition(&self) -> Tool {
438        Tool {
439            name: "tap_create_transfer".to_string(),
440            description:
441                "Initiates a new transfer between parties using the TAP Transfer message (TAIP-3)"
442                    .to_string(),
443            input_schema: schema::create_transfer_schema(),
444        }
445    }
446}
447
448/// Tool for authorizing transactions
449pub struct AuthorizeTool {
450    tap_integration: Arc<TapIntegration>,
451}
452
453/// Parameters for authorizing a transaction
454#[derive(Debug, Deserialize)]
455struct AuthorizeParams {
456    agent_did: String, // The DID of the agent that will sign and send this message
457    transaction_id: String,
458    #[serde(default)]
459    settlement_address: Option<String>,
460    #[serde(default)]
461    expiry: Option<String>,
462}
463
464/// Response for authorizing a transaction
465#[derive(Debug, Serialize)]
466struct AuthorizeResponse {
467    transaction_id: String,
468    message_id: String,
469    status: String,
470    authorized_at: String,
471}
472
473impl AuthorizeTool {
474    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
475        Self { tap_integration }
476    }
477
478    fn tap_integration(&self) -> &TapIntegration {
479        &self.tap_integration
480    }
481}
482
483#[async_trait::async_trait]
484impl ToolHandler for AuthorizeTool {
485    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
486        let params: AuthorizeParams = match arguments {
487            Some(args) => serde_json::from_value(args)
488                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
489            None => {
490                return Ok(error_text_response(
491                    "Missing required parameters".to_string(),
492                ))
493            }
494        };
495
496        debug!("Authorizing transaction: {}", params.transaction_id);
497
498        // Create authorize message
499        let authorize = Authorize {
500            transaction_id: params.transaction_id.clone(),
501            settlement_address: params.settlement_address,
502            expiry: params.expiry,
503        };
504
505        // Validate the authorize message
506        if let Err(e) = authorize.validate() {
507            return Ok(error_text_response(format!(
508                "Authorize validation failed: {}",
509                e
510            )));
511        }
512
513        // Create DIDComm message using the specified agent DID
514        let didcomm_message = match authorize.to_didcomm(&params.agent_did) {
515            Ok(msg) => msg,
516            Err(e) => {
517                return Ok(error_text_response(format!(
518                    "Failed to create DIDComm message: {}",
519                    e
520                )));
521            }
522        };
523
524        // Determine recipient from the message
525        let recipient_did = if !didcomm_message.to.is_empty() {
526            didcomm_message.to[0].clone()
527        } else {
528            return Ok(error_text_response(
529                "No recipient found for authorize message".to_string(),
530            ));
531        };
532
533        debug!(
534            "Sending authorize from {} to {} for transaction: {}",
535            params.agent_did, recipient_did, params.transaction_id
536        );
537
538        // Send the message through the TAP node (this will handle storage, logging, and delivery tracking)
539        match self
540            .tap_integration()
541            .node()
542            .send_message(params.agent_did.clone(), didcomm_message.clone())
543            .await
544        {
545            Ok(packed_message) => {
546                debug!(
547                    "Authorize message sent successfully to {}, packed message length: {}",
548                    recipient_did,
549                    packed_message.len()
550                );
551
552                auto_resolve_decisions(
553                    self.tap_integration(),
554                    &params.agent_did,
555                    &params.transaction_id,
556                    "authorize",
557                    Some(DecisionType::AuthorizationRequired),
558                )
559                .await;
560
561                let response = AuthorizeResponse {
562                    transaction_id: params.transaction_id,
563                    message_id: didcomm_message.id,
564                    status: "sent".to_string(),
565                    authorized_at: chrono::Utc::now().to_rfc3339(),
566                };
567
568                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
569                    Error::tool_execution(format!("Failed to serialize response: {}", e))
570                })?;
571
572                Ok(success_text_response(response_json))
573            }
574            Err(e) => {
575                error!("Failed to send authorize message: {}", e);
576                Ok(error_text_response(format!(
577                    "Failed to send authorize message: {}",
578                    e
579                )))
580            }
581        }
582    }
583
584    fn get_definition(&self) -> Tool {
585        Tool {
586            name: "tap_authorize".to_string(),
587            description: "Authorizes a TAP transaction using the Authorize message (TAIP-4)"
588                .to_string(),
589            input_schema: schema::authorize_schema(),
590        }
591    }
592}
593
594/// Tool for rejecting transactions
595pub struct RejectTool {
596    tap_integration: Arc<TapIntegration>,
597}
598
599/// Parameters for rejecting a transaction
600#[derive(Debug, Deserialize)]
601struct RejectParams {
602    agent_did: String, // The DID of the agent that will sign and send this message
603    transaction_id: String,
604    reason: String,
605}
606
607/// Response for rejecting a transaction
608#[derive(Debug, Serialize)]
609struct RejectResponse {
610    transaction_id: String,
611    message_id: String,
612    status: String,
613    reason: String,
614    rejected_at: String,
615}
616
617impl RejectTool {
618    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
619        Self { tap_integration }
620    }
621
622    fn tap_integration(&self) -> &TapIntegration {
623        &self.tap_integration
624    }
625}
626
627#[async_trait::async_trait]
628impl ToolHandler for RejectTool {
629    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
630        let params: RejectParams = match arguments {
631            Some(args) => serde_json::from_value(args)
632                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
633            None => {
634                return Ok(error_text_response(
635                    "Missing required parameters".to_string(),
636                ))
637            }
638        };
639
640        debug!(
641            "Rejecting transaction: {} with reason: {}",
642            params.transaction_id, params.reason
643        );
644
645        // Create reject message
646        let reject = Reject {
647            transaction_id: params.transaction_id.clone(),
648            reason: Some(params.reason.clone()),
649        };
650
651        // Validate the reject message
652        if let Err(e) = reject.validate() {
653            return Ok(error_text_response(format!(
654                "Reject validation failed: {}",
655                e
656            )));
657        }
658
659        // Create DIDComm message using the specified agent DID
660        let didcomm_message = match reject.to_didcomm(&params.agent_did) {
661            Ok(msg) => msg,
662            Err(e) => {
663                return Ok(error_text_response(format!(
664                    "Failed to create DIDComm message: {}",
665                    e
666                )));
667            }
668        };
669
670        // Determine recipient from the message
671        let recipient_did = if !didcomm_message.to.is_empty() {
672            didcomm_message.to[0].clone()
673        } else {
674            return Ok(error_text_response(
675                "No recipient found for reject message".to_string(),
676            ));
677        };
678
679        debug!(
680            "Sending reject from {} to {} for transaction: {}",
681            params.agent_did, recipient_did, params.transaction_id
682        );
683
684        // Send the message through the TAP node (this will handle storage, logging, and delivery tracking)
685        match self
686            .tap_integration()
687            .node()
688            .send_message(params.agent_did.clone(), didcomm_message.clone())
689            .await
690        {
691            Ok(packed_message) => {
692                debug!(
693                    "Reject message sent successfully to {}, packed message length: {}",
694                    recipient_did,
695                    packed_message.len()
696                );
697
698                auto_resolve_decisions(
699                    self.tap_integration(),
700                    &params.agent_did,
701                    &params.transaction_id,
702                    "reject",
703                    None, // Reject resolves all pending decisions
704                )
705                .await;
706
707                let response = RejectResponse {
708                    transaction_id: params.transaction_id,
709                    message_id: didcomm_message.id,
710                    status: "sent".to_string(),
711                    reason: params.reason,
712                    rejected_at: chrono::Utc::now().to_rfc3339(),
713                };
714
715                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
716                    Error::tool_execution(format!("Failed to serialize response: {}", e))
717                })?;
718
719                Ok(success_text_response(response_json))
720            }
721            Err(e) => {
722                error!("Failed to send reject message: {}", e);
723                Ok(error_text_response(format!(
724                    "Failed to send reject message: {}",
725                    e
726                )))
727            }
728        }
729    }
730
731    fn get_definition(&self) -> Tool {
732        Tool {
733            name: "tap_reject".to_string(),
734            description: "Rejects a TAP transaction using the Reject message (TAIP-4)".to_string(),
735            input_schema: schema::reject_schema(),
736        }
737    }
738}
739
740/// Tool for canceling transactions
741pub struct CancelTool {
742    tap_integration: Arc<TapIntegration>,
743}
744
745/// Parameters for canceling a transaction
746#[derive(Debug, Deserialize)]
747struct CancelParams {
748    agent_did: String, // The DID of the agent that will sign and send this message
749    transaction_id: String,
750    by: String,
751    #[serde(default)]
752    reason: Option<String>,
753}
754
755/// Response for canceling a transaction
756#[derive(Debug, Serialize)]
757struct CancelResponse {
758    transaction_id: String,
759    message_id: String,
760    status: String,
761    canceled_by: String,
762    reason: Option<String>,
763    canceled_at: String,
764}
765
766impl CancelTool {
767    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
768        Self { tap_integration }
769    }
770
771    fn tap_integration(&self) -> &TapIntegration {
772        &self.tap_integration
773    }
774}
775
776#[async_trait::async_trait]
777impl ToolHandler for CancelTool {
778    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
779        let params: CancelParams = match arguments {
780            Some(args) => serde_json::from_value(args)
781                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
782            None => {
783                return Ok(error_text_response(
784                    "Missing required parameters".to_string(),
785                ))
786            }
787        };
788
789        debug!(
790            "Canceling transaction: {} by: {}",
791            params.transaction_id, params.by
792        );
793
794        // Create cancel message
795        let cancel = Cancel {
796            transaction_id: params.transaction_id.clone(),
797            by: params.by.clone(),
798            reason: params.reason.clone(),
799        };
800
801        // Validate the cancel message
802        if let Err(e) = cancel.validate() {
803            return Ok(error_text_response(format!(
804                "Cancel validation failed: {}",
805                e
806            )));
807        }
808
809        // Create DIDComm message using the specified agent DID
810        let didcomm_message = match cancel.to_didcomm(&params.agent_did) {
811            Ok(msg) => msg,
812            Err(e) => {
813                return Ok(error_text_response(format!(
814                    "Failed to create DIDComm message: {}",
815                    e
816                )));
817            }
818        };
819
820        // Determine recipient from the message
821        let recipient_did = if !didcomm_message.to.is_empty() {
822            didcomm_message.to[0].clone()
823        } else {
824            return Ok(error_text_response(
825                "No recipient found for cancel message".to_string(),
826            ));
827        };
828
829        debug!(
830            "Sending cancel from {} to {} for transaction: {}",
831            params.agent_did, recipient_did, params.transaction_id
832        );
833
834        // Send the message through the TAP node (this will handle storage, logging, and delivery tracking)
835        match self
836            .tap_integration()
837            .node()
838            .send_message(params.agent_did.clone(), didcomm_message.clone())
839            .await
840        {
841            Ok(packed_message) => {
842                debug!(
843                    "Cancel message sent successfully to {}, packed message length: {}",
844                    recipient_did,
845                    packed_message.len()
846                );
847
848                auto_resolve_decisions(
849                    self.tap_integration(),
850                    &params.agent_did,
851                    &params.transaction_id,
852                    "cancel",
853                    None, // Cancel resolves all pending decisions
854                )
855                .await;
856
857                let response = CancelResponse {
858                    transaction_id: params.transaction_id,
859                    message_id: didcomm_message.id,
860                    status: "sent".to_string(),
861                    canceled_by: params.by,
862                    reason: params.reason,
863                    canceled_at: chrono::Utc::now().to_rfc3339(),
864                };
865
866                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
867                    Error::tool_execution(format!("Failed to serialize response: {}", e))
868                })?;
869
870                Ok(success_text_response(response_json))
871            }
872            Err(e) => {
873                error!("Failed to send cancel message: {}", e);
874                Ok(error_text_response(format!(
875                    "Failed to send cancel message: {}",
876                    e
877                )))
878            }
879        }
880    }
881
882    fn get_definition(&self) -> Tool {
883        Tool {
884            name: "tap_cancel".to_string(),
885            description: "Cancels a TAP transaction using the Cancel message (TAIP-5)".to_string(),
886            input_schema: schema::cancel_schema(),
887        }
888    }
889}
890
891/// Tool for settling transactions
892pub struct SettleTool {
893    tap_integration: Arc<TapIntegration>,
894}
895
896/// Parameters for settling a transaction
897#[derive(Debug, Deserialize)]
898struct SettleParams {
899    agent_did: String, // The DID of the agent that will sign and send this message
900    transaction_id: String,
901    settlement_id: String,
902    #[serde(default)]
903    amount: Option<String>,
904}
905
906/// Response for settling a transaction
907#[derive(Debug, Serialize)]
908struct SettleResponse {
909    transaction_id: String,
910    settlement_id: String,
911    message_id: String,
912    status: String,
913    amount: Option<String>,
914    settled_at: String,
915}
916
917impl SettleTool {
918    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
919        Self { tap_integration }
920    }
921
922    fn tap_integration(&self) -> &TapIntegration {
923        &self.tap_integration
924    }
925}
926
927#[async_trait::async_trait]
928impl ToolHandler for SettleTool {
929    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
930        let params: SettleParams = match arguments {
931            Some(args) => serde_json::from_value(args)
932                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
933            None => {
934                return Ok(error_text_response(
935                    "Missing required parameters".to_string(),
936                ))
937            }
938        };
939
940        debug!(
941            "Settling transaction: {} with settlement_id: {}",
942            params.transaction_id, params.settlement_id
943        );
944
945        // Create settle message
946        let settle = Settle {
947            transaction_id: params.transaction_id.clone(),
948            settlement_id: Some(params.settlement_id.clone()),
949            amount: params.amount.clone(),
950        };
951
952        // Validate the settle message
953        if let Err(e) = settle.validate() {
954            return Ok(error_text_response(format!(
955                "Settle validation failed: {}",
956                e
957            )));
958        }
959
960        // Create DIDComm message using the specified agent DID
961        let didcomm_message = match settle.to_didcomm(&params.agent_did) {
962            Ok(msg) => msg,
963            Err(e) => {
964                return Ok(error_text_response(format!(
965                    "Failed to create DIDComm message: {}",
966                    e
967                )));
968            }
969        };
970
971        // Determine recipient from the message
972        let recipient_did = if !didcomm_message.to.is_empty() {
973            didcomm_message.to[0].clone()
974        } else {
975            return Ok(error_text_response(
976                "No recipient found for settle message".to_string(),
977            ));
978        };
979
980        debug!(
981            "Sending settle from {} to {} for transaction: {}",
982            params.agent_did, recipient_did, params.transaction_id
983        );
984
985        // Send the message through the TAP node (this will handle storage, logging, and delivery tracking)
986        match self
987            .tap_integration()
988            .node()
989            .send_message(params.agent_did.clone(), didcomm_message.clone())
990            .await
991        {
992            Ok(packed_message) => {
993                debug!(
994                    "Settle message sent successfully to {}, packed message length: {}",
995                    recipient_did,
996                    packed_message.len()
997                );
998
999                auto_resolve_decisions(
1000                    self.tap_integration(),
1001                    &params.agent_did,
1002                    &params.transaction_id,
1003                    "settle",
1004                    Some(DecisionType::SettlementRequired),
1005                )
1006                .await;
1007
1008                let response = SettleResponse {
1009                    transaction_id: params.transaction_id,
1010                    settlement_id: params.settlement_id,
1011                    message_id: didcomm_message.id,
1012                    status: "sent".to_string(),
1013                    amount: params.amount,
1014                    settled_at: chrono::Utc::now().to_rfc3339(),
1015                };
1016
1017                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
1018                    Error::tool_execution(format!("Failed to serialize response: {}", e))
1019                })?;
1020
1021                Ok(success_text_response(response_json))
1022            }
1023            Err(e) => {
1024                error!("Failed to send settle message: {}", e);
1025                Ok(error_text_response(format!(
1026                    "Failed to send settle message: {}",
1027                    e
1028                )))
1029            }
1030        }
1031    }
1032
1033    fn get_definition(&self) -> Tool {
1034        Tool {
1035            name: "tap_settle".to_string(),
1036            description: "Settles a TAP transaction using the Settle message (TAIP-6)".to_string(),
1037            input_schema: schema::settle_schema(),
1038        }
1039    }
1040}
1041
1042/// Tool for reverting transactions
1043pub struct RevertTool {
1044    tap_integration: Arc<TapIntegration>,
1045}
1046
1047/// Parameters for reverting a transaction
1048#[derive(Debug, Deserialize)]
1049struct RevertParams {
1050    agent_did: String, // The DID of the agent that will sign and send this message
1051    transaction_id: String,
1052    settlement_address: String,
1053    reason: String,
1054}
1055
1056/// Response for reverting a transaction
1057#[derive(Debug, Serialize)]
1058struct RevertResponse {
1059    transaction_id: String,
1060    message_id: String,
1061    status: String,
1062    reason: String,
1063    settlement_address: String,
1064    reverted_at: String,
1065}
1066
1067impl RevertTool {
1068    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1069        Self { tap_integration }
1070    }
1071
1072    fn tap_integration(&self) -> &TapIntegration {
1073        &self.tap_integration
1074    }
1075}
1076
1077#[async_trait::async_trait]
1078impl ToolHandler for RevertTool {
1079    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1080        let params: RevertParams = match arguments {
1081            Some(args) => serde_json::from_value(args)
1082                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1083            None => {
1084                return Ok(error_text_response(
1085                    "Missing required parameters".to_string(),
1086                ))
1087            }
1088        };
1089
1090        debug!(
1091            "Reverting transaction: {} with reason: {}",
1092            params.transaction_id, params.reason
1093        );
1094
1095        // Create revert message
1096        let revert = Revert {
1097            transaction_id: params.transaction_id.clone(),
1098            settlement_address: params.settlement_address.clone(),
1099            reason: params.reason.clone(),
1100        };
1101
1102        // Validate the revert message
1103        if let Err(e) = revert.validate() {
1104            return Ok(error_text_response(format!(
1105                "Revert validation failed: {}",
1106                e
1107            )));
1108        }
1109
1110        // Create DIDComm message using the specified agent DID
1111        let didcomm_message = match revert.to_didcomm(&params.agent_did) {
1112            Ok(msg) => msg,
1113            Err(e) => {
1114                return Ok(error_text_response(format!(
1115                    "Failed to create DIDComm message: {}",
1116                    e
1117                )));
1118            }
1119        };
1120
1121        // Determine recipient from the message
1122        let recipient_did = if !didcomm_message.to.is_empty() {
1123            didcomm_message.to[0].clone()
1124        } else {
1125            return Ok(error_text_response(
1126                "No recipient found for revert message".to_string(),
1127            ));
1128        };
1129
1130        debug!(
1131            "Sending revert from {} to {} for transaction: {}",
1132            params.agent_did, recipient_did, params.transaction_id
1133        );
1134
1135        // Send the message through the TAP node
1136        match self
1137            .tap_integration()
1138            .node()
1139            .send_message(params.agent_did.clone(), didcomm_message.clone())
1140            .await
1141        {
1142            Ok(packed_message) => {
1143                debug!(
1144                    "Revert message sent successfully to {}, packed message length: {}",
1145                    recipient_did,
1146                    packed_message.len()
1147                );
1148
1149                auto_resolve_decisions(
1150                    self.tap_integration(),
1151                    &params.agent_did,
1152                    &params.transaction_id,
1153                    "revert",
1154                    None, // Revert resolves all pending decisions
1155                )
1156                .await;
1157
1158                let response = RevertResponse {
1159                    transaction_id: params.transaction_id,
1160                    message_id: didcomm_message.id,
1161                    status: "sent".to_string(),
1162                    reason: params.reason,
1163                    settlement_address: params.settlement_address,
1164                    reverted_at: chrono::Utc::now().to_rfc3339(),
1165                };
1166
1167                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
1168                    Error::tool_execution(format!("Failed to serialize response: {}", e))
1169                })?;
1170
1171                Ok(success_text_response(response_json))
1172            }
1173            Err(e) => {
1174                error!("Failed to send revert message: {}", e);
1175                Ok(error_text_response(format!(
1176                    "Failed to send revert message: {}",
1177                    e
1178                )))
1179            }
1180        }
1181    }
1182
1183    fn get_definition(&self) -> Tool {
1184        Tool {
1185            name: "tap_revert".to_string(),
1186            description: "Reverts a settled TAP transaction using the Revert message (TAIP-12)"
1187                .to_string(),
1188            input_schema: schema::revert_schema(),
1189        }
1190    }
1191}
1192
1193/// Tool for listing transactions
1194pub struct ListTransactionsTool {
1195    tap_integration: Arc<TapIntegration>,
1196}
1197
1198/// Parameters for listing transactions
1199#[derive(Debug, Deserialize, Serialize)]
1200struct ListTransactionsParams {
1201    agent_did: String, // The DID of the agent whose transactions to list
1202    #[serde(default)]
1203    filter: Option<TransactionFilter>,
1204    #[serde(default)]
1205    sort: Option<TransactionSort>,
1206    #[serde(default = "default_limit")]
1207    limit: u32,
1208    #[serde(default)]
1209    offset: u32,
1210}
1211
1212#[derive(Debug, Deserialize, Serialize)]
1213struct TransactionFilter {
1214    message_type: Option<String>,
1215    thread_id: Option<String>,
1216    from_did: Option<String>,
1217    to_did: Option<String>,
1218    date_from: Option<String>,
1219    date_to: Option<String>,
1220}
1221
1222#[derive(Debug, Deserialize, Serialize)]
1223struct TransactionSort {
1224    field: Option<String>,
1225    order: Option<String>,
1226}
1227
1228/// Response for listing transactions
1229#[derive(Debug, Serialize)]
1230struct ListTransactionsResponse {
1231    transactions: Vec<TransactionInfo>,
1232    total: usize,
1233    applied_filters: ListTransactionsParams,
1234}
1235
1236#[derive(Debug, Serialize)]
1237struct TransactionInfo {
1238    id: String,
1239    #[serde(rename = "type")]
1240    message_type: String,
1241    thread_id: Option<String>,
1242    from: Option<String>,
1243    to: Option<String>,
1244    direction: String,
1245    created_at: String,
1246    body: serde_json::Value,
1247}
1248
1249impl ListTransactionsTool {
1250    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1251        Self { tap_integration }
1252    }
1253
1254    fn tap_integration(&self) -> &TapIntegration {
1255        &self.tap_integration
1256    }
1257}
1258
1259#[async_trait::async_trait]
1260impl ToolHandler for ListTransactionsTool {
1261    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1262        let params: ListTransactionsParams = match arguments {
1263            Some(args) => serde_json::from_value(args)
1264                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1265            None => {
1266                return Ok(error_text_response(
1267                    "Missing required parameters: agent_did is required".to_string(),
1268                ))
1269            }
1270        };
1271
1272        debug!(
1273            "Listing transactions for agent {} with limit: {}, offset: {}",
1274            params.agent_did, params.limit, params.offset
1275        );
1276
1277        // Get messages from the agent's specific storage
1278        let storage = self
1279            .tap_integration()
1280            .storage_for_agent(&params.agent_did)
1281            .await?;
1282        let direction_filter = None; // No direction filter for now
1283        let messages = storage
1284            .list_messages(params.limit, params.offset, direction_filter)
1285            .await?;
1286
1287        // Apply additional filters
1288        let filtered_messages: Vec<_> = messages
1289            .into_iter()
1290            .filter(|msg| {
1291                if let Some(ref filter) = params.filter {
1292                    if let Some(ref msg_type) = filter.message_type {
1293                        if !msg.message_type.contains(msg_type) {
1294                            return false;
1295                        }
1296                    }
1297                    if let Some(ref thread_id) = filter.thread_id {
1298                        if msg.thread_id.as_ref() != Some(thread_id) {
1299                            return false;
1300                        }
1301                    }
1302                    if let Some(ref from_did) = filter.from_did {
1303                        if msg.from_did.as_ref() != Some(from_did) {
1304                            return false;
1305                        }
1306                    }
1307                    if let Some(ref to_did) = filter.to_did {
1308                        if msg.to_did.as_ref() != Some(to_did) {
1309                            return false;
1310                        }
1311                    }
1312                    // TODO: Apply date filters
1313                }
1314                true
1315            })
1316            .collect();
1317
1318        // Convert to transaction info
1319        let transactions: Vec<TransactionInfo> = filtered_messages
1320            .iter()
1321            .map(|msg| TransactionInfo {
1322                id: msg.message_id.clone(),
1323                message_type: msg.message_type.clone(),
1324                thread_id: msg.thread_id.clone(),
1325                from: msg.from_did.clone(),
1326                to: msg.to_did.clone(),
1327                direction: msg.direction.to_string(),
1328                created_at: msg.created_at.clone(),
1329                body: msg.message_json.clone(),
1330            })
1331            .collect();
1332
1333        let response = ListTransactionsResponse {
1334            total: transactions.len(),
1335            transactions,
1336            applied_filters: params,
1337        };
1338
1339        let response_json = serde_json::to_string_pretty(&response)
1340            .map_err(|e| Error::tool_execution(format!("Failed to serialize response: {}", e)))?;
1341
1342        Ok(success_text_response(response_json))
1343    }
1344
1345    fn get_definition(&self) -> Tool {
1346        Tool {
1347            name: "tap_list_transactions".to_string(),
1348            description: "Lists TAP transactions with filtering and pagination support".to_string(),
1349            input_schema: schema::list_transactions_schema(),
1350        }
1351    }
1352}
1353// New tools for Payment, Connect, Escrow, and Capture messages
1354
1355/// Tool for creating Payment messages (TAIP-14)
1356pub struct CreatePaymentTool {
1357    tap_integration: Arc<TapIntegration>,
1358}
1359
1360/// Parameters for creating a payment
1361#[derive(Debug, Deserialize)]
1362struct CreatePaymentParams {
1363    agent_did: String,
1364    #[serde(default)]
1365    asset: Option<String>,
1366    #[serde(default)]
1367    currency: Option<String>,
1368    amount: String,
1369    merchant: PartyInfo,
1370    #[serde(default)]
1371    agents: Vec<AgentInfo>,
1372    #[serde(default)]
1373    memo: Option<String>,
1374    #[serde(default)]
1375    expiry: Option<String>,
1376    #[serde(default)]
1377    invoice: Option<Value>,
1378    #[serde(default)]
1379    #[allow(dead_code)]
1380    settlement_address: Option<String>,
1381    #[serde(default)]
1382    fallback_settlement_addresses: Option<Vec<String>>,
1383    #[serde(default)]
1384    metadata: Option<Value>,
1385}
1386
1387/// Response for creating a payment
1388#[derive(Debug, Serialize)]
1389struct CreatePaymentResponse {
1390    transaction_id: String,
1391    message_id: String,
1392    status: String,
1393    created_at: String,
1394}
1395
1396impl CreatePaymentTool {
1397    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1398        Self { tap_integration }
1399    }
1400
1401    fn tap_integration(&self) -> &TapIntegration {
1402        &self.tap_integration
1403    }
1404}
1405
1406#[async_trait::async_trait]
1407impl ToolHandler for CreatePaymentTool {
1408    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1409        let params: CreatePaymentParams = match arguments {
1410            Some(args) => serde_json::from_value(args)
1411                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1412            None => {
1413                return Ok(error_text_response(
1414                    "Missing required parameters".to_string(),
1415                ))
1416            }
1417        };
1418
1419        debug!(
1420            "Creating payment: amount={}, merchant={}",
1421            params.amount, params.merchant.id
1422        );
1423
1424        // Create merchant party
1425        let mut merchant = Party::new(&params.merchant.id);
1426        if let Some(metadata) = params.merchant.metadata {
1427            if let Some(obj) = metadata.as_object() {
1428                for (key, value) in obj {
1429                    merchant = merchant.with_metadata_field(key.clone(), value.clone());
1430                }
1431            }
1432        }
1433
1434        // Create agents
1435        let agents: Vec<Agent> = params
1436            .agents
1437            .iter()
1438            .map(|info| Agent::new(&info.id, &info.role, &info.for_party))
1439            .collect();
1440
1441        // Create payment message based on whether it's asset or currency
1442        let mut payment = if let Some(asset) = params.asset {
1443            // Parse asset ID
1444            let asset_id = asset
1445                .parse::<AssetId>()
1446                .map_err(|e| Error::invalid_parameter(format!("Invalid asset ID: {}", e)))?;
1447            Payment::with_asset(asset_id, params.amount, merchant, agents)
1448        } else if let Some(currency) = params.currency {
1449            Payment::with_currency(currency, params.amount, merchant, agents)
1450        } else {
1451            return Ok(error_text_response(
1452                "Either asset or currency must be specified".to_string(),
1453            ));
1454        };
1455
1456        // Add optional fields
1457        if let Some(memo) = params.memo {
1458            payment.memo = Some(memo);
1459        }
1460        if let Some(expiry) = params.expiry {
1461            payment.expiry = Some(expiry);
1462        }
1463        // Wire invoice: try URL string first, then structured object
1464        if let Some(invoice_val) = params.invoice {
1465            if let Some(url) = invoice_val.as_str() {
1466                payment.invoice = Some(InvoiceReference::Url(url.to_string()));
1467            } else if invoice_val.is_object() {
1468                if let Ok(inv) = serde_json::from_value(invoice_val) {
1469                    payment.invoice = Some(InvoiceReference::Object(Box::new(inv)));
1470                }
1471            }
1472        }
1473        // Wire fallback settlement addresses
1474        if let Some(addresses) = params.fallback_settlement_addresses {
1475            let parsed: Vec<SettlementAddress> = addresses
1476                .into_iter()
1477                .filter_map(|a| SettlementAddress::from_string(a).ok())
1478                .collect();
1479            if !parsed.is_empty() {
1480                payment.fallback_settlement_addresses = Some(parsed);
1481            }
1482        }
1483        if let Some(metadata) = params.metadata {
1484            if let Some(obj) = metadata.as_object() {
1485                for (key, value) in obj {
1486                    payment.metadata.insert(key.clone(), value.clone());
1487                }
1488            }
1489        }
1490
1491        // Validate the payment message
1492        if let Err(e) = payment.validate() {
1493            return Ok(error_text_response(format!(
1494                "Payment validation failed: {}",
1495                e
1496            )));
1497        }
1498
1499        // Create DIDComm message
1500        let didcomm_message = match payment.to_didcomm(&params.agent_did) {
1501            Ok(msg) => msg,
1502            Err(e) => {
1503                return Ok(error_text_response(format!(
1504                    "Failed to create DIDComm message: {}",
1505                    e
1506                )));
1507            }
1508        };
1509
1510        // Send the message through the TAP node
1511        match self
1512            .tap_integration()
1513            .node()
1514            .send_message(params.agent_did.clone(), didcomm_message.clone())
1515            .await
1516        {
1517            Ok(_) => {
1518                let response = CreatePaymentResponse {
1519                    transaction_id: didcomm_message.id.clone(),
1520                    message_id: didcomm_message.id,
1521                    status: "sent".to_string(),
1522                    created_at: chrono::Utc::now().to_rfc3339(),
1523                };
1524
1525                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
1526                    Error::tool_execution(format!("Failed to serialize response: {}", e))
1527                })?;
1528
1529                Ok(success_text_response(response_json))
1530            }
1531            Err(e) => {
1532                error!("Failed to send payment: {}", e);
1533                Ok(error_text_response(format!(
1534                    "Failed to send payment: {}",
1535                    e
1536                )))
1537            }
1538        }
1539    }
1540
1541    fn get_definition(&self) -> Tool {
1542        Tool {
1543            name: "tap_payment".to_string(),
1544            description: "Creates a TAP payment request (TAIP-14) with optional invoice"
1545                .to_string(),
1546            input_schema: schema::create_payment_schema(),
1547        }
1548    }
1549}
1550
1551/// Tool for creating Connect messages (TAIP-15)
1552pub struct CreateConnectTool {
1553    tap_integration: Arc<TapIntegration>,
1554}
1555
1556/// Parameters for creating a connect message
1557#[derive(Debug, Deserialize)]
1558struct CreateConnectParams {
1559    agent_did: String,
1560    recipient_did: String,
1561    for_party: String,
1562    #[serde(default)]
1563    role: Option<String>,
1564    #[serde(default)]
1565    constraints: Option<ConnectionConstraintsInfo>,
1566    #[serde(default)]
1567    expiry: Option<String>,
1568    #[serde(default)]
1569    agreement: Option<String>,
1570    #[serde(default)]
1571    #[allow(dead_code)]
1572    metadata: Option<Value>,
1573}
1574
1575#[derive(Debug, Deserialize)]
1576struct ConnectionConstraintsInfo {
1577    #[serde(default)]
1578    transaction_limits: Option<TransactionLimitsInfo>,
1579    #[serde(default)]
1580    allowed_beneficiaries: Option<Vec<String>>,
1581    #[serde(default)]
1582    allowed_settlement_addresses: Option<Vec<String>>,
1583    #[serde(default)]
1584    allowed_assets: Option<Vec<String>>,
1585}
1586
1587#[derive(Debug, Deserialize)]
1588struct TransactionLimitsInfo {
1589    #[serde(default)]
1590    max_amount: Option<String>,
1591    #[serde(default)]
1592    #[allow(dead_code)]
1593    min_amount: Option<String>,
1594    #[serde(default)]
1595    daily_limit: Option<String>,
1596    #[serde(default)]
1597    #[allow(dead_code)]
1598    monthly_limit: Option<String>,
1599}
1600
1601/// Response for creating a connect message
1602#[derive(Debug, Serialize)]
1603struct CreateConnectResponse {
1604    connection_id: String,
1605    message_id: String,
1606    status: String,
1607    created_at: String,
1608}
1609
1610impl CreateConnectTool {
1611    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1612        Self { tap_integration }
1613    }
1614
1615    fn tap_integration(&self) -> &TapIntegration {
1616        &self.tap_integration
1617    }
1618}
1619
1620#[async_trait::async_trait]
1621impl ToolHandler for CreateConnectTool {
1622    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1623        let params: CreateConnectParams = match arguments {
1624            Some(args) => serde_json::from_value(args)
1625                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1626            None => {
1627                return Ok(error_text_response(
1628                    "Missing required parameters".to_string(),
1629                ))
1630            }
1631        };
1632
1633        debug!(
1634            "Creating connect message from {} to {}",
1635            params.agent_did, params.recipient_did
1636        );
1637
1638        // Create connect message
1639        // Connect requires transaction_id, agent_id, for_id, and optional role
1640        let transaction_id = format!("connect-{}", uuid::Uuid::new_v4());
1641        let mut connect = Connect::new(
1642            &transaction_id,
1643            &params.agent_did,
1644            &params.for_party,
1645            params.role.as_deref(),
1646        );
1647
1648        // Add constraints if provided
1649        if let Some(constraints_info) = params.constraints {
1650            let mut constraints = ConnectionConstraints {
1651                purposes: None,
1652                category_purposes: None,
1653                limits: None,
1654                allowed_beneficiaries: None,
1655                allowed_settlement_addresses: None,
1656                allowed_assets: None,
1657            };
1658
1659            if let Some(limits_info) = constraints_info.transaction_limits {
1660                let mut limits = TransactionLimits {
1661                    per_transaction: None,
1662                    per_day: None,
1663                    per_week: None,
1664                    per_month: None,
1665                    per_year: None,
1666                    currency: None,
1667                };
1668                limits.per_transaction = limits_info.max_amount;
1669                limits.per_day = limits_info.daily_limit;
1670                constraints.limits = Some(limits);
1671            }
1672
1673            if let Some(beneficiaries) = constraints_info.allowed_beneficiaries {
1674                constraints.allowed_beneficiaries =
1675                    Some(beneficiaries.into_iter().map(|b| Party::new(&b)).collect());
1676            }
1677            if let Some(addresses) = constraints_info.allowed_settlement_addresses {
1678                constraints.allowed_settlement_addresses = Some(addresses);
1679            }
1680            if let Some(assets) = constraints_info.allowed_assets {
1681                constraints.allowed_assets = Some(assets);
1682            }
1683
1684            connect.constraints = Some(constraints);
1685        }
1686
1687        // Add expiry and agreement
1688        if let Some(expiry) = params.expiry {
1689            connect.expiry = Some(expiry);
1690        }
1691        if let Some(agreement) = params.agreement {
1692            connect.agreement = Some(agreement);
1693        }
1694
1695        // Validate the connect message
1696        if let Err(e) = connect.validate() {
1697            return Ok(error_text_response(format!(
1698                "Connect validation failed: {}",
1699                e
1700            )));
1701        }
1702
1703        // Create DIDComm message
1704        let didcomm_message = match connect.to_didcomm(&params.agent_did) {
1705            Ok(mut msg) => {
1706                msg.to = vec![params.recipient_did.clone()];
1707                msg
1708            }
1709            Err(e) => {
1710                return Ok(error_text_response(format!(
1711                    "Failed to create DIDComm message: {}",
1712                    e
1713                )));
1714            }
1715        };
1716
1717        // Send the message through the TAP node
1718        match self
1719            .tap_integration()
1720            .node()
1721            .send_message(params.agent_did.clone(), didcomm_message.clone())
1722            .await
1723        {
1724            Ok(_) => {
1725                let response = CreateConnectResponse {
1726                    connection_id: didcomm_message.id.clone(),
1727                    message_id: didcomm_message.id,
1728                    status: "sent".to_string(),
1729                    created_at: chrono::Utc::now().to_rfc3339(),
1730                };
1731
1732                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
1733                    Error::tool_execution(format!("Failed to serialize response: {}", e))
1734                })?;
1735
1736                Ok(success_text_response(response_json))
1737            }
1738            Err(e) => {
1739                error!("Failed to send connect message: {}", e);
1740                Ok(error_text_response(format!(
1741                    "Failed to send connect message: {}",
1742                    e
1743                )))
1744            }
1745        }
1746    }
1747
1748    fn get_definition(&self) -> Tool {
1749        Tool {
1750            name: "tap_connect".to_string(),
1751            description: "Creates a TAP connection request (TAIP-15) to establish a relationship between parties".to_string(),
1752            input_schema: schema::create_connect_schema(),
1753        }
1754    }
1755}
1756
1757/// Tool for creating Escrow messages (TAIP-17)
1758pub struct CreateEscrowTool {
1759    tap_integration: Arc<TapIntegration>,
1760}
1761
1762/// Parameters for creating an escrow
1763#[derive(Debug, Deserialize)]
1764struct CreateEscrowParams {
1765    agent_did: String,
1766    #[serde(default)]
1767    asset: Option<String>,
1768    #[serde(default)]
1769    currency: Option<String>,
1770    amount: String,
1771    originator: PartyInfo,
1772    beneficiary: PartyInfo,
1773    expiry: String,
1774    agents: Vec<AgentInfo>,
1775    #[serde(default)]
1776    agreement: Option<String>,
1777    #[serde(default)]
1778    metadata: Option<Value>,
1779}
1780
1781/// Response for creating an escrow
1782#[derive(Debug, Serialize)]
1783struct CreateEscrowResponse {
1784    escrow_id: String,
1785    message_id: String,
1786    status: String,
1787    expiry: String,
1788    created_at: String,
1789}
1790
1791impl CreateEscrowTool {
1792    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1793        Self { tap_integration }
1794    }
1795
1796    fn tap_integration(&self) -> &TapIntegration {
1797        &self.tap_integration
1798    }
1799}
1800
1801#[async_trait::async_trait]
1802impl ToolHandler for CreateEscrowTool {
1803    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1804        let params: CreateEscrowParams = match arguments {
1805            Some(args) => serde_json::from_value(args)
1806                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1807            None => {
1808                return Ok(error_text_response(
1809                    "Missing required parameters".to_string(),
1810                ))
1811            }
1812        };
1813
1814        debug!(
1815            "Creating escrow: amount={}, originator={}, beneficiary={}, expiry={}",
1816            params.amount, params.originator.id, params.beneficiary.id, params.expiry
1817        );
1818
1819        // Create parties
1820        let mut originator = Party::new(&params.originator.id);
1821        if let Some(metadata) = params.originator.metadata {
1822            if let Some(obj) = metadata.as_object() {
1823                for (key, value) in obj {
1824                    originator = originator.with_metadata_field(key.clone(), value.clone());
1825                }
1826            }
1827        }
1828
1829        let mut beneficiary = Party::new(&params.beneficiary.id);
1830        if let Some(metadata) = params.beneficiary.metadata {
1831            if let Some(obj) = metadata.as_object() {
1832                for (key, value) in obj {
1833                    beneficiary = beneficiary.with_metadata_field(key.clone(), value.clone());
1834                }
1835            }
1836        }
1837
1838        // Create agents
1839        let agents: Vec<Agent> = params
1840            .agents
1841            .iter()
1842            .map(|info| Agent::new(&info.id, &info.role, &info.for_party))
1843            .collect();
1844
1845        // Verify exactly one EscrowAgent exists
1846        let escrow_agent_count = agents
1847            .iter()
1848            .filter(|a| a.role == Some("EscrowAgent".to_string()))
1849            .count();
1850        if escrow_agent_count != 1 {
1851            return Ok(error_text_response(format!(
1852                "Escrow must have exactly one agent with role 'EscrowAgent', found {}",
1853                escrow_agent_count
1854            )));
1855        }
1856
1857        // Create escrow message based on whether it's asset or currency
1858        let mut escrow = if let Some(asset) = params.asset {
1859            Escrow::new_with_asset(
1860                asset,
1861                params.amount,
1862                originator,
1863                beneficiary,
1864                params.expiry,
1865                agents,
1866            )
1867        } else if let Some(currency) = params.currency {
1868            Escrow::new_with_currency(
1869                currency,
1870                params.amount,
1871                originator,
1872                beneficiary,
1873                params.expiry,
1874                agents,
1875            )
1876        } else {
1877            return Ok(error_text_response(
1878                "Either asset or currency must be specified".to_string(),
1879            ));
1880        };
1881
1882        // Add optional fields
1883        if let Some(agreement) = params.agreement {
1884            escrow = escrow.with_agreement(agreement);
1885        }
1886        if let Some(metadata) = params.metadata {
1887            if let Some(obj) = metadata.as_object() {
1888                for (key, value) in obj {
1889                    escrow = escrow.with_metadata(key.clone(), value.clone());
1890                }
1891            }
1892        }
1893
1894        // Validate the escrow message
1895        if let Err(e) = escrow.validate() {
1896            return Ok(error_text_response(format!(
1897                "Escrow validation failed: {}",
1898                e
1899            )));
1900        }
1901
1902        // Create DIDComm message
1903        let didcomm_message = match escrow.to_didcomm(&params.agent_did) {
1904            Ok(msg) => msg,
1905            Err(e) => {
1906                return Ok(error_text_response(format!(
1907                    "Failed to create DIDComm message: {}",
1908                    e
1909                )));
1910            }
1911        };
1912
1913        // Send the message through the TAP node
1914        match self
1915            .tap_integration()
1916            .node()
1917            .send_message(params.agent_did.clone(), didcomm_message.clone())
1918            .await
1919        {
1920            Ok(_) => {
1921                let response = CreateEscrowResponse {
1922                    escrow_id: didcomm_message.id.clone(),
1923                    message_id: didcomm_message.id,
1924                    status: "created".to_string(),
1925                    expiry: escrow.expiry,
1926                    created_at: chrono::Utc::now().to_rfc3339(),
1927                };
1928
1929                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
1930                    Error::tool_execution(format!("Failed to serialize response: {}", e))
1931                })?;
1932
1933                Ok(success_text_response(response_json))
1934            }
1935            Err(e) => {
1936                error!("Failed to send escrow: {}", e);
1937                Ok(error_text_response(format!("Failed to send escrow: {}", e)))
1938            }
1939        }
1940    }
1941
1942    fn get_definition(&self) -> Tool {
1943        Tool {
1944            name: "tap_escrow".to_string(),
1945            description:
1946                "Creates a TAP escrow request (TAIP-17) for holding assets on behalf of parties"
1947                    .to_string(),
1948            input_schema: schema::create_escrow_schema(),
1949        }
1950    }
1951}
1952
1953/// Tool for creating Capture messages (TAIP-17)
1954pub struct CaptureTool {
1955    tap_integration: Arc<TapIntegration>,
1956}
1957
1958/// Parameters for capturing escrowed funds
1959#[derive(Debug, Deserialize)]
1960struct CaptureParams {
1961    agent_did: String,
1962    escrow_id: String,
1963    #[serde(default)]
1964    amount: Option<String>,
1965    #[serde(default)]
1966    settlement_address: Option<String>,
1967}
1968
1969/// Response for capturing escrowed funds
1970#[derive(Debug, Serialize)]
1971struct CaptureResponse {
1972    escrow_id: String,
1973    message_id: String,
1974    status: String,
1975    amount_captured: Option<String>,
1976    captured_at: String,
1977}
1978
1979impl CaptureTool {
1980    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
1981        Self { tap_integration }
1982    }
1983
1984    fn tap_integration(&self) -> &TapIntegration {
1985        &self.tap_integration
1986    }
1987}
1988
1989#[async_trait::async_trait]
1990impl ToolHandler for CaptureTool {
1991    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1992        let params: CaptureParams = match arguments {
1993            Some(args) => serde_json::from_value(args)
1994                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1995            None => {
1996                return Ok(error_text_response(
1997                    "Missing required parameters".to_string(),
1998                ))
1999            }
2000        };
2001
2002        debug!("Capturing escrow: {}", params.escrow_id);
2003
2004        // Create capture message
2005        let mut capture = if let Some(amount) = params.amount.clone() {
2006            Capture::with_amount(amount)
2007        } else {
2008            Capture::new()
2009        };
2010
2011        if let Some(address) = params.settlement_address {
2012            capture = capture.with_settlement_address(address);
2013        }
2014
2015        // Validate the capture message
2016        if let Err(e) = capture.validate() {
2017            return Ok(error_text_response(format!(
2018                "Capture validation failed: {}",
2019                e
2020            )));
2021        }
2022
2023        // Create DIDComm message with thread ID linking to the escrow
2024        let didcomm_message = match capture.to_didcomm(&params.agent_did) {
2025            Ok(mut msg) => {
2026                msg.thid = Some(params.escrow_id.clone());
2027                msg
2028            }
2029            Err(e) => {
2030                return Ok(error_text_response(format!(
2031                    "Failed to create DIDComm message: {}",
2032                    e
2033                )));
2034            }
2035        };
2036
2037        // Send the message through the TAP node
2038        match self
2039            .tap_integration()
2040            .node()
2041            .send_message(params.agent_did.clone(), didcomm_message.clone())
2042            .await
2043        {
2044            Ok(_) => {
2045                let response = CaptureResponse {
2046                    escrow_id: params.escrow_id,
2047                    message_id: didcomm_message.id,
2048                    status: "sent".to_string(),
2049                    amount_captured: params.amount,
2050                    captured_at: chrono::Utc::now().to_rfc3339(),
2051                };
2052
2053                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
2054                    Error::tool_execution(format!("Failed to serialize response: {}", e))
2055                })?;
2056
2057                Ok(success_text_response(response_json))
2058            }
2059            Err(e) => {
2060                error!("Failed to send capture: {}", e);
2061                Ok(error_text_response(format!(
2062                    "Failed to send capture: {}",
2063                    e
2064                )))
2065            }
2066        }
2067    }
2068
2069    fn get_definition(&self) -> Tool {
2070        Tool {
2071            name: "tap_capture".to_string(),
2072            description: "Captures escrowed funds (TAIP-17) to release them to the beneficiary"
2073                .to_string(),
2074            input_schema: schema::create_capture_schema(),
2075        }
2076    }
2077}
2078
2079/// Tool for creating Exchange messages (TAIP-18)
2080pub struct CreateExchangeTool {
2081    tap_integration: Arc<TapIntegration>,
2082}
2083
2084/// Parameters for creating an exchange request
2085#[derive(Debug, Deserialize)]
2086struct ExchangeParams {
2087    agent_did: String,
2088    from_assets: Vec<String>,
2089    to_assets: Vec<String>,
2090    #[serde(default)]
2091    from_amount: Option<String>,
2092    #[serde(default)]
2093    to_amount: Option<String>,
2094    requester_did: String,
2095    #[serde(default)]
2096    provider_did: Option<String>,
2097    #[serde(default)]
2098    agents: Vec<AgentInfo>,
2099}
2100
2101/// Response for creating an exchange
2102#[derive(Debug, Serialize)]
2103struct ExchangeResponse {
2104    transaction_id: String,
2105    message_id: String,
2106    status: String,
2107    created_at: String,
2108}
2109
2110impl CreateExchangeTool {
2111    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
2112        Self { tap_integration }
2113    }
2114
2115    fn tap_integration(&self) -> &TapIntegration {
2116        &self.tap_integration
2117    }
2118}
2119
2120#[async_trait::async_trait]
2121impl ToolHandler for CreateExchangeTool {
2122    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
2123        let params: ExchangeParams = match arguments {
2124            Some(args) => serde_json::from_value(args)
2125                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
2126            None => {
2127                return Ok(error_text_response(
2128                    "Missing required parameters".to_string(),
2129                ))
2130            }
2131        };
2132
2133        if params.from_amount.is_none() && params.to_amount.is_none() {
2134            return Ok(error_text_response(
2135                "Either from_amount or to_amount must be specified".to_string(),
2136            ));
2137        }
2138
2139        let requester = Party::new(&params.requester_did);
2140        let agents: Vec<Agent> = params
2141            .agents
2142            .iter()
2143            .map(|a| Agent::new(&a.id, &a.role, &a.for_party))
2144            .collect();
2145
2146        let mut exchange = if let Some(amount) = params.from_amount {
2147            Exchange::new_from(
2148                params.from_assets,
2149                params.to_assets,
2150                amount,
2151                requester,
2152                agents,
2153            )
2154        } else {
2155            Exchange::new_to(
2156                params.from_assets,
2157                params.to_assets,
2158                params.to_amount.unwrap(),
2159                requester,
2160                agents,
2161            )
2162        };
2163
2164        if let Some(provider_did) = params.provider_did {
2165            exchange = exchange.with_provider(Party::new(&provider_did));
2166        }
2167
2168        if let Err(e) = exchange.validate() {
2169            return Ok(error_text_response(format!(
2170                "Exchange validation failed: {}",
2171                e
2172            )));
2173        }
2174
2175        let didcomm_message = match exchange.to_didcomm(&params.agent_did) {
2176            Ok(msg) => msg,
2177            Err(e) => {
2178                return Ok(error_text_response(format!(
2179                    "Failed to create DIDComm message: {}",
2180                    e
2181                )));
2182            }
2183        };
2184
2185        match self
2186            .tap_integration()
2187            .node()
2188            .send_message(params.agent_did.clone(), didcomm_message.clone())
2189            .await
2190        {
2191            Ok(_) => {
2192                let response = ExchangeResponse {
2193                    transaction_id: didcomm_message.id.clone(),
2194                    message_id: didcomm_message.id,
2195                    status: "sent".to_string(),
2196                    created_at: chrono::Utc::now().to_rfc3339(),
2197                };
2198
2199                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
2200                    Error::tool_execution(format!("Failed to serialize response: {}", e))
2201                })?;
2202
2203                Ok(success_text_response(response_json))
2204            }
2205            Err(e) => {
2206                error!("Failed to send exchange: {}", e);
2207                Ok(error_text_response(format!(
2208                    "Failed to send exchange: {}",
2209                    e
2210                )))
2211            }
2212        }
2213    }
2214
2215    fn get_definition(&self) -> Tool {
2216        Tool {
2217            name: "tap_exchange".to_string(),
2218            description: "Creates a TAP exchange request (TAIP-18) for cross-asset quotes"
2219                .to_string(),
2220            input_schema: schema::create_exchange_schema(),
2221        }
2222    }
2223}
2224
2225/// Tool for creating Quote messages (TAIP-18)
2226pub struct CreateQuoteTool {
2227    tap_integration: Arc<TapIntegration>,
2228}
2229
2230/// Parameters for creating a quote response
2231#[derive(Debug, Deserialize)]
2232struct QuoteParams {
2233    agent_did: String,
2234    exchange_id: String,
2235    from_asset: String,
2236    to_asset: String,
2237    from_amount: String,
2238    to_amount: String,
2239    provider_did: String,
2240    #[serde(default)]
2241    agents: Vec<AgentInfo>,
2242    expires: String,
2243}
2244
2245/// Response for creating a quote
2246#[derive(Debug, Serialize)]
2247struct QuoteResponse {
2248    exchange_id: String,
2249    message_id: String,
2250    status: String,
2251    created_at: String,
2252}
2253
2254impl CreateQuoteTool {
2255    pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
2256        Self { tap_integration }
2257    }
2258
2259    fn tap_integration(&self) -> &TapIntegration {
2260        &self.tap_integration
2261    }
2262}
2263
2264#[async_trait::async_trait]
2265impl ToolHandler for CreateQuoteTool {
2266    async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
2267        let params: QuoteParams = match arguments {
2268            Some(args) => serde_json::from_value(args)
2269                .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
2270            None => {
2271                return Ok(error_text_response(
2272                    "Missing required parameters".to_string(),
2273                ))
2274            }
2275        };
2276
2277        let provider = Party::new(&params.provider_did);
2278        let agents: Vec<Agent> = params
2279            .agents
2280            .iter()
2281            .map(|a| Agent::new(&a.id, &a.role, &a.for_party))
2282            .collect();
2283
2284        let quote = Quote::new(
2285            params.from_asset,
2286            params.to_asset,
2287            params.from_amount,
2288            params.to_amount,
2289            provider,
2290            agents,
2291            params.expires,
2292        );
2293
2294        if let Err(e) = quote.validate() {
2295            return Ok(error_text_response(format!(
2296                "Quote validation failed: {}",
2297                e
2298            )));
2299        }
2300
2301        let didcomm_message = match quote.to_didcomm(&params.agent_did) {
2302            Ok(mut msg) => {
2303                msg.thid = Some(params.exchange_id.clone());
2304                msg
2305            }
2306            Err(e) => {
2307                return Ok(error_text_response(format!(
2308                    "Failed to create DIDComm message: {}",
2309                    e
2310                )));
2311            }
2312        };
2313
2314        match self
2315            .tap_integration()
2316            .node()
2317            .send_message(params.agent_did.clone(), didcomm_message.clone())
2318            .await
2319        {
2320            Ok(_) => {
2321                let response = QuoteResponse {
2322                    exchange_id: params.exchange_id,
2323                    message_id: didcomm_message.id,
2324                    status: "sent".to_string(),
2325                    created_at: chrono::Utc::now().to_rfc3339(),
2326                };
2327
2328                let response_json = serde_json::to_string_pretty(&response).map_err(|e| {
2329                    Error::tool_execution(format!("Failed to serialize response: {}", e))
2330                })?;
2331
2332                Ok(success_text_response(response_json))
2333            }
2334            Err(e) => {
2335                error!("Failed to send quote: {}", e);
2336                Ok(error_text_response(format!("Failed to send quote: {}", e)))
2337            }
2338        }
2339    }
2340
2341    fn get_definition(&self) -> Tool {
2342        Tool {
2343            name: "tap_quote".to_string(),
2344            description: "Creates a TAP quote response (TAIP-18) for an exchange request"
2345                .to_string(),
2346            input_schema: schema::create_quote_schema(),
2347        }
2348    }
2349}