@hubspot/cli 7.8.0-experimental.0 → 7.8.0

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 (357) hide show
  1. package/bin/cli.js +31 -27
  2. package/commands/__tests__/auth.test.js +5 -0
  3. package/commands/__tests__/doctor.test.js +16 -16
  4. package/commands/__tests__/getStarted.test.js +2 -2
  5. package/commands/__tests__/mcp.test.js +1 -1
  6. package/commands/__tests__/project.test.js +2 -3
  7. package/commands/account/auth.js +1 -0
  8. package/commands/account/clean.js +18 -27
  9. package/commands/account/createOverride.js +13 -31
  10. package/commands/account/info.js +20 -31
  11. package/commands/account/list.js +16 -22
  12. package/commands/account/remove.js +12 -20
  13. package/commands/account/removeOverride.js +11 -21
  14. package/commands/account/rename.js +6 -9
  15. package/commands/account/use.js +12 -26
  16. package/commands/account.js +2 -2
  17. package/commands/app/__tests__/migrate.test.js +5 -6
  18. package/commands/app/migrate.js +13 -19
  19. package/commands/app/secret/add.js +2 -1
  20. package/commands/app/secret/delete.js +2 -1
  21. package/commands/app/secret/list.js +2 -1
  22. package/commands/app/secret/update.js +2 -1
  23. package/commands/app/secret.js +2 -1
  24. package/commands/app.js +2 -2
  25. package/commands/auth.d.ts +1 -0
  26. package/commands/auth.js +17 -7
  27. package/commands/cms/convertFields.js +7 -9
  28. package/commands/cms/getReactModule.js +9 -14
  29. package/commands/cms/lighthouseScore.js +33 -36
  30. package/commands/cms.js +2 -2
  31. package/commands/completion.js +3 -3
  32. package/commands/config/set.d.ts +1 -1
  33. package/commands/config/set.js +64 -37
  34. package/commands/config.js +2 -2
  35. package/commands/create.js +2 -2
  36. package/commands/customObject/create.js +10 -12
  37. package/commands/customObject/schema/create.js +9 -11
  38. package/commands/customObject/schema/delete.js +16 -16
  39. package/commands/customObject/schema/fetch-all.js +12 -11
  40. package/commands/customObject/schema/fetch.js +15 -15
  41. package/commands/customObject/schema/list.js +4 -4
  42. package/commands/customObject/schema/update.js +13 -13
  43. package/commands/customObject/schema.js +2 -2
  44. package/commands/customObject.js +6 -7
  45. package/commands/doctor.js +8 -11
  46. package/commands/feedback.js +8 -13
  47. package/commands/fetch.js +8 -8
  48. package/commands/filemanager/fetch.js +7 -7
  49. package/commands/filemanager/upload.js +15 -34
  50. package/commands/filemanager.js +2 -2
  51. package/commands/function/deploy.js +11 -29
  52. package/commands/function/list.js +8 -8
  53. package/commands/function/server.js +9 -11
  54. package/commands/function.d.ts +1 -1
  55. package/commands/function.js +2 -2
  56. package/commands/getStarted.d.ts +1 -3
  57. package/commands/getStarted.js +68 -20
  58. package/commands/hubdb/clear.js +7 -15
  59. package/commands/hubdb/create.js +9 -15
  60. package/commands/hubdb/delete.js +8 -15
  61. package/commands/hubdb/fetch.js +6 -9
  62. package/commands/hubdb.d.ts +1 -1
  63. package/commands/hubdb.js +2 -2
  64. package/commands/init.js +2 -3
  65. package/commands/lint.js +16 -16
  66. package/commands/list.js +8 -14
  67. package/commands/logs.js +14 -20
  68. package/commands/mcp/__tests__/setup.test.js +2 -2
  69. package/commands/mcp/setup.js +11 -2
  70. package/commands/mcp.js +3 -3
  71. package/commands/mv.js +6 -17
  72. package/commands/open.js +5 -5
  73. package/commands/project/__tests__/add.test.js +15 -13
  74. package/commands/project/__tests__/create.test.js +6 -6
  75. package/commands/project/__tests__/deploy.test.js +3 -7
  76. package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
  77. package/commands/project/__tests__/installDeps.test.js +8 -8
  78. package/commands/project/__tests__/list.test.js +31 -0
  79. package/commands/project/__tests__/logs.test.js +1 -4
  80. package/commands/project/__tests__/migrate.test.js +7 -7
  81. package/commands/project/__tests__/migrateApp.test.js +3 -7
  82. package/commands/project/__tests__/profile.test.js +1 -1
  83. package/commands/project/__tests__/validate.test.js +98 -0
  84. package/commands/project/add.d.ts +2 -2
  85. package/commands/project/add.js +7 -10
  86. package/commands/project/cloneApp.js +14 -19
  87. package/commands/project/create.js +4 -11
  88. package/commands/project/deploy.js +5 -5
  89. package/commands/project/dev/deprecatedFlow.js +9 -18
  90. package/commands/project/dev/index.js +21 -18
  91. package/commands/project/dev/unifiedFlow.js +15 -8
  92. package/commands/project/download.js +15 -16
  93. package/commands/project/installDeps.d.ts +2 -2
  94. package/commands/project/installDeps.js +9 -9
  95. package/commands/project/list.d.ts +4 -0
  96. package/commands/project/list.js +62 -0
  97. package/commands/project/listBuilds.js +12 -21
  98. package/commands/project/logs.js +21 -24
  99. package/commands/project/migrate.js +33 -12
  100. package/commands/project/migrateApp.js +10 -17
  101. package/commands/project/open.js +6 -14
  102. package/commands/project/profile/add.js +3 -3
  103. package/commands/project/profile/delete.js +1 -2
  104. package/commands/project/profile.js +2 -3
  105. package/commands/project/upload.js +16 -25
  106. package/commands/project/validate.js +7 -7
  107. package/commands/project/watch.js +13 -22
  108. package/commands/project.js +4 -3
  109. package/commands/sandbox/__tests__/create.test.js +5 -5
  110. package/commands/sandbox/create.js +22 -32
  111. package/commands/sandbox/delete.js +39 -64
  112. package/commands/sandbox.js +2 -2
  113. package/commands/secret/addSecret.js +7 -17
  114. package/commands/secret/deleteSecret.js +10 -20
  115. package/commands/secret/listSecret.js +8 -10
  116. package/commands/secret/updateSecret.js +9 -17
  117. package/commands/secret.js +2 -2
  118. package/commands/testAccount/__tests__/delete.test.js +2 -4
  119. package/commands/testAccount/create.js +2 -2
  120. package/commands/testAccount/delete.d.ts +4 -3
  121. package/commands/testAccount/delete.js +155 -14
  122. package/commands/testAccount/importData.d.ts +1 -1
  123. package/commands/testAccount/importData.js +1 -1
  124. package/commands/testAccount.js +1 -1
  125. package/commands/theme/preview.js +1 -4
  126. package/lang/en.d.ts +365 -111
  127. package/lang/en.js +409 -158
  128. package/lang/en.lyaml +4 -4
  129. package/lib/__tests__/buildAccount.test.js +4 -3
  130. package/lib/__tests__/commonOpts.test.js +1 -1
  131. package/lib/__tests__/dependencyManagement.test.js +1 -1
  132. package/lib/__tests__/developerTestAccounts.test.js +3 -3
  133. package/lib/__tests__/hasFeature.test.js +145 -7
  134. package/lib/__tests__/npm.test.js +1 -1
  135. package/lib/__tests__/oauth.test.js +4 -4
  136. package/lib/__tests__/process.test.js +10 -5
  137. package/lib/__tests__/sandboxSync.test.js +8 -8
  138. package/lib/__tests__/sandboxes.test.js +8 -8
  139. package/lib/__tests__/serverlessLogs.test.js +1 -1
  140. package/lib/__tests__/usageTracking.test.js +5 -5
  141. package/lib/__tests__/validation.test.js +2 -1
  142. package/lib/__tests__/yargsUtils.test.js +83 -9
  143. package/lib/app/__tests__/migrate.test.js +19 -56
  144. package/lib/app/__tests__/migrate_legacy.test.js +1 -1
  145. package/lib/app/migrate.d.ts +2 -8
  146. package/lib/app/migrate.js +6 -81
  147. package/lib/app/migrate_legacy.js +20 -24
  148. package/lib/buildAccount.d.ts +2 -2
  149. package/lib/buildAccount.js +32 -64
  150. package/lib/commonOpts.d.ts +1 -1
  151. package/lib/commonOpts.js +25 -22
  152. package/lib/configMigrate.js +88 -9
  153. package/lib/configOptions.js +7 -0
  154. package/lib/constants.d.ts +22 -1
  155. package/lib/constants.js +26 -1
  156. package/lib/dependencyManagement.d.ts +0 -5
  157. package/lib/dependencyManagement.js +9 -36
  158. package/lib/developerTestAccounts.js +9 -23
  159. package/lib/doctor/Diagnosis.js +11 -23
  160. package/lib/doctor/DiagnosticInfoBuilder.js +12 -11
  161. package/lib/doctor/Doctor.js +42 -90
  162. package/lib/doctor/__tests__/Doctor.test.js +4 -4
  163. package/lib/errorHandlers/index.js +12 -20
  164. package/lib/errorHandlers/suppressError.js +11 -18
  165. package/lib/hasFeature.js +6 -0
  166. package/lib/lang.js +6 -5
  167. package/lib/links.d.ts +1 -0
  168. package/lib/links.js +14 -7
  169. package/lib/mcp/setup.js +1 -1
  170. package/lib/middleware/__test__/commandTargetingUtils.test.js +99 -0
  171. package/lib/middleware/__test__/configMiddleware.test.js +11 -11
  172. package/lib/middleware/__test__/yargsChecksMiddleware.test.js +6 -8
  173. package/lib/middleware/commandTargetingUtils.d.ts +8 -0
  174. package/lib/middleware/commandTargetingUtils.js +74 -0
  175. package/lib/middleware/configMiddleware.d.ts +1 -1
  176. package/lib/middleware/configMiddleware.js +21 -81
  177. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  178. package/lib/middleware/gitMiddleware.js +5 -1
  179. package/lib/middleware/notificationsMiddleware.js +5 -11
  180. package/lib/middleware/yargsChecksMiddleware.js +6 -9
  181. package/lib/npm.js +2 -2
  182. package/lib/oauth.js +5 -5
  183. package/lib/process.js +5 -4
  184. package/lib/projects/__tests__/AppDevModeInterface.test.js +87 -90
  185. package/lib/projects/__tests__/LocalDevProcess.test.js +231 -19
  186. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +89 -63
  187. package/lib/projects/__tests__/deploy.test.js +73 -8
  188. package/lib/projects/__tests__/localDevProjectHelpers.test.js +6 -2
  189. package/lib/projects/__tests__/platformVersion.test.js +8 -8
  190. package/lib/projects/__tests__/projects.test.js +12 -12
  191. package/lib/projects/__tests__/structure.test.js +3 -3
  192. package/lib/projects/__tests__/upload.test.d.ts +1 -0
  193. package/lib/projects/__tests__/upload.test.js +82 -0
  194. package/lib/projects/add/__tests__/legacyAddComponent.test.js +6 -6
  195. package/lib/projects/add/__tests__/v2AddComponent.test.d.ts +1 -0
  196. package/lib/projects/add/__tests__/{v3AddComponent.test.js → v2AddComponent.test.js} +39 -39
  197. package/lib/projects/add/{v3AddComponent.d.ts → v2AddComponent.d.ts} +1 -1
  198. package/lib/projects/add/{v3AddComponent.js → v2AddComponent.js} +5 -5
  199. package/lib/projects/create/__tests__/legacy.test.js +5 -5
  200. package/lib/projects/create/__tests__/v2.test.d.ts +1 -0
  201. package/lib/projects/create/__tests__/{v3.test.js → v2.test.js} +82 -7
  202. package/lib/projects/create/index.js +4 -4
  203. package/lib/projects/create/legacy.js +2 -2
  204. package/lib/projects/create/{v3.d.ts → v2.d.ts} +3 -3
  205. package/lib/projects/create/{v3.js → v2.js} +16 -13
  206. package/lib/projects/deploy.d.ts +1 -1
  207. package/lib/projects/deploy.js +2 -2
  208. package/lib/projects/localDev/AppDevModeInterface.d.ts +10 -1
  209. package/lib/projects/localDev/AppDevModeInterface.js +118 -89
  210. package/lib/projects/localDev/DevServerManager.d.ts +11 -29
  211. package/lib/projects/localDev/DevServerManager.js +19 -61
  212. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +40 -0
  213. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +120 -0
  214. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  215. package/lib/projects/localDev/LocalDevLogger.js +27 -6
  216. package/lib/projects/localDev/{LocalDevManager.js → LocalDevManager_DEPRECATED.js} +10 -11
  217. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  218. package/lib/projects/localDev/LocalDevProcess.js +93 -21
  219. package/lib/projects/localDev/LocalDevState.d.ts +12 -8
  220. package/lib/projects/localDev/LocalDevState.js +27 -17
  221. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +6 -1
  222. package/lib/projects/localDev/LocalDevWebsocketServer.js +94 -33
  223. package/lib/projects/localDev/helpers/account.d.ts +1 -1
  224. package/lib/projects/localDev/helpers/account.js +2 -2
  225. package/lib/projects/localDev/helpers/project.d.ts +3 -2
  226. package/lib/projects/localDev/helpers/project.js +49 -10
  227. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +7 -0
  228. package/lib/projects/localDev/localDevWebsocketServerUtils.js +19 -0
  229. package/lib/projects/platformVersion.d.ts +1 -1
  230. package/lib/projects/platformVersion.js +1 -1
  231. package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
  232. package/lib/projects/structure.js +6 -6
  233. package/lib/projects/upload.d.ts +1 -1
  234. package/lib/projects/upload.js +17 -8
  235. package/lib/projects/urls.d.ts +0 -1
  236. package/lib/projects/urls.js +0 -3
  237. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +1 -0
  238. package/lib/prompts/__tests__/projectAddPrompt.test.js +10 -10
  239. package/lib/prompts/accountNamePrompt.js +14 -19
  240. package/lib/prompts/accountsPrompt.js +2 -2
  241. package/lib/prompts/cmsFieldPrompt.js +2 -2
  242. package/lib/prompts/createApiSamplePrompt.js +5 -5
  243. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +10 -1
  244. package/lib/prompts/createFunctionPrompt.js +14 -14
  245. package/lib/prompts/createModulePrompt.js +9 -9
  246. package/lib/prompts/createTemplatePrompt.js +2 -2
  247. package/lib/prompts/downloadProjectPrompt.js +5 -8
  248. package/lib/prompts/installAppPrompt.d.ts +1 -6
  249. package/lib/prompts/installAppPrompt.js +1 -6
  250. package/lib/prompts/personalAccessKeyPrompt.js +3 -3
  251. package/lib/prompts/previewPrompt.js +6 -6
  252. package/lib/prompts/projectAddPrompt.d.ts +2 -2
  253. package/lib/prompts/projectAddPrompt.js +9 -2
  254. package/lib/prompts/projectDevTargetAccountPrompt.js +20 -32
  255. package/lib/prompts/projectNamePrompt.js +4 -8
  256. package/lib/prompts/projectsLogsPrompt.js +2 -4
  257. package/lib/prompts/promptUtils.js +30 -9
  258. package/lib/prompts/sandboxesPrompt.js +7 -7
  259. package/lib/prompts/secretPrompt.js +3 -3
  260. package/lib/prompts/selectAppPrompt.js +3 -3
  261. package/lib/prompts/selectHubDBTablePrompt.js +9 -13
  262. package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
  263. package/lib/prompts/selectPublicAppForMigrationPrompt.js +15 -19
  264. package/lib/prompts/setAsDefaultAccountPrompt.js +4 -8
  265. package/lib/prompts/uploadPrompt.js +5 -5
  266. package/lib/sandboxSync.js +24 -41
  267. package/lib/sandboxes.js +19 -47
  268. package/lib/schema.js +3 -3
  269. package/lib/serverlessLogs.js +11 -13
  270. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  271. package/lib/theme/__tests__/migrate.test.js +233 -0
  272. package/lib/theme/migrate.d.ts +13 -0
  273. package/lib/theme/migrate.js +90 -0
  274. package/lib/ui/SpinniesManager.d.ts +2 -0
  275. package/lib/ui/SpinniesManager.js +112 -8
  276. package/lib/ui/boxen.js +1 -2
  277. package/lib/ui/git.js +13 -10
  278. package/lib/ui/index.d.ts +4 -0
  279. package/lib/ui/index.js +47 -38
  280. package/lib/ui/serverlessFunctionLogs.js +9 -7
  281. package/lib/ui/uiMessages.d.ts +72 -0
  282. package/lib/ui/uiMessages.js +75 -0
  283. package/lib/usageTracking.js +8 -8
  284. package/lib/validation.js +20 -23
  285. package/lib/yargsUtils.d.ts +1 -1
  286. package/lib/yargsUtils.js +12 -5
  287. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  288. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  289. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  290. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  291. package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
  292. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  293. package/mcp-server/tools/cms/HsListTool.js +1 -1
  294. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  295. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  296. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  297. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
  298. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  299. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  300. package/mcp-server/tools/index.js +4 -0
  301. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  302. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  303. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  304. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  305. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  306. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  307. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  308. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  309. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +23 -0
  310. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +68 -0
  311. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +11 -0
  312. package/mcp-server/tools/project/GetApplicationInfoTool.js +49 -0
  313. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  314. package/mcp-server/tools/project/GetConfigValuesTool.js +13 -7
  315. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +2 -2
  316. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  317. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  318. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  319. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  320. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +2 -2
  321. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  322. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  323. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  324. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.d.ts +1 -0
  325. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +169 -0
  326. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.d.ts +1 -0
  327. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +115 -0
  328. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  329. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  330. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  331. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  332. package/mcp-server/tools/project/constants.d.ts +1 -1
  333. package/mcp-server/tools/project/constants.js +9 -3
  334. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  335. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  336. package/mcp-server/utils/cliConfig.d.ts +1 -0
  337. package/mcp-server/utils/cliConfig.js +12 -0
  338. package/mcp-server/utils/toolUsageTracking.js +2 -2
  339. package/package.json +8 -12
  340. package/types/LocalDev.d.ts +19 -3
  341. package/ui/components/HorizontalSelectPrompt.js +1 -1
  342. package/ui/index.js +1 -1
  343. package/commands/getStartedV2.d.ts +0 -9
  344. package/commands/getStartedV2.js +0 -39
  345. package/lib/middleware/__test__/utils.test.js +0 -51
  346. package/lib/middleware/utils.d.ts +0 -8
  347. package/lib/middleware/utils.js +0 -14
  348. package/lib/projects/localDev/DevServerManagerV2.d.ts +0 -22
  349. package/lib/projects/localDev/DevServerManagerV2.js +0 -81
  350. package/ui/components/Ascii.d.ts +0 -10
  351. package/ui/components/Ascii.js +0 -11
  352. package/ui/views/GetStarted.d.ts +0 -7
  353. package/ui/views/GetStarted.js +0 -157
  354. /package/{lib/middleware/__test__/utils.test.d.ts → commands/project/__tests__/list.test.d.ts} +0 -0
  355. /package/{lib/projects/add/__tests__/v3AddComponent.test.d.ts → commands/project/__tests__/validate.test.d.ts} +0 -0
  356. /package/lib/{projects/create/__tests__/v3.test.d.ts → middleware/__test__/commandTargetingUtils.test.d.ts} +0 -0
  357. /package/lib/projects/localDev/{LocalDevManager.d.ts → LocalDevManager_DEPRECATED.d.ts} +0 -0
@@ -0,0 +1,68 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
4
+ import { http } from '@hubspot/local-dev-lib/http';
5
+ import { formatTextContents } from '../../utils/content.js';
6
+ import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
7
+ import { getAccountId } from '@hubspot/local-dev-lib/config';
8
+ const inputSchema = {
9
+ appId: z
10
+ .string()
11
+ .regex(/^\d+$/, 'App ID must be a numeric string')
12
+ .describe('The numeric app ID as a string (e.g., "3003909"). Use get-applications-info to find available app IDs.'),
13
+ startDate: z
14
+ .string()
15
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format')
16
+ .optional()
17
+ .describe('Start date for the usage patterns query in ISO 8601 format (e.g., 2025-01-01).'),
18
+ endDate: z
19
+ .string()
20
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format')
21
+ .optional()
22
+ .describe('End date for the usage patterns query in ISO 8601 format (e.g., 2025-12-31).'),
23
+ };
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ const inputSchemaZodObject = z.object({ ...inputSchema });
26
+ const toolName = 'get-api-usage-patterns-by-app-id';
27
+ export class GetApiUsagePatternsByAppIdTool extends Tool {
28
+ constructor(mcpServer) {
29
+ super(mcpServer);
30
+ }
31
+ async handler({ appId, startDate, endDate, }) {
32
+ await trackToolUsage(toolName);
33
+ try {
34
+ // Get account ID from CLI config
35
+ const accountId = getAccountId();
36
+ if (!accountId) {
37
+ const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
38
+ return formatTextContents(authErrorMessage);
39
+ }
40
+ const response = await http.get(accountId, {
41
+ url: `app/feature/utilization/public/v3/insights/app/${appId}/usage-patterns`,
42
+ params: {
43
+ ...(startDate && { startDate }),
44
+ ...(endDate && { endDate }),
45
+ },
46
+ });
47
+ // Format the response for display
48
+ const { data } = response;
49
+ const formattedResult = JSON.stringify(data, null, 2);
50
+ return formatTextContents(formattedResult);
51
+ }
52
+ catch (error) {
53
+ if (isHubSpotHttpError(error)) {
54
+ // Handle HubSpot-specific HTTP errors
55
+ return formatTextContents(error.toString());
56
+ }
57
+ const errorMessage = `${error instanceof Error ? error.message : String(error)}`;
58
+ return formatTextContents(errorMessage);
59
+ }
60
+ }
61
+ register() {
62
+ return this.mcpServer.registerTool(toolName, {
63
+ title: 'Get API Usage Patterns by App ID',
64
+ description: 'Retrieves detailed API usage pattern analytics for a specific HubSpot application. Requires an appId (string) to identify the target application. Optionally accepts startDate and endDate parameters in YYYY-MM-DD format to filter results within a specific time range. Returns patternSummaries object containing usage statistics including portalPercentage (percentage of portals using this pattern) and numOfPortals (total count of portals) for different usage patterns. This data helps analyze how the application is being used across different HubSpot portals and can inform optimization decisions.',
65
+ inputSchema,
66
+ }, this.handler);
67
+ }
68
+ }
@@ -0,0 +1,11 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
5
+ export type GetApplicationInfoInputSchema = z.infer<typeof inputSchemaZodObject>;
6
+ export declare class GetApplicationInfoTool extends Tool<GetApplicationInfoInputSchema> {
7
+ constructor(mcpServer: McpServer);
8
+ handler({}: GetApplicationInfoInputSchema): Promise<TextContentResponse>;
9
+ register(): RegisteredTool;
10
+ }
11
+ export {};
@@ -0,0 +1,49 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
4
+ import { http } from '@hubspot/local-dev-lib/http';
5
+ import { formatTextContents } from '../../utils/content.js';
6
+ import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
7
+ import { getAccountId } from '@hubspot/local-dev-lib/config';
8
+ const inputSchema = {};
9
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
+ const inputSchemaZodObject = z.object({ ...inputSchema });
11
+ const toolName = 'get-applications-info';
12
+ export class GetApplicationInfoTool extends Tool {
13
+ constructor(mcpServer) {
14
+ super(mcpServer);
15
+ }
16
+ async handler({}) {
17
+ await trackToolUsage(toolName);
18
+ try {
19
+ // Get account ID from CLI config
20
+ const accountId = getAccountId();
21
+ if (!accountId) {
22
+ const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
23
+ return formatTextContents(authErrorMessage);
24
+ }
25
+ const response = await http.get(accountId, {
26
+ url: `app/feature/utilization/public/v3/insights/apps`,
27
+ });
28
+ // Format the response for display
29
+ const { data } = response;
30
+ const formattedResult = JSON.stringify(data, null, 2);
31
+ return formatTextContents(formattedResult);
32
+ }
33
+ catch (error) {
34
+ if (isHubSpotHttpError(error)) {
35
+ // Handle HubSpot-specific HTTP errors
36
+ return formatTextContents(error.toString());
37
+ }
38
+ const errorMessage = `${error instanceof Error ? error.message : String(error)}`;
39
+ return formatTextContents(errorMessage);
40
+ }
41
+ }
42
+ register() {
43
+ return this.mcpServer.registerTool(toolName, {
44
+ title: 'Get Applications Information',
45
+ description: 'Retrieves a list of all HubSpot applications available in the current account. Returns an array of applications, where each application contains an appId (numeric identifier) and appName (string). This information is useful for identifying available applications before using other tools that require specific application IDs, such as getting API usage patterns. No input parameters are required - this tool fetches all applications from the HubSpot Insights API.',
46
+ inputSchema,
47
+ }, this.handler);
48
+ }
49
+ }
@@ -2,19 +2,22 @@ import { TextContentResponse, Tool } from '../../types.js';
2
2
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { z } from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
5
6
  platformVersion: z.ZodString;
6
7
  featureType: z.ZodString;
7
8
  }, "strip", z.ZodTypeAny, {
8
9
  platformVersion: string;
10
+ absoluteCurrentWorkingDirectory: string;
9
11
  featureType: string;
10
12
  }, {
11
13
  platformVersion: string;
14
+ absoluteCurrentWorkingDirectory: string;
12
15
  featureType: string;
13
16
  }>;
14
17
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
18
  export declare class GetConfigValuesTool extends Tool<InputSchemaType> {
16
19
  constructor(mcpServer: McpServer);
17
- handler({ platformVersion, featureType, }: InputSchemaType): Promise<TextContentResponse>;
20
+ handler({ absoluteCurrentWorkingDirectory, platformVersion, featureType, }: InputSchemaType): Promise<TextContentResponse>;
18
21
  register(): RegisteredTool;
19
22
  }
20
23
  export {};
@@ -1,10 +1,12 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { formatTextContents } from '../../utils/content.js';
4
+ import { absoluteCurrentWorkingDirectory } from './constants.js';
4
5
  import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
5
- import { getAccountId, getConfigPath, loadConfig, } from '@hubspot/local-dev-lib/config';
6
- import { useV3Api } from '../../../lib/projects/platformVersion.js';
6
+ import { isV2Project } from '../../../lib/projects/platformVersion.js';
7
+ import { getAccountIdFromCliConfig } from '../../utils/cliConfig.js';
7
8
  const inputSchema = {
9
+ absoluteCurrentWorkingDirectory,
8
10
  platformVersion: z
9
11
  .string()
10
12
  .describe('The platform version for the project. Located in the hsproject.json file.'),
@@ -16,21 +18,25 @@ const inputSchema = {
16
18
  const inputSchemaZodObject = z.object({
17
19
  ...inputSchema,
18
20
  });
19
- const toolName = 'get-hubspot-feature-config-schema';
21
+ const toolName = 'get-feature-config-schema';
20
22
  export class GetConfigValuesTool extends Tool {
21
23
  constructor(mcpServer) {
22
24
  super(mcpServer);
23
25
  }
24
- async handler({ platformVersion, featureType, }) {
26
+ async handler({ absoluteCurrentWorkingDirectory, platformVersion, featureType, }) {
25
27
  try {
26
- if (!useV3Api(platformVersion)) {
28
+ if (!isV2Project(platformVersion)) {
27
29
  return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
28
30
  }
29
- loadConfig(getConfigPath());
31
+ const accountId = getAccountIdFromCliConfig(absoluteCurrentWorkingDirectory);
32
+ if (!accountId) {
33
+ const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
34
+ return formatTextContents(authErrorMessage);
35
+ }
30
36
  const schema = await getIntermediateRepresentationSchema({
31
37
  platformVersion,
32
38
  projectSourceDir: '',
33
- accountId: getAccountId(),
39
+ accountId,
34
40
  });
35
41
  const internalComponentType = mapToInternalType(featureType);
36
42
  if (schema[internalComponentType]) {
@@ -4,9 +4,9 @@ import { z } from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  command: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"hs init">, z.ZodLiteral<"hs auth">, z.ZodLiteral<"hs project create">, z.ZodLiteral<"hs project upload">]>>;
6
6
  }, "strip", z.ZodTypeAny, {
7
- command?: "hs auth" | "hs project upload" | "hs project create" | "hs init" | undefined;
7
+ command?: "hs auth" | "hs project create" | "hs project upload" | "hs init" | undefined;
8
8
  }, {
9
- command?: "hs auth" | "hs project upload" | "hs project create" | "hs init" | undefined;
9
+ command?: "hs auth" | "hs project create" | "hs project upload" | "hs init" | undefined;
10
10
  }>;
11
11
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
12
  export declare class GuidedWalkthroughTool extends Tool<InputSchemaType> {
@@ -24,7 +24,7 @@ const inputSchema = {
24
24
  const inputSchemaZodObject = z.object({
25
25
  ...inputSchema,
26
26
  });
27
- const toolName = 'guided-walkthrough-hubspot-cli';
27
+ const toolName = 'guided-walkthrough-cli';
28
28
  export class GuidedWalkthroughTool extends Tool {
29
29
  constructor(mcpServer) {
30
30
  super(mcpServer);
@@ -11,7 +11,7 @@ const inputSchema = {
11
11
  const inputSchemaZodObject = z.object({
12
12
  ...inputSchema,
13
13
  });
14
- const toolName = 'upload-hubspot-project';
14
+ const toolName = 'upload-project';
15
15
  export class UploadProjectTools extends Tool {
16
16
  constructor(mcpServer) {
17
17
  super(mcpServer);
@@ -24,7 +24,7 @@ export class UploadProjectTools extends Tool {
24
24
  register() {
25
25
  return this.mcpServer.registerTool(toolName, {
26
26
  title: 'Upload HubSpot Project',
27
- description: 'Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory. DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive',
27
+ description: 'DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive. Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.',
28
28
  inputSchema,
29
29
  }, this.handler);
30
30
  }
@@ -9,7 +9,7 @@ const inputSchema = {
9
9
  };
10
10
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
11
  const inputSchemaZodObject = z.object({ ...inputSchema });
12
- const toolName = 'validate-hubspot-project';
12
+ const toolName = 'validate-project';
13
13
  export class ValidateProjectTool extends Tool {
14
14
  constructor(mcpServer) {
15
15
  super(mcpServer);
@@ -28,7 +28,7 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
28
28
  describe('register', () => {
29
29
  it('should register tool with correct parameters', () => {
30
30
  const result = tool.register();
31
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-hubspot-project', expect.objectContaining({
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-project', expect.objectContaining({
32
32
  title: 'Add feature to HubSpot Project',
33
33
  description: expect.stringContaining('Adds a feature to an existing HubSpot project'),
34
34
  inputSchema: expect.any(Object),
@@ -6,7 +6,7 @@ vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
6
  vi.mock('../../../utils/project');
7
7
  vi.mock('../../../utils/command');
8
8
  vi.mock('../../../../lib/constants');
9
- vi.mock('../../../../lib/projects/create/v3');
9
+ vi.mock('../../../../lib/projects/create/v2');
10
10
  vi.mock('../../../utils/toolUsageTracking');
11
11
  const mockRunCommandInDir = runCommandInDir;
12
12
  const mockAddFlag = addFlag;
@@ -29,7 +29,7 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
29
29
  describe('register', () => {
30
30
  it('should register tool with correct parameters', () => {
31
31
  const result = tool.register();
32
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-hubspot-project', {
32
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-project', {
33
33
  title: 'Create HubSpot Project',
34
34
  description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
35
35
  inputSchema: expect.any(Object),
@@ -26,7 +26,7 @@ describe('mcp-server/tools/project/DeployProject', () => {
26
26
  describe('register', () => {
27
27
  it('should register tool with correct parameters', () => {
28
28
  const result = tool.register();
29
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-hubspot-project', {
29
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-project', {
30
30
  title: 'Deploy a build of HubSpot Project',
31
31
  description: expect.stringContaining('Takes a build number and a project name and deploys that build of the project'),
32
32
  inputSchema: expect.any(Object),
@@ -24,9 +24,9 @@ describe('mcp-server/tools/project/DocFetchTool', () => {
24
24
  describe('register', () => {
25
25
  it('should register tool with correct parameters', () => {
26
26
  const result = tool.register();
27
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-hubspot-doc', {
27
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-doc', {
28
28
  title: 'Fetch HubSpot Developer Documentation (single file)',
29
- description: 'Always use this immediately after `search-hubspot-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
29
+ description: 'Always use this immediately after `search-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
30
30
  inputSchema: expect.any(Object),
31
31
  }, tool.handler);
32
32
  expect(result).toBe(mockRegisteredTool);
@@ -1,14 +1,14 @@
1
1
  import { DocsSearchTool } from '../DocsSearchTool.js';
2
2
  import { http } from '@hubspot/local-dev-lib/http';
3
- import { getAccountId } from '@hubspot/local-dev-lib/config';
4
3
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
4
+ import { getAccountIdFromCliConfig } from '../../../utils/cliConfig.js';
5
5
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
6
  vi.mock('@hubspot/local-dev-lib/http');
7
- vi.mock('@hubspot/local-dev-lib/config');
8
7
  vi.mock('@hubspot/local-dev-lib/errors/index');
9
8
  vi.mock('../../../utils/toolUsageTracking');
9
+ vi.mock('../../../utils/cliConfig.js');
10
10
  const mockHttp = http;
11
- const mockGetAccountId = getAccountId;
11
+ const mockGetAccountIdFromCliConfig = getAccountIdFromCliConfig;
12
12
  const mockIsHubSpotHttpError = vi.mocked(isHubSpotHttpError);
13
13
  describe('mcp-server/tools/project/DocsSearchTool', () => {
14
14
  let mockMcpServer;
@@ -27,9 +27,9 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
27
27
  describe('register', () => {
28
28
  it('should register tool with correct parameters', () => {
29
29
  const result = tool.register();
30
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-hubspot-docs', {
30
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-docs', {
31
31
  title: 'Search HubSpot Developer Documentation',
32
- description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-hubspot-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
32
+ description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
33
33
  inputSchema: expect.any(Object),
34
34
  }, tool.handler);
35
35
  expect(result).toBe(mockRegisteredTool);
@@ -38,9 +38,10 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
38
38
  describe('handler', () => {
39
39
  const mockInput = {
40
40
  docsSearchQuery: 'test query',
41
+ absoluteCurrentWorkingDirectory: '/foo',
41
42
  };
42
43
  it('should return auth error message when no account ID is found', async () => {
43
- mockGetAccountId.mockReturnValue(null);
44
+ mockGetAccountIdFromCliConfig.mockReturnValue(null);
44
45
  const result = await tool.handler(mockInput);
45
46
  expect(result).toEqual({
46
47
  content: [
@@ -52,7 +53,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
52
53
  });
53
54
  });
54
55
  it('should return successful results when docs are found', async () => {
55
- mockGetAccountId.mockReturnValue(12345);
56
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
56
57
  const mockResponse = {
57
58
  results: [
58
59
  {
@@ -76,6 +77,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
76
77
  data: mockResponse,
77
78
  });
78
79
  const result = await tool.handler(mockInput);
80
+ expect(mockGetAccountIdFromCliConfig).toHaveBeenCalledWith('/foo');
79
81
  expect(mockHttp.post).toHaveBeenCalledWith(12345, {
80
82
  url: 'dev/docs/llms/v1/docs-search',
81
83
  data: {
@@ -103,7 +105,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
103
105
  expect(resultText).toContain('Test content 2');
104
106
  });
105
107
  it('should return no results message when no documentation is found', async () => {
106
- mockGetAccountId.mockReturnValue(12345);
108
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
107
109
  const mockResponse = {
108
110
  results: [],
109
111
  };
@@ -122,7 +124,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
122
124
  });
123
125
  });
124
126
  it('should return no results message when results is null', async () => {
125
- mockGetAccountId.mockReturnValue(12345);
127
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
126
128
  const mockResponse = {
127
129
  results: null,
128
130
  };
@@ -141,7 +143,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
141
143
  });
142
144
  });
143
145
  it('should handle HubSpot HTTP errors', async () => {
144
- mockGetAccountId.mockReturnValue(12345);
146
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
145
147
  const mockError = {
146
148
  toString: () => 'HubSpot API Error: 404 Not Found',
147
149
  };
@@ -158,7 +160,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
158
160
  });
159
161
  });
160
162
  it('should handle generic errors', async () => {
161
- mockGetAccountId.mockReturnValue(12345);
163
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
162
164
  const mockError = new Error('Network error');
163
165
  mockHttp.post.mockRejectedValue(mockError);
164
166
  mockIsHubSpotHttpError.mockReturnValue(false);
@@ -173,7 +175,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
173
175
  });
174
176
  });
175
177
  it('should handle non-Error rejections', async () => {
176
- mockGetAccountId.mockReturnValue(12345);
178
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
177
179
  mockHttp.post.mockRejectedValue('String error');
178
180
  mockIsHubSpotHttpError.mockReturnValue(false);
179
181
  const result = await tool.handler(mockInput);
@@ -0,0 +1,169 @@
1
+ import { GetApiUsagePatternsByAppIdTool } from '../GetApiUsagePatternsByAppIdTool.js';
2
+ import { z } from 'zod';
3
+ import { getAccountId } from '@hubspot/local-dev-lib/config';
4
+ import { http } from '@hubspot/local-dev-lib/http';
5
+ import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
6
+ vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
7
+ vi.mock('../../../utils/toolUsageTracking');
8
+ vi.mock('@hubspot/local-dev-lib/http');
9
+ vi.mock('@hubspot/local-dev-lib/errors/index');
10
+ vi.mock('@hubspot/local-dev-lib/config');
11
+ const mockGetAccountId = getAccountId;
12
+ const mockHttp = http;
13
+ const mockIsHubSpotHttpError = isHubSpotHttpError;
14
+ describe('mcp-server/tools/project/GetApiUsagePatternsByAppIdTool', () => {
15
+ let mockMcpServer;
16
+ let tool;
17
+ let mockRegisteredTool;
18
+ beforeEach(() => {
19
+ vi.clearAllMocks();
20
+ // @ts-expect-error Not mocking the whole thing
21
+ mockMcpServer = {
22
+ registerTool: vi.fn(),
23
+ };
24
+ mockRegisteredTool = {};
25
+ mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
26
+ tool = new GetApiUsagePatternsByAppIdTool(mockMcpServer);
27
+ });
28
+ describe('register', () => {
29
+ it('should register tool with correct parameters', () => {
30
+ const result = tool.register();
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-api-usage-patterns-by-app-id', {
32
+ title: 'Get API Usage Patterns by App ID',
33
+ description: 'Retrieves detailed API usage pattern analytics for a specific HubSpot application. Requires an appId (string) to identify the target application. Optionally accepts startDate and endDate parameters in YYYY-MM-DD format to filter results within a specific time range. Returns patternSummaries object containing usage statistics including portalPercentage (percentage of portals using this pattern) and numOfPortals (total count of portals) for different usage patterns. This data helps analyze how the application is being used across different HubSpot portals and can inform optimization decisions.',
34
+ inputSchema: expect.objectContaining({
35
+ appId: expect.objectContaining({
36
+ describe: expect.any(Function),
37
+ }),
38
+ startDate: expect.objectContaining({
39
+ optional: expect.any(Function),
40
+ }),
41
+ endDate: expect.objectContaining({
42
+ optional: expect.any(Function),
43
+ }),
44
+ }),
45
+ }, tool.handler);
46
+ expect(result).toBe(mockRegisteredTool);
47
+ });
48
+ });
49
+ describe('input validation', () => {
50
+ const inputSchema = z.object({
51
+ appId: z
52
+ .string()
53
+ .describe('The application ID to get API usage patterns for.'),
54
+ startDate: z
55
+ .string()
56
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format')
57
+ .optional()
58
+ .describe('Start date for the usage patterns query in ISO 8601 format (e.g., 2025-01-01).'),
59
+ endDate: z
60
+ .string()
61
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format')
62
+ .optional()
63
+ .describe('End date for the usage patterns query in ISO 8601 format (e.g., 2025-12-31).'),
64
+ });
65
+ it('should validate date format correctly', () => {
66
+ const validInput = {
67
+ appId: '12345',
68
+ startDate: '2025-01-01',
69
+ endDate: '2025-12-31',
70
+ };
71
+ const invalidInput = {
72
+ appId: '12345',
73
+ startDate: '2025-1-1',
74
+ endDate: '2025-12-31T00:00:00Z',
75
+ };
76
+ expect(() => inputSchema.parse(validInput)).not.toThrow();
77
+ expect(() => inputSchema.parse(invalidInput)).toThrow();
78
+ });
79
+ });
80
+ describe('handler', () => {
81
+ const input = {
82
+ appId: '12345',
83
+ startDate: '2025-01-01',
84
+ endDate: '2025-12-31',
85
+ };
86
+ beforeEach(() => {
87
+ mockGetAccountId.mockReturnValue(123456789);
88
+ mockIsHubSpotHttpError.mockReturnValue(false);
89
+ });
90
+ it('should return API usage patterns successfully', async () => {
91
+ const mockResponse = {
92
+ data: {
93
+ patternSummaries: {
94
+ additionalProp1: {
95
+ portalPercentage: 25.5,
96
+ numOfPortals: 150,
97
+ },
98
+ additionalProp2: {
99
+ portalPercentage: 18.2,
100
+ numOfPortals: 89,
101
+ },
102
+ additionalProp3: {
103
+ portalPercentage: 32.1,
104
+ numOfPortals: 201,
105
+ },
106
+ },
107
+ },
108
+ status: 200,
109
+ statusText: 'OK',
110
+ headers: {},
111
+ config: { headers: {} },
112
+ };
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ mockHttp.get.mockResolvedValue(mockResponse);
115
+ const result = await tool.handler(input);
116
+ expect(mockGetAccountId).toHaveBeenCalledWith();
117
+ expect(mockHttp.get).toHaveBeenCalledWith(123456789, {
118
+ url: 'app/feature/utilization/public/v3/insights/app/12345/usage-patterns',
119
+ params: {
120
+ startDate: '2025-01-01',
121
+ endDate: '2025-12-31',
122
+ },
123
+ });
124
+ expect(result).toEqual({
125
+ content: [
126
+ {
127
+ type: 'text',
128
+ text: JSON.stringify(mockResponse.data, null, 2),
129
+ },
130
+ ],
131
+ });
132
+ });
133
+ it('should return error when account ID cannot be determined', async () => {
134
+ mockGetAccountId.mockReturnValue(null);
135
+ const result = await tool.handler(input);
136
+ expect(result).toEqual({
137
+ content: [
138
+ {
139
+ type: 'text',
140
+ text: 'No account ID found. Please run `hs account auth` to configure an account, or set a default account with `hs account use <account>`',
141
+ },
142
+ ],
143
+ });
144
+ expect(mockHttp.get).not.toHaveBeenCalled();
145
+ });
146
+ it('should handle empty usage patterns response', async () => {
147
+ const mockResponse = {
148
+ data: {
149
+ patternSummaries: {},
150
+ },
151
+ status: 200,
152
+ statusText: 'OK',
153
+ headers: {},
154
+ config: { headers: {} },
155
+ };
156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
+ mockHttp.get.mockResolvedValue(mockResponse);
158
+ const result = await tool.handler(input);
159
+ expect(result).toEqual({
160
+ content: [
161
+ {
162
+ type: 'text',
163
+ text: JSON.stringify(mockResponse.data, null, 2),
164
+ },
165
+ ],
166
+ });
167
+ });
168
+ });
169
+ });