@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.
- package/connector/developerPortal.js +43 -0
- package/connector/proxy/index.js +10 -3
- package/connector/registry.js +8 -6
- package/handlers/admin.js +44 -21
- package/handlers/auth.js +97 -69
- package/handlers/calldown.js +10 -4
- package/handlers/contact.js +45 -112
- package/handlers/disposition.js +4 -142
- package/handlers/log.js +174 -259
- package/handlers/user.js +19 -6
- package/index.js +310 -122
- package/lib/analytics.js +3 -1
- package/lib/authSession.js +68 -0
- package/lib/callLogComposer.js +498 -420
- package/lib/errorHandler.js +206 -0
- package/lib/jwt.js +2 -0
- package/lib/logger.js +190 -0
- package/lib/oauth.js +21 -12
- package/lib/ringcentral.js +2 -10
- package/lib/sharedSMSComposer.js +471 -0
- package/mcp/SupportedPlatforms.md +12 -0
- package/mcp/lib/validator.js +91 -0
- package/mcp/mcpHandler.js +166 -0
- package/mcp/tools/checkAuthStatus.js +90 -0
- package/mcp/tools/collectAuthInfo.js +86 -0
- package/mcp/tools/createCallLog.js +299 -0
- package/mcp/tools/createMessageLog.js +283 -0
- package/mcp/tools/doAuth.js +185 -0
- package/mcp/tools/findContactByName.js +87 -0
- package/mcp/tools/findContactByPhone.js +96 -0
- package/mcp/tools/getCallLog.js +98 -0
- package/mcp/tools/getHelp.js +39 -0
- package/mcp/tools/getPublicConnectors.js +46 -0
- package/mcp/tools/index.js +58 -0
- package/mcp/tools/logout.js +63 -0
- package/mcp/tools/rcGetCallLogs.js +73 -0
- package/mcp/tools/setConnector.js +64 -0
- package/mcp/tools/updateCallLog.js +122 -0
- package/models/accountDataModel.js +34 -0
- package/models/cacheModel.js +3 -0
- package/package.json +6 -4
- package/releaseNotes.json +36 -0
- package/test/connector/registry.test.js +145 -0
- package/test/handlers/admin.test.js +583 -0
- package/test/handlers/auth.test.js +355 -0
- package/test/handlers/contact.test.js +852 -0
- package/test/handlers/log.test.js +872 -0
- package/test/lib/callLogComposer.test.js +1231 -0
- package/test/lib/debugTracer.test.js +328 -0
- package/test/lib/logger.test.js +206 -0
- package/test/lib/oauth.test.js +359 -0
- package/test/lib/ringcentral.test.js +473 -0
- package/test/lib/sharedSMSComposer.test.js +1084 -0
- package/test/lib/util.test.js +282 -0
- package/test/mcp/tools/collectAuthInfo.test.js +192 -0
- package/test/mcp/tools/createCallLog.test.js +412 -0
- package/test/mcp/tools/createMessageLog.test.js +580 -0
- package/test/mcp/tools/doAuth.test.js +363 -0
- package/test/mcp/tools/findContactByName.test.js +263 -0
- package/test/mcp/tools/findContactByPhone.test.js +284 -0
- package/test/mcp/tools/getCallLog.test.js +286 -0
- package/test/mcp/tools/getPublicConnectors.test.js +128 -0
- package/test/mcp/tools/logout.test.js +169 -0
- package/test/mcp/tools/setConnector.test.js +177 -0
- package/test/mcp/tools/updateCallLog.test.js +346 -0
- package/test/models/accountDataModel.test.js +98 -0
- package/test/models/dynamo/connectorSchema.test.js +189 -0
- package/test/models/models.test.js +539 -0
- package/test/setup.js +176 -176
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const jwt = require('../../lib/jwt');
|
|
2
|
+
const connectorRegistry = require('../../connector/registry');
|
|
3
|
+
const logCore = require('../../handlers/log');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MCP Tool: Get Call Log
|
|
7
|
+
*
|
|
8
|
+
* This tool retrieves call logs from the CRM platform by session IDs.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const toolDefinition = {
|
|
12
|
+
name: 'getCallLog',
|
|
13
|
+
description: '⚠️ REQUIRES AUTHENTICATION: User must first authenticate using the "auth" tool to obtain a JWT token before using this tool. | Get call logs from the CRM platform by session IDs. Returns log details if found.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
jwtToken: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.'
|
|
20
|
+
},
|
|
21
|
+
sessionIds: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Session IDs to retrieve, seprated by commas'
|
|
24
|
+
},
|
|
25
|
+
requireDetails: {
|
|
26
|
+
type: 'boolean',
|
|
27
|
+
description: 'Whether to require detailed log information. If true, will call CRM API. Otherwise will just query in our own database',
|
|
28
|
+
default: false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
required: ['jwtToken', 'sessionIds']
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Execute the getCallLog tool
|
|
37
|
+
* @param {Object} args - The tool arguments
|
|
38
|
+
* @param {string} args.jwtToken - JWT token with user and platform info
|
|
39
|
+
* @param {string} args.sessionIds - Session IDs to retrieve, seprated by commas
|
|
40
|
+
* @param {boolean} [args.requireDetails] - Whether to require detailed log information. If true, will call CRM API. Otherwise will just query in our own database.
|
|
41
|
+
* @returns {Object} Result object with call log information
|
|
42
|
+
*/
|
|
43
|
+
async function execute(args) {
|
|
44
|
+
try {
|
|
45
|
+
const { jwtToken, sessionIds, requireDetails = false } = args;
|
|
46
|
+
|
|
47
|
+
// Decode JWT to get userId and platform
|
|
48
|
+
const { id: userId, platform } = jwt.decodeJwt(jwtToken);
|
|
49
|
+
|
|
50
|
+
if (!userId) {
|
|
51
|
+
throw new Error('Invalid JWT token: userId not found');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get the platform connector module
|
|
55
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
56
|
+
|
|
57
|
+
if (!platformModule) {
|
|
58
|
+
throw new Error(`Platform connector not found for: ${platform}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if getCallLog is implemented
|
|
62
|
+
if (!platformModule.getCallLog) {
|
|
63
|
+
throw new Error(`getCallLog is not implemented for platform: ${platform}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Call the getCallLog method
|
|
67
|
+
const { successful, logs, returnMessage } = await logCore.getCallLog({
|
|
68
|
+
userId,
|
|
69
|
+
sessionIds,
|
|
70
|
+
platform,
|
|
71
|
+
requireDetails
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (successful) {
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
data: logs,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: returnMessage.message,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: error.message || 'Unknown error occurred',
|
|
91
|
+
errorDetails: error.stack
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
exports.definition = toolDefinition;
|
|
97
|
+
exports.execute = execute;
|
|
98
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool: Get Help
|
|
3
|
+
*
|
|
4
|
+
* This tool provides users with a friendly guide to available capabilities
|
|
5
|
+
* and how to get started with the RingCentral CRM integration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const toolDefinition = {
|
|
9
|
+
name: 'getHelp',
|
|
10
|
+
description: 'Get a quick guide on what this integration can do and how to get started.',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {},
|
|
14
|
+
required: []
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Execute the getHelp tool
|
|
20
|
+
* @returns {Object} Result object with help information
|
|
21
|
+
*/
|
|
22
|
+
async function execute() {
|
|
23
|
+
return {
|
|
24
|
+
success: true,
|
|
25
|
+
data: {
|
|
26
|
+
overview: "I help you connect RingCentral with your CRM to log calls.",
|
|
27
|
+
steps:[
|
|
28
|
+
'1. Tell me to show all connectors and let me connect you to your CRM',
|
|
29
|
+
'2. Follow authorization flow to authorize your CRM',
|
|
30
|
+
'3. Once authorized, I can help find contacts and log your calls'
|
|
31
|
+
],
|
|
32
|
+
supportedCRMs: ["Clio"],
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.definition = toolDefinition;
|
|
38
|
+
exports.execute = execute;
|
|
39
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const developerPortal = require('../../connector/developerPortal');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Tool: Get Public Connectors
|
|
5
|
+
*
|
|
6
|
+
* This tool retrieves a list of public connectors from the developer portal.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const toolDefinition = {
|
|
10
|
+
name: 'getPublicConnectors',
|
|
11
|
+
description: 'Auth flow step.1. Get a list of all available connectors from the developer portal. Returns a list of connector names for users to choose. Next step is calling step.2 "setConnector" tool.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {},
|
|
15
|
+
required: []
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Execute the getPublicConnectors tool
|
|
21
|
+
* @returns {Object} Result object with connector names
|
|
22
|
+
*/
|
|
23
|
+
async function execute() {
|
|
24
|
+
try {
|
|
25
|
+
const { connectors: publicConnectorList } = await developerPortal.getPublicConnectorList();
|
|
26
|
+
const connectorList = publicConnectorList;
|
|
27
|
+
if(process.env.RC_ACCOUNT_ID) {
|
|
28
|
+
const { privateConnectors } = await developerPortal.getPrivateConnectorList();
|
|
29
|
+
connectorList.push(...privateConnectors);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
data: connectorList.map(c => c.displayName)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
error: error.message || 'Unknown error occurred',
|
|
40
|
+
errorDetails: error.stack
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.definition = toolDefinition;
|
|
46
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools Index
|
|
3
|
+
*
|
|
4
|
+
* This file exports all available MCP tools for the RC Unified CRM Extension.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// const auth = require('./auth');
|
|
8
|
+
const getHelp = require('./getHelp');
|
|
9
|
+
const getPublicConnectors = require('./getPublicConnectors');
|
|
10
|
+
const setConnector = require('./setConnector');
|
|
11
|
+
const collectAuthInfo = require('./collectAuthInfo');
|
|
12
|
+
const doAuth = require('./doAuth');
|
|
13
|
+
const checkAuthStatus = require('./checkAuthStatus');
|
|
14
|
+
const logout = require('./logout');
|
|
15
|
+
const findContact = require('./findContactByPhone');
|
|
16
|
+
const findContactWithName = require('./findContactByName');
|
|
17
|
+
const getCallLog = require('./getCallLog');
|
|
18
|
+
const createCallLog = require('./createCallLog');
|
|
19
|
+
const updateCallLog = require('./updateCallLog');
|
|
20
|
+
const createMessageLog = require('./createMessageLog');
|
|
21
|
+
const rcGetCallLogs = require('./rcGetCallLogs');
|
|
22
|
+
|
|
23
|
+
// Export all tools
|
|
24
|
+
module.exports = {
|
|
25
|
+
getHelp,
|
|
26
|
+
getPublicConnectors,
|
|
27
|
+
setConnector,
|
|
28
|
+
collectAuthInfo,
|
|
29
|
+
doAuth,
|
|
30
|
+
checkAuthStatus,
|
|
31
|
+
logout,
|
|
32
|
+
findContact,
|
|
33
|
+
findContactWithName,
|
|
34
|
+
getCallLog,
|
|
35
|
+
createCallLog,
|
|
36
|
+
updateCallLog,
|
|
37
|
+
createMessageLog,
|
|
38
|
+
rcGetCallLogs
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Export tools as an array for easy iteration
|
|
42
|
+
module.exports.tools = [
|
|
43
|
+
getHelp,
|
|
44
|
+
getPublicConnectors,
|
|
45
|
+
setConnector,
|
|
46
|
+
collectAuthInfo,
|
|
47
|
+
doAuth,
|
|
48
|
+
checkAuthStatus,
|
|
49
|
+
logout,
|
|
50
|
+
findContact,
|
|
51
|
+
findContactWithName,
|
|
52
|
+
getCallLog,
|
|
53
|
+
createCallLog,
|
|
54
|
+
updateCallLog,
|
|
55
|
+
createMessageLog,
|
|
56
|
+
rcGetCallLogs
|
|
57
|
+
];
|
|
58
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const jwt = require('../../lib/jwt');
|
|
2
|
+
const { UserModel } = require('../../models/userModel');
|
|
3
|
+
const connectorRegistry = require('../../connector/registry');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MCP Tool: Logout
|
|
7
|
+
*
|
|
8
|
+
* This tool logs out the user from the CRM platform.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const toolDefinition = {
|
|
12
|
+
name: 'logout',
|
|
13
|
+
description: 'Logout the user from the CRM platform.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
jwtToken: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Execute the logout tool
|
|
27
|
+
* @param {Object} args - The tool arguments
|
|
28
|
+
* @param {string} args.jwtToken - JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.
|
|
29
|
+
* @returns {Object} Result object with logout information
|
|
30
|
+
*/
|
|
31
|
+
async function execute(args) {
|
|
32
|
+
try {
|
|
33
|
+
const { jwtToken } = args;
|
|
34
|
+
const { platform, id } = jwt.decodeJwt(jwtToken);
|
|
35
|
+
|
|
36
|
+
const userToLogout = await UserModel.findByPk(id);
|
|
37
|
+
if (!userToLogout) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: "User not found",
|
|
41
|
+
errorDetails: "User not found",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
45
|
+
await platformModule.unAuthorize({ user: userToLogout });
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
data: {
|
|
49
|
+
message: "IMPORTANT: Logout successful. Clear jwtToken, connectorManifest, connectorDisplayName, hostname and connectorName in memory. User is logged out from the CRM platform.",
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: error.message || 'Unknown error occurred',
|
|
57
|
+
errorDetails: error.stack
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exports.definition = toolDefinition;
|
|
63
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const { RingCentral } = require('../../lib/ringcentral');
|
|
2
|
+
const jwt = require('../../lib/jwt');
|
|
3
|
+
const { CallLogModel } = require('../../models/callLogModel');
|
|
4
|
+
|
|
5
|
+
const toolDefinition = {
|
|
6
|
+
name: 'rcGetCallLogs',
|
|
7
|
+
description: '⚠️ REQUIRES AUTHENTICATION: User must first authenticate using the "auth" tool to obtain a JWT token before using this tool. | Get call logs from RingCentral',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
jwtToken: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.'
|
|
14
|
+
},
|
|
15
|
+
timeFrom: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'MUST be ISO string. Default is 24 hours ago.'
|
|
18
|
+
},
|
|
19
|
+
timeTo: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'MUST be ISO string. Default is now.'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
required: ['jwtToken', 'timeFrom', 'timeTo']
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function execute(args) {
|
|
29
|
+
try {
|
|
30
|
+
const { jwtToken, rcAccessToken, timeFrom, timeTo } = args;
|
|
31
|
+
if (!rcAccessToken) {
|
|
32
|
+
throw new Error('RingCentral access token not found');
|
|
33
|
+
}
|
|
34
|
+
const { id: userId } = jwt.decodeJwt(jwtToken);
|
|
35
|
+
if (!userId) {
|
|
36
|
+
throw new Error('Invalid JWT token: userId not found');
|
|
37
|
+
}
|
|
38
|
+
const rcSDK = new RingCentral({
|
|
39
|
+
server: process.env.RINGCENTRAL_SERVER,
|
|
40
|
+
clientId: process.env.RINGCENTRAL_CLIENT_ID,
|
|
41
|
+
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
|
|
42
|
+
redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
|
|
43
|
+
});
|
|
44
|
+
const callLogData = await rcSDK.getCallLogData({
|
|
45
|
+
token: { access_token: rcAccessToken, token_type: 'Bearer' },
|
|
46
|
+
timeFrom: timeFrom ?? new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
|
|
47
|
+
timeTo: timeTo ?? new Date().toISOString(),
|
|
48
|
+
});
|
|
49
|
+
// hack: remove already logged calls
|
|
50
|
+
const existingCalls = [];
|
|
51
|
+
for (const call of callLogData.records) {
|
|
52
|
+
const existingCallLog = await CallLogModel.findOne({
|
|
53
|
+
where: {
|
|
54
|
+
sessionId: call.sessionId
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (existingCallLog) {
|
|
58
|
+
existingCalls.push(existingCallLog.sessionId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
callLogData.records = callLogData.records.filter(call => !existingCalls.includes(call.sessionId));
|
|
62
|
+
return callLogData;
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: e.message
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.definition = toolDefinition;
|
|
73
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const developerPortal = require('../../connector/developerPortal');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Tool: Set Connector
|
|
5
|
+
*
|
|
6
|
+
* This tool helps the user set the connector.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const toolDefinition = {
|
|
10
|
+
name: 'setConnector',
|
|
11
|
+
description: 'Auth flow step.2. Save connectorManifest to memory if successful. Next step is optional, if it is oauth, go to step.3 "collectAuthInfo" tool.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
connectorDisplayName: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Connector displayname to set'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
required: ['connectorDisplayName']
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Execute the setConnector tool
|
|
26
|
+
* @param {Object} args - The tool arguments
|
|
27
|
+
* @param {string} args.connectorDisplayName - Connector display name to set
|
|
28
|
+
* @returns {Object} Result object with connector information
|
|
29
|
+
*/
|
|
30
|
+
async function execute(args) {
|
|
31
|
+
try {
|
|
32
|
+
const { connectorDisplayName } = args;
|
|
33
|
+
const { connectors: publicConnectorList } = await developerPortal.getPublicConnectorList();
|
|
34
|
+
const { privateConnectors } = await developerPortal.getPrivateConnectorList();
|
|
35
|
+
const connectorList = [...publicConnectorList, ...privateConnectors];
|
|
36
|
+
const connector = connectorList.find(c => c.displayName === connectorDisplayName);
|
|
37
|
+
const connectorName = connector.name;
|
|
38
|
+
const connectorManifest = await developerPortal.getConnectorManifest({ connectorId: connector.id, isPrivate: connector.status === 'private' });
|
|
39
|
+
if (!connectorManifest) {
|
|
40
|
+
throw new Error(`Connector manifest not found: ${connectorDisplayName}`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
data: {
|
|
45
|
+
connectorManifest,
|
|
46
|
+
connectorDisplayName,
|
|
47
|
+
connectorName,
|
|
48
|
+
// Add explicit instruction
|
|
49
|
+
message: "IMPORTANT: Use connectorManifest, connectorDisplayName, and connectorName in the next few authentication steps.",
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: error.message || 'Unknown error occurred',
|
|
58
|
+
errorDetails: error.stack
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exports.definition = toolDefinition;
|
|
64
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const jwt = require('../../lib/jwt');
|
|
2
|
+
const connectorRegistry = require('../../connector/registry');
|
|
3
|
+
const logCore = require('../../handlers/log');
|
|
4
|
+
const util = require('../../lib/util');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MCP Tool: Update Call Log
|
|
8
|
+
*
|
|
9
|
+
* This tool updates an existing call log in the CRM platform.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const toolDefinition = {
|
|
13
|
+
name: 'updateCallLog',
|
|
14
|
+
description: '⚠️ REQUIRES AUTHENTICATION: User must first authenticate using the "auth" tool to obtain a JWT token before using this tool. | Update an existing call log in the CRM platform. Returns the updated log ID if successful.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
jwtToken: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'JWT token containing userId and platform information. If user does not have this, direct them to use the "auth" tool first.'
|
|
21
|
+
},
|
|
22
|
+
incomingData: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
description: 'Call log update data including sessionId, note, additionalSubmission, etc.',
|
|
25
|
+
properties: {
|
|
26
|
+
sessionId: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Session ID of the call log to update'
|
|
29
|
+
},
|
|
30
|
+
note: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Updated call note/description'
|
|
33
|
+
},
|
|
34
|
+
additionalSubmission: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
description: 'Additional platform-specific fields to update'
|
|
37
|
+
},
|
|
38
|
+
accountId: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'RingCentral account ID'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
required: ['sessionId']
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
required: ['jwtToken', 'incomingData']
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Execute the updateCallLog tool
|
|
52
|
+
* @param {Object} args - The tool arguments
|
|
53
|
+
* @param {string} args.jwtToken - JWT token with user and platform info
|
|
54
|
+
* @param {Object} args.incomingData - Call log update data
|
|
55
|
+
* @returns {Object} Result object with updated log ID
|
|
56
|
+
*/
|
|
57
|
+
async function execute(args) {
|
|
58
|
+
try {
|
|
59
|
+
const { jwtToken, incomingData } = args;
|
|
60
|
+
|
|
61
|
+
// Decode JWT to get userId and platform
|
|
62
|
+
const { id: userId, platform } = jwt.decodeJwt(jwtToken);
|
|
63
|
+
|
|
64
|
+
if (!userId) {
|
|
65
|
+
throw new Error('Invalid JWT token: userId not found');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Get the platform connector module
|
|
69
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
70
|
+
|
|
71
|
+
if (!platformModule) {
|
|
72
|
+
throw new Error(`Platform connector not found for: ${platform}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check if updateCallLog is implemented
|
|
76
|
+
if (!platformModule.updateCallLog) {
|
|
77
|
+
throw new Error(`updateCallLog is not implemented for platform: ${platform}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Calculate hashed account ID
|
|
81
|
+
const hashedAccountId = incomingData.accountId
|
|
82
|
+
? util.getHashValue(incomingData.accountId, process.env.HASH_KEY)
|
|
83
|
+
: undefined;
|
|
84
|
+
|
|
85
|
+
// Call the updateCallLog method
|
|
86
|
+
const { successful, logId, updatedNote, returnMessage } = await logCore.updateCallLog({
|
|
87
|
+
platform,
|
|
88
|
+
userId,
|
|
89
|
+
incomingData,
|
|
90
|
+
hashedAccountId,
|
|
91
|
+
isFromSSCL: false
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (successful) {
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
data: {
|
|
98
|
+
logId,
|
|
99
|
+
updatedNote,
|
|
100
|
+
message: returnMessage?.message || 'Call log updated successfully'
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: returnMessage?.message || 'Failed to update call log',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: error.message || 'Unknown error occurred',
|
|
115
|
+
errorDetails: error.stack
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
exports.definition = toolDefinition;
|
|
121
|
+
exports.execute = execute;
|
|
122
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const Sequelize = require('sequelize');
|
|
2
|
+
const { sequelize } = require('./sequelize');
|
|
3
|
+
|
|
4
|
+
// Model for account data with composite primary key
|
|
5
|
+
exports.AccountDataModel = sequelize.define('accountData', {
|
|
6
|
+
rcAccountId: {
|
|
7
|
+
type: Sequelize.STRING,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
platformName: {
|
|
11
|
+
type: Sequelize.STRING,
|
|
12
|
+
primaryKey: true,
|
|
13
|
+
},
|
|
14
|
+
dataKey: {
|
|
15
|
+
type: Sequelize.STRING,
|
|
16
|
+
primaryKey: true,
|
|
17
|
+
},
|
|
18
|
+
data: {
|
|
19
|
+
type: Sequelize.JSON,
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
exports.getOrRefreshAccountData = async function getOrRefreshAccountData({ rcAccountId, platformName, dataKey, forceRefresh, fetchFn }) {
|
|
24
|
+
const existing = await exports.AccountDataModel.findOne({ where: { rcAccountId, platformName, dataKey } });
|
|
25
|
+
if (existing && !forceRefresh) return existing.data;
|
|
26
|
+
|
|
27
|
+
const fresh = await fetchFn();
|
|
28
|
+
if (existing) {
|
|
29
|
+
await existing.update({ data: fresh });
|
|
30
|
+
} else {
|
|
31
|
+
await exports.AccountDataModel.create({ rcAccountId, platformName, dataKey, data: fresh });
|
|
32
|
+
}
|
|
33
|
+
return fresh;
|
|
34
|
+
}
|
package/models/cacheModel.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@app-connect/core",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.11",
|
|
4
4
|
"description": "RingCentral App Connect Core",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"axios": "^1.12.2",
|
|
18
|
-
"express": "^4.
|
|
18
|
+
"express": "^4.22.1",
|
|
19
19
|
"moment": "^2.29.4",
|
|
20
20
|
"moment-timezone": "^0.5.39",
|
|
21
21
|
"pg": "^8.8.0",
|
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@aws-sdk/client-dynamodb": "^3.751.0",
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.21.1",
|
|
26
27
|
"@aws-sdk/client-s3": "^3.947.0",
|
|
27
28
|
"@aws-sdk/s3-request-presigner": "^3.947.0",
|
|
28
|
-
"body-parser": "^1.20.
|
|
29
|
+
"body-parser": "^1.20.4",
|
|
30
|
+
"body-parser-xml": "^2.0.5",
|
|
29
31
|
"client-oauth2": "^4.3.3",
|
|
30
32
|
"cors": "^2.8.5",
|
|
31
33
|
"country-state-city": "^3.2.1",
|
|
@@ -48,7 +50,7 @@
|
|
|
48
50
|
"@octokit/rest": "^19.0.5",
|
|
49
51
|
"axios": "^1.12.2",
|
|
50
52
|
"eslint": "^9.22.0",
|
|
51
|
-
"express": "^4.
|
|
53
|
+
"express": "^4.22.1",
|
|
52
54
|
"globals": "^16.0.0",
|
|
53
55
|
"jest": "^29.3.1",
|
|
54
56
|
"moment": "^2.29.4",
|
package/releaseNotes.json
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
+
"1.7.11": {
|
|
3
|
+
"global": [
|
|
4
|
+
{
|
|
5
|
+
"type": "New",
|
|
6
|
+
"description": "Shared-SMS logging"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"type": "New",
|
|
10
|
+
"description": "MCP tools"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"1.7.10": {
|
|
15
|
+
"global": [
|
|
16
|
+
{
|
|
17
|
+
"type": "Fix",
|
|
18
|
+
"description": "Upon completing warm transfer, it opens contact page for a second time"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "New",
|
|
22
|
+
"description": "RingCX RingSense call logging event support for Server-side logging"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"1.7.9": {
|
|
27
|
+
"global": [
|
|
28
|
+
{
|
|
29
|
+
"type": "Better",
|
|
30
|
+
"description": "Contact match speed optimized"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "Better",
|
|
34
|
+
"description": "Minor improvements on calldown list"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
2
38
|
"1.7.8": {
|
|
3
39
|
"global": [
|
|
4
40
|
{
|