@app-connect/core 1.7.8 → 1.7.11

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 (69) 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 +44 -21
  5. package/handlers/auth.js +97 -69
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +45 -112
  8. package/handlers/disposition.js +4 -142
  9. package/handlers/log.js +174 -259
  10. package/handlers/user.js +19 -6
  11. package/index.js +310 -122
  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 -12
  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 +90 -0
  25. package/mcp/tools/collectAuthInfo.js +86 -0
  26. package/mcp/tools/createCallLog.js +299 -0
  27. package/mcp/tools/createMessageLog.js +283 -0
  28. package/mcp/tools/doAuth.js +185 -0
  29. package/mcp/tools/findContactByName.js +87 -0
  30. package/mcp/tools/findContactByPhone.js +96 -0
  31. package/mcp/tools/getCallLog.js +98 -0
  32. package/mcp/tools/getHelp.js +39 -0
  33. package/mcp/tools/getPublicConnectors.js +46 -0
  34. package/mcp/tools/index.js +58 -0
  35. package/mcp/tools/logout.js +63 -0
  36. package/mcp/tools/rcGetCallLogs.js +73 -0
  37. package/mcp/tools/setConnector.js +64 -0
  38. package/mcp/tools/updateCallLog.js +122 -0
  39. package/models/accountDataModel.js +34 -0
  40. package/models/cacheModel.js +3 -0
  41. package/package.json +6 -4
  42. package/releaseNotes.json +36 -0
  43. package/test/connector/registry.test.js +145 -0
  44. package/test/handlers/admin.test.js +583 -0
  45. package/test/handlers/auth.test.js +355 -0
  46. package/test/handlers/contact.test.js +852 -0
  47. package/test/handlers/log.test.js +872 -0
  48. package/test/lib/callLogComposer.test.js +1231 -0
  49. package/test/lib/debugTracer.test.js +328 -0
  50. package/test/lib/logger.test.js +206 -0
  51. package/test/lib/oauth.test.js +359 -0
  52. package/test/lib/ringcentral.test.js +473 -0
  53. package/test/lib/sharedSMSComposer.test.js +1084 -0
  54. package/test/lib/util.test.js +282 -0
  55. package/test/mcp/tools/collectAuthInfo.test.js +192 -0
  56. package/test/mcp/tools/createCallLog.test.js +412 -0
  57. package/test/mcp/tools/createMessageLog.test.js +580 -0
  58. package/test/mcp/tools/doAuth.test.js +363 -0
  59. package/test/mcp/tools/findContactByName.test.js +263 -0
  60. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  61. package/test/mcp/tools/getCallLog.test.js +286 -0
  62. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  63. package/test/mcp/tools/logout.test.js +169 -0
  64. package/test/mcp/tools/setConnector.test.js +177 -0
  65. package/test/mcp/tools/updateCallLog.test.js +346 -0
  66. package/test/models/accountDataModel.test.js +98 -0
  67. package/test/models/dynamo/connectorSchema.test.js +189 -0
  68. package/test/models/models.test.js +539 -0
  69. package/test/setup.js +176 -176
@@ -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,90 @@
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
+ },
20
+ required: ['sessionId']
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Execute the checkAuthStatus tool
26
+ * @param {Object} args - The tool arguments
27
+ * @param {string} args.sessionId - The session ID to check
28
+ * @returns {Object} Result object with authentication status
29
+ */
30
+ async function execute(args) {
31
+ try {
32
+ const { sessionId } = args;
33
+
34
+ const session = await getAuthSession(sessionId);
35
+
36
+ if (!session) {
37
+ return {
38
+ success: false,
39
+ error: 'Session not found or expired',
40
+ data: {
41
+ status: 'expired'
42
+ }
43
+ };
44
+ }
45
+
46
+ switch (session.status) {
47
+ case 'completed':
48
+ return {
49
+ success: true,
50
+ data: {
51
+ status: 'completed',
52
+ jwtToken: session.jwtToken,
53
+ userInfo: session.userInfo,
54
+ message: 'IMPORTANT: Authentication successful! Keep jwtToken in memory for future use.'
55
+ }
56
+ };
57
+
58
+ case 'failed':
59
+ return {
60
+ success: false,
61
+ error: 'Authentication failed',
62
+ data: {
63
+ status: 'failed',
64
+ errorMessage: session.errorMessage
65
+ }
66
+ };
67
+
68
+ case 'pending':
69
+ default:
70
+ return {
71
+ success: true,
72
+ data: {
73
+ status: 'pending',
74
+ message: 'Waiting for user to complete authorization. Poll again in a few seconds.'
75
+ }
76
+ };
77
+ }
78
+ }
79
+ catch (error) {
80
+ return {
81
+ success: false,
82
+ error: error.message || 'Unknown error occurred',
83
+ errorDetails: error.stack
84
+ };
85
+ }
86
+ }
87
+
88
+ exports.definition = toolDefinition;
89
+ exports.execute = execute;
90
+
@@ -0,0 +1,86 @@
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
+ };
36
+
37
+ /**
38
+ * Execute the collectAuthInfo tool
39
+ * @param {Object} args - The tool arguments
40
+ * @param {string} args.connectorManifest - Connector manifest from conversation or memory.
41
+ * @param {string} args.hostname - For "dynamic" type environment. User is to login to CRM account then copy and paste the hostname over here.
42
+ * @param {string} args.selection - For "selectable" type environment. User is to select a name (NOT value) of the options from the selectable list
43
+ * @param {string} args.connectorName - Connector name from conversation or memory.
44
+ * @returns {Object} Result object with hostname or selection
45
+ */
46
+ async function execute(args) {
47
+ try {
48
+ const { connectorManifest, hostname, selection, connectorName } = args;
49
+ const { isValid, errors } = isManifestValid({ connectorManifest, connectorName });
50
+ if (!isValid) {
51
+ return {
52
+ success: false,
53
+ error: "Invalid connector manifest",
54
+ errorDetails: errors.join(', '),
55
+ }
56
+ }
57
+ let result = '';
58
+ switch (connectorManifest.platforms[connectorName].environment.type) {
59
+ case 'selectable':
60
+ result = connectorManifest.platforms[connectorName].environment.selections.find(s => s.name === selection).const;
61
+ break;
62
+ case 'dynamic':
63
+ result = hostname;
64
+ break;
65
+ }
66
+ const url = new URL(result);
67
+ return {
68
+ success: true,
69
+ data: {
70
+ hostname: url.hostname,
71
+ // Add explicit instruction
72
+ message: "IMPORTANT: Use hostname in the next few authentication steps.",
73
+ }
74
+ }
75
+ }
76
+ catch (error) {
77
+ return {
78
+ success: false,
79
+ error: error.message || 'Unknown error occurred',
80
+ errorDetails: error.stack
81
+ };
82
+ }
83
+ }
84
+
85
+ exports.definition = toolDefinition;
86
+ exports.execute = execute;
@@ -0,0 +1,299 @@
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
+ };
168
+
169
+ /**
170
+ * Execute the createCallLog tool
171
+ * @param {Object} args - The tool arguments
172
+ * @param {string} args.jwtToken - JWT token with user and platform info
173
+ * @param {Object} args.incomingData - Call log data including logInfo (RingCentral call log schema), contactName, contactType, note, aiNote, transcript, and additionalSubmission
174
+ * @param {Object} args.incomingData.logInfo - RingCentral call log information with sessionId, direction, startTime, duration, from, to, result, recording, customSubject, etc.
175
+ * @param {string} [args.incomingData.contactName] - Contact name
176
+ * @param {string} [args.incomingData.contactType] - Contact type in CRM
177
+ * @param {string} [args.incomingData.note] - User-entered call note
178
+ * @param {string} [args.incomingData.aiNote] - AI-generated call summary
179
+ * @param {string} [args.incomingData.transcript] - Call transcript
180
+ * @param {Object} [args.incomingData.additionalSubmission] - Platform-specific custom fields
181
+ * @param {string} args.contactId - OPTIONAL: CRM contact ID to associate with the call
182
+ * @returns {Object} Result object with created log ID
183
+ */
184
+ async function execute(args) {
185
+ try {
186
+ const { jwtToken, incomingData } = args;
187
+
188
+ if (!jwtToken) {
189
+ throw new Error('Please go to Settings and authorize CRM platform');
190
+ }
191
+
192
+ if (!incomingData) {
193
+ throw new Error('Incoming data must be provided');
194
+ }
195
+
196
+ // Validate logInfo exists
197
+ if (!incomingData.logInfo) {
198
+ throw new Error('incomingData.logInfo is required');
199
+ }
200
+
201
+ const { logInfo } = incomingData;
202
+
203
+ // Check in DB if the call log already exists
204
+ const existingCallLog = await CallLogModel.findOne({
205
+ where: {
206
+ sessionId: logInfo.sessionId
207
+ }
208
+ });
209
+ if (existingCallLog) {
210
+ throw new Error(`Call log already exists for session ${logInfo.sessionId}`);
211
+ }
212
+
213
+ // Decode JWT to get userId and platform
214
+ const { id: userId, platform } = jwt.decodeJwt(jwtToken);
215
+
216
+ if (!userId) {
217
+ throw new Error('Invalid JWT token: userId not found');
218
+ }
219
+
220
+ // Get the platform connector module
221
+ const platformModule = connectorRegistry.getConnector(platform);
222
+
223
+ if (!platformModule) {
224
+ throw new Error(`Platform connector not found for: ${platform}`);
225
+ }
226
+
227
+ // Check if createCallLog is implemented
228
+ if (!platformModule.createCallLog) {
229
+ throw new Error(`createCallLog is not implemented for platform: ${platform}`);
230
+ }
231
+
232
+ // Calculate hashed account ID
233
+ const hashedAccountId = incomingData.logInfo?.accountId
234
+ ? util.getHashValue(incomingData.logInfo.accountId, process.env.HASH_KEY)
235
+ : undefined;
236
+
237
+ // Get contact Id from conversation
238
+ let contactId = args.contactId ?? null;
239
+ // Get contact Id from conversation if not provided
240
+ if (!contactId) {
241
+ const callDirection = logInfo.direction;
242
+ const contactNumber = callDirection === 'Inbound' ? logInfo.from.phoneNumber : logInfo.to.phoneNumber;
243
+ const contactInfo = await contactCore.findContact({
244
+ platform,
245
+ userId,
246
+ phoneNumber: contactNumber,
247
+ overridingFormat: '',
248
+ isExtension: 'false'
249
+ });
250
+ const filteredContact = contactInfo.contact?.filter(c => !c.isNewContact);
251
+ if (contactInfo.successful && filteredContact?.length > 0) {
252
+ contactId = filteredContact[0].id;
253
+ incomingData.contactId = contactId;
254
+ }
255
+ else {
256
+ return {
257
+ success: false,
258
+ error: 'Failed to get contact with number ' + contactNumber
259
+ }
260
+ }
261
+ }
262
+
263
+ // Call the createCallLog method
264
+ const { successful, logId, returnMessage } = await logCore.createCallLog({
265
+ platform,
266
+ userId,
267
+ incomingData,
268
+ hashedAccountId,
269
+ isFromSSCL: false
270
+ });
271
+
272
+ if (successful) {
273
+ return {
274
+ success: true,
275
+ data: {
276
+ logId,
277
+ message: returnMessage?.message || 'Call log created successfully'
278
+ }
279
+ };
280
+ }
281
+ else {
282
+ return {
283
+ success: false,
284
+ error: returnMessage?.message || 'Failed to create call log',
285
+ };
286
+ }
287
+ }
288
+ catch (error) {
289
+ return {
290
+ success: false,
291
+ error: error.message || 'Unknown error occurred',
292
+ errorDetails: error.stack
293
+ };
294
+ }
295
+ }
296
+
297
+ exports.definition = toolDefinition;
298
+ exports.execute = execute;
299
+