@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.
- package/bin/cli.js +2 -0
- package/commands/account/auth.d.ts +2 -0
- package/commands/account/auth.js +15 -7
- package/commands/auth.d.ts +2 -0
- package/commands/auth.js +17 -8
- package/commands/hubdb/list.d.ts +4 -0
- package/commands/hubdb/list.js +83 -0
- package/commands/hubdb.js +2 -0
- package/commands/mcp/setup.d.ts +13 -0
- package/commands/mcp/setup.js +248 -0
- package/commands/mcp/start.d.ts +10 -0
- package/commands/mcp/start.js +66 -0
- package/commands/mcp.d.ts +10 -0
- package/commands/mcp.js +20 -0
- package/commands/project/listBuilds.js +2 -5
- package/lang/en.d.ts +26 -4
- package/lang/en.js +28 -4
- package/lib/prompts/personalAccessKeyPrompt.js +35 -24
- package/lib/prompts/projectAddPrompt.js +1 -1
- package/lib/prompts/promptUtils.d.ts +2 -1
- package/lib/prompts/promptUtils.js +2 -1
- package/mcp-server/server.d.ts +1 -0
- package/mcp-server/server.js +18 -0
- package/mcp-server/tools/index.d.ts +2 -0
- package/mcp-server/tools/index.js +17 -0
- package/mcp-server/tools/project/AddFeatureToProject.d.ts +6 -0
- package/mcp-server/tools/project/AddFeatureToProject.js +100 -0
- package/mcp-server/tools/project/CreateProjectTool.d.ts +6 -0
- package/mcp-server/tools/project/CreateProjectTool.js +119 -0
- package/mcp-server/tools/project/DeployProject.d.ts +6 -0
- package/mcp-server/tools/project/DeployProject.js +50 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +6 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +54 -0
- package/mcp-server/tools/project/UploadProjectTools.d.ts +5 -0
- package/mcp-server/tools/project/UploadProjectTools.js +26 -0
- package/mcp-server/tools/project/constants.d.ts +3 -0
- package/mcp-server/tools/project/constants.js +13 -0
- package/mcp-server/types.d.ts +8 -0
- package/mcp-server/types.js +7 -0
- package/mcp-server/utils/command.d.ts +3 -0
- package/mcp-server/utils/command.js +16 -0
- package/mcp-server/utils/project.d.ts +5 -0
- package/mcp-server/utils/project.js +17 -0
- 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:
|
|
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.
|
|
2672
|
-
readonly
|
|
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:
|
|
2668
|
-
|
|
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)(
|
|
27
|
-
logger_1.logger.log(
|
|
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:
|
|
32
|
+
const { personalAcessKeyBrowserOpenPrep: choice } = await (0, promptUtils_1.promptUser)([
|
|
33
33
|
PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP,
|
|
34
34
|
]);
|
|
35
|
-
if (
|
|
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:
|
|
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
|
|
60
|
+
return en_1.lib.prompts.personalAccessKeyPrompt.errors.invalidAccountId;
|
|
59
61
|
},
|
|
60
62
|
};
|
|
61
63
|
const CLIENT_ID = {
|
|
62
64
|
name: 'clientId',
|
|
63
|
-
message:
|
|
65
|
+
message: en_1.lib.prompts.personalAccessKeyPrompt.enterClientId,
|
|
64
66
|
validate(val) {
|
|
65
67
|
if (typeof val !== 'string') {
|
|
66
|
-
return
|
|
68
|
+
return en_1.lib.prompts.personalAccessKeyPrompt.errors.invalidOauthClientId;
|
|
67
69
|
}
|
|
68
70
|
else if (val.length !== 36) {
|
|
69
|
-
return
|
|
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:
|
|
79
|
+
message: en_1.lib.prompts.personalAccessKeyPrompt.enterClientSecret,
|
|
77
80
|
validate(val) {
|
|
78
81
|
if (typeof val !== 'string') {
|
|
79
|
-
return
|
|
82
|
+
return en_1.lib.prompts.personalAccessKeyPrompt.errors
|
|
83
|
+
.invalidOauthClientSecret;
|
|
80
84
|
}
|
|
81
85
|
else if (val.length !== 36) {
|
|
82
|
-
return
|
|
86
|
+
return en_1.lib.prompts.personalAccessKeyPrompt.errors
|
|
87
|
+
.invalidOauthClientSecretLength;
|
|
83
88
|
}
|
|
84
89
|
else if (val[0] === '*') {
|
|
85
|
-
return
|
|
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: '
|
|
93
|
-
message:
|
|
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:
|
|
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
|
|
118
|
+
return en_1.lib.prompts.personalAccessKeyPrompt.errors
|
|
119
|
+
.invalidPersonalAccessKey;
|
|
110
120
|
}
|
|
111
121
|
else if (val[0] === '•') {
|
|
112
|
-
return
|
|
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:
|
|
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,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,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,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;
|