1use 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
25async 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
61pub struct CreateTransferTool {
63 tap_integration: Arc<TapIntegration>,
64}
65
66#[derive(Debug, Deserialize)]
68struct CreateTransferParams {
69 agent_did: String, 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#[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 let asset_id = params
148 .asset
149 .parse::<AssetId>()
150 .map_err(|e| Error::invalid_parameter(format!("Invalid asset ID: {}", e)))?;
151
152 let storage = match self
154 .tap_integration()
155 .storage_for_agent(¶ms.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 let mut originator = Party::new(¶ms.originator.id);
173 if let Ok(Some(customer)) = storage.get_customer(¶ms.originator.id).await {
174 if let Some(_profile) = customer.profile.as_object() {
176 let mut metadata = HashMap::new();
177
178 match customer.schema_type {
179 SchemaType::Person => {
180 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 originator = originator.with_name_hash(&full_name);
191 }
192
193 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 if let Some(lei_code) = customer.lei_code {
204 originator = originator.with_lei(&lei_code);
205 }
206
207 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 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 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 if !metadata.is_empty() {
236 originator = Party::with_metadata(&originator.id, metadata);
237 }
238 }
239 }
240 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(¶ms.beneficiary.id);
252 if let Ok(Some(customer)) = storage.get_customer(¶ms.beneficiary.id).await {
253 if let Some(_profile) = customer.profile.as_object() {
255 let mut metadata = HashMap::new();
256
257 match customer.schema_type {
258 SchemaType::Person => {
259 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 beneficiary = beneficiary.with_name_hash(&full_name);
270 }
271
272 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 if let Some(lei_code) = customer.lei_code {
283 beneficiary = beneficiary.with_lei(&lei_code);
284 }
285
286 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 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 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 if !metadata.is_empty() {
315 beneficiary = Party::with_metadata(&beneficiary.id, metadata);
316 }
317 }
318 }
319 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 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 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 if let Err(e) = transfer.validate() {
364 return Ok(error_text_response(format!(
365 "Transfer validation failed: {}",
366 e
367 )));
368 }
369
370 let didcomm_message = match transfer.to_didcomm(¶ms.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 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 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
448pub struct AuthorizeTool {
450 tap_integration: Arc<TapIntegration>,
451}
452
453#[derive(Debug, Deserialize)]
455struct AuthorizeParams {
456 agent_did: String, transaction_id: String,
458 #[serde(default)]
459 settlement_address: Option<String>,
460 #[serde(default)]
461 expiry: Option<String>,
462}
463
464#[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 let authorize = Authorize {
500 transaction_id: params.transaction_id.clone(),
501 settlement_address: params.settlement_address,
502 expiry: params.expiry,
503 };
504
505 if let Err(e) = authorize.validate() {
507 return Ok(error_text_response(format!(
508 "Authorize validation failed: {}",
509 e
510 )));
511 }
512
513 let didcomm_message = match authorize.to_didcomm(¶ms.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 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 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 ¶ms.agent_did,
555 ¶ms.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
594pub struct RejectTool {
596 tap_integration: Arc<TapIntegration>,
597}
598
599#[derive(Debug, Deserialize)]
601struct RejectParams {
602 agent_did: String, transaction_id: String,
604 reason: String,
605}
606
607#[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 let reject = Reject {
647 transaction_id: params.transaction_id.clone(),
648 reason: Some(params.reason.clone()),
649 };
650
651 if let Err(e) = reject.validate() {
653 return Ok(error_text_response(format!(
654 "Reject validation failed: {}",
655 e
656 )));
657 }
658
659 let didcomm_message = match reject.to_didcomm(¶ms.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 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 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 ¶ms.agent_did,
701 ¶ms.transaction_id,
702 "reject",
703 None, )
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
740pub struct CancelTool {
742 tap_integration: Arc<TapIntegration>,
743}
744
745#[derive(Debug, Deserialize)]
747struct CancelParams {
748 agent_did: String, transaction_id: String,
750 by: String,
751 #[serde(default)]
752 reason: Option<String>,
753}
754
755#[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 let cancel = Cancel {
796 transaction_id: params.transaction_id.clone(),
797 by: params.by.clone(),
798 reason: params.reason.clone(),
799 };
800
801 if let Err(e) = cancel.validate() {
803 return Ok(error_text_response(format!(
804 "Cancel validation failed: {}",
805 e
806 )));
807 }
808
809 let didcomm_message = match cancel.to_didcomm(¶ms.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 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 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 ¶ms.agent_did,
851 ¶ms.transaction_id,
852 "cancel",
853 None, )
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
891pub struct SettleTool {
893 tap_integration: Arc<TapIntegration>,
894}
895
896#[derive(Debug, Deserialize)]
898struct SettleParams {
899 agent_did: String, transaction_id: String,
901 settlement_id: String,
902 #[serde(default)]
903 amount: Option<String>,
904}
905
906#[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 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 if let Err(e) = settle.validate() {
954 return Ok(error_text_response(format!(
955 "Settle validation failed: {}",
956 e
957 )));
958 }
959
960 let didcomm_message = match settle.to_didcomm(¶ms.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 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 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 ¶ms.agent_did,
1002 ¶ms.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
1042pub struct RevertTool {
1044 tap_integration: Arc<TapIntegration>,
1045}
1046
1047#[derive(Debug, Deserialize)]
1049struct RevertParams {
1050 agent_did: String, transaction_id: String,
1052 settlement_address: String,
1053 reason: String,
1054}
1055
1056#[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 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 if let Err(e) = revert.validate() {
1104 return Ok(error_text_response(format!(
1105 "Revert validation failed: {}",
1106 e
1107 )));
1108 }
1109
1110 let didcomm_message = match revert.to_didcomm(¶ms.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 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 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 ¶ms.agent_did,
1152 ¶ms.transaction_id,
1153 "revert",
1154 None, )
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
1193pub struct ListTransactionsTool {
1195 tap_integration: Arc<TapIntegration>,
1196}
1197
1198#[derive(Debug, Deserialize, Serialize)]
1200struct ListTransactionsParams {
1201 agent_did: String, #[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#[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 let storage = self
1279 .tap_integration()
1280 .storage_for_agent(¶ms.agent_did)
1281 .await?;
1282 let direction_filter = None; let messages = storage
1284 .list_messages(params.limit, params.offset, direction_filter)
1285 .await?;
1286
1287 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 }
1314 true
1315 })
1316 .collect();
1317
1318 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}
1353pub struct CreatePaymentTool {
1357 tap_integration: Arc<TapIntegration>,
1358}
1359
1360#[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#[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 let mut merchant = Party::new(¶ms.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 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 let mut payment = if let Some(asset) = params.asset {
1443 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 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 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 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 if let Err(e) = payment.validate() {
1493 return Ok(error_text_response(format!(
1494 "Payment validation failed: {}",
1495 e
1496 )));
1497 }
1498
1499 let didcomm_message = match payment.to_didcomm(¶ms.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 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
1551pub struct CreateConnectTool {
1553 tap_integration: Arc<TapIntegration>,
1554}
1555
1556#[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#[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 let transaction_id = format!("connect-{}", uuid::Uuid::new_v4());
1641 let mut connect = Connect::new(
1642 &transaction_id,
1643 ¶ms.agent_did,
1644 ¶ms.for_party,
1645 params.role.as_deref(),
1646 );
1647
1648 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 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 if let Err(e) = connect.validate() {
1697 return Ok(error_text_response(format!(
1698 "Connect validation failed: {}",
1699 e
1700 )));
1701 }
1702
1703 let didcomm_message = match connect.to_didcomm(¶ms.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 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
1757pub struct CreateEscrowTool {
1759 tap_integration: Arc<TapIntegration>,
1760}
1761
1762#[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#[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 let mut originator = Party::new(¶ms.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(¶ms.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 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 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 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 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 if let Err(e) = escrow.validate() {
1896 return Ok(error_text_response(format!(
1897 "Escrow validation failed: {}",
1898 e
1899 )));
1900 }
1901
1902 let didcomm_message = match escrow.to_didcomm(¶ms.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 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
1953pub struct CaptureTool {
1955 tap_integration: Arc<TapIntegration>,
1956}
1957
1958#[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#[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 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 if let Err(e) = capture.validate() {
2017 return Ok(error_text_response(format!(
2018 "Capture validation failed: {}",
2019 e
2020 )));
2021 }
2022
2023 let didcomm_message = match capture.to_didcomm(¶ms.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 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
2079pub struct CreateExchangeTool {
2081 tap_integration: Arc<TapIntegration>,
2082}
2083
2084#[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#[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(¶ms.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(¶ms.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
2225pub struct CreateQuoteTool {
2227 tap_integration: Arc<TapIntegration>,
2228}
2229
2230#[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#[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(¶ms.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(¶ms.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}