Skip to main content

tap_mcp/tools/
customer_tools.rs

1//! Customer and connection tools for TAP MCP
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::{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
17/// Tool for listing customers (parties that an agent acts for)
18pub struct ListCustomersTool {
19    tap_integration: Arc<TapIntegration>,
20}
21
22/// Parameters for listing customers
23#[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/// Response for listing customers
33#[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        // Get storage for the agent
77        let storage = match self
78            .tap_integration()
79            .storage_for_agent(&params.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        // Get all customers for this agent from the database
96        let all_customers = match storage.list_customers(&params.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        // Convert database customers to our response format
108        let mut customers: Vec<CustomerInfo> = Vec::new();
109
110        for customer in all_customers {
111            // Convert customer metadata from profile
112            let mut metadata = HashMap::new();
113
114            if let Some(profile) = customer.profile.as_object() {
115                // Copy all profile fields as metadata
116                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            // Add specific fields if they exist
124            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            // Get transaction count for this customer
162            // We'll need to search through transactions to find where this customer is involved
163            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                        // Check if this customer is involved in the transaction
170                        let mut is_involved = false;
171
172                        if let TapMessage::Transfer(ref transfer) = tap_message {
173                            // Check if customer is originator
174                            if let Some(originator) = &transfer.originator {
175                                if originator.id == customer.id {
176                                    is_involved = true;
177                                }
178                            }
179                            // Check if customer is beneficiary
180                            if let Some(ref beneficiary) = transfer.beneficiary {
181                                if beneficiary.id == customer.id {
182                                    is_involved = true;
183                                }
184                            }
185                            // Check if any agent acts for this customer
186                            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        // Apply pagination - customers are already in a Vec
211        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
239/// Tool for listing connections (counterparties with transaction history)
240pub struct ListConnectionsTool {
241    tap_integration: Arc<TapIntegration>,
242}
243
244/// Parameters for listing connections
245#[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/// Response for listing connections
255#[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>, // Roles this counterparty has played
269}
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        // We need to search across all agent storages to find transactions involving this party
300        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        // Search through each agent's storage
311        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            // Process each transaction
336            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                    // Check if our party is the originator
344                    if let Some(originator) = &transfer.originator {
345                        if originator.id == params.party_id {
346                            party_is_involved = true;
347                            // Add beneficiary as counterparty
348                            if let Some(ref beneficiary) = transfer.beneficiary {
349                                counterparties.insert(beneficiary.id.clone());
350                            }
351                        }
352                    }
353
354                    // Check if our party is the beneficiary
355                    if let Some(ref beneficiary) = transfer.beneficiary {
356                        if beneficiary.id == params.party_id {
357                            party_is_involved = true;
358                            // Add originator as counterparty
359                            if let Some(originator) = &transfer.originator {
360                                counterparties.insert(originator.id.clone());
361                            }
362                        }
363                    }
364
365                    // Check if our party is represented by any agent
366                    for agent in &transfer.agents {
367                        if agent.for_parties().contains(&params.party_id) {
368                            party_is_involved = true;
369                            // Add other parties represented by other agents as counterparties
370                            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 != &params.party_id {
374                                            counterparties.insert(other_party.clone());
375                                        }
376                                    }
377                                }
378                            }
379                        }
380                    }
381
382                    // If this party is involved, record the counterparties
383                    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                            // Determine role of counterparty
400                            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                            // Add metadata from party objects
416                            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        // Apply pagination and sort by ID for consistent ordering
439        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
468/// Tool for getting customer details including IVMS101 data
469pub struct GetCustomerDetailsTool {
470    tap_integration: Arc<TapIntegration>,
471}
472
473/// Parameters for getting customer details
474#[derive(Debug, Deserialize)]
475struct GetCustomerDetailsParams {
476    agent_did: String,
477    customer_id: String,
478}
479
480/// Response for getting customer details
481#[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        // Get storage for the agent
516        let storage = match self
517            .tap_integration()
518            .storage_for_agent(&params.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        // Get customer data
535        let customer = match storage.get_customer(&params.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            // Convert customer to JSON value
545            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
578/// Tool for generating IVMS101 data for a customer
579pub struct GenerateIvms101Tool {
580    tap_integration: Arc<TapIntegration>,
581}
582
583/// Parameters for generating IVMS101
584#[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        // Get storage for the agent
619        let storage = match self
620            .tap_integration()
621            .storage_for_agent(&params.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        // Create customer manager
638        let customer_manager = CustomerManager::new(storage);
639
640        // Generate IVMS101 data
641        match customer_manager
642            .generate_ivms101_data(&params.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
670/// Tool for updating customer profile
671pub struct UpdateCustomerProfileTool {
672    tap_integration: Arc<TapIntegration>,
673}
674
675/// Parameters for updating customer profile
676#[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        // Get storage for the agent
712        let storage = match self
713            .tap_integration()
714            .storage_for_agent(&params.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        // Create customer manager
731        let customer_manager = CustomerManager::new(storage);
732
733        // Update customer profile
734        match customer_manager
735            .update_customer_profile(&params.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
761/// Tool for creating a new customer
762pub struct CreateCustomerTool {
763    tap_integration: Arc<TapIntegration>,
764}
765
766/// Parameters for creating a customer
767#[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        // Get storage for the agent
803        let storage = match self
804            .tap_integration()
805            .storage_for_agent(&params.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        // Create customer manager
822        let customer_manager = CustomerManager::new(storage.clone());
823
824        // Check if customer already exists
825        let existing = match storage.get_customer(&params.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            // Create new customer
838            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            // Create customer profile from schema.org data
855            let mut profile = json!({
856                "@context": "https://schema.org",
857                "@type": "Person",
858                "identifier": params.customer_id.clone(),
859            });
860
861            // Merge provided profile data
862            if let Value::Object(profile_obj) = &mut profile {
863                if let Value::Object(data_obj) = &params.profile_data {
864                    for (key, value) in data_obj {
865                        profile_obj.insert(key.clone(), value.clone());
866                    }
867                }
868            }
869
870            // Determine schema type based on provided data
871            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            // Create Customer struct
880            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            // Create the customer
938            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            // Update existing customer
956            match customer_manager
957                .update_customer_profile(&params.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
984/// Tool for updating customer from IVMS101 data
985pub struct UpdateCustomerFromIvms101Tool {
986    tap_integration: Arc<TapIntegration>,
987}
988
989/// Parameters for updating customer from IVMS101
990#[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        // Get storage for the agent
1026        let storage = match self
1027            .tap_integration()
1028            .storage_for_agent(&params.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        // Create customer manager
1045        let customer_manager = CustomerManager::new(storage);
1046
1047        // Update customer from IVMS101 data
1048        match customer_manager
1049            .update_customer_from_ivms101(&params.customer_id, &params.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}