@hubspot/cli 7.7.3-experimental.0 → 7.7.5-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 (44) hide show
  1. package/bin/cli.js +2 -0
  2. package/commands/account/auth.d.ts +2 -0
  3. package/commands/account/auth.js +15 -7
  4. package/commands/auth.d.ts +2 -0
  5. package/commands/auth.js +17 -8
  6. package/commands/hubdb/list.d.ts +4 -0
  7. package/commands/hubdb/list.js +83 -0
  8. package/commands/hubdb.js +2 -0
  9. package/commands/mcp/setup.d.ts +13 -0
  10. package/commands/mcp/setup.js +248 -0
  11. package/commands/mcp/start.d.ts +10 -0
  12. package/commands/mcp/start.js +66 -0
  13. package/commands/mcp.d.ts +10 -0
  14. package/commands/mcp.js +20 -0
  15. package/commands/project/listBuilds.js +2 -5
  16. package/lang/en.d.ts +26 -4
  17. package/lang/en.js +28 -4
  18. package/lib/prompts/personalAccessKeyPrompt.js +35 -24
  19. package/lib/prompts/projectAddPrompt.js +1 -1
  20. package/lib/prompts/promptUtils.d.ts +2 -1
  21. package/lib/prompts/promptUtils.js +2 -1
  22. package/mcp-server/server.d.ts +1 -0
  23. package/mcp-server/server.js +18 -0
  24. package/mcp-server/tools/index.d.ts +2 -0
  25. package/mcp-server/tools/index.js +17 -0
  26. package/mcp-server/tools/project/AddFeatureToProject.d.ts +6 -0
  27. package/mcp-server/tools/project/AddFeatureToProject.js +100 -0
  28. package/mcp-server/tools/project/CreateProjectTool.d.ts +6 -0
  29. package/mcp-server/tools/project/CreateProjectTool.js +119 -0
  30. package/mcp-server/tools/project/DeployProject.d.ts +6 -0
  31. package/mcp-server/tools/project/DeployProject.js +50 -0
  32. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +6 -0
  33. package/mcp-server/tools/project/GuidedWalkthroughTool.js +54 -0
  34. package/mcp-server/tools/project/UploadProjectTools.d.ts +5 -0
  35. package/mcp-server/tools/project/UploadProjectTools.js +26 -0
  36. package/mcp-server/tools/project/constants.d.ts +3 -0
  37. package/mcp-server/tools/project/constants.js +13 -0
  38. package/mcp-server/types.d.ts +8 -0
  39. package/mcp-server/types.js +7 -0
  40. package/mcp-server/utils/command.d.ts +3 -0
  41. package/mcp-server/utils/command.js +16 -0
  42. package/mcp-server/utils/project.d.ts +5 -0
  43. package/mcp-server/utils/project.js +17 -0
  44. package/package.json +4 -2
package/lang/en.d.ts CHANGED
@@ -26,6 +26,7 @@ The authentication method is ${string}, which is an access token tied to a speci
26
26
  Global configuration replaces hubspot.config.yml, and you will be prompted to migrate your existing config if one exists.`;
27
27
  readonly options: {
28
28
  readonly account: "HubSpot account to authenticate";
29
+ readonly personalAccessKey: "Enter existing personal access key";
29
30
  };
30
31
  readonly errors: {
31
32
  readonly failedToUpdateConfig: "Failed to update the configuration file. Please try again.";
@@ -153,6 +154,9 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
153
154
  readonly account: {
154
155
  readonly describe: "HubSpot account to authenticate";
155
156
  };
157
+ readonly personalAccessKey: {
158
+ readonly describe: "Enter existing personal access key";
159
+ };
156
160
  };
157
161
  readonly success: {
158
162
  readonly configFileUpdated: (accountName: string, configFilename: string, authType: string) => string;
@@ -665,6 +669,21 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
665
669
  readonly fetch: (tableId: string, path: string) => string;
666
670
  };
667
671
  };
672
+ readonly list: {
673
+ readonly tables: `${string}:`;
674
+ readonly describe: "List HubDB tables.";
675
+ readonly labels: {
676
+ readonly label: "Label";
677
+ readonly id: "ID";
678
+ readonly name: "Name";
679
+ readonly columns: "Columns";
680
+ readonly rows: "Rows";
681
+ };
682
+ readonly success: (accountId: number) => string;
683
+ readonly noTables: (accountId: number) => string;
684
+ readonly tablesDisplayed: (displayed: number, total: number, truncated?: number) => string;
685
+ readonly viewTablesLink: (baseUrl: string, accountId: number) => string;
686
+ };
668
687
  };
669
688
  };
670
689
  readonly init: {
@@ -1062,7 +1081,7 @@ ${string}`;
1062
1081
  readonly continueOrExitPrompt: "Press <enter> to load more, or ctrl+c to exit";
1063
1082
  readonly viewAllBuildsLink: "View all builds";
1064
1083
  readonly showingNextBuilds: (count: string, projectName: string) => string;
1065
- readonly showingRecentBuilds: (count: string, projectName: string, viewAllBuildsLink: string) => string;
1084
+ readonly showingRecentBuilds: (count: number, projectName: string, viewAllBuildsLink: string) => string;
1066
1085
  readonly errors: {
1067
1086
  readonly noBuilds: "No builds for this project were found.";
1068
1087
  readonly projectNotFound: (projectName: string) => string;
@@ -2665,11 +2684,14 @@ Run ${string} to upgrade to version ${string}`;
2665
2684
  readonly enterAccountId: "Enter the account ID for your account (the number under the DOMAIN column at https://app.hubspot.com/myaccounts-beta ): ";
2666
2685
  readonly enterClientId: "Enter your OAuth2 client ID: ";
2667
2686
  readonly enterClientSecret: "Enter your OAuth2 client secret: ";
2668
- readonly enterPersonalAccessKey: "Enter your personal access key: ";
2687
+ readonly enterPersonalAccessKey: "[--personal-access-key] Enter your personal access key: ";
2669
2688
  readonly selectScopes: "Select access scopes (see https://developers.hubspot.com/docs/methods/oauth2/initiate-oauth-integration#scopes)";
2670
2689
  readonly personalAccessKeySetupTitle: "HubSpot Personal Access Key Setup";
2671
- readonly personalAccessKeyBrowserOpenPrep: "A personal access key is required to authenticate the CLI to interact with your HubSpot account. We'll open a secure page in your default browser where you can view and copy your personal access key.";
2672
- readonly personalAccessKeyBrowserOpenPrompt: "Open HubSpot to copy your personal access key?";
2690
+ readonly personalAccessKeyBrowserOpenPrep: "A personal access key is required to authenticate the CLI to interact with your HubSpot account.";
2691
+ readonly personalAccessKeyPromptChoices: {
2692
+ readonly OPEN_BROWSER: "Open HubSpot to copy your personal access key";
2693
+ readonly PASTE_EXISTING: "Enter existing personal access key";
2694
+ };
2673
2695
  readonly logs: {
2674
2696
  readonly openingWebBrowser: (url: string) => string;
2675
2697
  };
package/lang/en.js CHANGED
@@ -36,6 +36,7 @@ exports.commands = {
36
36
  verboseDescribe: `Configure authentication for a HubSpot account. This will create or update the global config file at ${config_1.GLOBAL_CONFIG_PATH} that stores your account information.\n\nThe authentication method is ${chalk_1.default.bold(auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value)}, which is an access token tied to a specific user account.\n\nGlobal configuration replaces ${config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME}, and you will be prompted to migrate your existing config if one exists.`,
37
37
  options: {
38
38
  account: 'HubSpot account to authenticate',
39
+ personalAccessKey: 'Enter existing personal access key',
39
40
  },
40
41
  errors: {
41
42
  failedToUpdateConfig: 'Failed to update the configuration file. Please try again.',
@@ -163,6 +164,9 @@ exports.commands = {
163
164
  account: {
164
165
  describe: 'HubSpot account to authenticate',
165
166
  },
167
+ personalAccessKey: {
168
+ describe: 'Enter existing personal access key',
169
+ },
166
170
  },
167
171
  success: {
168
172
  configFileUpdated: (accountName, configFilename, authType) => `Account "${accountName}" updated in ${configFilename} using "${authType}"`,
@@ -675,6 +679,23 @@ exports.commands = {
675
679
  fetch: (tableId, path) => `Downloaded HubDB table ${tableId} to ${path}`,
676
680
  },
677
681
  },
682
+ list: {
683
+ tables: `${chalk_1.default.bold('Tables')}:`,
684
+ describe: 'List HubDB tables.',
685
+ labels: {
686
+ label: 'Label',
687
+ id: 'ID',
688
+ name: 'Name',
689
+ columns: 'Columns',
690
+ rows: 'Rows',
691
+ },
692
+ success: (accountId) => `Showing tables for account ${accountId}:`,
693
+ noTables: (accountId) => `No tables found for account ${accountId}.`,
694
+ tablesDisplayed: (displayed, total, truncated) => `Displaying ${displayed} of ${total} tables${truncated
695
+ ? `, the remaining ${truncated} tables were not displayed.`
696
+ : '.'}`,
697
+ viewTablesLink: (baseUrl, accountId) => (0, ui_1.uiLink)('Manage tables in HubSpot', `${baseUrl}/hubdb/${accountId}`),
698
+ },
678
699
  },
679
700
  },
680
701
  init: {
@@ -1015,7 +1036,7 @@ exports.commands = {
1015
1036
  },
1016
1037
  },
1017
1038
  creatingComponent: (projectName) => `\nAdding a new component to ${chalk_1.default.bold(projectName)}\n`,
1018
- success: (componentName, multiple = false) => `${componentName} ${multiple ? 'were' : 'was'} successfully added to your app.`,
1039
+ success: (componentName, multiple = false) => `${componentName || 'An app'} ${multiple ? 'were' : 'was'} successfully added to your ${componentName ? 'app' : 'project'}.`,
1019
1040
  error: {
1020
1041
  failedToDownloadComponent: 'Failed to download project component. Please try again later.',
1021
1042
  maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
@@ -2661,11 +2682,14 @@ exports.lib = {
2661
2682
  enterAccountId: 'Enter the account ID for your account (the number under the DOMAIN column at https://app.hubspot.com/myaccounts-beta ): ',
2662
2683
  enterClientId: 'Enter your OAuth2 client ID: ',
2663
2684
  enterClientSecret: 'Enter your OAuth2 client secret: ',
2664
- enterPersonalAccessKey: 'Enter your personal access key: ',
2685
+ enterPersonalAccessKey: '[--personal-access-key] Enter your personal access key: ',
2665
2686
  selectScopes: 'Select access scopes (see https://developers.hubspot.com/docs/methods/oauth2/initiate-oauth-integration#scopes)',
2666
2687
  personalAccessKeySetupTitle: 'HubSpot Personal Access Key Setup',
2667
- personalAccessKeyBrowserOpenPrep: "A personal access key is required to authenticate the CLI to interact with your HubSpot account. We'll open a secure page in your default browser where you can view and copy your personal access key.",
2668
- personalAccessKeyBrowserOpenPrompt: 'Open HubSpot to copy your personal access key?',
2688
+ personalAccessKeyBrowserOpenPrep: 'A personal access key is required to authenticate the CLI to interact with your HubSpot account.',
2689
+ personalAccessKeyPromptChoices: {
2690
+ OPEN_BROWSER: 'Open HubSpot to copy your personal access key',
2691
+ PASTE_EXISTING: 'Enter existing personal access key',
2692
+ },
2669
2693
  logs: {
2670
2694
  openingWebBrowser: (url) => `Opening ${url} in your web browser`,
2671
2695
  },
@@ -12,9 +12,9 @@ const urls_1 = require("@hubspot/local-dev-lib/urls");
12
12
  const logger_1 = require("@hubspot/local-dev-lib/logger");
13
13
  const promptUtils_1 = require("./promptUtils");
14
14
  const accountNamePrompt_1 = require("./accountNamePrompt");
15
- const lang_1 = require("../lang");
16
15
  const ui_1 = require("../ui");
17
16
  const exitCodes_1 = require("../enums/exitCodes");
17
+ const en_1 = require("../../lang/en");
18
18
  /**
19
19
  * Displays notification to user that we are about to open the browser,
20
20
  * then opens their browser to the personal-access-key shortlink
@@ -23,24 +23,26 @@ async function personalAccessKeyPrompt({ env, account, }) {
23
23
  const websiteOrigin = (0, urls_1.getHubSpotWebsiteOrigin)(env);
24
24
  let url = `${websiteOrigin}/l/personal-access-key`;
25
25
  if (process.env.BROWSER !== 'none') {
26
- (0, ui_1.uiInfoSection)((0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.personalAccessKeySetupTitle`), () => {
27
- logger_1.logger.log((0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.personalAccessKeyBrowserOpenPrep`));
26
+ (0, ui_1.uiInfoSection)(en_1.lib.prompts.personalAccessKeyPrompt.personalAccessKeySetupTitle, () => {
27
+ logger_1.logger.log(en_1.lib.prompts.personalAccessKeyPrompt.personalAccessKeyBrowserOpenPrep);
28
28
  });
29
29
  if (account) {
30
30
  url = `${websiteOrigin}/personal-access-key/${account}`;
31
31
  }
32
- const { personalAcessKeyBrowserOpenPrep: shouldOpen } = await (0, promptUtils_1.promptUser)([
32
+ const { personalAcessKeyBrowserOpenPrep: choice } = await (0, promptUtils_1.promptUser)([
33
33
  PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP,
34
34
  ]);
35
- if (shouldOpen) {
36
- (0, open_1.default)(url, { url: true });
37
- }
38
- else {
35
+ if (!choice) {
39
36
  (0, config_1.deleteEmptyConfigFile)();
40
37
  process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
41
38
  }
39
+ if (choice ===
40
+ en_1.lib.prompts.personalAccessKeyPrompt.personalAccessKeyPromptChoices
41
+ .OPEN_BROWSER) {
42
+ (0, open_1.default)(url, { url: true });
43
+ logger_1.logger.log(en_1.lib.prompts.personalAccessKeyPrompt.logs.openingWebBrowser(url));
44
+ }
42
45
  }
43
- logger_1.logger.log((0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.logs.openingWebBrowser`, { url }));
44
46
  const { personalAccessKey } = await (0, promptUtils_1.promptUser)(PERSONAL_ACCESS_KEY);
45
47
  return {
46
48
  personalAccessKey,
@@ -49,52 +51,59 @@ async function personalAccessKeyPrompt({ env, account, }) {
49
51
  }
50
52
  const ACCOUNT_ID = {
51
53
  name: 'accountId',
52
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.enterAccountId`),
54
+ message: en_1.lib.prompts.personalAccessKeyPrompt.enterAccountId,
53
55
  type: 'number',
54
56
  validate(val) {
55
57
  if (!Number.isNaN(val) && val !== undefined && val > 0) {
56
58
  return true;
57
59
  }
58
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidAccountId`);
60
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors.invalidAccountId;
59
61
  },
60
62
  };
61
63
  const CLIENT_ID = {
62
64
  name: 'clientId',
63
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.enterClientId`),
65
+ message: en_1.lib.prompts.personalAccessKeyPrompt.enterClientId,
64
66
  validate(val) {
65
67
  if (typeof val !== 'string') {
66
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientId`);
68
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientId;
67
69
  }
68
70
  else if (val.length !== 36) {
69
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientIdLength`);
71
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
72
+ .invalidOauthClientIdLength;
70
73
  }
71
74
  return true;
72
75
  },
73
76
  };
74
77
  const CLIENT_SECRET = {
75
78
  name: 'clientSecret',
76
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.enterClientSecret`),
79
+ message: en_1.lib.prompts.personalAccessKeyPrompt.enterClientSecret,
77
80
  validate(val) {
78
81
  if (typeof val !== 'string') {
79
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientSecret`);
82
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
83
+ .invalidOauthClientSecret;
80
84
  }
81
85
  else if (val.length !== 36) {
82
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientSecretLength`);
86
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
87
+ .invalidOauthClientSecretLength;
83
88
  }
84
89
  else if (val[0] === '*') {
85
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientSecretCopy`);
90
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
91
+ .invalidOauthClientSecretCopy;
86
92
  }
87
93
  return true;
88
94
  },
89
95
  };
90
96
  const PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP = {
91
97
  name: 'personalAcessKeyBrowserOpenPrep',
92
- type: 'confirm',
93
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.personalAccessKeyBrowserOpenPrompt`),
98
+ type: 'list',
99
+ message: 'Choose your preferred method of authentication',
100
+ choices: Object.values(en_1.lib.prompts.personalAccessKeyPrompt.personalAccessKeyPromptChoices),
101
+ default: en_1.lib.prompts.personalAccessKeyPrompt.personalAccessKeyPromptChoices
102
+ .OPEN_BROWSER,
94
103
  };
95
104
  const PERSONAL_ACCESS_KEY = {
96
105
  name: 'personalAccessKey',
97
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.enterPersonalAccessKey`),
106
+ message: en_1.lib.prompts.personalAccessKeyPrompt.enterPersonalAccessKey,
98
107
  transformer: (val) => {
99
108
  if (!val)
100
109
  return val;
@@ -106,10 +115,12 @@ const PERSONAL_ACCESS_KEY = {
106
115
  },
107
116
  validate(val) {
108
117
  if (!val || typeof val !== 'string') {
109
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidPersonalAccessKey`);
118
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
119
+ .invalidPersonalAccessKey;
110
120
  }
111
121
  else if (val[0] === '•') {
112
- return (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.errors.invalidPersonalAccessKeyCopy`);
122
+ return en_1.lib.prompts.personalAccessKeyPrompt.errors
123
+ .invalidPersonalAccessKeyCopy;
113
124
  }
114
125
  return true;
115
126
  },
@@ -117,7 +128,7 @@ const PERSONAL_ACCESS_KEY = {
117
128
  const SCOPES = {
118
129
  type: 'checkbox',
119
130
  name: 'scopes',
120
- message: (0, lang_1.i18n)(`lib.prompts.personalAccessKeyPrompt.selectScopes`),
131
+ message: en_1.lib.prompts.personalAccessKeyPrompt.selectScopes,
121
132
  default: [...auth_1.DEFAULT_OAUTH_SCOPES],
122
133
  choices: [...auth_1.OAUTH_SCOPES],
123
134
  };
@@ -66,7 +66,7 @@ async function projectAddPromptV3(components, selectedFeatures) {
66
66
  {
67
67
  name: 'componentTemplate',
68
68
  message: en_1.lib.prompts.projectAddPrompt.selectType,
69
- when: selectedComponents.length === 0,
69
+ when: !selectedFeatures && selectedComponents.length === 0,
70
70
  type: 'checkbox',
71
71
  choices: components,
72
72
  },
@@ -4,10 +4,11 @@ export declare function confirmPrompt(message: string, options?: {
4
4
  defaultAnswer?: boolean;
5
5
  when?: PromptWhen;
6
6
  }): Promise<boolean>;
7
- export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, }: {
7
+ export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, validate, }: {
8
8
  choices: PromptChoices<T>;
9
9
  when?: PromptWhen;
10
10
  defaultAnswer?: string | number | boolean;
11
+ validate?: (input: T[]) => (boolean | string) | Promise<boolean | string>;
11
12
  }): Promise<T>;
12
13
  export declare function inputPrompt(message: string, { when, validate, defaultAnswer, }?: {
13
14
  when?: boolean | (() => boolean);
@@ -22,7 +22,7 @@ async function confirmPrompt(message, options = {}) {
22
22
  ]);
23
23
  return choice;
24
24
  }
25
- async function listPrompt(message, { choices, when, defaultAnswer, }) {
25
+ async function listPrompt(message, { choices, when, defaultAnswer, validate, }) {
26
26
  const { choice } = await promptUser([
27
27
  {
28
28
  name: 'choice',
@@ -31,6 +31,7 @@ async function listPrompt(message, { choices, when, defaultAnswer, }) {
31
31
  choices,
32
32
  when,
33
33
  default: defaultAnswer,
34
+ validate,
34
35
  },
35
36
  ]);
36
37
  return choice;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
4
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const tools_1 = require("./tools");
6
+ const server = new mcp_js_1.McpServer({
7
+ name: 'HubSpot CLI MCP Server',
8
+ version: '0.0.1',
9
+ description: 'Helps perform tasks for local development of HubSpot projects.',
10
+ capabilities: {
11
+ tools: {},
12
+ prompts: {},
13
+ },
14
+ });
15
+ (0, tools_1.registerProjectTools)(server);
16
+ // Start receiving messages on stdin and sending messages on stdout
17
+ const transport = new stdio_js_1.StdioServerTransport();
18
+ server.connect(transport);
@@ -0,0 +1,2 @@
1
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerProjectTools(mcpServer: McpServer): RegisteredTool[];
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerProjectTools = registerProjectTools;
4
+ const UploadProjectTools_1 = require("./project/UploadProjectTools");
5
+ const CreateProjectTool_1 = require("./project/CreateProjectTool");
6
+ const GuidedWalkthroughTool_1 = require("./project/GuidedWalkthroughTool");
7
+ const DeployProject_1 = require("./project/DeployProject");
8
+ const AddFeatureToProject_1 = require("./project/AddFeatureToProject");
9
+ function registerProjectTools(mcpServer) {
10
+ return [
11
+ UploadProjectTools_1.UploadProjectTools.register(mcpServer),
12
+ CreateProjectTool_1.CreateProjectTool.register(mcpServer),
13
+ GuidedWalkthroughTool_1.GuidedWalkthroughTool.register(mcpServer),
14
+ DeployProject_1.DeployProject.register(mcpServer),
15
+ AddFeatureToProject_1.AddFeatureToProject.register(mcpServer),
16
+ ];
17
+ }
@@ -0,0 +1,6 @@
1
+ import { Tool } from '../../types';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare class AddFeatureToProject extends Tool {
4
+ private static mcpServer;
5
+ static register(mcpServer: McpServer): RegisteredTool;
6
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddFeatureToProject = void 0;
4
+ const types_1 = require("../../types");
5
+ const zod_1 = require("zod");
6
+ const constants_1 = require("../../../lib/constants");
7
+ const command_1 = require("../../utils/command");
8
+ const constants_2 = require("./constants");
9
+ const project_1 = require("../../utils/project");
10
+ class AddFeatureToProject extends types_1.Tool {
11
+ static mcpServer;
12
+ static register(mcpServer) {
13
+ this.mcpServer = mcpServer;
14
+ return this.mcpServer.registerTool('add-feature-to-hubspot-project', {
15
+ title: 'Add feature to HubSpot Project',
16
+ description: 'Adds a feature to an existing HubSpot project',
17
+ inputSchema: {
18
+ absoluteProjectPath: constants_2.absoluteProjectPath,
19
+ addApp: zod_1.z.boolean().describe('Would you like to add an app?'),
20
+ distribution: zod_1.z
21
+ .optional(zod_1.z.union([
22
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
23
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
24
+ ]))
25
+ .describe('Private is used if you do not wish to distribute your application on the HubSpot marketplace. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.'),
26
+ auth: zod_1.z
27
+ .optional(zod_1.z.union([
28
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
29
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
30
+ ]))
31
+ .describe('Static uses a static non changing authentication token, and is only available for private distribution. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.')
32
+ .optional(),
33
+ features: zod_1.z
34
+ .array(zod_1.z
35
+ .union([
36
+ zod_1.z.literal('card'),
37
+ zod_1.z.literal('settings'),
38
+ zod_1.z.literal('app-function'),
39
+ zod_1.z.literal('webhooks'),
40
+ ])
41
+ .describe('The features to include in the project, multiple options can be selected'))
42
+ .optional(),
43
+ },
44
+ }, async ({ absoluteProjectPath, distribution, auth, features, addApp }) => {
45
+ try {
46
+ let command = `hs project add`;
47
+ const content = [];
48
+ if (distribution) {
49
+ command = (0, command_1.addFlag)(command, 'distribution', distribution);
50
+ }
51
+ else if (addApp) {
52
+ content.push({
53
+ type: 'text',
54
+ text: `Ask the user how they would you like to distribute the application? Options are ${constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${constants_1.APP_DISTRIBUTION_TYPES.PRIVATE}`,
55
+ });
56
+ }
57
+ if (auth) {
58
+ command = (0, command_1.addFlag)(command, 'auth', auth);
59
+ }
60
+ else if (addApp) {
61
+ content.push({
62
+ type: 'text',
63
+ text: `Ask the user which auth type they would like to use? Options are ${constants_1.APP_AUTH_TYPES.STATIC} and ${constants_1.APP_AUTH_TYPES.OAUTH}`,
64
+ });
65
+ }
66
+ if (content.length > 0) {
67
+ return {
68
+ content,
69
+ };
70
+ }
71
+ // If features isn't provided, pass an empty array to bypass the prompt
72
+ command = (0, command_1.addFlag)(command, 'features', features || []);
73
+ const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: stdout,
79
+ },
80
+ {
81
+ type: 'text',
82
+ text: stderr,
83
+ },
84
+ ],
85
+ };
86
+ }
87
+ catch (error) {
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text',
92
+ text: error instanceof Error ? error.message : `${error}`,
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ });
98
+ }
99
+ }
100
+ exports.AddFeatureToProject = AddFeatureToProject;
@@ -0,0 +1,6 @@
1
+ import { Tool } from '../../types';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare class CreateProjectTool extends Tool {
4
+ private static mcpServer;
5
+ static register(mcpServer: McpServer): RegisteredTool;
6
+ }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CreateProjectTool = void 0;
4
+ const types_1 = require("../../types");
5
+ const zod_1 = require("zod");
6
+ const constants_1 = require("../../../lib/constants");
7
+ const command_1 = require("../../utils/command");
8
+ const v3_1 = require("../../../lib/projects/create/v3");
9
+ const constants_2 = require("./constants");
10
+ const project_1 = require("../../utils/project");
11
+ class CreateProjectTool extends types_1.Tool {
12
+ static mcpServer;
13
+ static register(mcpServer) {
14
+ this.mcpServer = mcpServer;
15
+ return this.mcpServer.registerTool('create-hubspot-project', {
16
+ title: 'Create HubSpot Project',
17
+ description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
18
+ inputSchema: {
19
+ absoluteCurrentWorkingDirectory: constants_2.absoluteCurrentWorkingDirectory,
20
+ name: zod_1.z
21
+ .string()
22
+ .describe('The name of the project to be created. This name is how your project will appear in HubSpot.'),
23
+ destination: zod_1.z
24
+ .string()
25
+ .describe('Relative path to the directory the project will be created in. It is not recommended to use the current directory unless it is an empty directory'),
26
+ projectBase: zod_1.z
27
+ .union([zod_1.z.literal(v3_1.EMPTY_PROJECT), zod_1.z.literal(v3_1.PROJECT_WITH_APP)])
28
+ .describe('Empty will create and empty project, and app will create a project with an app inside of it.'),
29
+ distribution: zod_1.z
30
+ .optional(zod_1.z.union([
31
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
32
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
33
+ ]))
34
+ .describe('Private is used if you do not wish to distribute your application on the HubSpot marketplace. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.'),
35
+ auth: zod_1.z
36
+ .optional(zod_1.z.union([
37
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
38
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
39
+ ]))
40
+ .describe('Static uses a static non changing authentication token, and is only available for private distribution. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.')
41
+ .optional(),
42
+ features: zod_1.z
43
+ .array(zod_1.z
44
+ .union([
45
+ zod_1.z.literal('card'),
46
+ zod_1.z.literal('settings'),
47
+ zod_1.z.literal('app-function'),
48
+ zod_1.z.literal('webhooks'),
49
+ ])
50
+ .describe('The features to include in the project, multiple options can be selected'))
51
+ .optional(),
52
+ },
53
+ }, async ({ name, destination, projectBase, distribution, auth, features, absoluteCurrentWorkingDirectory, }) => {
54
+ let command = (0, command_1.addFlag)('hs project create', 'platform-version', '2025.2');
55
+ const content = [];
56
+ if (name) {
57
+ command = (0, command_1.addFlag)(command, 'name', name);
58
+ }
59
+ if (destination) {
60
+ command = (0, command_1.addFlag)(command, 'dest', destination);
61
+ }
62
+ if (projectBase) {
63
+ command = (0, command_1.addFlag)(command, 'project-base', projectBase);
64
+ }
65
+ if (distribution) {
66
+ command = (0, command_1.addFlag)(command, 'distribution', distribution);
67
+ }
68
+ else if (projectBase === v3_1.PROJECT_WITH_APP) {
69
+ content.push({
70
+ type: 'text',
71
+ text: `Ask the user how they would you like to distribute the application? Options are ${constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${constants_1.APP_DISTRIBUTION_TYPES.PRIVATE}`,
72
+ });
73
+ }
74
+ if (auth) {
75
+ command = (0, command_1.addFlag)(command, 'auth', auth);
76
+ }
77
+ else if (projectBase === v3_1.PROJECT_WITH_APP) {
78
+ content.push({
79
+ type: 'text',
80
+ text: `Ask the user which auth type they would like to use? Options are ${constants_1.APP_AUTH_TYPES.STATIC} and ${constants_1.APP_AUTH_TYPES.OAUTH}`,
81
+ });
82
+ }
83
+ if (content.length > 0) {
84
+ return {
85
+ content,
86
+ };
87
+ }
88
+ if (features && features.length) {
89
+ command = (0, command_1.addFlag)(command, 'features', features);
90
+ }
91
+ try {
92
+ const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteCurrentWorkingDirectory, command);
93
+ return {
94
+ content: [
95
+ {
96
+ type: 'text',
97
+ text: stdout,
98
+ },
99
+ {
100
+ type: 'text',
101
+ text: stderr,
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ catch (error) {
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: error instanceof Error ? error.message : `${error}`,
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ });
117
+ }
118
+ }
119
+ exports.CreateProjectTool = CreateProjectTool;
@@ -0,0 +1,6 @@
1
+ import { Tool } from '../../types';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare class DeployProject extends Tool {
4
+ private static mcpServer;
5
+ static register(mcpServer: McpServer): RegisteredTool;
6
+ }