@hubspot/cli 8.10.0 → 8.11.0-beta.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 (61) hide show
  1. package/commands/hubdb/list.d.ts +1 -1
  2. package/commands/mcp/setup.d.ts +2 -0
  3. package/commands/mcp/setup.js +29 -1
  4. package/commands/project/appInstallStatus.d.ts +1 -1
  5. package/commands/project/info.d.ts +1 -1
  6. package/commands/project/installApp.d.ts +1 -1
  7. package/commands/project/profile/add.d.ts +2 -1
  8. package/commands/project/profile/add.js +22 -1
  9. package/commands/project/profile/delete.d.ts +1 -1
  10. package/commands/project/upload.js +4 -1
  11. package/commands/project/validate.d.ts +1 -1
  12. package/commands/testAccount/importData.d.ts +1 -1
  13. package/lang/en.d.ts +11 -0
  14. package/lang/en.js +11 -0
  15. package/lib/mcp/setup.d.ts +6 -1
  16. package/lib/mcp/setup.js +29 -18
  17. package/lib/projects/upload.d.ts +1 -1
  18. package/lib/projects/upload.js +4 -8
  19. package/mcp-server/Tool.d.ts +8 -3
  20. package/mcp-server/Tool.js +28 -8
  21. package/mcp-server/server.js +10 -6
  22. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +2 -2
  23. package/mcp-server/tools/cms/HsCreateFunctionTool.js +10 -17
  24. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  25. package/mcp-server/tools/cms/HsCreateModuleTool.js +12 -18
  26. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +2 -2
  27. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -10
  28. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +2 -2
  29. package/mcp-server/tools/cms/HsFunctionLogsTool.js +9 -9
  30. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +2 -2
  31. package/mcp-server/tools/cms/HsListFunctionsTool.js +7 -7
  32. package/mcp-server/tools/cms/HsListTool.d.ts +2 -2
  33. package/mcp-server/tools/cms/HsListTool.js +6 -10
  34. package/mcp-server/tools/index.js +2 -0
  35. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
  36. package/mcp-server/tools/project/AddFeatureToProjectTool.js +8 -9
  37. package/mcp-server/tools/project/AuthAccountTool.d.ts +18 -0
  38. package/mcp-server/tools/project/AuthAccountTool.js +78 -0
  39. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  40. package/mcp-server/tools/project/CreateProjectTool.js +13 -12
  41. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +2 -2
  42. package/mcp-server/tools/project/CreateTestAccountTool.js +14 -15
  43. package/mcp-server/tools/project/DeployProjectTool.d.ts +2 -2
  44. package/mcp-server/tools/project/DeployProjectTool.js +10 -10
  45. package/mcp-server/tools/project/DocFetchTool.js +1 -1
  46. package/mcp-server/tools/project/DocsSearchTool.js +9 -3
  47. package/mcp-server/tools/project/FindProjectsTool.js +1 -1
  48. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -4
  49. package/mcp-server/tools/project/GetApplicationInfoTool.js +9 -4
  50. package/mcp-server/tools/project/GetBuildLogsTool.js +9 -3
  51. package/mcp-server/tools/project/GetBuildStatusTool.js +9 -3
  52. package/mcp-server/tools/project/GetConfigValuesTool.js +9 -3
  53. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  54. package/mcp-server/tools/project/UploadProjectTools.d.ts +2 -2
  55. package/mcp-server/tools/project/UploadProjectTools.js +9 -8
  56. package/mcp-server/tools/project/ValidateProjectTool.d.ts +2 -2
  57. package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
  58. package/mcp-server/utils/command.d.ts +25 -2
  59. package/mcp-server/utils/command.js +67 -17
  60. package/mcp-server/utils/toolUsageTracking.js +8 -1
  61. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
1
  import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type HubdbListArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs;
2
+ export type HubdbListArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs;
3
3
  declare const hubdbListCommand: YargsCommandModule<unknown, HubdbListArgs>;
4
4
  export default hubdbListCommand;
@@ -1,6 +1,8 @@
1
1
  import { CommonArgs, YargsCommandModule } from '../../types/Yargs.js';
2
2
  interface MCPSetupArgs extends CommonArgs {
3
3
  client?: string[];
4
+ standalone?: boolean;
5
+ cliVersion?: string;
4
6
  }
5
7
  declare const mcpSetupCommand: YargsCommandModule<unknown, MCPSetupArgs>;
6
8
  export default mcpSetupCommand;
@@ -8,7 +8,11 @@ const describe = commands.mcp.setup.describe;
8
8
  async function handler(args) {
9
9
  const { exit } = args;
10
10
  try {
11
- await configureMcpServer(args.client);
11
+ await configureMcpServer({
12
+ targets: args.client,
13
+ standalone: args.standalone,
14
+ cliVersion: args.cliVersion,
15
+ });
12
16
  }
13
17
  catch (e) {
14
18
  return exit(EXIT_CODES.ERROR);
@@ -21,6 +25,30 @@ function setupBuilder(yargs) {
21
25
  type: 'array',
22
26
  choices: [...supportedTools.map(tool => tool.value)],
23
27
  });
28
+ yargs.option('standalone', {
29
+ describe: commands.mcp.setup.args.standalone,
30
+ type: 'boolean',
31
+ });
32
+ yargs.option('cli-version', {
33
+ describe: commands.mcp.setup.args.cliVersion,
34
+ type: 'string',
35
+ });
36
+ yargs.check(argv => {
37
+ if (argv.cliVersion !== undefined && argv.standalone !== true) {
38
+ throw new Error(commands.mcp.setup.errors.cliVersionRequiresStandalone);
39
+ }
40
+ return true;
41
+ });
42
+ yargs.example([
43
+ [
44
+ '$0 mcp setup --client claude --no-standalone',
45
+ commands.mcp.setup.examples.nonInteractive,
46
+ ],
47
+ [
48
+ '$0 mcp setup --client cursor --standalone --cli-version 8.0.1',
49
+ commands.mcp.setup.examples.standalone,
50
+ ],
51
+ ]);
24
52
  return yargs;
25
53
  }
26
54
  const builder = makeYargsBuilder(setupBuilder, command, describe, {
@@ -1,4 +1,4 @@
1
1
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectInstallStatusArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs;
2
+ export type ProjectInstallStatusArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs;
3
3
  declare const projectInstallStatusCommand: YargsCommandModule<unknown, ProjectInstallStatusArgs>;
4
4
  export default projectInstallStatusCommand;
@@ -1,4 +1,4 @@
1
1
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectInfoArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs;
2
+ export type ProjectInfoArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs;
3
3
  declare const projectInfoCommand: YargsCommandModule<unknown, ProjectInfoArgs>;
4
4
  export default projectInfoCommand;
@@ -1,5 +1,5 @@
1
1
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectInstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
2
+ export type ProjectInstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
3
  force: boolean;
4
4
  profile?: string;
5
5
  };
@@ -1,7 +1,8 @@
1
1
  import { YargsCommandModule, CommonArgs } from '../../../types/Yargs.js';
2
- type ProjectProfileAddArgs = CommonArgs & {
2
+ export type ProjectProfileAddArgs = CommonArgs & {
3
3
  name?: string;
4
4
  targetAccount?: number;
5
+ copyFrom?: string;
5
6
  };
6
7
  declare const projectProfileAddCommand: YargsCommandModule<unknown, ProjectProfileAddArgs>;
7
8
  export default projectProfileAddCommand;
@@ -157,7 +157,20 @@ async function handler(args) {
157
157
  accountId: targetAccountId,
158
158
  variables: {},
159
159
  };
160
- const profileToCopyVariablesFrom = await selectProfileToCopyVariablesFrom(existingProfiles);
160
+ let profileToCopyVariablesFrom;
161
+ if (args.copyFrom) {
162
+ if (existingProfiles.includes(args.copyFrom)) {
163
+ profileToCopyVariablesFrom = args.copyFrom;
164
+ }
165
+ else {
166
+ uiLogger.error(commands.project.profile.add.errors.copyFromProfileNotFound(args.copyFrom));
167
+ return exit(EXIT_CODES.ERROR);
168
+ }
169
+ }
170
+ else {
171
+ profileToCopyVariablesFrom =
172
+ await selectProfileToCopyVariablesFrom(existingProfiles);
173
+ }
161
174
  if (profileToCopyVariablesFrom) {
162
175
  try {
163
176
  const profileToCopyFileContent = loadHsProfileFile(projectSourceDir, profileToCopyVariablesFrom);
@@ -190,8 +203,16 @@ function projectProfileAddBuilder(yargs) {
190
203
  describe: commands.project.profile.add.options.targetAccount,
191
204
  type: 'number',
192
205
  });
206
+ yargs.option('copy-from', {
207
+ describe: commands.project.profile.add.options.copyFrom,
208
+ type: 'string',
209
+ });
193
210
  yargs.example([
194
211
  ['$0 project profile add qa', commands.project.profile.add.example],
212
+ [
213
+ '$0 project profile add qa --target-account 123 --copy-from staging',
214
+ commands.project.profile.add.exampleCopyFrom,
215
+ ],
195
216
  ]);
196
217
  return yargs;
197
218
  }
@@ -1,5 +1,5 @@
1
1
  import { YargsCommandModule, CommonArgs } from '../../../types/Yargs.js';
2
- type ProjectProfileDeleteArgs = CommonArgs & {
2
+ export type ProjectProfileDeleteArgs = CommonArgs & {
3
3
  name?: string;
4
4
  };
5
5
  declare const projectProfileDeleteCommand: YargsCommandModule<unknown, ProjectProfileDeleteArgs>;
@@ -113,7 +113,10 @@ async function handler(args) {
113
113
  (!result.buildResult.isAutoDeployEnabled || preview || skipAutoDeploy)) {
114
114
  uiLogger.log(chalk.bold(commands.project.upload.logs.buildSucceeded(result.buildId)));
115
115
  if (!preview) {
116
- uiLogger.log(commands.project.upload.logs.autoDeployDisabled(`hs project deploy --build=${result.buildId}`));
116
+ const deployCommand = `hs project deploy --build=${result.buildId}`;
117
+ uiLogger.log(skipAutoDeploy
118
+ ? commands.project.upload.logs.autoDeploySkipped(deployCommand)
119
+ : commands.project.upload.logs.autoDeployDisabled(deployCommand));
117
120
  logFeedbackMessage(result.buildId);
118
121
  }
119
122
  await displayWarnLogs(targetAccountId, projectConfig.name, result.buildId);
@@ -1,5 +1,5 @@
1
1
  import { CommonArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectValidateArgs = CommonArgs & {
2
+ export type ProjectValidateArgs = CommonArgs & {
3
3
  profile?: string;
4
4
  };
5
5
  declare const projectValidateCommand: YargsCommandModule<unknown, ProjectValidateArgs>;
@@ -1,7 +1,7 @@
1
1
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs.js';
2
2
  export declare const command = "import-data";
3
3
  export declare const describe: string;
4
- type CrmImportDataArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
4
+ export type CrmImportDataArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
5
5
  filePath: string | undefined;
6
6
  skipConfirm: boolean | undefined;
7
7
  };
package/lang/en.d.ts CHANGED
@@ -1330,11 +1330,18 @@ export declare const commands: {
1330
1330
  vsCode: string;
1331
1331
  args: {
1332
1332
  client: string;
1333
+ standalone: string;
1334
+ cliVersion: string;
1333
1335
  docsSearch: string;
1334
1336
  };
1337
+ examples: {
1338
+ nonInteractive: string;
1339
+ standalone: string;
1340
+ };
1335
1341
  success: (derivedTargets: string[]) => string;
1336
1342
  errors: {
1337
1343
  errorParsingJsonFIle: (filename: string, errorMessage: string) => string;
1344
+ cliVersionRequiresStandalone: string;
1338
1345
  };
1339
1346
  spinners: {
1340
1347
  failedToConfigure: string;
@@ -1429,6 +1436,7 @@ export declare const commands: {
1429
1436
  describe: string;
1430
1437
  verboseDescribe: string;
1431
1438
  example: string;
1439
+ exampleCopyFrom: string;
1432
1440
  logs: {
1433
1441
  copyExistingProfile: (profileName: string) => string;
1434
1442
  copyExistingProfiles: string;
@@ -1452,6 +1460,7 @@ export declare const commands: {
1452
1460
  invalidTargetAccount: string;
1453
1461
  noAccountsConfigured: string;
1454
1462
  failedToLoadProfile: (profileName: string) => string;
1463
+ copyFromProfileNotFound: (profileName: string) => string;
1455
1464
  failedToCreateProfile: string;
1456
1465
  };
1457
1466
  positionals: {
@@ -1459,6 +1468,7 @@ export declare const commands: {
1459
1468
  };
1460
1469
  options: {
1461
1470
  targetAccount: string;
1471
+ copyFrom: string;
1462
1472
  };
1463
1473
  };
1464
1474
  delete: {
@@ -1880,6 +1890,7 @@ export declare const commands: {
1880
1890
  readyToGoLive: string;
1881
1891
  runCommand: (command: string) => string;
1882
1892
  autoDeployDisabled: (deployCommand: string) => string;
1893
+ autoDeploySkipped: (deployCommand: string) => string;
1883
1894
  };
1884
1895
  errors: {
1885
1896
  noProjectConfig: string;
package/lang/en.js CHANGED
@@ -1339,11 +1339,18 @@ export const commands = {
1339
1339
  vsCode: 'VSCode',
1340
1340
  args: {
1341
1341
  client: 'Target apps to configure',
1342
+ standalone: 'Use npx for all of the hs commands run by the MCP server. This allows you to use the MCP server without having the CLI globally installed.',
1343
+ cliVersion: 'Pin a specific CLI version for standalone mode',
1342
1344
  docsSearch: 'Should the docs search mcp server be installed',
1343
1345
  },
1346
+ examples: {
1347
+ nonInteractive: 'Configure Claude Code without prompts',
1348
+ standalone: 'Configure Cursor in standalone mode with a pinned CLI version',
1349
+ },
1344
1350
  success: (derivedTargets) => `You can now use the HubSpot CLI MCP Server in ${derivedTargets.join(', ')}. ${chalk.bold('You may need to restart these tools to apply the changes')}.`,
1345
1351
  errors: {
1346
1352
  errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
1353
+ cliVersionRequiresStandalone: '`--cli-version` requires `--standalone`. Pass `--standalone` to use a specific CLI version.',
1347
1354
  },
1348
1355
  spinners: {
1349
1356
  failedToConfigure: 'Failed to configure the HubSpot mcp server.',
@@ -1444,6 +1451,7 @@ export const commands = {
1444
1451
  describe: 'Add a new project profile',
1445
1452
  verboseDescribe: `Add a new project profile\n\nProfiles enable you to reference variables in your component configuration files. Use the syntax ${chalk.bold('\${VARIABLE_NAME}')} to reference profile variables in your component configuration files. Then target the profile using the ${uiCommandReference('--profile')} flag when you upload your project.`,
1446
1453
  example: 'Add a new project profile named hsprofile.qa.json',
1454
+ exampleCopyFrom: 'Add a profile and copy variables from the staging profile',
1447
1455
  logs: {
1448
1456
  copyExistingProfile: (profileName) => `Found an existing profile. We can copy the variables from ${chalk.bold(profileName)} into your new profile.`,
1449
1457
  copyExistingProfiles: 'We can copy the variables from one of your existing profiles into your new profile.',
@@ -1467,6 +1475,7 @@ export const commands = {
1467
1475
  invalidTargetAccount: 'Target account is not configured in the CLI',
1468
1476
  noAccountsConfigured: 'No accounts configured in the CLI',
1469
1477
  failedToLoadProfile: (profileName) => `Unable to copy variables. Failed to load profile ${chalk.bold(profileName)}`,
1478
+ copyFromProfileNotFound: (profileName) => `Profile ${chalk.bold(profileName)} does not exist. Available profiles can be found in your project source directory.`,
1470
1479
  failedToCreateProfile: 'Failed to create profile',
1471
1480
  },
1472
1481
  positionals: {
@@ -1474,6 +1483,7 @@ export const commands = {
1474
1483
  },
1475
1484
  options: {
1476
1485
  targetAccount: 'The target account ID for this profile',
1486
+ copyFrom: 'Copy variables from an existing profile',
1477
1487
  },
1478
1488
  },
1479
1489
  delete: {
@@ -1895,6 +1905,7 @@ export const commands = {
1895
1905
  readyToGoLive: '🚀 Ready to take your project live?',
1896
1906
  runCommand: (command) => `Run \`${uiCommandReference(command)}\``,
1897
1907
  autoDeployDisabled: (deployCommand) => `Automatic deploys are disabled for this project. Run ${uiCommandReference(deployCommand)} to deploy this build.`,
1908
+ autoDeploySkipped: (deployCommand) => `Auto-deploy was skipped for this build. Run ${uiCommandReference(deployCommand)} to deploy this build.`,
1898
1909
  },
1899
1910
  errors: {
1900
1911
  noProjectConfig: 'No project detected. Run this command from a project directory.',
@@ -8,7 +8,12 @@ interface McpCommand {
8
8
  args: string[];
9
9
  env?: Record<string, string>;
10
10
  }
11
- export declare function configureMcpServer(targets: string[] | undefined): Promise<string[]>;
11
+ interface ConfigureMcpServerOptions {
12
+ targets?: string[];
13
+ standalone?: boolean;
14
+ cliVersion?: string;
15
+ }
16
+ export declare function configureMcpServer(options: ConfigureMcpServerOptions): Promise<string[]>;
12
17
  export declare function setupVsCode(mcpCommand?: McpCommand): Promise<boolean>;
13
18
  export declare function setupClaudeCode(mcpCommand?: McpCommand): Promise<boolean>;
14
19
  export declare function setupCursor(mcpCommand?: McpCommand): boolean;
package/lib/mcp/setup.js CHANGED
@@ -32,8 +32,9 @@ const defaultMcpCommand = {
32
32
  command: 'hs',
33
33
  args: ['mcp', 'start'],
34
34
  };
35
- export async function configureMcpServer(targets) {
35
+ export async function configureMcpServer(options) {
36
36
  try {
37
+ const { targets, standalone, cliVersion: cliVersionFlag } = options;
37
38
  let derivedTargets = [];
38
39
  if (!targets || targets.length === 0) {
39
40
  const { selectedTargets } = await promptUser({
@@ -52,23 +53,33 @@ export async function configureMcpServer(targets) {
52
53
  else {
53
54
  derivedTargets = targets;
54
55
  }
55
- // Prompt for standalone mode
56
- const { useStandaloneMode } = await promptUser({
57
- name: 'useStandaloneMode',
58
- type: 'confirm',
59
- message: commands.mcp.setup.prompts.standaloneMode,
60
- default: false,
61
- });
62
- const { cliVersion } = useStandaloneMode
63
- ? await promptUser({
64
- name: 'cliVersion',
65
- type: 'input',
66
- message: commands.mcp.setup.prompts.cliVersion,
67
- validate: (v) => !v || /^[\d]+\.[\d]+\.[\d]+([-+][\w.]+)?$/.test(v.trim())
68
- ? true
69
- : 'Please enter a valid semver version (e.g. 8.0.1) or leave blank for latest.',
70
- })
71
- : { cliVersion: '' };
56
+ let useStandaloneMode = standalone;
57
+ if (useStandaloneMode === undefined) {
58
+ const response = await promptUser({
59
+ name: 'useStandaloneMode',
60
+ type: 'confirm',
61
+ message: commands.mcp.setup.prompts.standaloneMode,
62
+ default: false,
63
+ });
64
+ useStandaloneMode = response.useStandaloneMode;
65
+ }
66
+ let cliVersion = '';
67
+ if (useStandaloneMode) {
68
+ if (cliVersionFlag !== undefined) {
69
+ cliVersion = cliVersionFlag;
70
+ }
71
+ else {
72
+ const response = await promptUser({
73
+ name: 'cliVersion',
74
+ type: 'input',
75
+ message: commands.mcp.setup.prompts.cliVersion,
76
+ validate: (v) => !v || /^[\d]+\.[\d]+\.[\d]+([-+][\w.]+)?$/.test(v.trim())
77
+ ? true
78
+ : 'Please enter a valid semver version (e.g. 8.0.1) or leave blank for latest.',
79
+ });
80
+ cliVersion = response.cliVersion;
81
+ }
82
+ }
72
83
  const cliPackage = cliVersion
73
84
  ? `@hubspot/cli@${cliVersion}`
74
85
  : '@hubspot/cli';
@@ -24,7 +24,7 @@ type HandleProjectUploadArg<T> = {
24
24
  profile?: string;
25
25
  force?: boolean;
26
26
  };
27
- export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, skipNpmAudit, skipAutoDeploy: _skipAutoDeploy, force, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
27
+ export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, skipNpmAudit, skipAutoDeploy, force, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
28
28
  export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig, projectDir: string): Promise<void>;
29
29
  export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
30
30
  type HandleTranslateArg = {
@@ -21,7 +21,7 @@ import { archiveWorkspacesAndDependencies, getPackageJsonPathsToUpdate, getLockf
21
21
  import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
22
22
  import { validateLintConfigOnUpload } from './validateLintConfigOnUpload.js';
23
23
  import { runNpmAuditsBeforeProjectUpload } from './npmAuditOnUpload.js';
24
- async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
24
+ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation, skipAutoDeploy) {
25
25
  const accountIdentifier = uiAccountDescription(accountId) || `${accountId}`;
26
26
  SpinniesManager.add('upload', {
27
27
  text: lib.projectUpload.uploadProjectFiles.add(projectName, accountIdentifier),
@@ -30,8 +30,7 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
30
30
  let buildId;
31
31
  let error;
32
32
  try {
33
- // TODO(skip-auto-deploy): Pass skipAutoDeploy once local-dev-lib is bumped
34
- const { data: upload } = await uploadProject(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation);
33
+ const { data: upload } = await uploadProject(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation, skipAutoDeploy);
35
34
  buildId = upload.buildId;
36
35
  SpinniesManager.succeed('upload', {
37
36
  text: lib.projectUpload.uploadProjectFiles.succeed(projectName, accountIdentifier),
@@ -48,10 +47,7 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
48
47
  }
49
48
  return { buildId, error };
50
49
  }
51
- // TODO(skip-auto-deploy): Use skipAutoDeploy once local-dev-lib is bumped to support it
52
- export async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, skipNpmAudit = false,
53
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
- skipAutoDeploy: _skipAutoDeploy = false, force = false, }) {
50
+ export async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, skipNpmAudit = false, skipAutoDeploy = false, force = false, }) {
55
51
  const srcDir = path.resolve(projectDir, projectConfig.srcDir);
56
52
  await validateSourceDirectory(srcDir, projectConfig, projectDir);
57
53
  await validateNoHSMetaMismatch(srcDir, projectConfig);
@@ -119,7 +115,7 @@ skipAutoDeploy: _skipAutoDeploy = false, force = false, }) {
119
115
  return resolve({ projectNotFound: true });
120
116
  }
121
117
  const projectId = project?.id;
122
- const { buildId, error } = await uploadProjectFiles(accountId, projectConfig.name, tempFile.name, uploadMessage, projectConfig.platformVersion, intermediateRepresentation);
118
+ const { buildId, error } = await uploadProjectFiles(accountId, projectConfig.name, tempFile.name, uploadMessage, projectConfig.platformVersion, intermediateRepresentation, skipAutoDeploy);
123
119
  if (error) {
124
120
  resolve({ uploadError: error, projectId });
125
121
  }
@@ -1,15 +1,20 @@
1
1
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
3
+ import { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js';
2
4
  import { McpLogger } from './utils/logger.js';
3
5
  import { TextContentResponse } from './types.js';
6
+ import { CommandResults, Command } from './utils/command.js';
7
+ export type ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;
4
8
  export declare class Tool<InputSchema> {
5
9
  protected mcpServer: McpServer;
6
10
  protected logger: McpLogger;
7
11
  protected toolName: string;
8
12
  constructor(mcpServer: McpServer, logger: McpLogger, toolName: string);
9
13
  register(): RegisteredTool;
10
- handler(input: InputSchema): TextContentResponse | Promise<TextContentResponse>;
11
- protected getTrackingMeta(input: InputSchema): {
14
+ handler(_input: InputSchema): TextContentResponse | Promise<TextContentResponse>;
15
+ protected runCommand(directory: string, command: Command, extra?: ToolExtra): Promise<CommandResults>;
16
+ protected getTrackingMeta(_input: InputSchema): {
12
17
  [key: string]: string;
13
18
  } | undefined;
14
- protected wrappedHandler(input: InputSchema): Promise<TextContentResponse>;
19
+ protected wrappedHandler(input: InputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
15
20
  }
@@ -1,6 +1,7 @@
1
1
  import { formatTextContents } from './utils/content.js';
2
2
  import { getErrorMessage } from '../lib/errorHandlers/index.js';
3
3
  import { trackToolUsage } from './utils/toolUsageTracking.js';
4
+ import { runCommandInDir } from './utils/command.js';
4
5
  export class Tool {
5
6
  mcpServer;
6
7
  logger;
@@ -13,17 +14,36 @@ export class Tool {
13
14
  register() {
14
15
  throw new Error('Must implement register');
15
16
  }
16
- handler(
17
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
18
- input) {
17
+ handler(_input) {
19
18
  throw new Error('Must implement handler');
20
19
  }
21
- getTrackingMeta(
22
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
- input) {
20
+ runCommand(directory, command, extra) {
21
+ let progressCount = 0;
22
+ return runCommandInDir(directory, command, async (chunk) => {
23
+ try {
24
+ const message = `${chunk.trimEnd()}`;
25
+ const token = extra?._meta?.progressToken;
26
+ if (token !== undefined && extra?.sendNotification) {
27
+ progressCount++;
28
+ await extra.sendNotification({
29
+ method: 'notifications/progress',
30
+ params: {
31
+ progressToken: token,
32
+ progress: progressCount,
33
+ message,
34
+ },
35
+ });
36
+ }
37
+ }
38
+ catch {
39
+ // Swallow notification errors so a failed notification never crashes the tool
40
+ }
41
+ });
42
+ }
43
+ getTrackingMeta(_input) {
24
44
  return undefined;
25
45
  }
26
- async wrappedHandler(input) {
46
+ async wrappedHandler(input, extra) {
27
47
  const startTime = Date.now();
28
48
  try {
29
49
  // `input` is logged unredacted. Tool input schemas MUST NOT include
@@ -34,7 +54,7 @@ export class Tool {
34
54
  args: input,
35
55
  });
36
56
  await trackToolUsage(this.toolName, this.getTrackingMeta(input));
37
- const result = await this.handler(input);
57
+ const result = await this.handler(input, extra);
38
58
  this.logger.debug(this.toolName, {
39
59
  message: 'Tool completed',
40
60
  durationMs: Date.now() - startTime,
@@ -17,22 +17,26 @@ WHEN TO USE THIS SERVER
17
17
  via \`search-docs\` + \`fetch-doc\` rather than from prior knowledge.
18
18
 
19
19
  REQUIRED WORKFLOWS
20
- 1. Documentation lookup: always call \`search-docs\` first, then
20
+ 1. Authentication: call \`auth-account\` when the user wants to connect a
21
+ HubSpot account, when any tool reports "No account ID found", or when
22
+ there are no authenticated accounts. The tool opens a browser tab for
23
+ the user to authorize — no Personal Access Key is required up front.
24
+ 2. Documentation lookup: always call \`search-docs\` first, then
21
25
  \`fetch-doc\` on the most relevant result(s) before planning, writing
22
26
  code, or answering platform/API questions. Do not answer from memory.
23
- 2. Locating a HubSpot project: when the current working directory is not
27
+ 3. Locating a HubSpot project: when the current working directory is not
24
28
  a HubSpot project (no \`hsproject.json\`) or you need to determine
25
29
  whether a directory contains one, call \`find-projects\` before
26
30
  running any tool that requires a project path.
27
- 3. Editing \`*-hsmeta.json\`: call \`get-feature-config-schema\` for that
31
+ 4. Editing \`*-hsmeta.json\`: call \`get-feature-config-schema\` for that
28
32
  feature type first to learn the allowed fields and values.
29
- 4. Debugging a failed build: start with \`get-build-status\` to surface
33
+ 5. Debugging a failed build: start with \`get-build-status\` to surface
30
34
  error messages, and only reach for \`get-build-logs\` for deeper
31
35
  troubleshooting or warnings.
32
- 5. Reading serverless function logs: call \`list-cms-serverless-functions\`
36
+ 6. Reading serverless function logs: call \`list-cms-serverless-functions\`
33
37
  first to discover the endpoint path, then
34
38
  \`get-cms-serverless-function-logs\`.
35
- 6. App analytics: call \`get-apps-info\` to discover \`appId\` values
39
+ 7. App analytics: call \`get-apps-info\` to discover \`appId\` values
36
40
  before \`get-api-usage-patterns-by-app-id\`.
37
41
 
38
42
  OUTPUT
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -20,7 +20,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
20
20
  export type HsCreateFunctionInputSchema = z.infer<typeof inputSchemaZodObject>;
21
21
  export declare class HsCreateFunctionTool extends Tool<HsCreateFunctionInputSchema> {
22
22
  constructor(mcpServer: McpServer, logger: McpLogger);
23
- handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }: HsCreateFunctionInputSchema): Promise<TextContentResponse>;
23
+ handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }: HsCreateFunctionInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
24
24
  register(): RegisteredTool;
25
25
  }
26
26
  export {};
@@ -1,10 +1,9 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/command.js';
5
4
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
- import { addFlag } from '../../utils/command.js';
7
5
  import { HTTP_METHODS } from '../../../types/Cms.js';
6
+ import { HubSpotCommand } from '../../utils/command.js';
8
7
  import { setupHubSpotConfig } from '../../utils/config.js';
9
8
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
10
9
  const inputSchema = {
@@ -37,7 +36,7 @@ export class HsCreateFunctionTool extends Tool {
37
36
  constructor(mcpServer, logger) {
38
37
  super(mcpServer, logger, toolName);
39
38
  }
40
- async handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }) {
39
+ async handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }, extra) {
41
40
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
42
41
  const content = [];
43
42
  // Require functions folder
@@ -59,28 +58,22 @@ export class HsCreateFunctionTool extends Tool {
59
58
  };
60
59
  }
61
60
  // Build the command
62
- let command = 'hs cms function create';
61
+ const command = new HubSpotCommand('cms function create');
63
62
  if (dest) {
64
- command += ` "${dest}"`;
63
+ command.addArg(dest);
65
64
  }
66
- // Add function-specific flags
67
65
  if (functionsFolder) {
68
- command = addFlag(command, 'functions-folder', functionsFolder);
66
+ command.addFlag('functions-folder', functionsFolder);
69
67
  }
70
68
  if (filename) {
71
- command = addFlag(command, 'filename', filename);
72
- }
73
- if (endpointMethod) {
74
- command = addFlag(command, 'endpoint-method', endpointMethod);
75
- }
76
- else {
77
- command = addFlag(command, 'endpoint-method', 'GET');
69
+ command.addFlag('filename', filename);
78
70
  }
71
+ command.addFlag('endpoint-method', endpointMethod ?? 'GET');
79
72
  if (endpointPath) {
80
- command = addFlag(command, 'endpoint-path', endpointPath);
73
+ command.addFlag('endpoint-path', endpointPath);
81
74
  }
82
75
  try {
83
- const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
76
+ const { stdout, stderr } = await this.runCommand(absoluteCurrentWorkingDirectory, command, extra);
84
77
  return formatTextContents(stdout, stderr);
85
78
  }
86
79
  catch (error) {
@@ -102,6 +95,6 @@ export class HsCreateFunctionTool extends Tool {
102
95
  idempotentHint: false,
103
96
  openWorldHint: false,
104
97
  },
105
- }, input => this.wrappedHandler(input));
98
+ }, (input, extra) => this.wrappedHandler(input, extra));
106
99
  }
107
100
  }
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -16,7 +16,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
16
16
  export type HsCreateModuleInputSchema = z.infer<typeof inputSchemaZodObject>;
17
17
  export declare class HsCreateModuleTool extends Tool<HsCreateModuleInputSchema> {
18
18
  constructor(mcpServer: McpServer, logger: McpLogger);
19
- handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }: HsCreateModuleInputSchema): Promise<TextContentResponse>;
19
+ handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }: HsCreateModuleInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
20
20
  register(): RegisteredTool;
21
21
  }
22
22
  export {};