@app-connect/core 1.7.10 → 1.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/connector/developerPortal.js +43 -0
  2. package/connector/proxy/index.js +10 -3
  3. package/connector/registry.js +8 -6
  4. package/handlers/admin.js +135 -22
  5. package/handlers/auth.js +89 -67
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +4 -104
  8. package/handlers/disposition.js +7 -145
  9. package/handlers/log.js +174 -258
  10. package/handlers/user.js +19 -6
  11. package/index.js +280 -47
  12. package/lib/analytics.js +3 -1
  13. package/lib/authSession.js +68 -0
  14. package/lib/callLogComposer.js +498 -420
  15. package/lib/errorHandler.js +206 -0
  16. package/lib/jwt.js +2 -0
  17. package/lib/logger.js +190 -0
  18. package/lib/oauth.js +21 -10
  19. package/lib/ringcentral.js +2 -10
  20. package/lib/sharedSMSComposer.js +471 -0
  21. package/mcp/SupportedPlatforms.md +12 -0
  22. package/mcp/lib/validator.js +91 -0
  23. package/mcp/mcpHandler.js +166 -0
  24. package/mcp/tools/checkAuthStatus.js +110 -0
  25. package/mcp/tools/collectAuthInfo.js +91 -0
  26. package/mcp/tools/createCallLog.js +308 -0
  27. package/mcp/tools/createContact.js +117 -0
  28. package/mcp/tools/createMessageLog.js +283 -0
  29. package/mcp/tools/doAuth.js +190 -0
  30. package/mcp/tools/findContactByName.js +92 -0
  31. package/mcp/tools/findContactByPhone.js +101 -0
  32. package/mcp/tools/getCallLog.js +98 -0
  33. package/mcp/tools/getGoogleFilePicker.js +103 -0
  34. package/mcp/tools/getHelp.js +44 -0
  35. package/mcp/tools/getPublicConnectors.js +53 -0
  36. package/mcp/tools/index.js +64 -0
  37. package/mcp/tools/logout.js +68 -0
  38. package/mcp/tools/rcGetCallLogs.js +78 -0
  39. package/mcp/tools/setConnector.js +69 -0
  40. package/mcp/tools/updateCallLog.js +122 -0
  41. package/models/cacheModel.js +3 -0
  42. package/package.json +71 -70
  43. package/releaseNotes.json +24 -0
  44. package/test/handlers/log.test.js +11 -4
  45. package/test/lib/logger.test.js +206 -0
  46. package/test/lib/ringcentral.test.js +0 -6
  47. package/test/lib/sharedSMSComposer.test.js +1084 -0
  48. package/test/mcp/tools/collectAuthInfo.test.js +234 -0
  49. package/test/mcp/tools/createCallLog.test.js +425 -0
  50. package/test/mcp/tools/createMessageLog.test.js +580 -0
  51. package/test/mcp/tools/doAuth.test.js +376 -0
  52. package/test/mcp/tools/findContactByName.test.js +263 -0
  53. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  54. package/test/mcp/tools/getCallLog.test.js +286 -0
  55. package/test/mcp/tools/getGoogleFilePicker.test.js +281 -0
  56. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  57. package/test/mcp/tools/logout.test.js +169 -0
  58. package/test/mcp/tools/setConnector.test.js +177 -0
  59. package/test/mcp/tools/updateCallLog.test.js +346 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * MCP Server for RC Unified CRM Extension
3
+ *
4
+ * This module provides MCP (Model Context Protocol) interface for the CRM extension.
5
+ * It exposes tools that can be called by AI assistants or other MCP clients.
6
+ */
7
+
8
+ const tools = require('./tools');
9
+ const logger = require('../lib/logger');
10
+
11
+ const JSON_RPC_INTERNAL_ERROR = -32603;
12
+ const JSON_RPC_METHOD_NOT_FOUND = -32601;
13
+
14
+ async function handleMcpRequest(req, res) {
15
+ try {
16
+ const { method, params, id } = req.body;
17
+ logger.info('Received MCP request:', { method });
18
+
19
+ let response;
20
+
21
+ switch (method) {
22
+ case 'initialize':
23
+ response = {
24
+ jsonrpc: '2.0',
25
+ id,
26
+ result: {
27
+ protocolVersion: '2024-11-05',
28
+ capabilities: {
29
+ tools: {},
30
+ prompts: {}
31
+ },
32
+ serverInfo: {
33
+ name: 'rc-unified-crm-extension',
34
+ version: '1.0.0'
35
+ }
36
+ }
37
+ };
38
+ break;
39
+ case 'tools/list':
40
+ response = {
41
+ jsonrpc: '2.0',
42
+ id,
43
+ result: {
44
+ tools: getTools()
45
+ }
46
+ };
47
+ break;
48
+ case 'tools/call':
49
+ const { name: toolName, arguments: args } = params;
50
+ const rcAccessToken = req.headers['authorization']?.split('Bearer ')?.[1];
51
+ if (args && rcAccessToken) {
52
+ args.rcAccessToken = rcAccessToken;
53
+ }
54
+ try {
55
+ const result = await executeTool(toolName, args || {});
56
+ response = {
57
+ jsonrpc: '2.0',
58
+ id,
59
+ result: {
60
+ content: [
61
+ {
62
+ type: 'text',
63
+ text: JSON.stringify(result, null, 2)
64
+ }
65
+ ]
66
+ }
67
+ };
68
+ } catch (toolError) {
69
+ response = {
70
+ jsonrpc: '2.0',
71
+ id,
72
+ error: {
73
+ code: JSON_RPC_INTERNAL_ERROR,
74
+ message: `Tool execution failed: ${toolError.message}`,
75
+ data: {
76
+ error: toolError.message,
77
+ stack: process.env.NODE_ENV !== 'production' ? toolError.stack : undefined
78
+ }
79
+ }
80
+ };
81
+ }
82
+ break;
83
+ case 'ping':
84
+ response = {
85
+ jsonrpc: '2.0',
86
+ id,
87
+ result: {}
88
+ };
89
+ break;
90
+ default:
91
+ response = {
92
+ jsonrpc: '2.0',
93
+ id,
94
+ error: {
95
+ code: JSON_RPC_METHOD_NOT_FOUND,
96
+ message: `Method not found: ${method}`
97
+ }
98
+ };
99
+ }
100
+
101
+ res.status(200).json(response);
102
+ } catch (error) {
103
+ logger.error('Error handling MCP request:', { stack: error.stack });
104
+ const errorResponse = {
105
+ jsonrpc: '2.0',
106
+ id: req.body?.id || null,
107
+ error: {
108
+ code: JSON_RPC_INTERNAL_ERROR,
109
+ message: 'Internal server error',
110
+ data: {
111
+ error: error.message,
112
+ stack: process.env.NODE_ENV !== 'production' ? error.stack : undefined
113
+ }
114
+ }
115
+ };
116
+ res.status(200).json(errorResponse);
117
+ }
118
+ }
119
+
120
+
121
+ /**
122
+ * Get all registered MCP tools
123
+ * @returns {Array} Array of tool definitions
124
+ */
125
+ function getTools() {
126
+ return tools.tools.map(tool => tool.definition);
127
+ }
128
+
129
+ /**
130
+ * Execute a specific MCP tool
131
+ * @param {string} toolName - Name of the tool to execute
132
+ * @param {Object} args - Arguments to pass to the tool
133
+ * @returns {Promise<Object>} Tool execution result
134
+ */
135
+ async function executeTool(toolName, args) {
136
+ // Find the tool by name
137
+ const tool = tools.tools.find(t => t.definition.name === toolName);
138
+
139
+ if (!tool) {
140
+ throw new Error(`Tool not found: ${toolName}`);
141
+ }
142
+
143
+ // Execute the tool
144
+ return await tool.execute(args);
145
+ }
146
+
147
+ /**
148
+ * Get a specific tool definition
149
+ * @param {string} toolName - Name of the tool
150
+ * @returns {Object} Tool definition
151
+ */
152
+ function getToolDefinition(toolName) {
153
+ const tool = tools.tools.find(t => t.definition.name === toolName);
154
+
155
+ if (!tool) {
156
+ throw new Error(`Tool not found: ${toolName}`);
157
+ }
158
+
159
+ return tool.definition;
160
+ }
161
+
162
+ exports.handleMcpRequest = handleMcpRequest;
163
+ exports.getTools = getTools;
164
+ exports.executeTool = executeTool;
165
+ exports.getToolDefinition = getToolDefinition;
166
+ exports.tools = tools.tools;
@@ -0,0 +1,110 @@
1
+ const { getAuthSession } = require('../../lib/authSession');
2
+
3
+ /**
4
+ * MCP Tool: Check Auth Status
5
+ *
6
+ * Polls the status of an ongoing OAuth authentication session
7
+ */
8
+
9
+ const toolDefinition = {
10
+ name: 'checkAuthStatus',
11
+ description: 'Auth flow step.5. Check the status of an ongoing OAuth authentication session. Poll this after user clicks the auth link.',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ sessionId: {
16
+ type: 'string',
17
+ description: 'The session ID returned from doAuth tool'
18
+ },
19
+ platform: {
20
+ type: 'string',
21
+ description: 'The platform to check the status of'
22
+ }
23
+ },
24
+ required: ['sessionId']
25
+ },
26
+ annotations: {
27
+ readOnlyHint: true,
28
+ openWorldHint: false,
29
+ destructiveHint: false
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Execute the checkAuthStatus tool
35
+ * @param {Object} args - The tool arguments
36
+ * @param {string} args.sessionId - The session ID to check
37
+ * @returns {Object} Result object with authentication status
38
+ */
39
+ async function execute(args) {
40
+ try {
41
+ const { sessionId, platform } = args;
42
+
43
+ const session = await getAuthSession(sessionId);
44
+
45
+ if (!session) {
46
+ return {
47
+ success: false,
48
+ error: 'Session not found or expired',
49
+ data: {
50
+ status: 'expired'
51
+ }
52
+ };
53
+ }
54
+
55
+ switch (session.status) {
56
+ case 'completed':
57
+ if(platform === 'googleSheets') {
58
+ return {
59
+ success: true,
60
+ data: {
61
+ status: 'completed',
62
+ jwtToken: session.jwtToken,
63
+ userInfo: session.userInfo,
64
+ message: 'IMPORTANT: Authentication successful! Keep jwtToken in memory for future use. DO NOT directly show it to user. Next step is to call getGoogleFilePicker tool.'
65
+ }
66
+ };
67
+ }
68
+ return {
69
+ success: true,
70
+ data: {
71
+ status: 'completed',
72
+ jwtToken: session.jwtToken,
73
+ userInfo: session.userInfo,
74
+ message: 'IMPORTANT: Authentication successful! Keep jwtToken in memory for future use. DO NOT directly show it to user.'
75
+ }
76
+ };
77
+
78
+ case 'failed':
79
+ return {
80
+ success: false,
81
+ error: 'Authentication failed',
82
+ data: {
83
+ status: 'failed',
84
+ errorMessage: session.errorMessage
85
+ }
86
+ };
87
+
88
+ case 'pending':
89
+ default:
90
+ return {
91
+ success: true,
92
+ data: {
93
+ status: 'pending',
94
+ message: 'Waiting for user to complete authorization. Poll again in a few seconds.'
95
+ }
96
+ };
97
+ }
98
+ }
99
+ catch (error) {
100
+ return {
101
+ success: false,
102
+ error: error.message || 'Unknown error occurred',
103
+ errorDetails: error.stack
104
+ };
105
+ }
106
+ }
107
+
108
+ exports.definition = toolDefinition;
109
+ exports.execute = execute;
110
+
@@ -0,0 +1,91 @@
1
+ /**
2
+ * MCP Tool: User Authentication
3
+ *
4
+ * This tool authenticates the user with the CRM platform.
5
+ * It uses the platform-specific connector to authenticate the user.
6
+ */
7
+
8
+ const { isManifestValid } = require('../lib/validator');
9
+
10
+ const toolDefinition = {
11
+ name: 'collectAuthInfo',
12
+ description: '(This step is skipped if auth type is "apiKey" or environment type is "fixed", this is a MUST if environment type is "dynamic" or "selectable") Auth flow step.3. Get information that is required for authentication. Next step is calling step.4 "doAuth" tool.',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ connectorManifest: {
17
+ type: 'object',
18
+ description: 'connectorManifest variable from above conversation. Must be the full manifest object, not just serverUrl'
19
+ },
20
+ hostname: {
21
+ type: 'string',
22
+ description: 'For "dynamic" type environment. User is to login to CRM account then copy and paste the hostname over here.'
23
+ },
24
+ selection: {
25
+ type: 'string',
26
+ description: 'For "selectable" type environment. User is to select a name (NOT value) of the options from the selectable list'
27
+ },
28
+ connectorName: {
29
+ type: 'string',
30
+ description: 'connectorName variable from above conversation.'
31
+ }
32
+ },
33
+ required: ['connectorManifest', 'connectorName']
34
+ },
35
+ annotations: {
36
+ readOnlyHint: true,
37
+ openWorldHint: false,
38
+ destructiveHint: false
39
+ }
40
+ };
41
+
42
+ /**
43
+ * Execute the collectAuthInfo tool
44
+ * @param {Object} args - The tool arguments
45
+ * @param {string} args.connectorManifest - Connector manifest from conversation or memory.
46
+ * @param {string} args.hostname - For "dynamic" type environment. User is to login to CRM account then copy and paste the hostname over here.
47
+ * @param {string} args.selection - For "selectable" type environment. User is to select a name (NOT value) of the options from the selectable list
48
+ * @param {string} args.connectorName - Connector name from conversation or memory.
49
+ * @returns {Object} Result object with hostname or selection
50
+ */
51
+ async function execute(args) {
52
+ try {
53
+ const { connectorManifest, hostname, selection, connectorName } = args;
54
+ const { isValid, errors } = isManifestValid({ connectorManifest, connectorName });
55
+ if (!isValid) {
56
+ return {
57
+ success: false,
58
+ error: "Invalid connector manifest",
59
+ errorDetails: errors.join(', '),
60
+ }
61
+ }
62
+ let result = '';
63
+ switch (connectorManifest.platforms[connectorName].environment.type) {
64
+ case 'selectable':
65
+ result = connectorManifest.platforms[connectorName].environment.selections.find(s => s.name === selection).const;
66
+ break;
67
+ case 'dynamic':
68
+ result = hostname;
69
+ break;
70
+ }
71
+ const url = new URL(result);
72
+ return {
73
+ success: true,
74
+ data: {
75
+ hostname: url.hostname,
76
+ // Add explicit instruction
77
+ message: "IMPORTANT: Use hostname in the next few authentication steps.",
78
+ }
79
+ }
80
+ }
81
+ catch (error) {
82
+ return {
83
+ success: false,
84
+ error: error.message || 'Unknown error occurred',
85
+ errorDetails: error.stack
86
+ };
87
+ }
88
+ }
89
+
90
+ exports.definition = toolDefinition;
91
+ exports.execute = execute;
@@ -0,0 +1,308 @@
1
+ const jwt = require('../../lib/jwt');
2
+ const connectorRegistry = require('../../connector/registry');
3
+ const logCore = require('../../handlers/log');
4
+ const contactCore = require('../../handlers/contact');
5
+ const util = require('../../lib/util');
6
+ const { CallLogModel } = require('../../models/callLogModel');
7
+ /**
8
+ * MCP Tool: Create Call Log
9
+ *
10
+ * This tool creates a call log in the CRM platform.
11
+ */
12
+
13
+ const toolDefinition = {
14
+ name: 'createCallLog',
15
+ description: '⚠️ REQUIRES AUTHENTICATION: User must first authenticate using the "auth" tool to obtain a JWT token before using this tool. | Create only one call log in the CRM platform. Returns the created log ID if successful.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ jwtToken: {
20
+ type: 'string',
21
+ description: 'JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.'
22
+ },
23
+ incomingData: {
24
+ type: 'object',
25
+ description: 'Call log data to create',
26
+ properties: {
27
+ logInfo: {
28
+ type: 'object',
29
+ description: 'RingCentral call log information (follows RingCentral Call Log schema)',
30
+ properties: {
31
+ id: {
32
+ type: 'string',
33
+ description: 'Call log ID from RingCentral'
34
+ },
35
+ sessionId: {
36
+ type: 'string',
37
+ description: 'Unique session identifier for the call'
38
+ },
39
+ telephonySessionId: {
40
+ type: 'string',
41
+ description: 'Telephony session ID'
42
+ },
43
+ startTime: {
44
+ type: 'string',
45
+ description: 'Call start time in ISO 8601 format'
46
+ },
47
+ duration: {
48
+ type: 'number',
49
+ description: 'Call duration in seconds'
50
+ },
51
+ type: {
52
+ type: 'string',
53
+ description: 'Call type (e.g., Voice)'
54
+ },
55
+ direction: {
56
+ type: 'string',
57
+ description: 'Call direction: "Inbound" or "Outbound"'
58
+ },
59
+ action: {
60
+ type: 'string',
61
+ description: 'Call action (e.g., Phone Call)'
62
+ },
63
+ result: {
64
+ type: 'string',
65
+ description: 'Call result (e.g., Accepted, Missed, Voicemail)'
66
+ },
67
+ to: {
68
+ type: 'object',
69
+ description: 'Recipient information',
70
+ properties: {
71
+ phoneNumber: {
72
+ type: 'string',
73
+ description: 'Recipient phone number in E.164 format'
74
+ },
75
+ name: {
76
+ type: 'string',
77
+ description: 'Recipient name'
78
+ }
79
+ }
80
+ },
81
+ from: {
82
+ type: 'object',
83
+ description: 'Caller information',
84
+ properties: {
85
+ phoneNumber: {
86
+ type: 'string',
87
+ description: 'Caller phone number in E.164 format'
88
+ },
89
+ name: {
90
+ type: 'string',
91
+ description: 'Caller name'
92
+ },
93
+ location: {
94
+ type: 'string',
95
+ description: 'Caller location'
96
+ }
97
+ }
98
+ },
99
+ recording: {
100
+ type: 'object',
101
+ description: 'Recording information',
102
+ properties: {
103
+ link: {
104
+ type: 'string',
105
+ description: 'Recording link URL'
106
+ }
107
+ }
108
+ },
109
+ customSubject: {
110
+ type: 'string',
111
+ description: 'Custom subject for the call log'
112
+ },
113
+ legs: {
114
+ type: 'array',
115
+ description: 'Call legs information (for multi-party calls)',
116
+ items: {
117
+ type: 'object'
118
+ }
119
+ },
120
+ accountId: {
121
+ type: 'string',
122
+ description: 'RingCentral account ID'
123
+ }
124
+ },
125
+ required: ['id', 'sessionId', 'direction', 'startTime', 'duration', 'to', 'from']
126
+ },
127
+ note: {
128
+ type: 'string',
129
+ description: 'User-entered call note/description'
130
+ },
131
+ aiNote: {
132
+ type: 'string',
133
+ description: 'AI-generated summary of the phone call'
134
+ },
135
+ transcript: {
136
+ type: 'string',
137
+ description: 'Call transcript text'
138
+ },
139
+ additionalSubmission: {
140
+ type: 'object',
141
+ description: 'Additional platform-specific custom fields (e.g., deals, matters, nonBillable flags, assigned users)',
142
+ properties: {
143
+ isAssignedToUser: {
144
+ type: 'boolean',
145
+ description: 'Whether to assign to a specific user'
146
+ },
147
+ adminAssignedUserToken: {
148
+ type: 'string',
149
+ description: 'JWT token of the assigned user'
150
+ },
151
+ adminAssignedUserRcId: {
152
+ type: 'string',
153
+ description: 'RingCentral extension ID of the assigned user'
154
+ }
155
+ }
156
+ }
157
+ },
158
+ required: ['logInfo']
159
+ },
160
+ contactId: {
161
+ type: 'string',
162
+ description: 'OPTIONAL: CRM contact ID to associate with the call.'
163
+ }
164
+ },
165
+ required: ['jwtToken', 'incomingData']
166
+ },
167
+ annotations: {
168
+ readOnlyHint: false,
169
+ openWorldHint: true,
170
+ destructiveHint: false
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Execute the createCallLog tool
176
+ * @param {Object} args - The tool arguments
177
+ * @param {string} args.jwtToken - JWT token with user and platform info
178
+ * @param {Object} args.incomingData - Call log data including logInfo (RingCentral call log schema), contactName, contactType, note, aiNote, transcript, and additionalSubmission
179
+ * @param {Object} args.incomingData.logInfo - RingCentral call log information with sessionId, direction, startTime, duration, from, to, result, recording, customSubject, etc.
180
+ * @param {string} [args.incomingData.contactName] - Contact name
181
+ * @param {string} [args.incomingData.contactType] - Contact type in CRM
182
+ * @param {string} [args.incomingData.note] - User-entered call note
183
+ * @param {string} [args.incomingData.aiNote] - AI-generated call summary
184
+ * @param {string} [args.incomingData.transcript] - Call transcript
185
+ * @param {Object} [args.incomingData.additionalSubmission] - Platform-specific custom fields
186
+ * @param {string} args.contactId - OPTIONAL: CRM contact ID to associate with the call
187
+ * @returns {Object} Result object with created log ID
188
+ */
189
+ async function execute(args) {
190
+ try {
191
+ const { jwtToken, incomingData } = args;
192
+
193
+ if (!jwtToken) {
194
+ throw new Error('Please go to Settings and authorize CRM platform');
195
+ }
196
+
197
+ if (!incomingData) {
198
+ throw new Error('Incoming data must be provided');
199
+ }
200
+
201
+ // Validate logInfo exists
202
+ if (!incomingData.logInfo) {
203
+ throw new Error('incomingData.logInfo is required');
204
+ }
205
+
206
+ const { logInfo } = incomingData;
207
+
208
+ // Check in DB if the call log already exists
209
+ const existingCallLog = await CallLogModel.findOne({
210
+ where: {
211
+ sessionId: logInfo.sessionId
212
+ }
213
+ });
214
+ if (existingCallLog) {
215
+ throw new Error(`Call log already exists for session ${logInfo.sessionId}`);
216
+ }
217
+
218
+ // Decode JWT to get userId and platform
219
+ const { id: userId, platform } = jwt.decodeJwt(jwtToken);
220
+
221
+ if (!userId) {
222
+ throw new Error('Invalid JWT token: userId not found');
223
+ }
224
+
225
+ // Get the platform connector module
226
+ const platformModule = connectorRegistry.getConnector(platform);
227
+
228
+ if (!platformModule) {
229
+ throw new Error(`Platform connector not found for: ${platform}`);
230
+ }
231
+
232
+ // Check if createCallLog is implemented
233
+ if (!platformModule.createCallLog) {
234
+ throw new Error(`createCallLog is not implemented for platform: ${platform}`);
235
+ }
236
+
237
+ // Calculate hashed account ID
238
+ const hashedAccountId = incomingData.logInfo?.accountId
239
+ ? util.getHashValue(incomingData.logInfo.accountId, process.env.HASH_KEY)
240
+ : undefined;
241
+
242
+ // Get contact Id from conversation
243
+ let contactId = args.contactId ?? null;
244
+ // Get contact Id from conversation if not provided
245
+ if (!contactId) {
246
+ const callDirection = logInfo.direction;
247
+ const contactNumber = callDirection === 'Inbound' ? logInfo.from.phoneNumber : logInfo.to.phoneNumber;
248
+ const contactInfo = await contactCore.findContact({
249
+ platform,
250
+ userId,
251
+ phoneNumber: contactNumber,
252
+ overridingFormat: '',
253
+ isExtension: 'false'
254
+ });
255
+ const filteredContact = contactInfo.contact?.filter(c => !c.isNewContact);
256
+ if (contactInfo.successful && filteredContact?.length > 0) {
257
+ contactId = filteredContact[0].id;
258
+ incomingData.contactId = contactId;
259
+ }
260
+ else {
261
+ return {
262
+ success: false,
263
+ message: 'Cannot find the contact. Please create the contact first.',
264
+ error: 'Failed to get contact with number ' + contactNumber
265
+ }
266
+ }
267
+ }
268
+ else {
269
+ incomingData.contactId = contactId;
270
+ }
271
+
272
+ // Call the createCallLog method
273
+ const { successful, logId, returnMessage } = await logCore.createCallLog({
274
+ platform,
275
+ userId,
276
+ incomingData,
277
+ hashedAccountId,
278
+ isFromSSCL: false
279
+ });
280
+
281
+ if (successful) {
282
+ return {
283
+ success: true,
284
+ data: {
285
+ logId,
286
+ message: returnMessage?.message || 'Call log created successfully'
287
+ }
288
+ };
289
+ }
290
+ else {
291
+ return {
292
+ success: false,
293
+ error: returnMessage?.message || 'Failed to create call log',
294
+ };
295
+ }
296
+ }
297
+ catch (error) {
298
+ return {
299
+ success: false,
300
+ error: error.message || 'Unknown error occurred',
301
+ errorDetails: error.stack
302
+ };
303
+ }
304
+ }
305
+
306
+ exports.definition = toolDefinition;
307
+ exports.execute = execute;
308
+