@hubspot/cli 7.7.15-experimental.0 → 7.7.16-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.
@@ -1,13 +1,7 @@
1
- import { ArgumentsCamelCase, Argv } from 'yargs';
1
+ import { YargsCommandModule } from '../../types/Yargs';
2
2
  interface MCPSetupArgs {
3
3
  client?: string[];
4
4
  addDocsSearch?: boolean;
5
5
  }
6
- declare function handler(args: ArgumentsCamelCase<MCPSetupArgs>): Promise<void>;
7
- declare const _default: {
8
- command: string[];
9
- describe: undefined;
10
- builder: (yargs: Argv) => Promise<Argv<{}>>;
11
- handler: typeof handler;
12
- };
13
- export default _default;
6
+ declare const mcpSetupCommand: YargsCommandModule<unknown, MCPSetupArgs>;
7
+ export default mcpSetupCommand;
@@ -1,50 +1,12 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const child_process_1 = require("child_process");
7
- const util_1 = require("util");
8
- const path_1 = __importDefault(require("path"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const os_1 = __importDefault(require("os"));
11
- const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
12
3
  const exitCodes_1 = require("../../lib/enums/exitCodes");
13
- const promptUtils_1 = require("../../lib/prompts/promptUtils");
14
4
  const yargsUtils_1 = require("../../lib/yargsUtils");
15
5
  const en_1 = require("../../lang/en");
16
- const errorHandlers_1 = require("../../lib/errorHandlers");
17
6
  const logger_1 = require("../../lib/ui/logger");
7
+ const setup_1 = require("../../lib/mcp/setup");
18
8
  const command = ['setup', 'update'];
19
9
  const describe = undefined; // Leave hidden for now
20
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
21
- const claudeCode = 'claude';
22
- const windsurf = 'windsurf';
23
- const cursor = 'cursor';
24
- const mcpServerName = 'hubspot-cli-mcp';
25
- const supportedMintlifyClients = [windsurf, cursor];
26
- const supportedTools = [
27
- { name: en_1.commands.mcp.setup.claudeCode, value: claudeCode },
28
- { name: en_1.commands.mcp.setup.cursor, value: cursor },
29
- { name: en_1.commands.mcp.setup.windsurf, value: windsurf },
30
- ];
31
- const hsCommand = 'hs';
32
- const mcpCommandArgs = ['mcp', 'start'];
33
- function setupBuilder(yargs) {
34
- yargs
35
- .option('client', {
36
- describe: en_1.commands.mcp.setup.args.client,
37
- type: 'array',
38
- choices: [...supportedTools.map(tool => tool.value)],
39
- })
40
- .option('add-docs-search', {
41
- type: 'boolean',
42
- });
43
- return yargs;
44
- }
45
- const builder = (0, yargsUtils_1.makeYargsBuilder)(setupBuilder, command, describe, {
46
- useGlobalOptions: true,
47
- });
48
10
  async function handler(args) {
49
11
  try {
50
12
  await import('@modelcontextprotocol/sdk/server/mcp.js');
@@ -53,192 +15,36 @@ async function handler(args) {
53
15
  logger_1.uiLogger.error(en_1.commands.mcp.setup.errors.needsNode20);
54
16
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
55
17
  }
56
- const derivedTargets = await addMcpServerToConfig(args.client);
57
- if (args.addDocsSearch) {
58
- logger_1.uiLogger.info(en_1.commands.mcp.setup.installingDocSearch);
59
- logger_1.uiLogger.log('');
60
- await new Promise(() => {
61
- const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
62
- const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
63
- const childProcess = (0, child_process_1.spawn)(`npx`, docsSearchClients && docsSearchClients.length
64
- ? [...subcommands, '--client', ...docsSearchClients]
65
- : subcommands, {
66
- stdio: 'inherit',
67
- });
68
- childProcess.on('exit', code => {
69
- if (code !== 0) {
70
- process.exit(exitCodes_1.EXIT_CODES.ERROR);
71
- }
72
- process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
73
- });
74
- });
75
- }
76
- process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
77
- }
78
- async function addMcpServerToConfig(targets) {
79
18
  try {
80
- let derivedTargets = [];
81
- if (!targets || targets.length === 0) {
82
- const { selectedTargets } = await (0, promptUtils_1.promptUser)({
83
- name: 'selectedTargets',
84
- type: 'checkbox',
85
- message: en_1.commands.mcp.setup.prompts.targets,
86
- choices: supportedTools,
87
- validate: (choices) => {
88
- return choices.length === 0
89
- ? en_1.commands.mcp.setup.prompts.targetsRequired
90
- : true;
91
- },
92
- });
93
- derivedTargets = selectedTargets;
94
- }
95
- else {
96
- derivedTargets = targets;
97
- }
98
- SpinniesManager_1.default.init();
99
- if (derivedTargets.includes(claudeCode)) {
100
- await runSetupFunction(setupClaudeCode);
101
- }
102
- if (derivedTargets.includes(cursor)) {
103
- await runSetupFunction(setupCursor);
19
+ const derivedTargets = await (0, setup_1.addMcpServerToConfig)(args.client);
20
+ if (args.addDocsSearch) {
21
+ await (0, setup_1.addMintlifyMcpServer)(derivedTargets);
104
22
  }
105
- if (derivedTargets.includes(windsurf)) {
106
- await runSetupFunction(setupWindsurf);
107
- }
108
- logger_1.uiLogger.info(en_1.commands.mcp.setup.success(derivedTargets));
109
- return derivedTargets;
110
- }
111
- catch (error) {
112
- SpinniesManager_1.default.fail('mcpSetup', {
113
- text: en_1.commands.mcp.setup.spinners.failedToConfigure,
114
- });
115
- (0, errorHandlers_1.logError)(error);
116
- process.exit(exitCodes_1.EXIT_CODES.ERROR);
117
23
  }
118
- }
119
- async function runSetupFunction(func) {
120
- const result = await func();
121
- if (!result) {
24
+ catch (e) {
122
25
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
123
26
  }
27
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
124
28
  }
125
- function setupMcpConfigFile(config) {
126
- try {
127
- SpinniesManager_1.default.add('spinner', {
128
- text: config.configuringMessage,
129
- });
130
- if (!fs_1.default.existsSync(config.configPath)) {
131
- SpinniesManager_1.default.succeed('spinner', {
132
- text: config.configMissingMessage ||
133
- `Config file not found at ${config.configPath}`,
134
- });
135
- return false;
136
- }
137
- let mcpConfig = {};
138
- try {
139
- const configContent = fs_1.default.readFileSync(config.configPath, 'utf8');
140
- mcpConfig = JSON.parse(configContent);
141
- }
142
- catch (error) {
143
- SpinniesManager_1.default.fail('spinner', {
144
- text: config.failedMessage,
145
- });
146
- (0, errorHandlers_1.logError)(error);
147
- return false;
148
- }
149
- // Initialize mcpServers if it doesn't exist
150
- if (!mcpConfig.mcpServers) {
151
- mcpConfig.mcpServers = {};
152
- }
153
- // Add or update HubSpot CLI MCP server
154
- mcpConfig.mcpServers[mcpServerName] = {
155
- command: hsCommand,
156
- args: mcpCommandArgs,
157
- };
158
- // Write the updated config
159
- fs_1.default.writeFileSync(config.configPath, JSON.stringify(mcpConfig, null, 2));
160
- SpinniesManager_1.default.succeed('spinner', {
161
- text: config.configuredMessage,
162
- });
163
- return true;
164
- }
165
- catch (error) {
166
- SpinniesManager_1.default.fail('spinner', {
167
- text: config.failedMessage,
168
- });
169
- (0, errorHandlers_1.logError)(error);
170
- return false;
171
- }
172
- }
173
- async function setupClaudeCode() {
174
- try {
175
- SpinniesManager_1.default.add('claudeCode', {
176
- text: en_1.commands.mcp.setup.spinners.configuringClaudeCode,
177
- });
178
- try {
179
- // Check if claude command is available
180
- await execAsync('claude --version');
181
- // Run claude mcp add command
182
- const mcpConfig = JSON.stringify({
183
- type: 'stdio',
184
- command: hsCommand,
185
- args: mcpCommandArgs,
186
- });
187
- const { stdout } = await execAsync('claude mcp list');
188
- if (stdout.includes(mcpServerName)) {
189
- SpinniesManager_1.default.update('claudeCode', {
190
- text: en_1.commands.mcp.setup.spinners.alreadyInstalled,
191
- });
192
- await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
193
- }
194
- await execAsync(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
195
- SpinniesManager_1.default.succeed('claudeCode', {
196
- text: en_1.commands.mcp.setup.spinners.configuredClaudeCode,
197
- });
198
- return true;
199
- }
200
- catch (error) {
201
- if (error instanceof Error &&
202
- error.message.includes('claude: command not found')) {
203
- SpinniesManager_1.default.fail('claudeCode', {
204
- text: en_1.commands.mcp.setup.spinners.claudeCodeNotFound,
205
- });
206
- }
207
- else {
208
- SpinniesManager_1.default.fail('claudeCode', {
209
- text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
210
- });
211
- (0, errorHandlers_1.logError)(error);
212
- }
213
- return false;
214
- }
215
- }
216
- catch (error) {
217
- SpinniesManager_1.default.fail('claudeCode', {
218
- text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
219
- });
220
- (0, errorHandlers_1.logError)(error);
221
- return false;
222
- }
223
- }
224
- function setupCursor() {
225
- const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
226
- return setupMcpConfigFile({
227
- configPath: cursorConfigPath,
228
- configuringMessage: en_1.commands.mcp.setup.spinners.configuringCursor,
229
- configuredMessage: en_1.commands.mcp.setup.spinners.configuredCursor,
230
- failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureCursor,
231
- configMissingMessage: en_1.commands.mcp.setup.spinners.noCursorMcpFile(cursorConfigPath),
232
- });
233
- }
234
- function setupWindsurf() {
235
- const windsurfConfigPath = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
236
- return setupMcpConfigFile({
237
- configPath: windsurfConfigPath,
238
- configuringMessage: en_1.commands.mcp.setup.spinners.configuringWindsurf,
239
- configuredMessage: en_1.commands.mcp.setup.spinners.configuredWindsurf,
240
- failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureWindsurf,
241
- configMissingMessage: en_1.commands.mcp.setup.spinners.noWindsurfFile(windsurfConfigPath),
29
+ function setupBuilder(yargs) {
30
+ yargs
31
+ .option('client', {
32
+ describe: en_1.commands.mcp.setup.args.client,
33
+ type: 'array',
34
+ choices: [...setup_1.supportedTools.map(tool => tool.value)],
35
+ })
36
+ .option('add-docs-search', {
37
+ type: 'boolean',
242
38
  });
39
+ return yargs;
243
40
  }
244
- exports.default = { command, describe, builder, handler };
41
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(setupBuilder, command, describe, {
42
+ useGlobalOptions: true,
43
+ });
44
+ const mcpSetupCommand = {
45
+ command,
46
+ describe,
47
+ handler,
48
+ builder,
49
+ };
50
+ exports.default = mcpSetupCommand;
@@ -1,9 +1,3 @@
1
- import { Argv } from 'yargs';
2
- declare function handler(): Promise<void>;
3
- declare const _default: {
4
- command: string;
5
- describe: undefined;
6
- builder: (yargs: Argv) => Promise<Argv<{}>>;
7
- handler: typeof handler;
8
- };
9
- export default _default;
1
+ import { YargsCommandModule } from '../../types/Yargs';
2
+ declare const mcpStartCommand: YargsCommandModule<unknown, unknown>;
3
+ export default mcpStartCommand;
@@ -11,14 +11,9 @@ const yargsUtils_1 = require("../../lib/yargsUtils");
11
11
  const logger_1 = require("../../lib/ui/logger");
12
12
  const errorHandlers_1 = require("../../lib/errorHandlers");
13
13
  const en_1 = require("../../lang/en");
14
+ const process_1 = require("../../lib/process");
14
15
  const command = 'start';
15
16
  const describe = undefined; // Leave hidden for now
16
- function startBuilder(yargs) {
17
- return yargs;
18
- }
19
- const builder = (0, yargsUtils_1.makeYargsBuilder)(startBuilder, command, describe, {
20
- useGlobalOptions: true,
21
- });
22
17
  async function handler() {
23
18
  try {
24
19
  await import('@modelcontextprotocol/sdk/server/mcp.js');
@@ -54,13 +49,7 @@ async function startMcpServer() {
54
49
  child.on('close', () => {
55
50
  logger_1.uiLogger.info(en_1.commands.mcp.start.stoppedSuccessfully);
56
51
  });
57
- // Handle graceful shutdown
58
- process.on('SIGINT', () => {
59
- logger_1.uiLogger.info(en_1.commands.mcp.start.shuttingDown);
60
- child.kill('SIGTERM');
61
- process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
62
- });
63
- process.on('SIGTERM', () => {
52
+ (0, process_1.handleExit)(() => {
64
53
  logger_1.uiLogger.info(en_1.commands.mcp.start.shuttingDown);
65
54
  child.kill('SIGTERM');
66
55
  process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
@@ -71,4 +60,16 @@ async function startMcpServer() {
71
60
  (0, errorHandlers_1.logError)(error);
72
61
  }
73
62
  }
74
- exports.default = { command, describe, builder, handler };
63
+ function startBuilder(yargs) {
64
+ return yargs;
65
+ }
66
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(startBuilder, command, describe, {
67
+ useGlobalOptions: true,
68
+ });
69
+ const mcpStartCommand = {
70
+ command,
71
+ describe,
72
+ handler,
73
+ builder,
74
+ };
75
+ exports.default = mcpStartCommand;
package/commands/mcp.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const start_1 = __importDefault(require("./mcp/start"));
7
7
  const setup_1 = __importDefault(require("./mcp/setup"));
8
8
  const yargsUtils_1 = require("../lib/yargsUtils");
9
+ const en_1 = require("../lang/en");
9
10
  const command = 'mcp';
10
11
  // Leave this as undefined to hide the command
11
12
  const describe = undefined;
@@ -13,7 +14,7 @@ function mcpBuilder(yargs) {
13
14
  yargs.command(start_1.default).command(setup_1.default).demandCommand(1, '');
14
15
  return yargs;
15
16
  }
16
- const builder = (0, yargsUtils_1.makeYargsBuilder)(mcpBuilder, command, describe, {
17
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(mcpBuilder, command, en_1.commands.mcp.describe, {
17
18
  useGlobalOptions: true,
18
19
  });
19
20
  const mcpCommand = {
package/lang/en.d.ts CHANGED
@@ -765,6 +765,7 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
765
765
  readonly tailLogs: (functionPath: string, accountId: string) => string;
766
766
  };
767
767
  readonly mcp: {
768
+ readonly describe: "Commands for managing HubSpot MCP servers";
768
769
  readonly setup: {
769
770
  readonly installingDocSearch: "Adding the docs-search mcp server";
770
771
  readonly claudeCode: "Claude Code";
package/lang/en.js CHANGED
@@ -777,6 +777,7 @@ exports.commands = {
777
777
  tailLogs: (functionPath, accountId) => `Waiting for log entries for "${functionPath}" on account "${accountId}".\n`,
778
778
  },
779
779
  mcp: {
780
+ describe: 'Commands for managing HubSpot MCP servers',
780
781
  setup: {
781
782
  installingDocSearch: 'Adding the docs-search mcp server',
782
783
  claudeCode: 'Claude Code',
@@ -0,0 +1,12 @@
1
+ export declare const supportedTools: ({
2
+ name: "Claude Code";
3
+ value: string;
4
+ } | {
5
+ name: "Cursor";
6
+ value: string;
7
+ } | {
8
+ name: "Windsurf";
9
+ value: string;
10
+ })[];
11
+ export declare function addMintlifyMcpServer(installTargets: string[]): Promise<void>;
12
+ export declare function addMcpServerToConfig(targets: string[] | undefined): Promise<string[]>;
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.supportedTools = void 0;
7
+ exports.addMintlifyMcpServer = addMintlifyMcpServer;
8
+ exports.addMcpServerToConfig = addMcpServerToConfig;
9
+ const logger_1 = require("../ui/logger");
10
+ const en_1 = require("../../lang/en");
11
+ const child_process_1 = require("child_process");
12
+ const promptUtils_1 = require("../prompts/promptUtils");
13
+ const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
14
+ const errorHandlers_1 = require("../errorHandlers");
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const command_1 = require("../../mcp-server/utils/command");
17
+ const path_1 = __importDefault(require("path"));
18
+ const os_1 = __importDefault(require("os"));
19
+ const mcpServerName = 'hubspot-cli-mcp';
20
+ const claudeCode = 'claude';
21
+ const windsurf = 'windsurf';
22
+ const cursor = 'cursor';
23
+ const supportedMintlifyClients = [windsurf, cursor];
24
+ exports.supportedTools = [
25
+ { name: en_1.commands.mcp.setup.claudeCode, value: claudeCode },
26
+ { name: en_1.commands.mcp.setup.cursor, value: cursor },
27
+ { name: en_1.commands.mcp.setup.windsurf, value: windsurf },
28
+ ];
29
+ const hsCommand = 'hs';
30
+ const mcpCommandArgs = ['mcp', 'start'];
31
+ async function addMintlifyMcpServer(installTargets) {
32
+ await runSetupFunction(() => setupMintlify(installTargets));
33
+ }
34
+ async function setupMintlify(derivedTargets) {
35
+ logger_1.uiLogger.info(en_1.commands.mcp.setup.installingDocSearch);
36
+ logger_1.uiLogger.log('');
37
+ return new Promise(resolve => {
38
+ const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
39
+ const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
40
+ const childProcess = (0, child_process_1.spawn)(`npx`, docsSearchClients && docsSearchClients.length
41
+ ? [...subcommands, '--client', ...docsSearchClients]
42
+ : subcommands, {
43
+ stdio: 'inherit',
44
+ });
45
+ childProcess.on('exit', code => {
46
+ if (code !== 0) {
47
+ resolve(false);
48
+ }
49
+ resolve(true);
50
+ });
51
+ });
52
+ }
53
+ async function addMcpServerToConfig(targets) {
54
+ try {
55
+ let derivedTargets = [];
56
+ if (!targets || targets.length === 0) {
57
+ const { selectedTargets } = await (0, promptUtils_1.promptUser)({
58
+ name: 'selectedTargets',
59
+ type: 'checkbox',
60
+ message: en_1.commands.mcp.setup.prompts.targets,
61
+ choices: exports.supportedTools,
62
+ validate: (choices) => {
63
+ return choices.length === 0
64
+ ? en_1.commands.mcp.setup.prompts.targetsRequired
65
+ : true;
66
+ },
67
+ });
68
+ derivedTargets = selectedTargets;
69
+ }
70
+ else {
71
+ derivedTargets = targets;
72
+ }
73
+ SpinniesManager_1.default.init();
74
+ if (derivedTargets.includes(claudeCode)) {
75
+ await runSetupFunction(setupClaudeCode);
76
+ }
77
+ if (derivedTargets.includes(cursor)) {
78
+ await runSetupFunction(setupCursor);
79
+ }
80
+ if (derivedTargets.includes(windsurf)) {
81
+ await runSetupFunction(setupWindsurf);
82
+ }
83
+ logger_1.uiLogger.info(en_1.commands.mcp.setup.success(derivedTargets));
84
+ return derivedTargets;
85
+ }
86
+ catch (error) {
87
+ SpinniesManager_1.default.fail('mcpSetup', {
88
+ text: en_1.commands.mcp.setup.spinners.failedToConfigure,
89
+ });
90
+ throw error;
91
+ }
92
+ }
93
+ async function runSetupFunction(func) {
94
+ const result = await func();
95
+ if (!result) {
96
+ throw new Error();
97
+ }
98
+ }
99
+ function setupMcpConfigFile(config) {
100
+ try {
101
+ SpinniesManager_1.default.add('spinner', {
102
+ text: config.configuringMessage,
103
+ });
104
+ if (!fs_1.default.existsSync(config.configPath)) {
105
+ SpinniesManager_1.default.fail('spinner', {
106
+ text: config.configMissingMessage,
107
+ });
108
+ return false;
109
+ }
110
+ let mcpConfig = {};
111
+ try {
112
+ const configContent = fs_1.default.readFileSync(config.configPath, 'utf8');
113
+ mcpConfig = JSON.parse(configContent);
114
+ }
115
+ catch (error) {
116
+ SpinniesManager_1.default.fail('spinner', {
117
+ text: config.failedMessage,
118
+ });
119
+ (0, errorHandlers_1.logError)(error);
120
+ return false;
121
+ }
122
+ // Initialize mcpServers if it doesn't exist
123
+ if (!mcpConfig.mcpServers) {
124
+ mcpConfig.mcpServers = {};
125
+ }
126
+ // Add or update HubSpot CLI MCP server
127
+ mcpConfig.mcpServers[mcpServerName] = {
128
+ command: hsCommand,
129
+ args: mcpCommandArgs,
130
+ };
131
+ // Write the updated config
132
+ fs_1.default.writeFileSync(config.configPath, JSON.stringify(mcpConfig, null, 2));
133
+ SpinniesManager_1.default.succeed('spinner', {
134
+ text: config.configuredMessage,
135
+ });
136
+ return true;
137
+ }
138
+ catch (error) {
139
+ SpinniesManager_1.default.fail('spinner', {
140
+ text: config.failedMessage,
141
+ });
142
+ (0, errorHandlers_1.logError)(error);
143
+ return false;
144
+ }
145
+ }
146
+ async function setupClaudeCode() {
147
+ try {
148
+ SpinniesManager_1.default.add('claudeCode', {
149
+ text: en_1.commands.mcp.setup.spinners.configuringClaudeCode,
150
+ });
151
+ try {
152
+ // Check if claude command is available
153
+ await (0, command_1.execAsync)('claude --version');
154
+ // Run claude mcp add command
155
+ const mcpConfig = JSON.stringify({
156
+ type: 'stdio',
157
+ command: hsCommand,
158
+ args: mcpCommandArgs,
159
+ });
160
+ const { stdout } = await (0, command_1.execAsync)('claude mcp list');
161
+ if (stdout.includes(mcpServerName)) {
162
+ SpinniesManager_1.default.update('claudeCode', {
163
+ text: en_1.commands.mcp.setup.spinners.alreadyInstalled,
164
+ });
165
+ await (0, command_1.execAsync)(`claude mcp remove "${mcpServerName}" --scope user`);
166
+ }
167
+ await (0, command_1.execAsync)(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
168
+ SpinniesManager_1.default.succeed('claudeCode', {
169
+ text: en_1.commands.mcp.setup.spinners.configuredClaudeCode,
170
+ });
171
+ return true;
172
+ }
173
+ catch (error) {
174
+ if (error instanceof Error &&
175
+ error.message.includes('claude: command not found')) {
176
+ SpinniesManager_1.default.fail('claudeCode', {
177
+ text: en_1.commands.mcp.setup.spinners.claudeCodeNotFound,
178
+ });
179
+ }
180
+ else {
181
+ SpinniesManager_1.default.fail('claudeCode', {
182
+ text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
183
+ });
184
+ (0, errorHandlers_1.logError)(error);
185
+ }
186
+ return false;
187
+ }
188
+ }
189
+ catch (error) {
190
+ SpinniesManager_1.default.fail('claudeCode', {
191
+ text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
192
+ });
193
+ (0, errorHandlers_1.logError)(error);
194
+ return false;
195
+ }
196
+ }
197
+ function setupCursor() {
198
+ const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
199
+ return setupMcpConfigFile({
200
+ configPath: cursorConfigPath,
201
+ configuringMessage: en_1.commands.mcp.setup.spinners.configuringCursor,
202
+ configuredMessage: en_1.commands.mcp.setup.spinners.configuredCursor,
203
+ failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureCursor,
204
+ configMissingMessage: en_1.commands.mcp.setup.spinners.noCursorMcpFile(cursorConfigPath),
205
+ });
206
+ }
207
+ function setupWindsurf() {
208
+ const windsurfConfigPath = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
209
+ return setupMcpConfigFile({
210
+ configPath: windsurfConfigPath,
211
+ configuringMessage: en_1.commands.mcp.setup.spinners.configuringWindsurf,
212
+ configuredMessage: en_1.commands.mcp.setup.spinners.configuredWindsurf,
213
+ failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureWindsurf,
214
+ configMissingMessage: en_1.commands.mcp.setup.spinners.noWindsurfFile(windsurfConfigPath),
215
+ });
216
+ }
@@ -7,11 +7,12 @@ const constants_1 = require("../../../lib/constants");
7
7
  const command_1 = require("../../utils/command");
8
8
  const constants_2 = require("./constants");
9
9
  const project_1 = require("../../utils/project");
10
+ const content_1 = require("../../utils/content");
10
11
  const inputSchema = {
11
12
  absoluteProjectPath: constants_2.absoluteProjectPath,
12
13
  addApp: zod_1.z
13
14
  .boolean()
14
- .describe('Should an app be added? If there is no app in the project, and app must be added to add a feature'),
15
+ .describe('Should an app be added? If there is no app in the project, an app must be added to add a feature'),
15
16
  distribution: zod_1.z
16
17
  .optional(zod_1.z.union([
17
18
  zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
@@ -51,19 +52,13 @@ class AddFeatureToProject extends types_1.Tool {
51
52
  command = (0, command_1.addFlag)(command, 'distribution', distribution);
52
53
  }
53
54
  else if (addApp) {
54
- content.push({
55
- type: 'text',
56
- 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}`,
57
- });
55
+ content.push((0, content_1.formatTextContent)(`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}`));
58
56
  }
59
57
  if (auth) {
60
58
  command = (0, command_1.addFlag)(command, 'auth', auth);
61
59
  }
62
60
  else if (addApp) {
63
- content.push({
64
- type: 'text',
65
- 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}`,
66
- });
61
+ content.push((0, content_1.formatTextContent)(`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}`));
67
62
  }
68
63
  if (content.length > 0) {
69
64
  return {
@@ -73,28 +68,10 @@ class AddFeatureToProject extends types_1.Tool {
73
68
  // If features isn't provided, pass an empty array to bypass the prompt
74
69
  command = (0, command_1.addFlag)(command, 'features', features || []);
75
70
  const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
76
- return {
77
- content: [
78
- {
79
- type: 'text',
80
- text: stdout,
81
- },
82
- {
83
- type: 'text',
84
- text: stderr,
85
- },
86
- ],
87
- };
71
+ return (0, content_1.formatTextContents)(stdout, stderr);
88
72
  }
89
73
  catch (error) {
90
- return {
91
- content: [
92
- {
93
- type: 'text',
94
- text: error instanceof Error ? error.message : `${error}`,
95
- },
96
- ],
97
- };
74
+ return (0, content_1.formatTextContents)(error instanceof Error ? error.message : `${error}`);
98
75
  }
99
76
  }
100
77
  register() {
@@ -8,6 +8,7 @@ const command_1 = require("../../utils/command");
8
8
  const v3_1 = require("../../../lib/projects/create/v3");
9
9
  const constants_2 = require("./constants");
10
10
  const project_1 = require("../../utils/project");
11
+ const content_1 = require("../../utils/content");
11
12
  const inputSchema = {
12
13
  absoluteCurrentWorkingDirectory: constants_2.absoluteCurrentWorkingDirectory,
13
14
  name: zod_1.z
@@ -15,10 +16,10 @@ const inputSchema = {
15
16
  .describe('The name of the project to be created. This name is how your project will appear in HubSpot.'),
16
17
  destination: zod_1.z
17
18
  .string()
18
- .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'),
19
+ .describe('Relative path to the directory the project will be created in. DO NOT use the current directory unless the user has explicitly stated to do so.'),
19
20
  projectBase: zod_1.z
20
21
  .union([zod_1.z.literal(v3_1.EMPTY_PROJECT), zod_1.z.literal(v3_1.PROJECT_WITH_APP)])
21
- .describe('Empty will create and empty project, and app will create a project with an app inside of it.'),
22
+ .describe('Empty will create an empty project, and app will create a project with an app inside of it.'),
22
23
  distribution: zod_1.z
23
24
  .optional(zod_1.z.union([
24
25
  zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
@@ -65,19 +66,13 @@ class CreateProjectTool extends types_1.Tool {
65
66
  command = (0, command_1.addFlag)(command, 'distribution', distribution);
66
67
  }
67
68
  else if (projectBase === v3_1.PROJECT_WITH_APP) {
68
- content.push({
69
- type: 'text',
70
- 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}`,
71
- });
69
+ content.push((0, content_1.formatTextContent)(`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
70
  }
73
71
  if (auth) {
74
72
  command = (0, command_1.addFlag)(command, 'auth', auth);
75
73
  }
76
74
  else if (projectBase === v3_1.PROJECT_WITH_APP) {
77
- content.push({
78
- type: 'text',
79
- 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}`,
80
- });
75
+ content.push((0, content_1.formatTextContent)(`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
76
  }
82
77
  if (content.length > 0) {
83
78
  return {
@@ -88,28 +83,10 @@ class CreateProjectTool extends types_1.Tool {
88
83
  command = (0, command_1.addFlag)(command, 'features', features || []);
89
84
  try {
90
85
  const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteCurrentWorkingDirectory, command);
91
- return {
92
- content: [
93
- {
94
- type: 'text',
95
- text: stdout,
96
- },
97
- {
98
- type: 'text',
99
- text: stderr,
100
- },
101
- ],
102
- };
86
+ return (0, content_1.formatTextContents)(stdout, stderr);
103
87
  }
104
88
  catch (error) {
105
- return {
106
- content: [
107
- {
108
- type: 'text',
109
- text: error instanceof Error ? error.message : `${error}`,
110
- },
111
- ],
112
- };
89
+ return (0, content_1.formatTextContents)(error instanceof Error ? error.message : `${error}`);
113
90
  }
114
91
  }
115
92
  register() {
@@ -6,6 +6,7 @@ const zod_1 = require("zod");
6
6
  const command_1 = require("../../utils/command");
7
7
  const constants_1 = require("./constants");
8
8
  const project_1 = require("../../utils/project");
9
+ const content_1 = require("../../utils/content");
9
10
  const inputSchema = {
10
11
  absoluteProjectPath: constants_1.absoluteProjectPath,
11
12
  buildNumber: zod_1.z
@@ -25,10 +26,7 @@ class DeployProject extends types_1.Tool {
25
26
  const content = [];
26
27
  if (!buildNumber) {
27
28
  const { stdout } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project list-builds --limit 100`);
28
- content.push({
29
- type: 'text',
30
- text: `Ask the user which build number they would like to deploy? Build information: ${stdout}`,
31
- });
29
+ content.push((0, content_1.formatTextContent)(`Ask the user which build number they would like to deploy? Build information: ${stdout}`));
32
30
  }
33
31
  else {
34
32
  command = (0, command_1.addFlag)(command, 'build', buildNumber);
@@ -39,12 +37,7 @@ class DeployProject extends types_1.Tool {
39
37
  };
40
38
  }
41
39
  const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
42
- return {
43
- content: [
44
- { type: 'text', text: stdout },
45
- { type: 'text', text: stderr },
46
- ],
47
- };
40
+ return (0, content_1.formatTextContents)(stdout, stderr);
48
41
  }
49
42
  register() {
50
43
  return this.mcpServer.registerTool('deploy-hubspot-project', {
@@ -4,6 +4,7 @@ exports.GuidedWalkthroughTool = void 0;
4
4
  const types_1 = require("../../types");
5
5
  const zod_1 = require("zod");
6
6
  const command_1 = require("../../utils/command");
7
+ const content_1 = require("../../utils/content");
7
8
  const nextCommands = {
8
9
  'hs init': 'hs auth',
9
10
  'hs auth': 'hs project create',
@@ -32,23 +33,9 @@ class GuidedWalkthroughTool extends types_1.Tool {
32
33
  async handler({ command }) {
33
34
  if (command) {
34
35
  const { stdout } = await (0, command_1.execAsync)(`${command} --help`);
35
- return {
36
- content: [
37
- {
38
- type: 'text',
39
- text: `Display this help output for the user amd wait for them to acknowledge: ${stdout}. ${nextCommands[command] ? `Once they are ready, A good command to look at next is ${nextCommands[command]}` : ''}`,
40
- },
41
- ],
42
- };
36
+ return (0, content_1.formatTextContents)(`Display this help output for the user amd wait for them to acknowledge: ${stdout}. ${nextCommands[command] ? `Once they are ready, A good command to look at next is ${nextCommands[command]}` : ''}`);
43
37
  }
44
- return {
45
- content: [
46
- {
47
- type: 'text',
48
- text: 'Is there another command you would like to learn more about?',
49
- },
50
- ],
51
- };
38
+ return (0, content_1.formatTextContents)('Is there another command you would like to learn more about?');
52
39
  }
53
40
  register() {
54
41
  return this.mcpServer.registerTool('guided-walkthrough-hubspot-cli', {
@@ -8,6 +8,7 @@ const types_1 = require("../../types");
8
8
  const project_1 = require("../../utils/project");
9
9
  const constants_1 = require("./constants");
10
10
  const zod_1 = __importDefault(require("zod"));
11
+ const content_1 = require("../../utils/content");
11
12
  const inputSchema = {
12
13
  absoluteProjectPath: constants_1.absoluteProjectPath,
13
14
  };
@@ -21,12 +22,7 @@ class UploadProjectTools extends types_1.Tool {
21
22
  }
22
23
  async handler({ absoluteProjectPath, }) {
23
24
  const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project upload --force-create`);
24
- return {
25
- content: [
26
- { type: 'text', text: stdout },
27
- { type: 'text', text: stderr },
28
- ],
29
- };
25
+ return (0, content_1.formatTextContents)(stdout, stderr);
30
26
  }
31
27
  register() {
32
28
  return this.mcpServer.registerTool('upload-hubspot-project', {
@@ -0,0 +1,3 @@
1
+ import { TextContent, TextContentResponse } from '../types';
2
+ export declare function formatTextContents(...outputs: (string | undefined)[]): TextContentResponse;
3
+ export declare function formatTextContent(text: string): TextContent;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatTextContents = formatTextContents;
4
+ exports.formatTextContent = formatTextContent;
5
+ function formatTextContents(...outputs) {
6
+ const content = [];
7
+ outputs.forEach(output => {
8
+ if (output !== undefined) {
9
+ content.push(formatTextContent(output));
10
+ }
11
+ });
12
+ return {
13
+ content,
14
+ };
15
+ }
16
+ function formatTextContent(text) {
17
+ return {
18
+ type: 'text',
19
+ text,
20
+ };
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.7.15-experimental.0",
3
+ "version": "7.7.16-experimental.0",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",