@app-connect/core 1.7.37 → 1.7.38

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 (286) hide show
  1. package/README.md +17 -16
  2. package/dist/connector/developerPortal.d.ts +6 -0
  3. package/{connector → dist/connector}/developerPortal.js +29 -12
  4. package/dist/connector/mock.d.ts +1 -0
  5. package/{connector → dist/connector}/mock.js +33 -17
  6. package/dist/connector/proxy/engine.d.ts +10 -0
  7. package/dist/connector/proxy/engine.js +222 -0
  8. package/dist/connector/proxy/index.d.ts +5 -0
  9. package/dist/connector/proxy/index.js +511 -0
  10. package/dist/connector/registry.d.ts +1 -0
  11. package/dist/connector/registry.js +243 -0
  12. package/dist/handlers/admin.d.ts +1 -0
  13. package/{handlers → dist/handlers}/admin.js +137 -78
  14. package/dist/handlers/appointment.d.ts +9 -0
  15. package/{handlers → dist/handlers}/appointment.js +58 -29
  16. package/dist/handlers/auth.d.ts +9 -0
  17. package/{handlers → dist/handlers}/auth.js +68 -40
  18. package/dist/handlers/calldown.d.ts +10 -0
  19. package/dist/handlers/calldown.js +134 -0
  20. package/dist/handlers/contact.d.ts +1 -0
  21. package/{handlers → dist/handlers}/contact.js +61 -39
  22. package/dist/handlers/disposition.d.ts +1 -0
  23. package/{handlers → dist/handlers}/disposition.js +20 -10
  24. package/dist/handlers/log.d.ts +1 -0
  25. package/{handlers → dist/handlers}/log.js +91 -124
  26. package/dist/handlers/managedAuth.d.ts +1 -0
  27. package/{handlers → dist/handlers}/managedAuth.js +134 -61
  28. package/dist/handlers/managedOAuth.d.ts +13 -0
  29. package/{handlers → dist/handlers}/managedOAuth.js +102 -30
  30. package/dist/handlers/plugin.d.ts +1 -0
  31. package/{handlers → dist/handlers}/plugin.js +59 -27
  32. package/dist/handlers/user.d.ts +1 -0
  33. package/{handlers → dist/handlers}/user.js +45 -27
  34. package/dist/index.d.ts +1 -0
  35. package/{index.js → dist/index.js} +538 -449
  36. package/dist/jest.config.d.ts +1 -0
  37. package/dist/jest.config.js +65 -0
  38. package/dist/lib/analytics.d.ts +4 -0
  39. package/{lib → dist/lib}/analytics.js +8 -6
  40. package/dist/lib/authSession.d.ts +5 -0
  41. package/{lib → dist/lib}/authSession.js +15 -43
  42. package/dist/lib/cacheCleanup.d.ts +4 -0
  43. package/{lib → dist/lib}/cacheCleanup.js +5 -17
  44. package/dist/lib/callLogComposer.d.ts +109 -0
  45. package/{lib → dist/lib}/callLogComposer.js +157 -197
  46. package/dist/lib/callLogLookup.d.ts +6 -0
  47. package/{lib → dist/lib}/callLogLookup.js +12 -34
  48. package/dist/lib/constants.d.ts +6 -0
  49. package/dist/lib/constants.js +9 -0
  50. package/dist/lib/debugTracer.d.ts +17 -0
  51. package/{lib → dist/lib}/debugTracer.js +7 -83
  52. package/dist/lib/encode.d.ts +3 -0
  53. package/dist/lib/encode.js +50 -0
  54. package/dist/lib/errorHandler.d.ts +7 -0
  55. package/dist/lib/errorHandler.js +161 -0
  56. package/dist/lib/generalErrorMessage.d.ts +7 -0
  57. package/{lib → dist/lib}/generalErrorMessage.js +6 -6
  58. package/dist/lib/jwt.d.ts +4 -0
  59. package/dist/lib/jwt.js +18 -0
  60. package/dist/lib/logger.d.ts +24 -0
  61. package/dist/lib/logger.js +126 -0
  62. package/dist/lib/migrateCallLogsSchema.d.ts +32 -0
  63. package/{lib → dist/lib}/migrateCallLogsSchema.js +75 -160
  64. package/dist/lib/oauth.d.ts +4 -0
  65. package/{lib → dist/lib}/oauth.js +157 -160
  66. package/dist/lib/ringcentral.d.ts +24 -0
  67. package/dist/lib/ringcentral.js +226 -0
  68. package/dist/lib/s3ErrorLogReport.d.ts +4 -0
  69. package/{lib → dist/lib}/s3ErrorLogReport.js +11 -23
  70. package/dist/lib/sharedSMSComposer.d.ts +7 -0
  71. package/{lib → dist/lib}/sharedSMSComposer.js +21 -107
  72. package/dist/lib/util.d.ts +7 -0
  73. package/{lib → dist/lib}/util.js +9 -20
  74. package/dist/mcp/lib/oauthError.d.ts +1 -0
  75. package/dist/mcp/lib/oauthError.js +25 -0
  76. package/dist/mcp/lib/validator.d.ts +1 -0
  77. package/{mcp → dist/mcp}/lib/validator.js +16 -20
  78. package/dist/mcp/lib/widgetSessionToken.d.ts +1 -0
  79. package/dist/mcp/lib/widgetSessionToken.js +46 -0
  80. package/dist/mcp/mcpHandler.d.ts +8 -0
  81. package/{mcp → dist/mcp}/mcpHandler.js +98 -88
  82. package/dist/mcp/tools/cancelAppointment.d.ts +1 -0
  83. package/{mcp → dist/mcp}/tools/cancelAppointment.js +7 -16
  84. package/dist/mcp/tools/checkAuthStatus.d.ts +1 -0
  85. package/{mcp → dist/mcp}/tools/checkAuthStatus.js +21 -14
  86. package/dist/mcp/tools/confirmAppointment.d.ts +1 -0
  87. package/{mcp → dist/mcp}/tools/confirmAppointment.js +7 -16
  88. package/dist/mcp/tools/createAppointment.d.ts +1 -0
  89. package/{mcp → dist/mcp}/tools/createAppointment.js +7 -19
  90. package/dist/mcp/tools/createCallLog.d.ts +1 -0
  91. package/{mcp → dist/mcp}/tools/createCallLog.js +18 -29
  92. package/dist/mcp/tools/createContact.d.ts +1 -0
  93. package/{mcp → dist/mcp}/tools/createContact.js +8 -20
  94. package/dist/mcp/tools/createMessageLog.d.ts +1 -0
  95. package/{mcp → dist/mcp}/tools/createMessageLog.js +14 -26
  96. package/dist/mcp/tools/doAuth.d.ts +1 -0
  97. package/{mcp → dist/mcp}/tools/doAuth.js +17 -13
  98. package/dist/mcp/tools/findContactByName.d.ts +1 -0
  99. package/{mcp → dist/mcp}/tools/findContactByName.js +9 -15
  100. package/dist/mcp/tools/findContactByPhone.d.ts +1 -0
  101. package/{mcp → dist/mcp}/tools/findContactByPhone.js +8 -15
  102. package/dist/mcp/tools/getCallLog.d.ts +1 -0
  103. package/{mcp → dist/mcp}/tools/getCallLog.js +12 -21
  104. package/dist/mcp/tools/getGoogleFilePicker.d.ts +1 -0
  105. package/{mcp → dist/mcp}/tools/getGoogleFilePicker.js +9 -18
  106. package/dist/mcp/tools/getHelp.d.ts +1 -0
  107. package/{mcp → dist/mcp}/tools/getHelp.js +5 -6
  108. package/dist/mcp/tools/getPublicConnectors.d.ts +1 -0
  109. package/{mcp → dist/mcp}/tools/getPublicConnectors.js +19 -18
  110. package/dist/mcp/tools/getSessionInfo.d.ts +1 -0
  111. package/{mcp → dist/mcp}/tools/getSessionInfo.js +10 -16
  112. package/dist/mcp/tools/index.d.ts +8 -0
  113. package/dist/mcp/tools/index.js +50 -0
  114. package/dist/mcp/tools/listAppointments.d.ts +1 -0
  115. package/{mcp → dist/mcp}/tools/listAppointments.js +7 -19
  116. package/dist/mcp/tools/logout.d.ts +1 -0
  117. package/{mcp → dist/mcp}/tools/logout.js +16 -14
  118. package/dist/mcp/tools/rcGetCallLogs.d.ts +1 -0
  119. package/{mcp → dist/mcp}/tools/rcGetCallLogs.js +10 -8
  120. package/dist/mcp/tools/updateAppointment.d.ts +1 -0
  121. package/{mcp → dist/mcp}/tools/updateAppointment.js +17 -22
  122. package/dist/mcp/tools/updateCallLog.d.ts +1 -0
  123. package/{mcp → dist/mcp}/tools/updateCallLog.js +16 -26
  124. package/dist/models/accountDataModel.d.ts +3 -0
  125. package/dist/models/accountDataModel.js +39 -0
  126. package/dist/models/adminConfigModel.d.ts +2 -0
  127. package/dist/models/adminConfigModel.js +39 -0
  128. package/dist/models/cacheModel.d.ts +2 -0
  129. package/{models → dist/models}/cacheModel.js +7 -3
  130. package/dist/models/callDownListModel.d.ts +2 -0
  131. package/{models → dist/models}/callDownListModel.js +8 -4
  132. package/dist/models/callLogModel.d.ts +2 -0
  133. package/dist/models/callLogModel.js +43 -0
  134. package/dist/models/dynamo/connectorSchema.d.ts +2 -0
  135. package/dist/models/dynamo/connectorSchema.js +141 -0
  136. package/dist/models/dynamo/lockSchema.d.ts +2 -0
  137. package/{models → dist/models}/dynamo/lockSchema.js +5 -7
  138. package/dist/models/dynamo/noteCacheSchema.d.ts +2 -0
  139. package/{models → dist/models}/dynamo/noteCacheSchema.js +4 -6
  140. package/dist/models/llmSessionModel.d.ts +2 -0
  141. package/dist/models/llmSessionModel.js +21 -0
  142. package/dist/models/messageLogModel.d.ts +2 -0
  143. package/{models → dist/models}/messageLogModel.js +8 -4
  144. package/dist/models/sequelize.d.ts +2 -0
  145. package/dist/models/sequelize.js +68 -0
  146. package/dist/models/userModel.d.ts +2 -0
  147. package/dist/models/userModel.js +49 -0
  148. package/dist/package.json +100 -0
  149. package/dist/releaseNotes.json +1222 -0
  150. package/dist/tsconfig.json +14 -0
  151. package/dist/types/admin.d.ts +141 -0
  152. package/dist/types/analytics.d.ts +14 -0
  153. package/dist/types/appointment.d.ts +50 -0
  154. package/dist/types/auth.d.ts +137 -0
  155. package/dist/types/authSession.d.ts +35 -0
  156. package/dist/types/cacheCleanup.d.ts +12 -0
  157. package/dist/types/calldown.d.ts +54 -0
  158. package/dist/types/common.d.ts +46 -0
  159. package/dist/types/connector.d.ts +165 -0
  160. package/dist/types/contact.d.ts +125 -0
  161. package/dist/types/errorHandler.d.ts +60 -0
  162. package/dist/types/index.d.ts +76 -0
  163. package/dist/types/json.d.ts +9 -0
  164. package/dist/types/jwt.d.ts +8 -0
  165. package/dist/types/logging.d.ts +224 -0
  166. package/dist/types/managedAuth.d.ts +114 -0
  167. package/dist/types/managedOAuth.d.ts +59 -0
  168. package/dist/types/manifest.d.ts +120 -0
  169. package/dist/types/migration.d.ts +15 -0
  170. package/dist/types/plugin.d.ts +74 -0
  171. package/dist/types/ringcentral.d.ts +61 -0
  172. package/dist/types/s3ErrorLogReport.d.ts +16 -0
  173. package/dist/types/sharedSMSComposer.d.ts +105 -0
  174. package/dist/types/user.d.ts +46 -0
  175. package/dist/types/util.d.ts +7 -0
  176. package/package.json +34 -6
  177. package/releaseNotes.json +8 -0
  178. package/.env.test +0 -5
  179. package/connector/proxy/engine.js +0 -165
  180. package/connector/proxy/index.js +0 -500
  181. package/connector/registry.js +0 -252
  182. package/docs/README.md +0 -50
  183. package/docs/architecture.md +0 -94
  184. package/docs/connectors.md +0 -116
  185. package/docs/handlers.md +0 -128
  186. package/docs/libraries.md +0 -98
  187. package/docs/models.md +0 -146
  188. package/docs/routes.md +0 -119
  189. package/docs/tests.md +0 -73
  190. package/handlers/calldown.js +0 -99
  191. package/jest.config.js +0 -57
  192. package/lib/constants.js +0 -9
  193. package/lib/encode.js +0 -30
  194. package/lib/errorHandler.js +0 -218
  195. package/lib/jwt.js +0 -18
  196. package/lib/logger.js +0 -190
  197. package/lib/ringcentral.js +0 -267
  198. package/mcp/README.md +0 -420
  199. package/mcp/tools/index.js +0 -51
  200. package/mcp/ui/App/App.tsx +0 -358
  201. package/mcp/ui/App/components/AuthInfoForm.tsx +0 -113
  202. package/mcp/ui/App/components/AuthSuccess.tsx +0 -22
  203. package/mcp/ui/App/components/ConnectorList.tsx +0 -82
  204. package/mcp/ui/App/components/DebugPanel.tsx +0 -43
  205. package/mcp/ui/App/components/OAuthConnect.tsx +0 -270
  206. package/mcp/ui/App/lib/callTool.ts +0 -130
  207. package/mcp/ui/App/lib/debugLog.ts +0 -41
  208. package/mcp/ui/App/lib/developerPortal.ts +0 -111
  209. package/mcp/ui/App/main.css +0 -6
  210. package/mcp/ui/App/root.tsx +0 -13
  211. package/mcp/ui/dist/index.html +0 -53
  212. package/mcp/ui/vite.config.ts +0 -16
  213. package/models/accountDataModel.js +0 -34
  214. package/models/adminConfigModel.js +0 -35
  215. package/models/callLogModel.js +0 -39
  216. package/models/dynamo/connectorSchema.js +0 -146
  217. package/models/llmSessionModel.js +0 -17
  218. package/models/sequelize.js +0 -17
  219. package/models/userModel.js +0 -45
  220. package/test/connector/developerPortal.test.js +0 -166
  221. package/test/connector/mock.test.js +0 -131
  222. package/test/connector/proxy/engine.test.js +0 -211
  223. package/test/connector/proxy/index.test.js +0 -525
  224. package/test/connector/proxy/sample.json +0 -162
  225. package/test/connector/registry.test.js +0 -416
  226. package/test/handlers/admin.test.js +0 -960
  227. package/test/handlers/appointment.test.js +0 -260
  228. package/test/handlers/auth.test.js +0 -1096
  229. package/test/handlers/calldown.test.js +0 -310
  230. package/test/handlers/contact.test.js +0 -1014
  231. package/test/handlers/disposition.test.js +0 -396
  232. package/test/handlers/log.test.js +0 -1847
  233. package/test/handlers/managedAuth.test.js +0 -458
  234. package/test/handlers/managedOAuth.test.js +0 -262
  235. package/test/handlers/plugin.test.js +0 -429
  236. package/test/handlers/user.test.js +0 -381
  237. package/test/index.test.js +0 -301
  238. package/test/lib/analytics.test.js +0 -146
  239. package/test/lib/authSession.test.js +0 -173
  240. package/test/lib/cacheCleanup.test.js +0 -127
  241. package/test/lib/callLogComposer.test.js +0 -1231
  242. package/test/lib/debugTracer.test.js +0 -328
  243. package/test/lib/encode.test.js +0 -59
  244. package/test/lib/errorHandler.test.js +0 -246
  245. package/test/lib/generalErrorMessage.test.js +0 -82
  246. package/test/lib/jwt.test.js +0 -176
  247. package/test/lib/logger.test.js +0 -206
  248. package/test/lib/oauth.test.js +0 -359
  249. package/test/lib/ringcentral.test.js +0 -467
  250. package/test/lib/s3ErrorLogReport.test.js +0 -187
  251. package/test/lib/sharedSMSComposer.test.js +0 -1084
  252. package/test/lib/util.test.js +0 -329
  253. package/test/mcp/mcpHandler.test.js +0 -73
  254. package/test/mcp/mcpHandlerMore.test.js +0 -384
  255. package/test/mcp/tools/appointmentTools.test.js +0 -362
  256. package/test/mcp/tools/checkAuthStatus.test.js +0 -83
  257. package/test/mcp/tools/createCallLog.test.js +0 -436
  258. package/test/mcp/tools/createContact.test.js +0 -58
  259. package/test/mcp/tools/createMessageLog.test.js +0 -595
  260. package/test/mcp/tools/doAuth.test.js +0 -113
  261. package/test/mcp/tools/findContactByName.test.js +0 -275
  262. package/test/mcp/tools/findContactByPhone.test.js +0 -296
  263. package/test/mcp/tools/getCallLog.test.js +0 -298
  264. package/test/mcp/tools/getGoogleFilePicker.test.js +0 -281
  265. package/test/mcp/tools/getPublicConnectors.test.js +0 -107
  266. package/test/mcp/tools/getSessionInfo.test.js +0 -127
  267. package/test/mcp/tools/logout.test.js +0 -233
  268. package/test/mcp/tools/rcGetCallLogs.test.js +0 -56
  269. package/test/mcp/tools/updateCallLog.test.js +0 -360
  270. package/test/models/accountDataModel.test.js +0 -98
  271. package/test/models/callDownListModel.test.js +0 -125
  272. package/test/models/dynamo/connectorSchema.test.js +0 -189
  273. package/test/models/dynamo/lockSchema.test.js +0 -37
  274. package/test/models/dynamo/noteCacheSchema.test.js +0 -45
  275. package/test/models/llmSessionModel.test.js +0 -91
  276. package/test/models/models.test.js +0 -660
  277. package/test/routes/calldownRoutes.test.js +0 -224
  278. package/test/routes/coreRouterBroadRoutes.test.js +0 -855
  279. package/test/routes/dispositionRoutes.test.js +0 -192
  280. package/test/routes/managedAuthRoutes.test.js +0 -386
  281. package/test/routes/pluginRoutes.test.js +0 -262
  282. package/test/setup.js +0 -178
  283. /package/{mcp → dist/mcp}/ui/index.html +0 -0
  284. /package/{mcp → dist/mcp}/ui/package-lock.json +0 -0
  285. /package/{mcp → dist/mcp}/ui/package.json +0 -0
  286. /package/{mcp → dist/mcp}/ui/tsconfig.json +0 -0
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * MCP Server for RC Unified CRM Extension
3
4
  *
@@ -5,32 +6,41 @@
5
6
  * Fully compatible with stateless deployments (AWS Lambda, etc.).
6
7
  * All auth context is resolved per-request; rcExtensionId is cached in CacheModel.
7
8
  */
8
-
9
- const axios = require('axios');
10
- const { Op } = require('sequelize');
11
- const tools = require('./tools');
12
- const { LlmSessionModel } = require('../models/llmSessionModel');
13
- const { CacheModel } = require('../models/cacheModel');
14
- const { UserModel } = require('../models/userModel');
15
- const { getHashValue } = require('../lib/util');
16
- const jwt = require('../lib/jwt');
17
- const logger = require('../lib/logger');
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ // @ts-check
11
+ const axios = /** @type {any} */ (require('axios'));
12
+ const { Op } = /** @type {any} */ (require('sequelize'));
13
+ const tools = /** @type {any} */ (require('./tools'));
14
+ const { LlmSessionModel: RawLlmSessionModel } = require('../models/llmSessionModel');
15
+ const LlmSessionModel = /** @type {any} */ (RawLlmSessionModel);
16
+ const { CacheModel: RawCacheModel } = require('../models/cacheModel');
17
+ const CacheModel = /** @type {any} */ (RawCacheModel);
18
+ const { UserModel: RawUserModel } = require('../models/userModel');
19
+ const UserModel = /** @type {any} */ (RawUserModel);
20
+ const { getHashValue: rawGetHashValue } = require('../lib/util');
21
+ const getHashValue = /** @type {any} */ (rawGetHashValue);
22
+ const jwt = /** @type {any} */ (require('../lib/jwt'));
23
+ const logger = /** @type {any} */ (require('../lib/logger'));
24
+ const { verifyWidgetSessionToken: rawVerifyWidgetSessionToken } = require('./lib/widgetSessionToken');
25
+ const verifyWidgetSessionToken = /** @type {any} */ (rawVerifyWidgetSessionToken);
26
+ const { MCP_OAUTH_INVALID_TOKEN_MESSAGE, buildMcpOAuthError: rawBuildMcpOAuthError, } = require('./lib/oauthError');
27
+ const buildMcpOAuthError = /** @type {any} */ (rawBuildMcpOAuthError);
18
28
  const fs = require('fs');
19
29
  const path = require('path');
20
-
21
30
  /**
22
31
  * Increment this to bust ChatGPT's widget resource cache after every UI build.
23
32
  * This is the single source of truth — injected into getPublicConnectors _meta at response time.
24
33
  */
25
- const WIDGET_VERSION = 10;
34
+ const WIDGET_VERSION = 11;
26
35
  const WIDGET_URI = `ui://widget/ConnectorList-v${WIDGET_VERSION}.html`;
27
36
  const RC_EXTENSION_CACHE_KEY = 'rcExtensionId';
28
37
  const RC_EXTENSION_CACHE_STATUS = 'resolved';
29
-
30
38
  const JSON_RPC_INTERNAL_ERROR = -32603;
31
39
  const JSON_RPC_METHOD_NOT_FOUND = -32601;
32
40
  const JSON_RPC_INVALID_PARAMS = -32602;
33
-
41
+ function getWidgetSessionToken(req) {
42
+ return req.headers?.['x-app-connect-widget-token'] || req.headers?.['X-App-Connect-Widget-Token'];
43
+ }
34
44
  /**
35
45
  * JSON Schema definitions for tools that accept parameters.
36
46
  * Without inputSchema, ChatGPT silently drops all arguments when calling the tool.
@@ -84,7 +94,6 @@ const inputSchemas = {
84
94
  required: [],
85
95
  },
86
96
  };
87
-
88
97
  const standardToolResultOutputSchema = {
89
98
  type: 'object',
90
99
  properties: {
@@ -96,7 +105,6 @@ const standardToolResultOutputSchema = {
96
105
  },
97
106
  required: ['success'],
98
107
  };
99
-
100
108
  /**
101
109
  * JSON Schema definitions for each tool's structuredContent response.
102
110
  * MCP clients use this from tools/list to understand and validate tool output.
@@ -173,38 +181,33 @@ const outputSchemas = {
173
181
  confirmAppointment: standardToolResultOutputSchema,
174
182
  cancelAppointment: standardToolResultOutputSchema,
175
183
  };
176
-
177
184
  /**
178
185
  * Verify an RC access token and return the caller's extension ID.
179
186
  * Throws if the token is invalid.
180
187
  */
181
188
  async function resolveRcExtensionId(rcAccessToken) {
182
- const resp = await axios.get(
183
- 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
184
- { headers: { Authorization: `Bearer ${rcAccessToken}` } }
185
- );
189
+ const resp = await axios.get('https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~', { headers: { Authorization: `Bearer ${rcAccessToken}` } });
186
190
  return resp.data?.id?.toString() ?? null;
187
191
  }
188
-
189
192
  /**
190
193
  * Resolve rcExtensionId for the current request.
191
194
  * Checks CacheModel first (avoids RC API call on Lambda cold starts),
192
195
  * falls back to a live RC API verification and persists the result.
193
196
  */
194
197
  async function resolveSessionContext(rcAccessToken, openaiSessionId) {
195
- if (!rcAccessToken) return { rcExtensionId: null };
196
-
198
+ if (!rcAccessToken)
199
+ return { rcExtensionId: null };
197
200
  if (openaiSessionId) {
198
201
  try {
199
202
  const cached = await CacheModel.findByPk(`${openaiSessionId}-${RC_EXTENSION_CACHE_KEY}`);
200
203
  if (cached?.data?.rcExtensionId && (!cached.expiry || cached.expiry > new Date())) {
201
204
  return { rcExtensionId: cached.data.rcExtensionId };
202
205
  }
203
- } catch (err) {
206
+ }
207
+ catch (err) {
204
208
  logger.warn('CacheModel lookup failed:', { message: err.message });
205
209
  }
206
210
  }
207
-
208
211
  let rcExtensionId = null;
209
212
  try {
210
213
  rcExtensionId = await resolveRcExtensionId(rcAccessToken);
@@ -218,13 +221,19 @@ async function resolveSessionContext(rcAccessToken, openaiSessionId) {
218
221
  expiry: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24h TTL
219
222
  });
220
223
  }
221
- } catch (err) {
224
+ }
225
+ catch (err) {
222
226
  logger.warn('Failed to resolve RC extension ID:', { message: err.message });
227
+ return {
228
+ rcExtensionId: null,
229
+ rcTokenError: buildMcpOAuthError({
230
+ message: MCP_OAUTH_INVALID_TOKEN_MESSAGE,
231
+ errorDetails: err.message,
232
+ }),
233
+ };
223
234
  }
224
-
225
235
  return { rcExtensionId };
226
236
  }
227
-
228
237
  /**
229
238
  * Build the tools list to return in tools/list responses.
230
239
  * Injects schema metadata and stamps WIDGET_URI into getPublicConnectors _meta.
@@ -244,7 +253,6 @@ function getToolsList() {
244
253
  return def;
245
254
  });
246
255
  }
247
-
248
256
  function getStructuredContent(result) {
249
257
  if (result?.structuredContent && typeof result.structuredContent === 'object' && !Array.isArray(result.structuredContent)) {
250
258
  return result.structuredContent;
@@ -254,7 +262,6 @@ function getStructuredContent(result) {
254
262
  }
255
263
  return { result: result ?? null };
256
264
  }
257
-
258
265
  function buildToolResult(toolDefinition, result) {
259
266
  const structuredContent = getStructuredContent(result);
260
267
  const responseResult = {
@@ -262,17 +269,14 @@ function buildToolResult(toolDefinition, result) {
262
269
  ? result.content
263
270
  : [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],
264
271
  };
265
-
266
272
  if (toolDefinition?.outputSchema || outputSchemas[toolDefinition?.name] || result?.structuredContent) {
267
273
  responseResult.structuredContent = structuredContent;
268
274
  }
269
275
  if (structuredContent?.success === false) {
270
276
  responseResult.isError = true;
271
277
  }
272
-
273
278
  return responseResult;
274
279
  }
275
-
276
280
  /**
277
281
  * Handle incoming MCP HTTP requests.
278
282
  * Stateless: each POST is handled independently with no session state between requests.
@@ -281,12 +285,9 @@ async function handleMcpRequest(req, res) {
281
285
  try {
282
286
  const { method, params, id } = req.body;
283
287
  logger.info('Received MCP request:', { method });
284
-
285
288
  const rcAccessToken = req.headers['authorization']?.split('Bearer ')?.[1];
286
289
  const openaiSessionId = params?._meta?.['openai/session'] ?? null;
287
-
288
290
  let response;
289
-
290
291
  switch (method) {
291
292
  case 'initialize':
292
293
  response = {
@@ -305,7 +306,6 @@ async function handleMcpRequest(req, res) {
305
306
  },
306
307
  };
307
308
  break;
308
-
309
309
  case 'tools/list':
310
310
  response = {
311
311
  jsonrpc: '2.0',
@@ -313,15 +313,22 @@ async function handleMcpRequest(req, res) {
313
313
  result: { tools: getToolsList() },
314
314
  };
315
315
  break;
316
-
317
316
  case 'tools/call': {
318
317
  const { name: toolName, arguments: args } = params;
319
318
  const toolArgs = { ...(args || {}) };
320
-
321
- if (rcAccessToken) toolArgs.rcAccessToken = rcAccessToken;
322
- if (openaiSessionId) toolArgs.openaiSessionId = openaiSessionId;
323
-
324
- const { rcExtensionId } = await resolveSessionContext(rcAccessToken, openaiSessionId);
319
+ if (rcAccessToken)
320
+ toolArgs.rcAccessToken = rcAccessToken;
321
+ if (openaiSessionId)
322
+ toolArgs.openaiSessionId = openaiSessionId;
323
+ const { rcExtensionId, rcTokenError } = await resolveSessionContext(rcAccessToken, openaiSessionId);
324
+ if (rcTokenError) {
325
+ response = {
326
+ jsonrpc: '2.0',
327
+ id,
328
+ result: buildToolResult({ outputSchema: standardToolResultOutputSchema }, rcTokenError),
329
+ };
330
+ break;
331
+ }
325
332
  if (rcExtensionId) {
326
333
  toolArgs.rcExtensionId = rcExtensionId;
327
334
  if (!toolArgs.jwtToken) {
@@ -378,19 +385,18 @@ async function handleMcpRequest(req, res) {
378
385
  }
379
386
  }
380
387
  }
381
-
382
388
  try {
383
389
  const tool = tools.tools.find(t => t.definition.name === toolName);
384
- if (!tool) throw new Error(`Tool not found: ${toolName}`);
385
-
390
+ if (!tool)
391
+ throw new Error(`Tool not found: ${toolName}`);
386
392
  const result = await tool.execute(toolArgs);
387
-
388
393
  response = {
389
394
  jsonrpc: '2.0',
390
395
  id,
391
396
  result: buildToolResult(tool.definition, result),
392
397
  };
393
- } catch (toolError) {
398
+ }
399
+ catch (toolError) {
394
400
  response = {
395
401
  jsonrpc: '2.0',
396
402
  id,
@@ -402,23 +408,21 @@ async function handleMcpRequest(req, res) {
402
408
  }
403
409
  break;
404
410
  }
405
-
406
411
  case 'resources/list':
407
412
  response = {
408
413
  jsonrpc: '2.0',
409
414
  id,
410
415
  result: {
411
416
  resources: [{
412
- uri: WIDGET_URI,
413
- name: 'connector-list-widget',
414
- title: 'ConnectorList',
415
- description: 'ChatGPT widget for connector selection',
416
- mimeType: 'text/html+skybridge',
417
- }],
417
+ uri: WIDGET_URI,
418
+ name: 'connector-list-widget',
419
+ title: 'ConnectorList',
420
+ description: 'ChatGPT widget for connector selection',
421
+ mimeType: 'text/html+skybridge',
422
+ }],
418
423
  },
419
424
  };
420
425
  break;
421
-
422
426
  case 'resources/read': {
423
427
  const uri = params?.uri;
424
428
  if (!uri?.startsWith('ui://widget/')) {
@@ -429,45 +433,44 @@ async function handleMcpRequest(req, res) {
429
433
  };
430
434
  break;
431
435
  }
432
-
433
436
  const appUrl = process.env.APP_SERVER || 'http://localhost:6066';
434
437
  const distPath = path.join(__dirname, 'ui', 'dist', 'index.html');
435
438
  const devPath = path.join(__dirname, 'ui', 'index.html');
436
439
  let htmlContent;
437
- try { htmlContent = fs.readFileSync(distPath, 'utf8'); }
438
- catch { htmlContent = fs.readFileSync(devPath, 'utf8'); }
439
-
440
+ try {
441
+ htmlContent = fs.readFileSync(distPath, 'utf8');
442
+ }
443
+ catch {
444
+ htmlContent = fs.readFileSync(devPath, 'utf8');
445
+ }
440
446
  response = {
441
447
  jsonrpc: '2.0',
442
448
  id,
443
449
  result: {
444
450
  contents: [{
445
- uri: WIDGET_URI,
446
- mimeType: 'text/html+skybridge',
447
- text: htmlContent,
448
- _meta: {
449
- 'openai/widgetPrefersBorder': true,
450
- 'openai/widgetDomain': appUrl,
451
- 'openai/widgetCSP': {
452
- connect_domains: [appUrl, 'https://appconnect.labs.ringcentral.com'],
453
- resource_domains: [appUrl],
451
+ uri: WIDGET_URI,
452
+ mimeType: 'text/html+skybridge',
453
+ text: htmlContent,
454
+ _meta: {
455
+ 'openai/widgetPrefersBorder': true,
456
+ 'openai/widgetDomain': appUrl,
457
+ 'openai/widgetCSP': {
458
+ connect_domains: [appUrl, 'https://appconnect.labs.ringcentral.com'],
459
+ resource_domains: [appUrl],
460
+ },
454
461
  },
455
- },
456
- }],
462
+ }],
457
463
  },
458
464
  };
459
465
  break;
460
466
  }
461
-
462
467
  case 'ping':
463
468
  response = { jsonrpc: '2.0', id, result: {} };
464
469
  break;
465
-
466
470
  case 'notifications/initialized':
467
471
  case 'notifications/cancelled':
468
472
  // JSON-RPC notifications — no id, no response expected
469
473
  return res.status(200).end();
470
-
471
474
  default:
472
475
  response = {
473
476
  jsonrpc: '2.0',
@@ -475,9 +478,9 @@ async function handleMcpRequest(req, res) {
475
478
  error: { code: JSON_RPC_METHOD_NOT_FOUND, message: `Method not found: ${method}` },
476
479
  };
477
480
  }
478
-
479
481
  res.status(200).json(response);
480
- } catch (error) {
482
+ }
483
+ catch (error) {
481
484
  logger.error('Error handling MCP request:', { stack: error.stack });
482
485
  res.status(200).json({
483
486
  jsonrpc: '2.0',
@@ -490,7 +493,6 @@ async function handleMcpRequest(req, res) {
490
493
  });
491
494
  }
492
495
  }
493
-
494
496
  /**
495
497
  * Handle widget tool calls via direct HTTP (bypasses MCP protocol).
496
498
  * The ChatGPT postMessage bridge does not forward tool arguments,
@@ -499,25 +501,34 @@ async function handleMcpRequest(req, res) {
499
501
  async function handleWidgetToolCall(req, res) {
500
502
  try {
501
503
  logger.info('Widget tool call received. body:', JSON.stringify(req.body));
502
-
503
504
  const { tool: toolName, toolArgs: args } = req.body || {};
504
-
505
505
  logger.info('Widget tool call parsed:', { toolName, args: JSON.stringify(args) });
506
-
506
+ const widgetContext = verifyWidgetSessionToken(getWidgetSessionToken(req));
507
+ if (!widgetContext) {
508
+ return res.status(401).json({ success: false, error: 'Invalid widget session' });
509
+ }
507
510
  if (!toolName) {
508
511
  return res.status(400).json({ success: false, error: 'Missing tool name' });
509
512
  }
510
-
511
- const allWidgetCallable = [...tools.tools, ...tools.widgetTools];
512
- const tool = allWidgetCallable.find(t => t.definition.name === toolName);
513
+ const tool = tools.widgetTools.find(t => t.definition.name === toolName);
513
514
  if (!tool) {
514
515
  return res.status(404).json({ success: false, error: `Unknown tool: ${toolName}` });
515
516
  }
516
-
517
- const result = await tool.execute(args || {});
517
+ const toolArgs = args && typeof args === 'object' ? args : {};
518
+ if (widgetContext.openaiSessionId &&
519
+ toolArgs.sessionId &&
520
+ toolArgs.sessionId !== widgetContext.openaiSessionId) {
521
+ return res.status(403).json({ success: false, error: 'Widget session mismatch' });
522
+ }
523
+ const result = await tool.execute({
524
+ ...toolArgs,
525
+ rcExtensionId: widgetContext.rcExtensionId,
526
+ openaiSessionId: widgetContext.openaiSessionId,
527
+ });
518
528
  logger.info('Widget tool call result:', { toolName, success: result?.success });
519
529
  res.json(result);
520
- } catch (error) {
530
+ }
531
+ catch (error) {
521
532
  logger.error('Widget tool call error:', { stack: error.stack });
522
533
  res.status(500).json({
523
534
  success: false,
@@ -525,6 +536,5 @@ async function handleWidgetToolCall(req, res) {
525
536
  });
526
537
  }
527
538
  }
528
-
529
539
  exports.handleMcpRequest = handleMcpRequest;
530
540
  exports.handleWidgetToolCall = handleWidgetToolCall;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,13 +1,14 @@
1
- const jwt = require('../../lib/jwt');
2
- const connectorRegistry = require('../../connector/registry');
3
- const appointmentCore = require('../../handlers/appointment');
4
-
1
+ "use strict";
2
+ // @ts-check
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const jwt = /** @type {any} */ (require('../../lib/jwt'));
5
+ const connectorRegistry = /** @type {any} */ (require('../../connector/registry'));
6
+ const appointmentCore = /** @type {any} */ (require('../../handlers/appointment'));
5
7
  /**
6
8
  * MCP Tool: Cancel Appointment
7
9
  *
8
10
  * Cancels (deletes) an existing appointment/event in the CRM platform.
9
11
  */
10
-
11
12
  const toolDefinition = {
12
13
  name: 'cancelAppointment',
13
14
  description: '⚠️ REQUIRES CRM CONNECTION. | Cancel an existing appointment or event in the CRM platform by its ID. This action is destructive — the appointment will be removed.',
@@ -27,51 +28,42 @@ const toolDefinition = {
27
28
  destructiveHint: true
28
29
  }
29
30
  };
30
-
31
31
  /**
32
32
  * Execute the cancelAppointment tool
33
33
  * @param {Object} args
34
34
  * @param {string} args.jwtToken - Injected server-side JWT token
35
35
  * @param {string} args.appointmentId - CRM appointment ID to cancel
36
- * @returns {Object} Result with cancelled appointment data
36
+ * @returns {Promise<any>} Result with cancelled appointment data
37
37
  */
38
38
  async function execute(args) {
39
39
  try {
40
40
  const { jwtToken, appointmentId } = args;
41
-
42
41
  if (!jwtToken) {
43
42
  throw new Error('Please go to Settings and authorize CRM platform');
44
43
  }
45
-
46
44
  if (!appointmentId) {
47
45
  throw new Error('appointmentId is required');
48
46
  }
49
-
50
47
  const decodedToken = jwt.decodeJwt(jwtToken);
51
48
  if (!decodedToken) {
52
49
  throw new Error('Invalid JWT token');
53
50
  }
54
51
  const { id: userId, platform } = decodedToken;
55
-
56
52
  if (!userId) {
57
53
  throw new Error('Invalid JWT token: userId not found');
58
54
  }
59
-
60
55
  const platformModule = connectorRegistry.getConnector(platform);
61
56
  if (!platformModule) {
62
57
  throw new Error(`Platform connector not found for: ${platform}`);
63
58
  }
64
-
65
59
  if (!platformModule.cancelAppointment) {
66
60
  throw new Error(`cancelAppointment is not implemented for platform: ${platform}`);
67
61
  }
68
-
69
62
  const { successful, appointment, returnMessage } = await appointmentCore.cancelAppointment({
70
63
  platform,
71
64
  userId,
72
65
  appointmentId
73
66
  });
74
-
75
67
  if (successful) {
76
68
  return {
77
69
  success: true,
@@ -96,6 +88,5 @@ async function execute(args) {
96
88
  };
97
89
  }
98
90
  }
99
-
100
91
  exports.definition = toolDefinition;
101
92
  exports.execute = execute;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,12 +1,15 @@
1
- const { getAuthSession } = require('../../lib/authSession');
2
- const { LlmSessionModel } = require('../../models/llmSessionModel');
3
-
1
+ "use strict";
2
+ // @ts-check
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const { getAuthSession: rawGetAuthSession } = require('../../lib/authSession');
5
+ const getAuthSession = /** @type {any} */ (rawGetAuthSession);
6
+ const { LlmSessionModel: RawLlmSessionModel } = require('../../models/llmSessionModel');
7
+ const LlmSessionModel = /** @type {any} */ (RawLlmSessionModel);
4
8
  /**
5
9
  * MCP Tool: Check Auth Status
6
10
  *
7
11
  * Polls the status of an ongoing OAuth authentication session
8
12
  */
9
-
10
13
  const toolDefinition = {
11
14
  name: 'checkAuthStatus',
12
15
  description: 'Check the status of an ongoing OAuth authentication session. Poll this after user clicks the auth link.',
@@ -26,13 +29,12 @@ const toolDefinition = {
26
29
  destructiveHint: false
27
30
  }
28
31
  };
29
-
30
32
  /**
31
33
  * Execute the checkAuthStatus tool
32
34
  * @param {Object} args - The tool arguments
33
35
  * @param {string} args.sessionId - The session ID to check
34
36
  * @param {string} args.rcExtensionId - The RC extension ID
35
- * @returns {Object} Result object with authentication status
37
+ * @returns {Promise<any>} Result object with authentication status
36
38
  */
37
39
  async function execute(args) {
38
40
  try {
@@ -44,16 +46,26 @@ async function execute(args) {
44
46
  throw new Error('rcExtensionId is required');
45
47
  }
46
48
  const session = await getAuthSession(sessionId);
47
-
48
49
  if (!session) {
49
50
  return {
50
51
  success: false,
51
52
  error: 'CRM auth session not found or expired. Ask the user to start the auth flow again.'
52
53
  };
53
54
  }
54
-
55
+ if (session.rcExtensionId && session.rcExtensionId !== rcExtensionId) {
56
+ return {
57
+ success: false,
58
+ error: 'CRM auth session does not belong to this RingCentral extension.'
59
+ };
60
+ }
55
61
  switch (session.status) {
56
62
  case 'completed':
63
+ if (!session.jwtToken) {
64
+ return {
65
+ success: false,
66
+ error: 'CRM auth session completed without a CRM token.'
67
+ };
68
+ }
57
69
  await LlmSessionModel.upsert({
58
70
  id: rcExtensionId,
59
71
  jwtToken: session.jwtToken,
@@ -62,12 +74,10 @@ async function execute(args) {
62
74
  return {
63
75
  data: {
64
76
  status: 'completed',
65
- jwtToken: session.jwtToken,
66
77
  userInfo: session.userInfo,
67
- message: 'IMPORTANT: Authentication successful! Keep jwtToken in memory for future use. DO NOT directly show it to user.'
78
+ message: 'Authentication successful. CRM token stored server-side for future tool calls.'
68
79
  }
69
80
  };
70
-
71
81
  case 'expired':
72
82
  return {
73
83
  data: {
@@ -75,7 +85,6 @@ async function execute(args) {
75
85
  errorMessage: 'Authentication session expired. Ask the user to start the auth flow again.'
76
86
  }
77
87
  };
78
-
79
88
  case 'failed':
80
89
  return {
81
90
  data: {
@@ -83,7 +92,6 @@ async function execute(args) {
83
92
  errorMessage: session.errorMessage || 'Unknown error'
84
93
  }
85
94
  };
86
-
87
95
  case 'pending':
88
96
  default:
89
97
  return {
@@ -100,6 +108,5 @@ async function execute(args) {
100
108
  };
101
109
  }
102
110
  }
103
-
104
111
  exports.definition = toolDefinition;
105
112
  exports.execute = execute;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,13 +1,14 @@
1
- const jwt = require('../../lib/jwt');
2
- const connectorRegistry = require('../../connector/registry');
3
- const appointmentCore = require('../../handlers/appointment');
4
-
1
+ "use strict";
2
+ // @ts-check
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const jwt = /** @type {any} */ (require('../../lib/jwt'));
5
+ const connectorRegistry = /** @type {any} */ (require('../../connector/registry'));
6
+ const appointmentCore = /** @type {any} */ (require('../../handlers/appointment'));
5
7
  /**
6
8
  * MCP Tool: Confirm Appointment
7
9
  *
8
10
  * Marks an existing appointment/event as confirmed in the CRM platform.
9
11
  */
10
-
11
12
  const toolDefinition = {
12
13
  name: 'confirmAppointment',
13
14
  description: '⚠️ REQUIRES CRM CONNECTION. | Confirm an existing appointment or event in the CRM platform by its ID. Returns the updated appointment details.',
@@ -27,51 +28,42 @@ const toolDefinition = {
27
28
  destructiveHint: false
28
29
  }
29
30
  };
30
-
31
31
  /**
32
32
  * Execute the confirmAppointment tool
33
33
  * @param {Object} args
34
34
  * @param {string} args.jwtToken - Injected server-side JWT token
35
35
  * @param {string} args.appointmentId - CRM appointment ID to confirm
36
- * @returns {Object} Result with updated appointment data
36
+ * @returns {Promise<any>} Result with updated appointment data
37
37
  */
38
38
  async function execute(args) {
39
39
  try {
40
40
  const { jwtToken, appointmentId } = args;
41
-
42
41
  if (!jwtToken) {
43
42
  throw new Error('Please go to Settings and authorize CRM platform');
44
43
  }
45
-
46
44
  if (!appointmentId) {
47
45
  throw new Error('appointmentId is required');
48
46
  }
49
-
50
47
  const decodedToken = jwt.decodeJwt(jwtToken);
51
48
  if (!decodedToken) {
52
49
  throw new Error('Invalid JWT token');
53
50
  }
54
51
  const { id: userId, platform } = decodedToken;
55
-
56
52
  if (!userId) {
57
53
  throw new Error('Invalid JWT token: userId not found');
58
54
  }
59
-
60
55
  const platformModule = connectorRegistry.getConnector(platform);
61
56
  if (!platformModule) {
62
57
  throw new Error(`Platform connector not found for: ${platform}`);
63
58
  }
64
-
65
59
  if (!platformModule.confirmAppointment) {
66
60
  throw new Error(`confirmAppointment is not implemented for platform: ${platform}`);
67
61
  }
68
-
69
62
  const { successful, appointment, returnMessage } = await appointmentCore.confirmAppointment({
70
63
  platform,
71
64
  userId,
72
65
  appointmentId
73
66
  });
74
-
75
67
  if (successful) {
76
68
  return {
77
69
  success: true,
@@ -96,6 +88,5 @@ async function execute(args) {
96
88
  };
97
89
  }
98
90
  }
99
-
100
91
  exports.definition = toolDefinition;
101
92
  exports.execute = execute;
@@ -0,0 +1 @@
1
+ export {};