@hubspot/cli 7.9.0-experimental.0 → 7.9.1-experimental.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 (196) hide show
  1. package/bin/cli.js +5 -4
  2. package/commands/__tests__/getStarted.test.js +10 -0
  3. package/commands/account/__tests__/rename.test.js +42 -0
  4. package/commands/account/auth.js +10 -14
  5. package/commands/account/clean.js +11 -19
  6. package/commands/account/createOverride.js +15 -11
  7. package/commands/account/info.js +8 -5
  8. package/commands/account/list.js +13 -18
  9. package/commands/account/remove.js +23 -22
  10. package/commands/account/removeOverride.js +6 -6
  11. package/commands/account/rename.d.ts +1 -1
  12. package/commands/account/rename.js +6 -3
  13. package/commands/account/use.js +19 -8
  14. package/commands/app/__tests__/migrate.test.js +8 -4
  15. package/commands/app/migrate.js +2 -2
  16. package/commands/auth.js +18 -14
  17. package/commands/config/migrate.js +5 -5
  18. package/commands/config/set.js +1 -2
  19. package/commands/customObject/createSchema.js +2 -3
  20. package/commands/customObject/updateSchema.js +2 -3
  21. package/commands/getStarted.js +10 -5
  22. package/commands/hubdb/__tests__/list.test.js +1 -0
  23. package/commands/hubdb/list.js +2 -2
  24. package/commands/hubdb.d.ts +1 -1
  25. package/commands/init.js +36 -32
  26. package/commands/project/__tests__/deploy.test.js +10 -5
  27. package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
  28. package/commands/project/__tests__/logs.test.js +4 -0
  29. package/commands/project/__tests__/validate.test.js +2 -2
  30. package/commands/project/cloneApp.js +2 -2
  31. package/commands/project/deploy.js +2 -2
  32. package/commands/project/dev/deprecatedFlow.js +4 -5
  33. package/commands/project/dev/index.js +14 -4
  34. package/commands/project/dev/unifiedFlow.js +4 -5
  35. package/commands/project/listBuilds.js +7 -1
  36. package/commands/project/logs.js +2 -3
  37. package/commands/project/profile/add.js +6 -7
  38. package/commands/project/profile/delete.js +2 -2
  39. package/commands/project/upload.js +9 -3
  40. package/commands/project/validate.js +9 -3
  41. package/commands/project/watch.js +7 -2
  42. package/commands/sandbox/__tests__/create.test.js +14 -5
  43. package/commands/sandbox/create.js +4 -5
  44. package/commands/sandbox/delete.js +23 -20
  45. package/commands/testAccount/__tests__/create.test.js +68 -0
  46. package/commands/testAccount/create.d.ts +8 -0
  47. package/commands/testAccount/create.js +134 -44
  48. package/commands/testAccount/delete.js +9 -8
  49. package/commands/testAccount/importData.d.ts +1 -1
  50. package/lang/en.d.ts +3204 -3205
  51. package/lang/en.js +33 -9
  52. package/lib/__tests__/buildAccount.test.js +22 -30
  53. package/lib/__tests__/commonOpts.test.js +9 -13
  54. package/lib/__tests__/developerTestAccounts.test.js +29 -17
  55. package/lib/__tests__/importData.test.js +20 -10
  56. package/lib/__tests__/oauth.test.js +19 -8
  57. package/lib/__tests__/sandboxSync.test.js +33 -11
  58. package/lib/__tests__/sandboxes.test.js +30 -19
  59. package/lib/__tests__/usageTracking.test.js +10 -10
  60. package/lib/__tests__/validation.test.js +32 -32
  61. package/lib/accountTypes.d.ts +9 -9
  62. package/lib/accountTypes.js +2 -4
  63. package/lib/app/__tests__/migrate.test.js +15 -0
  64. package/lib/app/__tests__/migrate_legacy.test.js +9 -0
  65. package/lib/app/migrate_legacy.d.ts +2 -2
  66. package/lib/buildAccount.d.ts +4 -4
  67. package/lib/buildAccount.js +7 -14
  68. package/lib/commonOpts.js +3 -3
  69. package/lib/configMigrate.d.ts +2 -2
  70. package/lib/configMigrate.js +42 -18
  71. package/lib/configOptions.js +3 -2
  72. package/lib/constants.d.ts +1 -0
  73. package/lib/constants.js +6 -0
  74. package/lib/developerTestAccounts.d.ts +3 -3
  75. package/lib/developerTestAccounts.js +4 -7
  76. package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
  77. package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
  78. package/lib/doctor/Doctor.js +4 -3
  79. package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
  80. package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
  81. package/lib/doctor/__tests__/Doctor.test.js +14 -0
  82. package/lib/importData.js +8 -7
  83. package/lib/links.js +5 -5
  84. package/lib/mcp/__tests__/setup.test.js +127 -0
  85. package/lib/mcp/setup.d.ts +4 -12
  86. package/lib/mcp/setup.js +34 -1
  87. package/lib/middleware/__test__/commandTargetingUtils.test.js +3 -3
  88. package/lib/middleware/__test__/configMiddleware.test.js +23 -22
  89. package/lib/middleware/__test__/gitMiddleware.test.js +9 -7
  90. package/lib/middleware/autoUpdateMiddleware.d.ts +3 -1
  91. package/lib/middleware/autoUpdateMiddleware.js +10 -2
  92. package/lib/middleware/commandTargetingUtils.js +2 -2
  93. package/lib/middleware/configMiddleware.d.ts +6 -1
  94. package/lib/middleware/configMiddleware.js +36 -15
  95. package/lib/middleware/gitMiddleware.js +8 -4
  96. package/lib/oauth.d.ts +2 -2
  97. package/lib/oauth.js +8 -10
  98. package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
  99. package/lib/projects/__tests__/DevServerManager.test.js +1 -0
  100. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  101. package/lib/projects/__tests__/components.test.js +148 -24
  102. package/lib/projects/__tests__/deploy.test.js +1 -0
  103. package/lib/projects/__tests__/projects.test.js +13 -42
  104. package/lib/projects/components.js +76 -20
  105. package/lib/projects/config.js +5 -9
  106. package/lib/projects/create/__tests__/v2.test.js +11 -0
  107. package/lib/projects/localDev/AppDevModeInterface.js +2 -2
  108. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  109. package/lib/projects/localDev/LocalDevLogger.js +4 -4
  110. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
  111. package/lib/projects/localDev/helpers/account.d.ts +10 -10
  112. package/lib/projects/localDev/helpers/account.js +6 -11
  113. package/lib/projects/urls.js +5 -6
  114. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.d.ts +1 -0
  115. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.js +153 -0
  116. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
  117. package/lib/prompts/accountNamePrompt.js +3 -3
  118. package/lib/prompts/accountsPrompt.d.ts +1 -1
  119. package/lib/prompts/accountsPrompt.js +6 -7
  120. package/lib/prompts/confirmImportDataPrompt.js +2 -2
  121. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +5 -0
  122. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +76 -66
  123. package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
  124. package/lib/prompts/downloadProjectPrompt.js +5 -2
  125. package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
  126. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  127. package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
  128. package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
  129. package/lib/prompts/sandboxesPrompt.js +7 -8
  130. package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
  131. package/lib/sandboxSync.d.ts +2 -2
  132. package/lib/sandboxSync.js +3 -9
  133. package/lib/sandboxes.d.ts +4 -4
  134. package/lib/sandboxes.js +6 -11
  135. package/lib/serverlessLogs.js +2 -2
  136. package/lib/theme/__tests__/migrate.test.js +15 -0
  137. package/lib/ui/index.js +6 -3
  138. package/lib/usageTracking.js +15 -8
  139. package/lib/validation.js +13 -11
  140. package/mcp-server/tools/cms/HsCreateFunctionTool.js +8 -2
  141. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +4 -4
  142. package/mcp-server/tools/cms/HsCreateModuleTool.js +8 -2
  143. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -2
  144. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +4 -4
  145. package/mcp-server/tools/cms/HsFunctionLogsTool.js +6 -2
  146. package/mcp-server/tools/cms/HsListFunctionsTool.js +5 -1
  147. package/mcp-server/tools/cms/HsListTool.js +5 -1
  148. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
  149. package/mcp-server/tools/index.js +4 -0
  150. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  151. package/mcp-server/tools/project/AddFeatureToProjectTool.js +9 -3
  152. package/mcp-server/tools/project/CreateProjectTool.js +8 -2
  153. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +41 -0
  154. package/mcp-server/tools/project/CreateTestAccountTool.js +150 -0
  155. package/mcp-server/tools/project/DeployProjectTool.d.ts +1 -1
  156. package/mcp-server/tools/project/DeployProjectTool.js +8 -2
  157. package/mcp-server/tools/project/DocFetchTool.d.ts +1 -1
  158. package/mcp-server/tools/project/DocFetchTool.js +9 -5
  159. package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
  160. package/mcp-server/tools/project/DocsSearchTool.js +12 -8
  161. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
  162. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +11 -7
  163. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +1 -1
  164. package/mcp-server/tools/project/GetApplicationInfoTool.js +11 -7
  165. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
  166. package/mcp-server/tools/project/GetBuildStatusTool.js +164 -0
  167. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
  168. package/mcp-server/tools/project/GetConfigValuesTool.js +11 -7
  169. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
  170. package/mcp-server/tools/project/GuidedWalkthroughTool.js +7 -3
  171. package/mcp-server/tools/project/UploadProjectTools.d.ts +9 -3
  172. package/mcp-server/tools/project/UploadProjectTools.js +51 -5
  173. package/mcp-server/tools/project/ValidateProjectTool.d.ts +1 -1
  174. package/mcp-server/tools/project/ValidateProjectTool.js +7 -3
  175. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.d.ts +1 -0
  176. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +454 -0
  177. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
  178. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +25 -13
  179. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
  180. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
  181. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
  182. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
  183. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
  184. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +56 -4
  185. package/mcp-server/utils/__tests__/content.test.js +21 -20
  186. package/mcp-server/utils/__tests__/feedbackTracking.test.js +33 -28
  187. package/mcp-server/utils/content.d.ts +1 -1
  188. package/mcp-server/utils/content.js +2 -2
  189. package/mcp-server/utils/feedbackTracking.d.ts +1 -1
  190. package/mcp-server/utils/feedbackTracking.js +3 -3
  191. package/mcp-server/utils/toolUsageTracking.js +4 -3
  192. package/package.json +8 -7
  193. package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
  194. package/mcp-server/utils/cliConfig.d.ts +0 -1
  195. package/mcp-server/utils/cliConfig.js +0 -12
  196. /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → lib/mcp/__tests__/setup.test.d.ts} +0 -0
package/lib/validation.js CHANGED
@@ -4,16 +4,17 @@ import { uiLogger } from '../lib/ui/logger.js';
4
4
  import { CMS_PUBLISH_MODE } from '@hubspot/local-dev-lib/constants/files';
5
5
  import { API_KEY_AUTH_METHOD, OAUTH_AUTH_METHOD, PERSONAL_ACCESS_KEY_AUTH_METHOD, } from '@hubspot/local-dev-lib/constants/auth';
6
6
  import { commaSeparatedValues } from '@hubspot/local-dev-lib/text';
7
- import { getConfigPath, getAccountConfig, getAccountId, loadConfigFromEnvironment, } from '@hubspot/local-dev-lib/config';
7
+ import { getConfigFilePath, getConfigAccountById, } from '@hubspot/local-dev-lib/config';
8
8
  import { getOauthManager } from '@hubspot/local-dev-lib/oauth';
9
9
  import { accessTokenForPersonalAccessKey } from '@hubspot/local-dev-lib/personalAccessKey';
10
10
  import { getAbsoluteFilePath, getCwd, getExt, } from '@hubspot/local-dev-lib/path';
11
+ import { ENVIRONMENT_VARIABLES } from '@hubspot/local-dev-lib/constants/config';
11
12
  import { getCmsPublishMode } from './commonOpts.js';
12
13
  import { logError } from './errorHandlers/index.js';
13
14
  import { lib } from '../lang/en.js';
14
15
  export async function validateAccount(options) {
15
16
  const { derivedAccountId, userProvidedAccount } = options;
16
- const accountId = getAccountId(derivedAccountId);
17
+ const accountId = derivedAccountId;
17
18
  if (!accountId) {
18
19
  if (userProvidedAccount) {
19
20
  uiLogger.error(lib.validation.accountNotFoundInConfig(userProvidedAccount));
@@ -23,33 +24,34 @@ export async function validateAccount(options) {
23
24
  }
24
25
  return false;
25
26
  }
26
- if (userProvidedAccount && loadConfigFromEnvironment()) {
27
+ if (userProvidedAccount &&
28
+ process.env[ENVIRONMENT_VARIABLES.USE_ENVIRONMENT_HUBSPOT_CONFIG]) {
27
29
  throw new Error(lib.validation.userProvidedAccount);
28
30
  }
29
- const accountConfig = getAccountConfig(accountId);
31
+ const accountConfig = getConfigAccountById(accountId);
30
32
  if (!accountConfig) {
31
33
  uiLogger.error(lib.validation.accountNotConfigured(accountId));
32
34
  return false;
33
35
  }
34
- const { authType, auth, apiKey, personalAccessKey } = accountConfig;
36
+ const { authType } = accountConfig;
35
37
  if (typeof authType === 'string' && authType !== authType.toLowerCase()) {
36
- uiLogger.error(lib.validation.invalidAuthType(authType, accountId, getConfigPath() || '', commaSeparatedValues([
38
+ uiLogger.error(lib.validation.invalidAuthType(authType, accountId, getConfigFilePath(), commaSeparatedValues([
37
39
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
38
40
  OAUTH_AUTH_METHOD,
39
41
  API_KEY_AUTH_METHOD,
40
42
  ].map(method => method.value))));
41
43
  }
42
44
  if (authType === 'oauth2') {
43
- if (typeof auth !== 'object') {
45
+ if (typeof accountConfig.auth !== 'object') {
44
46
  uiLogger.error(lib.validation.oauth2ConfigMissing(accountId));
45
47
  return false;
46
48
  }
47
- const { clientId, clientSecret, tokenInfo } = auth;
49
+ const { clientId, clientSecret, tokenInfo } = accountConfig.auth;
48
50
  if (!clientId || !clientSecret || !tokenInfo || !tokenInfo.refreshToken) {
49
51
  uiLogger.error(lib.validation.oauth2ConfigIncorrect(accountId));
50
52
  return false;
51
53
  }
52
- const oauth = getOauthManager(accountId, accountConfig);
54
+ const oauth = getOauthManager(accountConfig);
53
55
  try {
54
56
  let accessToken;
55
57
  if (oauth) {
@@ -66,7 +68,7 @@ export async function validateAccount(options) {
66
68
  }
67
69
  }
68
70
  else if (authType === 'personalaccesskey') {
69
- if (!personalAccessKey) {
71
+ if (!accountConfig.personalAccessKey) {
70
72
  uiLogger.error(lib.validation.personalAccessKeyMissing(accountId));
71
73
  return false;
72
74
  }
@@ -82,7 +84,7 @@ export async function validateAccount(options) {
82
84
  return false;
83
85
  }
84
86
  }
85
- else if (!apiKey) {
87
+ else if (!accountConfig.apiKey) {
86
88
  uiLogger.error(lib.validation.authConfigurationMissing(accountId));
87
89
  return false;
88
90
  }
@@ -80,10 +80,10 @@ export class HsCreateFunctionTool extends Tool {
80
80
  }
81
81
  try {
82
82
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
83
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
83
+ return formatTextContents(stdout, stderr);
84
84
  }
85
85
  catch (error) {
86
- return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
86
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
87
87
  }
88
88
  }
89
89
  register() {
@@ -91,6 +91,12 @@ export class HsCreateFunctionTool extends Tool {
91
91
  title: 'Create HubSpot CMS Serverless Function',
92
92
  description: `Creates a new HubSpot CMS serverless function using the hs create function command. Functions can be created non-interactively by specifying functionsFolder, filename, and endpointPath. Supports all HTTP methods (${HTTP_METHODS.join(', ')}).`,
93
93
  inputSchema,
94
+ annotations: {
95
+ readOnlyHint: false,
96
+ destructiveHint: false,
97
+ idempotentHint: false,
98
+ openWorldHint: false,
99
+ },
94
100
  }, this.handler);
95
101
  }
96
102
  }
@@ -13,20 +13,20 @@ declare const inputSchemaZodObject: z.ZodObject<{
13
13
  }, "strip", z.ZodTypeAny, {
14
14
  absoluteCurrentWorkingDirectory: string;
15
15
  dest?: string | undefined;
16
+ global?: boolean | undefined;
16
17
  moduleLabel?: string | undefined;
17
18
  reactType?: boolean | undefined;
18
- global?: boolean | undefined;
19
- availableForNewContent?: boolean | undefined;
20
19
  contentTypes?: string | undefined;
20
+ availableForNewContent?: boolean | undefined;
21
21
  userSuppliedName?: string | undefined;
22
22
  }, {
23
23
  absoluteCurrentWorkingDirectory: string;
24
24
  dest?: string | undefined;
25
+ global?: boolean | undefined;
25
26
  moduleLabel?: string | undefined;
26
27
  reactType?: boolean | undefined;
27
- global?: boolean | undefined;
28
- availableForNewContent?: boolean | undefined;
29
28
  contentTypes?: string | undefined;
29
+ availableForNewContent?: boolean | undefined;
30
30
  userSuppliedName?: string | undefined;
31
31
  }>;
32
32
  export type HsCreateModuleInputSchema = z.infer<typeof inputSchemaZodObject>;
@@ -102,10 +102,10 @@ export class HsCreateModuleTool extends Tool {
102
102
  }
103
103
  try {
104
104
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
105
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
105
+ return formatTextContents(stdout, stderr);
106
106
  }
107
107
  catch (error) {
108
- return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
108
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
109
109
  }
110
110
  }
111
111
  register() {
@@ -113,6 +113,12 @@ export class HsCreateModuleTool extends Tool {
113
113
  title: 'Create HubSpot CMS Module',
114
114
  description: 'Creates a new HubSpot CMS module using the hs create module command. Modules can be created non-interactively by specifying moduleLabel and other module options. You can create either HubL or React modules by setting the reactType parameter.',
115
115
  inputSchema,
116
+ annotations: {
117
+ readOnlyHint: false,
118
+ destructiveHint: false,
119
+ idempotentHint: false,
120
+ openWorldHint: false,
121
+ },
116
122
  }, this.handler);
117
123
  }
118
124
  }
@@ -59,10 +59,10 @@ export class HsCreateTemplateTool extends Tool {
59
59
  }
60
60
  try {
61
61
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
62
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
62
+ return formatTextContents(stdout, stderr);
63
63
  }
64
64
  catch (error) {
65
- return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
65
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
66
66
  }
67
67
  }
68
68
  register() {
@@ -70,6 +70,12 @@ export class HsCreateTemplateTool extends Tool {
70
70
  title: 'Create HubSpot CMS Template',
71
71
  description: `Creates a new HubSpot CMS template using the hs create template command. Templates can be created non-interactively by specifying templateType. Supports all template types including: ${TEMPLATE_TYPES.join(', ')}.`,
72
72
  inputSchema,
73
+ annotations: {
74
+ readOnlyHint: false,
75
+ destructiveHint: false,
76
+ idempotentHint: false,
77
+ openWorldHint: false,
78
+ },
73
79
  }, this.handler);
74
80
  }
75
81
  }
@@ -12,16 +12,16 @@ declare const inputSchemaZodObject: z.ZodObject<{
12
12
  endpoint: string;
13
13
  absoluteCurrentWorkingDirectory: string;
14
14
  account?: string | undefined;
15
- limit?: number | undefined;
16
- compact?: boolean | undefined;
17
15
  latest?: boolean | undefined;
16
+ compact?: boolean | undefined;
17
+ limit?: number | undefined;
18
18
  }, {
19
19
  endpoint: string;
20
20
  absoluteCurrentWorkingDirectory: string;
21
21
  account?: string | undefined;
22
- limit?: number | undefined;
23
- compact?: boolean | undefined;
24
22
  latest?: boolean | undefined;
23
+ compact?: boolean | undefined;
24
+ limit?: number | undefined;
25
25
  }>;
26
26
  export type HsFunctionLogsInputSchema = z.infer<typeof inputSchemaZodObject>;
27
27
  export declare class HsFunctionLogsTool extends Tool<HsFunctionLogsInputSchema> {
@@ -52,11 +52,11 @@ export class HsFunctionLogsTool extends Tool {
52
52
  }
53
53
  try {
54
54
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
55
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
55
+ return formatTextContents(stdout, stderr);
56
56
  }
57
57
  catch (error) {
58
58
  const errorMessage = error instanceof Error ? error.message : String(error);
59
- return formatTextContents(absoluteCurrentWorkingDirectory, `Error executing hs logs command: ${errorMessage}`);
59
+ return formatTextContents(`Error executing hs logs command: ${errorMessage}`);
60
60
  }
61
61
  }
62
62
  register() {
@@ -64,6 +64,10 @@ export class HsFunctionLogsTool extends Tool {
64
64
  title: 'Get HubSpot CMS serverless function logs for an endpoint',
65
65
  description: 'Retrieve logs for HubSpot CMS serverless functions. Use this tool to help debug issues with serverless functions by reading the production logs. Supports various options like latest, compact, and limiting results. Use after listing functions with list-cms-serverless-functions to get the endpoint path.',
66
66
  inputSchema,
67
+ annotations: {
68
+ readOnlyHint: true,
69
+ openWorldHint: true,
70
+ },
67
71
  }, this.handler);
68
72
  }
69
73
  }
@@ -34,7 +34,7 @@ export class HsListFunctionsTool extends Tool {
34
34
  }
35
35
  try {
36
36
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
37
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
37
+ return formatTextContents(stdout, stderr);
38
38
  }
39
39
  catch (error) {
40
40
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -53,6 +53,10 @@ export class HsListFunctionsTool extends Tool {
53
53
  title: 'List HubSpot CMS Serverless Functions',
54
54
  description: 'Get a list of all serverless functions deployed in a HubSpot portal/account. Shows function routes, HTTP methods, secrets, and timestamps.',
55
55
  inputSchema,
56
+ annotations: {
57
+ readOnlyHint: true,
58
+ openWorldHint: true,
59
+ },
56
60
  }, this.handler);
57
61
  }
58
62
  }
@@ -34,7 +34,7 @@ export class HsListTool extends Tool {
34
34
  }
35
35
  try {
36
36
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
37
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
37
+ return formatTextContents(stdout, stderr);
38
38
  }
39
39
  catch (error) {
40
40
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -53,6 +53,10 @@ export class HsListTool extends Tool {
53
53
  title: 'List HubSpot CMS Directory Contents',
54
54
  description: 'List remote contents of a HubSpot CMS directory.',
55
55
  inputSchema,
56
+ annotations: {
57
+ readOnlyHint: true,
58
+ openWorldHint: true,
59
+ },
56
60
  }, this.handler);
57
61
  }
58
62
  }
@@ -10,6 +10,7 @@ vi.mock('../../../utils/toolUsageTracking', () => ({
10
10
  trackToolUsage: vi.fn(),
11
11
  }));
12
12
  vi.mock('../../../utils/feedbackTracking');
13
+ vi.mock('@hubspot/local-dev-lib/config');
13
14
  const mockMcpFeedbackRequest = mcpFeedbackRequest;
14
15
  const mockRunCommandInDir = runCommandInDir;
15
16
  const mockAddFlag = addFlag;
@@ -9,17 +9,20 @@ import { DocsSearchTool } from './project/DocsSearchTool.js';
9
9
  import { DocFetchTool } from './project/DocFetchTool.js';
10
10
  import { GetApiUsagePatternsByAppIdTool } from './project/GetApiUsagePatternsByAppIdTool.js';
11
11
  import { GetApplicationInfoTool } from './project/GetApplicationInfoTool.js';
12
+ import { GetBuildStatusTool } from './project/GetBuildStatusTool.js';
12
13
  import { HsListTool } from './cms/HsListTool.js';
13
14
  import { HsCreateModuleTool } from './cms/HsCreateModuleTool.js';
14
15
  import { HsCreateTemplateTool } from './cms/HsCreateTemplateTool.js';
15
16
  import { HsCreateFunctionTool } from './cms/HsCreateFunctionTool.js';
16
17
  import { HsListFunctionsTool } from './cms/HsListFunctionsTool.js';
17
18
  import { HsFunctionLogsTool } from './cms/HsFunctionLogsTool.js';
19
+ import { CreateTestAccountTool } from './project/CreateTestAccountTool.js';
18
20
  export function registerProjectTools(mcpServer) {
19
21
  return [
20
22
  new UploadProjectTools(mcpServer).register(),
21
23
  new CreateProjectTool(mcpServer).register(),
22
24
  new GuidedWalkthroughTool(mcpServer).register(),
25
+ new CreateTestAccountTool(mcpServer).register(),
23
26
  new DeployProjectTool(mcpServer).register(),
24
27
  new AddFeatureToProjectTool(mcpServer).register(),
25
28
  new ValidateProjectTool(mcpServer).register(),
@@ -28,6 +31,7 @@ export function registerProjectTools(mcpServer) {
28
31
  new DocFetchTool(mcpServer).register(),
29
32
  new GetApiUsagePatternsByAppIdTool(mcpServer).register(),
30
33
  new GetApplicationInfoTool(mcpServer).register(),
34
+ new GetBuildStatusTool(mcpServer).register(),
31
35
  ];
32
36
  }
33
37
  export function registerCmsTools(mcpServer) {
@@ -26,7 +26,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
26
26
  export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
27
27
  export declare class AddFeatureToProjectTool extends Tool<AddFeatureInputSchema> {
28
28
  constructor(mcpServer: McpServer);
29
- handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, distribution, auth, features, addApp, }: AddFeatureInputSchema): Promise<TextContentResponse>;
29
+ handler({ absoluteProjectPath, distribution, auth, features, addApp, }: AddFeatureInputSchema): Promise<TextContentResponse>;
30
30
  register(): RegisteredTool;
31
31
  }
32
32
  export {};
@@ -35,7 +35,7 @@ export class AddFeatureToProjectTool extends Tool {
35
35
  constructor(mcpServer) {
36
36
  super(mcpServer);
37
37
  }
38
- async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, distribution, auth, features, addApp, }) {
38
+ async handler({ absoluteProjectPath, distribution, auth, features, addApp, }) {
39
39
  try {
40
40
  await trackToolUsage(toolName);
41
41
  let command = `hs project add`;
@@ -60,10 +60,10 @@ export class AddFeatureToProjectTool extends Tool {
60
60
  // If features isn't provided, pass an empty array to bypass the prompt
61
61
  command = addFlag(command, 'features', features || []);
62
62
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, command);
63
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
63
+ return formatTextContents(stdout, stderr);
64
64
  }
65
65
  catch (error) {
66
- return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
66
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
67
67
  }
68
68
  }
69
69
  register() {
@@ -72,6 +72,12 @@ export class AddFeatureToProjectTool extends Tool {
72
72
  description: `Adds a feature to an existing HubSpot project.
73
73
  Only works for projects with platformVersion '2025.2' and beyond`,
74
74
  inputSchema,
75
+ annotations: {
76
+ readOnlyHint: false,
77
+ destructiveHint: false,
78
+ idempotentHint: false,
79
+ openWorldHint: false,
80
+ },
75
81
  }, this.handler);
76
82
  }
77
83
  }
@@ -77,10 +77,10 @@ export class CreateProjectTool extends Tool {
77
77
  command = addFlag(command, 'features', features || []);
78
78
  try {
79
79
  const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
80
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
80
+ return formatTextContents(stdout, stderr);
81
81
  }
82
82
  catch (error) {
83
- return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
83
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
84
84
  }
85
85
  }
86
86
  register() {
@@ -88,6 +88,12 @@ export class CreateProjectTool extends Tool {
88
88
  title: 'Create HubSpot Project',
89
89
  description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
90
90
  inputSchema,
91
+ annotations: {
92
+ readOnlyHint: false,
93
+ destructiveHint: false,
94
+ idempotentHint: false,
95
+ openWorldHint: false,
96
+ },
91
97
  }, this.handler);
92
98
  }
93
99
  }
@@ -0,0 +1,41 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ declare const createTestAccountInputSchema: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ configPath: z.ZodOptional<z.ZodString>;
7
+ name: z.ZodOptional<z.ZodString>;
8
+ description: z.ZodOptional<z.ZodString>;
9
+ marketingLevel: z.ZodOptional<z.ZodEnum<["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"]>>;
10
+ opsLevel: z.ZodOptional<z.ZodEnum<["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"]>>;
11
+ serviceLevel: z.ZodOptional<z.ZodEnum<["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"]>>;
12
+ salesLevel: z.ZodOptional<z.ZodEnum<["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"]>>;
13
+ contentLevel: z.ZodOptional<z.ZodEnum<["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"]>>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ absoluteCurrentWorkingDirectory: string;
16
+ name?: string | undefined;
17
+ description?: string | undefined;
18
+ marketingLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
19
+ opsLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
20
+ serviceLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
21
+ salesLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
22
+ contentLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
23
+ configPath?: string | undefined;
24
+ }, {
25
+ absoluteCurrentWorkingDirectory: string;
26
+ name?: string | undefined;
27
+ description?: string | undefined;
28
+ marketingLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
29
+ opsLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
30
+ serviceLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
31
+ salesLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
32
+ contentLevel?: "FREE" | "STARTER" | "PROFESSIONAL" | "ENTERPRISE" | undefined;
33
+ configPath?: string | undefined;
34
+ }>;
35
+ export type CreateTestAccountInputSchema = z.infer<typeof createTestAccountInputSchema>;
36
+ export declare class CreateTestAccountTool extends Tool<CreateTestAccountInputSchema> {
37
+ constructor(mcpServer: McpServer);
38
+ handler({ absoluteCurrentWorkingDirectory, name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, configPath, }: CreateTestAccountInputSchema): Promise<TextContentResponse>;
39
+ register(): RegisteredTool;
40
+ }
41
+ export {};
@@ -0,0 +1,150 @@
1
+ import { Tool } from '../../types.js';
2
+ import { absoluteCurrentWorkingDirectory } from './constants.js';
3
+ import { z } from 'zod';
4
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
5
+ import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
+ import { addFlag } from '../../utils/command.js';
7
+ import { runCommandInDir } from '../../utils/project.js';
8
+ import { ACCOUNT_LEVEL_CHOICES } from '../../../lib/constants.js';
9
+ import fs from 'fs';
10
+ import { getConfigAccountByName } from '@hubspot/local-dev-lib/config';
11
+ const inputSchema = {
12
+ absoluteCurrentWorkingDirectory,
13
+ configPath: z
14
+ .string()
15
+ .optional()
16
+ .describe('Path to a test account configuration JSON file. Mutually exclusive with all other parameters.\n\n' +
17
+ 'Config file format:\n' +
18
+ '{\n' +
19
+ ' "accountName": "AllHubsProfessional",\n' +
20
+ ' "description": "Professional test account",\n' +
21
+ ' "marketingLevel": "PROFESSIONAL",\n' +
22
+ ' "opsLevel": "PROFESSIONAL",\n' +
23
+ ' "serviceLevel": "PROFESSIONAL",\n' +
24
+ ' "salesLevel": "PROFESSIONAL",\n' +
25
+ ' "contentLevel": "PROFESSIONAL"\n' +
26
+ '}'),
27
+ name: z
28
+ .string()
29
+ .optional()
30
+ .describe('Name for the test account. Required when not using configPath.'),
31
+ description: z
32
+ .string()
33
+ .optional()
34
+ .describe('Description for the test account. Required when not using configPath.'),
35
+ marketingLevel: z
36
+ .enum(ACCOUNT_LEVEL_CHOICES)
37
+ .optional()
38
+ .describe(`Marketing Hub tier level. Options: ${ACCOUNT_LEVEL_CHOICES.join(', ')}. Defaults to ENTERPRISE if not specified.`),
39
+ opsLevel: z
40
+ .enum(ACCOUNT_LEVEL_CHOICES)
41
+ .optional()
42
+ .describe(`Operations Hub tier level. Options: ${ACCOUNT_LEVEL_CHOICES.join(', ')}. Defaults to ENTERPRISE if not specified.`),
43
+ serviceLevel: z
44
+ .enum(ACCOUNT_LEVEL_CHOICES)
45
+ .optional()
46
+ .describe(`Service Hub tier level. Options: ${ACCOUNT_LEVEL_CHOICES.join(', ')}. Defaults to ENTERPRISE if not specified.`),
47
+ salesLevel: z
48
+ .enum(ACCOUNT_LEVEL_CHOICES)
49
+ .optional()
50
+ .describe(`Sales Hub tier level. Options: ${ACCOUNT_LEVEL_CHOICES.join(', ')}. Defaults to ENTERPRISE if not specified.`),
51
+ contentLevel: z
52
+ .enum(ACCOUNT_LEVEL_CHOICES)
53
+ .optional()
54
+ .describe(`CMS Hub tier level. Options: ${ACCOUNT_LEVEL_CHOICES.join(', ')}. Defaults to ENTERPRISE if not specified.`),
55
+ };
56
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
+ const createTestAccountInputSchema = z.object({ ...inputSchema });
58
+ const toolName = 'create-test-account';
59
+ export class CreateTestAccountTool extends Tool {
60
+ constructor(mcpServer) {
61
+ super(mcpServer);
62
+ }
63
+ async handler({ absoluteCurrentWorkingDirectory, name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, configPath, }) {
64
+ await trackToolUsage(toolName);
65
+ let command = 'hs test-account create';
66
+ const content = [];
67
+ // Use config file if provided (LLM should check for config first)
68
+ if (configPath) {
69
+ let configJson;
70
+ try {
71
+ const config = fs.readFileSync(configPath, 'utf8');
72
+ configJson = JSON.parse(config);
73
+ }
74
+ catch (error) {
75
+ const errorMessage = error instanceof Error ? error.message : String(error);
76
+ return {
77
+ content: [
78
+ formatTextContent(`Failed to read or parse config file at "${configPath}": ${errorMessage}. Please ensure the file exists and contains valid JSON.`),
79
+ ],
80
+ };
81
+ }
82
+ if (configJson.accountName) {
83
+ const account = getConfigAccountByName(configJson.accountName);
84
+ if (account) {
85
+ content.push(formatTextContent(`The account name "${configJson.accountName}" already exists in the CLI config. Please use a different name.`));
86
+ }
87
+ }
88
+ command = addFlag(command, 'config-path', configPath);
89
+ }
90
+ // Use flags if name is provided (when no config used)
91
+ else if (name) {
92
+ const account = getConfigAccountByName(name);
93
+ if (account) {
94
+ content.push(formatTextContent(`The account name "${name}" already exists in the CLI config. Please use a different name.`));
95
+ }
96
+ command = addFlag(command, 'name', name);
97
+ command = addFlag(command, 'description', description || name);
98
+ command = addFlag(command, 'marketing-level', marketingLevel || 'ENTERPRISE');
99
+ command = addFlag(command, 'ops-level', opsLevel || 'ENTERPRISE');
100
+ command = addFlag(command, 'service-level', serviceLevel || 'ENTERPRISE');
101
+ command = addFlag(command, 'sales-level', salesLevel || 'ENTERPRISE');
102
+ command = addFlag(command, 'content-level', contentLevel || 'ENTERPRISE');
103
+ }
104
+ else {
105
+ content.push(formatTextContent(`Ask the user for the account config JSON path or the name of the test account to create.`));
106
+ }
107
+ if (content.length > 0) {
108
+ return {
109
+ content,
110
+ };
111
+ }
112
+ // No flags or config - command will prompt user interactively
113
+ try {
114
+ const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
115
+ return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
116
+ }
117
+ catch (error) {
118
+ return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
119
+ }
120
+ }
121
+ register() {
122
+ return this.mcpServer.registerTool(toolName, {
123
+ title: 'Create HubSpot Test Account',
124
+ description: 'Creates a HubSpot developer test account. Test accounts are temporary HubSpot portals used for local development, testing apps, and QA workflows.\n\n' +
125
+ 'WORKFLOW:\n' +
126
+ '1. Check the current working directory for existing test account config files (e.g., test-account.json, test-portal-config.json)\n' +
127
+ '2. If config file found:\n' +
128
+ ' - Show the user what file(s) you found\n' +
129
+ ' - Ask if they want to use the existing config\n' +
130
+ ' - If YES: Use configPath parameter only\n' +
131
+ ' - If NO: Proceed to step 3\n' +
132
+ '3. If no config file OR user declined:\n' +
133
+ ' - Ask the user for ALL account details:\n' +
134
+ ' * Account name (required)\n' +
135
+ ' * Description (optional, defaults to account name if not specified)\n' +
136
+ ' * Hub tier levels for each hub (optional, default to ENTERPRISE if not specified)\n' +
137
+ ' - Call this tool with name, description, and all tier level parameters\n' +
138
+ ' - IMPORTANT: Always provide all parameters to ensure non-interactive execution\n\n' +
139
+ 'Available Hub Tier Levels: FREE, STARTER, PROFESSIONAL, ENTERPRISE\n' +
140
+ 'Available Hubs: Marketing (marketingLevel), Sales (salesLevel), Service (serviceLevel), Operations (opsLevel), CMS (contentLevel)',
141
+ inputSchema,
142
+ annotations: {
143
+ readOnlyHint: false,
144
+ destructiveHint: false,
145
+ idempotentHint: false,
146
+ openWorldHint: true,
147
+ },
148
+ }, this.handler);
149
+ }
150
+ }
@@ -17,7 +17,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
17
17
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
18
18
  export declare class DeployProjectTool extends Tool<InputSchemaType> {
19
19
  constructor(mcpServer: McpServer);
20
- handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
20
+ handler({ absoluteProjectPath, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
21
21
  register(): RegisteredTool;
22
22
  }
23
23
  export {};
@@ -21,7 +21,7 @@ export class DeployProjectTool extends Tool {
21
21
  constructor(mcpServer) {
22
22
  super(mcpServer);
23
23
  }
24
- async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }) {
24
+ async handler({ absoluteProjectPath, buildNumber, }) {
25
25
  await trackToolUsage(toolName);
26
26
  let command = `hs project deploy`;
27
27
  const content = [];
@@ -38,13 +38,19 @@ export class DeployProjectTool extends Tool {
38
38
  };
39
39
  }
40
40
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, command);
41
- return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
41
+ return formatTextContents(stdout, stderr);
42
42
  }
43
43
  register() {
44
44
  return this.mcpServer.registerTool(toolName, {
45
45
  title: 'Deploy a build of HubSpot Project',
46
46
  description: 'Takes a build number and a project name and deploys that build of the project. DO NOT run this tool unless the user specifies they would like to deploy the project.',
47
47
  inputSchema,
48
+ annotations: {
49
+ readOnlyHint: false,
50
+ destructiveHint: true,
51
+ idempotentHint: true,
52
+ openWorldHint: true,
53
+ },
48
54
  }, this.handler);
49
55
  }
50
56
  }
@@ -14,7 +14,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
14
14
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
15
  export declare class DocFetchTool extends Tool<InputSchemaType> {
16
16
  constructor(mcpServer: McpServer);
17
- handler({ docUrl, absoluteCurrentWorkingDirectory, }: InputSchemaType): Promise<TextContentResponse>;
17
+ handler({ docUrl }: InputSchemaType): Promise<TextContentResponse>;
18
18
  register(): RegisteredTool;
19
19
  }
20
20
  export {};