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::{json, Value};
10use std::collections::{HashMap, HashSet};
11use std::sync::Arc;
12use tap_msg::message::TapMessage;
13use tap_node::customer::CustomerManager;
14use tap_node::storage::models::{Customer, SchemaType};
15use tracing::{debug, error};
16
17pub struct ListCustomersTool {
19 tap_integration: Arc<TapIntegration>,
20}
21
22#[derive(Debug, Deserialize)]
24struct ListCustomersParams {
25 agent_did: String,
26 #[serde(default = "default_limit")]
27 limit: u32,
28 #[serde(default)]
29 offset: u32,
30}
31
32#[derive(Debug, Serialize)]
34struct ListCustomersResponse {
35 customers: Vec<CustomerInfo>,
36 total: usize,
37}
38
39#[derive(Debug, Serialize)]
40struct CustomerInfo {
41 #[serde(rename = "@id")]
42 id: String,
43 metadata: HashMap<String, serde_json::Value>,
44 transaction_count: usize,
45 transaction_ids: Vec<String>,
46}
47
48impl ListCustomersTool {
49 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
50 Self { tap_integration }
51 }
52
53 fn tap_integration(&self) -> &TapIntegration {
54 &self.tap_integration
55 }
56}
57
58#[async_trait::async_trait]
59impl ToolHandler for ListCustomersTool {
60 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
61 let params: ListCustomersParams = match arguments {
62 Some(args) => serde_json::from_value(args)
63 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
64 None => {
65 return Ok(error_text_response(
66 "Missing required parameters".to_string(),
67 ))
68 }
69 };
70
71 debug!(
72 "Listing customers for agent {} with limit={}, offset={}",
73 params.agent_did, params.limit, params.offset
74 );
75
76 let storage = match self
78 .tap_integration()
79 .storage_for_agent(¶ms.agent_did)
80 .await
81 {
82 Ok(storage) => storage,
83 Err(e) => {
84 error!(
85 "Failed to get storage for agent {}: {}",
86 params.agent_did, e
87 );
88 return Ok(error_text_response(format!(
89 "Failed to get storage for agent {}: {}",
90 params.agent_did, e
91 )));
92 }
93 };
94
95 let all_customers = match storage.list_customers(¶ms.agent_did, 1000, 0).await {
97 Ok(customers) => customers,
98 Err(e) => {
99 error!("Failed to list customers: {}", e);
100 return Ok(error_text_response(format!(
101 "Failed to list customers: {}",
102 e
103 )));
104 }
105 };
106
107 let mut customers: Vec<CustomerInfo> = Vec::new();
109
110 for customer in all_customers {
111 let mut metadata = HashMap::new();
113
114 if let Some(profile) = customer.profile.as_object() {
115 for (key, value) in profile {
117 if key != "@context" && key != "@type" && key != "identifier" {
118 metadata.insert(key.clone(), value.clone());
119 }
120 }
121 }
122
123 if let Some(given_name) = &customer.given_name {
125 metadata.insert(
126 "givenName".to_string(),
127 serde_json::Value::String(given_name.clone()),
128 );
129 }
130 if let Some(family_name) = &customer.family_name {
131 metadata.insert(
132 "familyName".to_string(),
133 serde_json::Value::String(family_name.clone()),
134 );
135 }
136 if let Some(display_name) = &customer.display_name {
137 metadata.insert(
138 "name".to_string(),
139 serde_json::Value::String(display_name.clone()),
140 );
141 }
142 if let Some(country) = &customer.address_country {
143 metadata.insert(
144 "addressCountry".to_string(),
145 serde_json::Value::String(country.clone()),
146 );
147 }
148 if let Some(locality) = &customer.address_locality {
149 metadata.insert(
150 "addressLocality".to_string(),
151 serde_json::Value::String(locality.clone()),
152 );
153 }
154 if let Some(postal_code) = &customer.postal_code {
155 metadata.insert(
156 "postalCode".to_string(),
157 serde_json::Value::String(postal_code.clone()),
158 );
159 }
160
161 let mut transaction_ids = Vec::new();
164 if let Ok(transactions) = storage.list_transactions(1000, 0).await {
165 for transaction in transactions {
166 if let Ok(tap_message) =
167 serde_json::from_value::<TapMessage>(transaction.message_json.clone())
168 {
169 let mut is_involved = false;
171
172 if let TapMessage::Transfer(ref transfer) = tap_message {
173 if let Some(originator) = &transfer.originator {
175 if originator.id == customer.id {
176 is_involved = true;
177 }
178 }
179 if let Some(ref beneficiary) = transfer.beneficiary {
181 if beneficiary.id == customer.id {
182 is_involved = true;
183 }
184 }
185 for agent in &transfer.agents {
187 if agent.for_parties().contains(&customer.id) {
188 is_involved = true;
189 }
190 }
191 }
192
193 if is_involved {
194 transaction_ids.push(transaction.reference_id);
195 }
196 }
197 }
198 }
199
200 customers.push(CustomerInfo {
201 id: customer.id,
202 metadata,
203 transaction_count: transaction_ids.len(),
204 transaction_ids,
205 });
206 }
207
208 let total = customers.len();
209
210 customers.sort_by(|a, b| a.id.cmp(&b.id));
212
213 let paginated_customers: Vec<CustomerInfo> = customers
214 .into_iter()
215 .skip(params.offset as usize)
216 .take(params.limit as usize)
217 .collect();
218
219 let response = ListCustomersResponse {
220 customers: paginated_customers,
221 total,
222 };
223
224 let response_json = serde_json::to_string_pretty(&response)
225 .map_err(|e| Error::tool_execution(format!("Failed to serialize response: {}", e)))?;
226
227 Ok(success_text_response(response_json))
228 }
229
230 fn get_definition(&self) -> Tool {
231 Tool {
232 name: "tap_list_customers".to_string(),
233 description: "Lists customers (parties) that a specific agent acts on behalf of. Includes metadata about each party and transaction history.".to_string(),
234 input_schema: schema::list_customers_schema(),
235 }
236 }
237}
238
239pub struct ListConnectionsTool {
241 tap_integration: Arc<TapIntegration>,
242}
243
244#[derive(Debug, Deserialize)]
246struct ListConnectionsParams {
247 party_id: String,
248 #[serde(default = "default_limit")]
249 limit: u32,
250 #[serde(default)]
251 offset: u32,
252}
253
254#[derive(Debug, Serialize)]
256struct ListConnectionsResponse {
257 connections: Vec<ConnectionInfo>,
258 total: usize,
259}
260
261#[derive(Debug, Serialize)]
262struct ConnectionInfo {
263 #[serde(rename = "@id")]
264 id: String,
265 metadata: HashMap<String, serde_json::Value>,
266 transaction_count: usize,
267 transaction_ids: Vec<String>,
268 roles: Vec<String>, }
270
271impl ListConnectionsTool {
272 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
273 Self { tap_integration }
274 }
275
276 fn tap_integration(&self) -> &TapIntegration {
277 &self.tap_integration
278 }
279}
280
281#[async_trait::async_trait]
282impl ToolHandler for ListConnectionsTool {
283 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
284 let params: ListConnectionsParams = match arguments {
285 Some(args) => serde_json::from_value(args)
286 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
287 None => {
288 return Ok(error_text_response(
289 "Missing required parameters".to_string(),
290 ))
291 }
292 };
293
294 debug!(
295 "Listing connections for party {} with limit={}, offset={}",
296 params.party_id, params.limit, params.offset
297 );
298
299 let agent_infos = match self.tap_integration().list_agents().await {
301 Ok(agents) => agents,
302 Err(e) => {
303 error!("Failed to list agents: {}", e);
304 return Ok(error_text_response(format!("Failed to list agents: {}", e)));
305 }
306 };
307
308 let mut connections: HashMap<String, ConnectionInfo> = HashMap::new();
309
310 for agent_info in agent_infos {
312 let storage = match self
313 .tap_integration()
314 .storage_for_agent(&agent_info.id)
315 .await
316 {
317 Ok(storage) => storage,
318 Err(e) => {
319 debug!("Failed to get storage for agent {}: {}", agent_info.id, e);
320 continue;
321 }
322 };
323
324 let transactions = match storage.list_transactions(1000, 0).await {
325 Ok(transactions) => transactions,
326 Err(e) => {
327 debug!(
328 "Failed to get transactions for agent {}: {}",
329 agent_info.id, e
330 );
331 continue;
332 }
333 };
334
335 for transaction in transactions {
337 if let Ok(TapMessage::Transfer(ref transfer)) =
338 serde_json::from_value::<TapMessage>(transaction.message_json.clone())
339 {
340 let mut party_is_involved = false;
341 let mut counterparties = HashSet::new();
342
343 if let Some(originator) = &transfer.originator {
345 if originator.id == params.party_id {
346 party_is_involved = true;
347 if let Some(ref beneficiary) = transfer.beneficiary {
349 counterparties.insert(beneficiary.id.clone());
350 }
351 }
352 }
353
354 if let Some(ref beneficiary) = transfer.beneficiary {
356 if beneficiary.id == params.party_id {
357 party_is_involved = true;
358 if let Some(originator) = &transfer.originator {
360 counterparties.insert(originator.id.clone());
361 }
362 }
363 }
364
365 for agent in &transfer.agents {
367 if agent.for_parties().contains(¶ms.party_id) {
368 party_is_involved = true;
369 for other_agent in &transfer.agents {
371 if other_agent.id != agent.id {
372 for other_party in other_agent.for_parties() {
373 if other_party != ¶ms.party_id {
374 counterparties.insert(other_party.clone());
375 }
376 }
377 }
378 }
379 }
380 }
381
382 if party_is_involved {
384 for counterparty_id in counterparties {
385 let connection = connections
386 .entry(counterparty_id.clone())
387 .or_insert_with(|| ConnectionInfo {
388 id: counterparty_id.clone(),
389 metadata: HashMap::new(),
390 transaction_count: 0,
391 transaction_ids: Vec::new(),
392 roles: Vec::new(),
393 });
394 connection.transaction_count += 1;
395 connection
396 .transaction_ids
397 .push(transaction.reference_id.clone());
398
399 if let Some(originator) = &transfer.originator {
401 if counterparty_id == originator.id
402 && !connection.roles.contains(&"originator".to_string())
403 {
404 connection.roles.push("originator".to_string());
405 }
406 }
407 if let Some(ref beneficiary) = transfer.beneficiary {
408 if counterparty_id == beneficiary.id
409 && !connection.roles.contains(&"beneficiary".to_string())
410 {
411 connection.roles.push("beneficiary".to_string());
412 }
413 }
414
415 if let Some(originator) = &transfer.originator {
417 if counterparty_id == originator.id {
418 for (key, value) in &originator.metadata {
419 connection.metadata.insert(key.clone(), value.clone());
420 }
421 }
422 }
423 if let Some(ref beneficiary) = transfer.beneficiary {
424 if counterparty_id == beneficiary.id {
425 for (key, value) in &beneficiary.metadata {
426 connection.metadata.insert(key.clone(), value.clone());
427 }
428 }
429 }
430 }
431 }
432 }
433 }
434 }
435
436 let total = connections.len();
437
438 let mut connection_list: Vec<ConnectionInfo> = connections.into_values().collect();
440 connection_list.sort_by(|a, b| a.id.cmp(&b.id));
441
442 let paginated_connections: Vec<ConnectionInfo> = connection_list
443 .into_iter()
444 .skip(params.offset as usize)
445 .take(params.limit as usize)
446 .collect();
447
448 let response = ListConnectionsResponse {
449 connections: paginated_connections,
450 total,
451 };
452
453 let response_json = serde_json::to_string_pretty(&response)
454 .map_err(|e| Error::tool_execution(format!("Failed to serialize response: {}", e)))?;
455
456 Ok(success_text_response(response_json))
457 }
458
459 fn get_definition(&self) -> Tool {
460 Tool {
461 name: "tap_list_connections".to_string(),
462 description: "Lists all counterparties (connections) of a specific party. Includes metadata about each counterparty and transaction history.".to_string(),
463 input_schema: schema::list_connections_schema(),
464 }
465 }
466}
467
468pub struct GetCustomerDetailsTool {
470 tap_integration: Arc<TapIntegration>,
471}
472
473#[derive(Debug, Deserialize)]
475struct GetCustomerDetailsParams {
476 agent_did: String,
477 customer_id: String,
478}
479
480#[derive(Debug, Serialize)]
482struct GetCustomerDetailsResponse {
483 customer: Option<serde_json::Value>,
484 ivms101_data: Option<serde_json::Value>,
485}
486
487impl GetCustomerDetailsTool {
488 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
489 Self { tap_integration }
490 }
491
492 fn tap_integration(&self) -> &TapIntegration {
493 &self.tap_integration
494 }
495}
496
497#[async_trait::async_trait]
498impl ToolHandler for GetCustomerDetailsTool {
499 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
500 let params: GetCustomerDetailsParams = match arguments {
501 Some(args) => serde_json::from_value(args)
502 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
503 None => {
504 return Ok(error_text_response(
505 "Missing required parameters".to_string(),
506 ))
507 }
508 };
509
510 debug!(
511 "Getting customer details for customer {} via agent {}",
512 params.customer_id, params.agent_did
513 );
514
515 let storage = match self
517 .tap_integration()
518 .storage_for_agent(¶ms.agent_did)
519 .await
520 {
521 Ok(storage) => storage,
522 Err(e) => {
523 error!(
524 "Failed to get storage for agent {}: {}",
525 params.agent_did, e
526 );
527 return Ok(error_text_response(format!(
528 "Failed to get storage for agent {}: {}",
529 params.agent_did, e
530 )));
531 }
532 };
533
534 let customer = match storage.get_customer(¶ms.customer_id).await {
536 Ok(customer) => customer,
537 Err(e) => {
538 debug!("Failed to get customer {}: {}", params.customer_id, e);
539 None
540 }
541 };
542
543 let response = if let Some(customer) = customer {
544 let customer_json = serde_json::to_value(&customer).map_err(|e| {
546 Error::tool_execution(format!("Failed to serialize customer: {}", e))
547 })?;
548
549 let profile = customer_json.get("profile").cloned();
550 let ivms101 = customer_json.get("ivms101_data").cloned();
551
552 GetCustomerDetailsResponse {
553 customer: Some(profile.unwrap_or(customer_json)),
554 ivms101_data: ivms101,
555 }
556 } else {
557 GetCustomerDetailsResponse {
558 customer: None,
559 ivms101_data: None,
560 }
561 };
562
563 let response_json = serde_json::to_string_pretty(&response)
564 .map_err(|e| Error::tool_execution(format!("Failed to serialize response: {}", e)))?;
565
566 Ok(success_text_response(response_json))
567 }
568
569 fn get_definition(&self) -> Tool {
570 Tool {
571 name: "tap_get_customer_details".to_string(),
572 description: "Gets detailed information about a specific customer including their profile and IVMS101 data if available.".to_string(),
573 input_schema: schema::get_customer_details_schema(),
574 }
575 }
576}
577
578pub struct GenerateIvms101Tool {
580 tap_integration: Arc<TapIntegration>,
581}
582
583#[derive(Debug, Deserialize)]
585struct GenerateIvms101Params {
586 agent_did: String,
587 customer_id: String,
588}
589
590impl GenerateIvms101Tool {
591 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
592 Self { tap_integration }
593 }
594
595 fn tap_integration(&self) -> &TapIntegration {
596 &self.tap_integration
597 }
598}
599
600#[async_trait::async_trait]
601impl ToolHandler for GenerateIvms101Tool {
602 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
603 let params: GenerateIvms101Params = match arguments {
604 Some(args) => serde_json::from_value(args)
605 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
606 None => {
607 return Ok(error_text_response(
608 "Missing required parameters".to_string(),
609 ))
610 }
611 };
612
613 debug!(
614 "Generating IVMS101 data for customer {} via agent {}",
615 params.customer_id, params.agent_did
616 );
617
618 let storage = match self
620 .tap_integration()
621 .storage_for_agent(¶ms.agent_did)
622 .await
623 {
624 Ok(storage) => storage,
625 Err(e) => {
626 error!(
627 "Failed to get storage for agent {}: {}",
628 params.agent_did, e
629 );
630 return Ok(error_text_response(format!(
631 "Failed to get storage for agent {}: {}",
632 params.agent_did, e
633 )));
634 }
635 };
636
637 let customer_manager = CustomerManager::new(storage);
639
640 match customer_manager
642 .generate_ivms101_data(¶ms.customer_id)
643 .await
644 {
645 Ok(ivms_data) => {
646 let response_json = serde_json::to_string_pretty(&ivms_data).map_err(|e| {
647 Error::tool_execution(format!("Failed to serialize IVMS101 data: {}", e))
648 })?;
649 Ok(success_text_response(response_json))
650 }
651 Err(e) => {
652 error!("Failed to generate IVMS101 data: {}", e);
653 Ok(error_text_response(format!(
654 "Failed to generate IVMS101 data: {}",
655 e
656 )))
657 }
658 }
659 }
660
661 fn get_definition(&self) -> Tool {
662 Tool {
663 name: "tap_generate_ivms101".to_string(),
664 description: "Generates IVMS101 compliant data for a customer based on their stored profile information.".to_string(),
665 input_schema: schema::generate_ivms101_schema(),
666 }
667 }
668}
669
670pub struct UpdateCustomerProfileTool {
672 tap_integration: Arc<TapIntegration>,
673}
674
675#[derive(Debug, Deserialize)]
677struct UpdateCustomerProfileParams {
678 agent_did: String,
679 customer_id: String,
680 profile_data: Value,
681}
682
683impl UpdateCustomerProfileTool {
684 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
685 Self { tap_integration }
686 }
687
688 fn tap_integration(&self) -> &TapIntegration {
689 &self.tap_integration
690 }
691}
692
693#[async_trait::async_trait]
694impl ToolHandler for UpdateCustomerProfileTool {
695 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
696 let params: UpdateCustomerProfileParams = match arguments {
697 Some(args) => serde_json::from_value(args)
698 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
699 None => {
700 return Ok(error_text_response(
701 "Missing required parameters".to_string(),
702 ))
703 }
704 };
705
706 debug!(
707 "Updating profile for customer {} via agent {}",
708 params.customer_id, params.agent_did
709 );
710
711 let storage = match self
713 .tap_integration()
714 .storage_for_agent(¶ms.agent_did)
715 .await
716 {
717 Ok(storage) => storage,
718 Err(e) => {
719 error!(
720 "Failed to get storage for agent {}: {}",
721 params.agent_did, e
722 );
723 return Ok(error_text_response(format!(
724 "Failed to get storage for agent {}: {}",
725 params.agent_did, e
726 )));
727 }
728 };
729
730 let customer_manager = CustomerManager::new(storage);
732
733 match customer_manager
735 .update_customer_profile(¶ms.customer_id, params.profile_data)
736 .await
737 {
738 Ok(_) => Ok(success_text_response(format!(
739 "Successfully updated profile for customer {}",
740 params.customer_id
741 ))),
742 Err(e) => {
743 error!("Failed to update customer profile: {}", e);
744 Ok(error_text_response(format!(
745 "Failed to update customer profile: {}",
746 e
747 )))
748 }
749 }
750 }
751
752 fn get_definition(&self) -> Tool {
753 Tool {
754 name: "tap_update_customer_profile".to_string(),
755 description: "Updates the schema.org profile data for a customer. The profile_data should be a JSON object with schema.org fields.".to_string(),
756 input_schema: schema::update_customer_profile_schema(),
757 }
758 }
759}
760
761pub struct CreateCustomerTool {
763 tap_integration: Arc<TapIntegration>,
764}
765
766#[derive(Debug, Deserialize)]
768struct CreateCustomerParams {
769 agent_did: String,
770 customer_id: String,
771 profile_data: Value,
772}
773
774impl CreateCustomerTool {
775 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
776 Self { tap_integration }
777 }
778
779 fn tap_integration(&self) -> &TapIntegration {
780 &self.tap_integration
781 }
782}
783
784#[async_trait::async_trait]
785impl ToolHandler for CreateCustomerTool {
786 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
787 let params: CreateCustomerParams = match arguments {
788 Some(args) => serde_json::from_value(args)
789 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
790 None => {
791 return Ok(error_text_response(
792 "Missing required parameters".to_string(),
793 ))
794 }
795 };
796
797 debug!(
798 "Creating customer {} via agent {}",
799 params.customer_id, params.agent_did
800 );
801
802 let storage = match self
804 .tap_integration()
805 .storage_for_agent(¶ms.agent_did)
806 .await
807 {
808 Ok(storage) => storage,
809 Err(e) => {
810 error!(
811 "Failed to get storage for agent {}: {}",
812 params.agent_did, e
813 );
814 return Ok(error_text_response(format!(
815 "Failed to get storage for agent {}: {}",
816 params.agent_did, e
817 )));
818 }
819 };
820
821 let customer_manager = CustomerManager::new(storage.clone());
823
824 let existing = match storage.get_customer(¶ms.customer_id).await {
826 Ok(existing) => existing,
827 Err(e) => {
828 error!("Failed to check existing customer: {}", e);
829 return Ok(error_text_response(format!(
830 "Failed to check existing customer: {}",
831 e
832 )));
833 }
834 };
835
836 if existing.is_none() {
837 let display_name = params
839 .profile_data
840 .get("givenName")
841 .and_then(|v| v.as_str())
842 .map(|given| {
843 if let Some(family) = params
844 .profile_data
845 .get("familyName")
846 .and_then(|v| v.as_str())
847 {
848 format!("{} {}", given, family)
849 } else {
850 given.to_string()
851 }
852 });
853
854 let mut profile = json!({
856 "@context": "https://schema.org",
857 "@type": "Person",
858 "identifier": params.customer_id.clone(),
859 });
860
861 if let Value::Object(profile_obj) = &mut profile {
863 if let Value::Object(data_obj) = ¶ms.profile_data {
864 for (key, value) in data_obj {
865 profile_obj.insert(key.clone(), value.clone());
866 }
867 }
868 }
869
870 let schema_type = if params.profile_data.get("@type").and_then(|v| v.as_str())
872 == Some("Organization")
873 {
874 SchemaType::Organization
875 } else {
876 SchemaType::Person
877 };
878
879 let customer = Customer {
881 id: params.customer_id.clone(),
882 agent_did: params.agent_did.clone(),
883 schema_type,
884 given_name: params
885 .profile_data
886 .get("givenName")
887 .and_then(|v| v.as_str())
888 .map(String::from),
889 family_name: params
890 .profile_data
891 .get("familyName")
892 .and_then(|v| v.as_str())
893 .map(String::from),
894 display_name,
895 legal_name: params
896 .profile_data
897 .get("legalName")
898 .and_then(|v| v.as_str())
899 .map(String::from),
900 lei_code: params
901 .profile_data
902 .get("leiCode")
903 .and_then(|v| v.as_str())
904 .map(String::from),
905 mcc_code: params
906 .profile_data
907 .get("mccCode")
908 .and_then(|v| v.as_str())
909 .map(String::from),
910 address_country: params
911 .profile_data
912 .get("addressCountry")
913 .and_then(|v| v.as_str())
914 .map(String::from),
915 address_locality: params
916 .profile_data
917 .get("addressLocality")
918 .and_then(|v| v.as_str())
919 .map(String::from),
920 postal_code: params
921 .profile_data
922 .get("postalCode")
923 .and_then(|v| v.as_str())
924 .map(String::from),
925 street_address: params
926 .profile_data
927 .get("streetAddress")
928 .and_then(|v| v.as_str())
929 .map(String::from),
930 profile,
931 ivms101_data: None,
932 verified_at: None,
933 created_at: chrono::Utc::now().to_rfc3339(),
934 updated_at: chrono::Utc::now().to_rfc3339(),
935 };
936
937 match storage.upsert_customer(&customer).await {
939 Ok(_) => {
940 debug!("Created new customer {}", params.customer_id);
941 Ok(success_text_response(format!(
942 "Successfully created customer {}",
943 params.customer_id
944 )))
945 }
946 Err(e) => {
947 error!("Failed to create customer: {}", e);
948 Ok(error_text_response(format!(
949 "Failed to create customer: {}",
950 e
951 )))
952 }
953 }
954 } else {
955 match customer_manager
957 .update_customer_profile(¶ms.customer_id, params.profile_data)
958 .await
959 {
960 Ok(_) => Ok(success_text_response(format!(
961 "Successfully updated existing customer {}",
962 params.customer_id
963 ))),
964 Err(e) => {
965 error!("Failed to update customer: {}", e);
966 Ok(error_text_response(format!(
967 "Failed to update customer: {}",
968 e
969 )))
970 }
971 }
972 }
973 }
974
975 fn get_definition(&self) -> Tool {
976 Tool {
977 name: "tap_create_customer".to_string(),
978 description: "Creates a new customer profile for an agent. The customer_id should be a DID or unique identifier. The profile_data should be a JSON object with schema.org fields (e.g., givenName, familyName, addressCountry). If a customer with the same ID already exists, their profile will be updated.".to_string(),
979 input_schema: schema::create_customer_schema(),
980 }
981 }
982}
983
984pub struct UpdateCustomerFromIvms101Tool {
986 tap_integration: Arc<TapIntegration>,
987}
988
989#[derive(Debug, Deserialize)]
991struct UpdateCustomerFromIvms101Params {
992 agent_did: String,
993 customer_id: String,
994 ivms101_data: Value,
995}
996
997impl UpdateCustomerFromIvms101Tool {
998 pub fn new(tap_integration: Arc<TapIntegration>) -> Self {
999 Self { tap_integration }
1000 }
1001
1002 fn tap_integration(&self) -> &TapIntegration {
1003 &self.tap_integration
1004 }
1005}
1006
1007#[async_trait::async_trait]
1008impl ToolHandler for UpdateCustomerFromIvms101Tool {
1009 async fn handle(&self, arguments: Option<Value>) -> Result<CallToolResult> {
1010 let params: UpdateCustomerFromIvms101Params = match arguments {
1011 Some(args) => serde_json::from_value(args)
1012 .map_err(|e| Error::invalid_parameter(format!("Invalid parameters: {}", e)))?,
1013 None => {
1014 return Ok(error_text_response(
1015 "Missing required parameters".to_string(),
1016 ))
1017 }
1018 };
1019
1020 debug!(
1021 "Updating customer {} from IVMS101 data via agent {}",
1022 params.customer_id, params.agent_did
1023 );
1024
1025 let storage = match self
1027 .tap_integration()
1028 .storage_for_agent(¶ms.agent_did)
1029 .await
1030 {
1031 Ok(storage) => storage,
1032 Err(e) => {
1033 error!(
1034 "Failed to get storage for agent {}: {}",
1035 params.agent_did, e
1036 );
1037 return Ok(error_text_response(format!(
1038 "Failed to get storage for agent {}: {}",
1039 params.agent_did, e
1040 )));
1041 }
1042 };
1043
1044 let customer_manager = CustomerManager::new(storage);
1046
1047 match customer_manager
1049 .update_customer_from_ivms101(¶ms.customer_id, ¶ms.ivms101_data)
1050 .await
1051 {
1052 Ok(_) => Ok(success_text_response(format!(
1053 "Successfully updated customer {} from IVMS101 data",
1054 params.customer_id
1055 ))),
1056 Err(e) => {
1057 error!("Failed to update customer from IVMS101: {}", e);
1058 Ok(error_text_response(format!(
1059 "Failed to update customer from IVMS101: {}",
1060 e
1061 )))
1062 }
1063 }
1064 }
1065
1066 fn get_definition(&self) -> Tool {
1067 Tool {
1068 name: "tap_update_customer_from_ivms101".to_string(),
1069 description: "Updates a customer's profile using IVMS101 data. This extracts name, address and other fields from IVMS101 format.".to_string(),
1070 input_schema: schema::update_customer_from_ivms101_schema(),
1071 }
1072 }
1073}