@hubspot/cli 7.7.6-experimental.0 → 7.7.8-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.
@@ -2,12 +2,11 @@ import { ArgumentsCamelCase, Argv } from 'yargs';
2
2
  interface MCPSetupArgs {
3
3
  targets?: string[];
4
4
  }
5
- declare function builder(yargs: Argv): Argv;
6
5
  declare function handler(argv: ArgumentsCamelCase<MCPSetupArgs>): Promise<void>;
7
6
  declare const _default: {
8
7
  command: string[];
9
8
  describe: undefined;
10
- builder: typeof builder;
9
+ builder: (yargs: Argv) => Promise<Argv<{}>>;
11
10
  handler: typeof handler;
12
11
  };
13
12
  export default _default;
@@ -13,15 +13,16 @@ const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"
13
13
  const exitCodes_1 = require("../../lib/enums/exitCodes");
14
14
  const promptUtils_1 = require("../../lib/prompts/promptUtils");
15
15
  const chalk_1 = __importDefault(require("chalk"));
16
+ const yargsUtils_1 = require("../../lib/yargsUtils");
16
17
  const command = ['setup', 'update'];
17
- const describe = undefined;
18
+ const describe = undefined; // Leave hidden for now
18
19
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
19
20
  const supportedTools = [
20
21
  { name: 'Claude', value: 'claude' },
21
22
  { name: 'Claude Desktop', value: 'claude-desktop' },
22
23
  { name: 'Cursor', value: 'cursor' },
23
24
  ];
24
- function builder(yargs) {
25
+ function setupBuilder(yargs) {
25
26
  yargs.option('targets', {
26
27
  describe: 'Target application to configure',
27
28
  type: 'array',
@@ -29,10 +30,19 @@ function builder(yargs) {
29
30
  });
30
31
  return yargs;
31
32
  }
33
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(setupBuilder, command, describe, {
34
+ useGlobalOptions: true,
35
+ });
32
36
  async function handler(argv) {
37
+ try {
38
+ await import('@modelcontextprotocol/sdk/server/mcp.js');
39
+ }
40
+ catch (e) {
41
+ logger_1.logger.error(`This feature requires node >=20`);
42
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
43
+ }
33
44
  await addMcpServerToConfig(argv.targets);
34
45
  }
35
- exports.default = { command, describe, builder, handler };
36
46
  async function addMcpServerToConfig(targets) {
37
47
  try {
38
48
  let derivedTargets = [];
@@ -136,8 +146,8 @@ async function setupClaudeCode() {
136
146
  // Run claude mcp add command
137
147
  const mcpConfig = JSON.stringify({
138
148
  type: 'stdio',
139
- command: 'hs',
140
- args: ['mcp', 'start'],
149
+ command: 'yarn',
150
+ args: ['hs', 'mcp', 'start'],
141
151
  });
142
152
  const { stdout } = await execAsync('claude mcp list');
143
153
  if (stdout.includes('hubspot-cli-mcp')) {
@@ -236,3 +246,4 @@ function getClaudeDesktopConfigPath() {
236
246
  return path_1.default.join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
237
247
  }
238
248
  }
249
+ exports.default = { command, describe, builder, handler };
@@ -1,10 +1,9 @@
1
1
  import { Argv } from 'yargs';
2
- declare function builder(yargs: Argv): Argv;
3
2
  declare function handler(): Promise<void>;
4
3
  declare const _default: {
5
4
  command: string;
6
5
  describe: undefined;
7
- builder: typeof builder;
6
+ builder: (yargs: Argv) => Promise<Argv<{}>>;
8
7
  handler: typeof handler;
9
8
  };
10
9
  export default _default;
@@ -7,15 +7,26 @@ const logger_1 = require("@hubspot/local-dev-lib/logger");
7
7
  const child_process_1 = require("child_process");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
+ const exitCodes_1 = require("../../lib/enums/exitCodes");
11
+ const yargsUtils_1 = require("../../lib/yargsUtils");
10
12
  const command = 'start';
11
- const describe = undefined; // Keep this command hidden, doesn't seem useful to expose
12
- function builder(yargs) {
13
+ const describe = undefined; // Leave hidden for now
14
+ function startBuilder(yargs) {
13
15
  return yargs;
14
16
  }
17
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(startBuilder, command, describe, {
18
+ useGlobalOptions: true,
19
+ });
15
20
  async function handler() {
21
+ try {
22
+ await import('@modelcontextprotocol/sdk/server/mcp.js');
23
+ }
24
+ catch (e) {
25
+ logger_1.logger.error(`This feature requires node >=20`);
26
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
27
+ }
16
28
  await startMcpServer();
17
29
  }
18
- exports.default = { command, describe, builder, handler };
19
30
  async function startMcpServer() {
20
31
  try {
21
32
  const serverPath = path_1.default.join(__dirname, '..', '..', 'mcp-server', 'server.js');
@@ -64,3 +75,4 @@ async function startMcpServer() {
64
75
  logger_1.logger.error('Error starting MCP server:', error);
65
76
  }
66
77
  }
78
+ exports.default = { command, describe, builder, handler };
package/commands/mcp.d.ts CHANGED
@@ -1,10 +1,3 @@
1
- import { Argv } from 'yargs';
2
- declare function builder(yargs: Argv): Argv;
3
- declare function handler(): void;
4
- declare const _default: {
5
- command: string;
6
- describe: string;
7
- builder: typeof builder;
8
- handler: typeof handler;
9
- };
10
- export default _default;
1
+ import { YargsCommandModuleBucket } from '../types/Yargs';
2
+ declare const mcpCommand: YargsCommandModuleBucket;
3
+ export default mcpCommand;
package/commands/mcp.js CHANGED
@@ -5,16 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  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
+ const yargsUtils_1 = require("../lib/yargsUtils");
8
9
  const command = 'mcp';
9
10
  const describe = 'Manage the Model Context Protocol server';
10
- function builder(yargs) {
11
- return yargs
12
- .command(start_1.default)
13
- .command(setup_1.default)
14
- .demandCommand(1, 'You must specify a subcommand')
15
- .help();
11
+ function mcpBuilder(yargs) {
12
+ yargs.command(start_1.default).command(setup_1.default).demandCommand(1, '');
13
+ return yargs;
16
14
  }
17
- function handler() {
18
- // This function is required by yargs but not used since we have subcommands
19
- }
20
- exports.default = { command, describe, builder, handler };
15
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(mcpBuilder, command, describe, {
16
+ useGlobalOptions: true,
17
+ });
18
+ const mcpCommand = {
19
+ command,
20
+ describe,
21
+ builder,
22
+ handler: () => { },
23
+ };
24
+ exports.default = mcpCommand;
@@ -8,10 +8,10 @@ const DeployProject_1 = require("./project/DeployProject");
8
8
  const AddFeatureToProject_1 = require("./project/AddFeatureToProject");
9
9
  function registerProjectTools(mcpServer) {
10
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),
11
+ new UploadProjectTools_1.UploadProjectTools(mcpServer).register(),
12
+ new CreateProjectTool_1.CreateProjectTool(mcpServer).register(),
13
+ new GuidedWalkthroughTool_1.GuidedWalkthroughTool(mcpServer).register(),
14
+ new DeployProject_1.DeployProject(mcpServer).register(),
15
+ new AddFeatureToProject_1.AddFeatureToProject(mcpServer).register(),
16
16
  ];
17
17
  }
@@ -1,6 +1,29 @@
1
- import { Tool } from '../../types';
1
+ import { TextContentResponse, Tool } from '../../types';
2
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;
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteProjectPath: z.ZodString;
6
+ addApp: z.ZodBoolean;
7
+ distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
8
+ auth: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>;
9
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">]>, "many">>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ absoluteProjectPath: string;
12
+ addApp: boolean;
13
+ auth?: "oauth" | "static" | undefined;
14
+ distribution?: "marketplace" | "private" | undefined;
15
+ features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
16
+ }, {
17
+ absoluteProjectPath: string;
18
+ addApp: boolean;
19
+ auth?: "oauth" | "static" | undefined;
20
+ distribution?: "marketplace" | "private" | undefined;
21
+ features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
22
+ }>;
23
+ export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
24
+ export declare class AddFeatureToProject extends Tool<AddFeatureInputSchema> {
25
+ constructor(mcpServer: McpServer);
26
+ handler({ absoluteProjectPath, distribution, auth, features, addApp, }: AddFeatureInputSchema): Promise<TextContentResponse>;
27
+ register(): RegisteredTool;
6
28
  }
29
+ export {};
@@ -7,94 +7,100 @@ 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 inputSchema = {
11
+ absoluteProjectPath: constants_2.absoluteProjectPath,
12
+ addApp: zod_1.z.boolean().describe('Would you like to add an app?'),
13
+ distribution: zod_1.z
14
+ .optional(zod_1.z.union([
15
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
16
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
17
+ ]))
18
+ .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.'),
19
+ auth: zod_1.z
20
+ .optional(zod_1.z.union([
21
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
22
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
23
+ ]))
24
+ .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.'),
25
+ features: zod_1.z
26
+ .array(zod_1.z
27
+ .union([
28
+ zod_1.z.literal('card'),
29
+ zod_1.z.literal('settings'),
30
+ zod_1.z.literal('app-function'),
31
+ zod_1.z.literal('webhooks'),
32
+ ])
33
+ .describe('The features to include in the project, multiple options can be selected'))
34
+ .optional(),
35
+ };
36
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ const inputSchemaZodObject = zod_1.z.object({
38
+ ...inputSchema,
39
+ });
10
40
  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
- };
41
+ constructor(mcpServer) {
42
+ super(mcpServer);
43
+ }
44
+ async handler({ 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
+ });
86
56
  }
87
- catch (error) {
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) {
88
67
  return {
89
- content: [
90
- {
91
- type: 'text',
92
- text: error instanceof Error ? error.message : `${error}`,
93
- },
94
- ],
68
+ content,
95
69
  };
96
70
  }
97
- });
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
+ register() {
99
+ return this.mcpServer.registerTool('add-feature-to-hubspot-project', {
100
+ title: 'Add feature to HubSpot Project',
101
+ description: 'Adds a feature to an existing HubSpot project',
102
+ inputSchema,
103
+ }, this.handler);
98
104
  }
99
105
  }
100
106
  exports.AddFeatureToProject = AddFeatureToProject;
@@ -1,6 +1,35 @@
1
- import { Tool } from '../../types';
1
+ import { TextContentResponse, Tool } from '../../types';
2
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;
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ name: z.ZodString;
7
+ destination: z.ZodString;
8
+ projectBase: z.ZodUnion<[z.ZodLiteral<"empty">, z.ZodLiteral<"app">]>;
9
+ distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
10
+ auth: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>>;
11
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">]>, "many">>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ name: string;
14
+ projectBase: "app" | "empty";
15
+ absoluteCurrentWorkingDirectory: string;
16
+ destination: string;
17
+ auth?: "oauth" | "static" | undefined;
18
+ distribution?: "marketplace" | "private" | undefined;
19
+ features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
20
+ }, {
21
+ name: string;
22
+ projectBase: "app" | "empty";
23
+ absoluteCurrentWorkingDirectory: string;
24
+ destination: string;
25
+ auth?: "oauth" | "static" | undefined;
26
+ distribution?: "marketplace" | "private" | undefined;
27
+ features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
28
+ }>;
29
+ export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
30
+ export declare class CreateProjectTool extends Tool<CreateProjectInputSchema> {
31
+ constructor(mcpServer: McpServer);
32
+ handler({ name, destination, projectBase, distribution, auth, features, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema): Promise<TextContentResponse>;
33
+ register(): RegisteredTool;
6
34
  }
35
+ export {};
@@ -8,112 +8,117 @@ 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 inputSchema = {
12
+ absoluteCurrentWorkingDirectory: constants_2.absoluteCurrentWorkingDirectory,
13
+ name: zod_1.z
14
+ .string()
15
+ .describe('The name of the project to be created. This name is how your project will appear in HubSpot.'),
16
+ destination: zod_1.z
17
+ .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
+ projectBase: zod_1.z
20
+ .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
+ distribution: zod_1.z
23
+ .optional(zod_1.z.union([
24
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
25
+ zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
26
+ ]))
27
+ .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.'),
28
+ auth: zod_1.z
29
+ .optional(zod_1.z.union([
30
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
31
+ zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
32
+ ]))
33
+ .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.')
34
+ .optional(),
35
+ features: zod_1.z
36
+ .array(zod_1.z
37
+ .union([
38
+ zod_1.z.literal('card'),
39
+ zod_1.z.literal('settings'),
40
+ zod_1.z.literal('app-function'),
41
+ zod_1.z.literal('webhooks'),
42
+ ])
43
+ .describe('The features to include in the project, multiple options can be selected'))
44
+ .optional(),
45
+ };
46
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
+ const inputSchemaZodObject = zod_1.z.object({ ...inputSchema });
11
48
  class CreateProjectTool extends types_1.Tool {
12
- static mcpServer;
13
- static register(mcpServer) {
14
- this.mcpServer = mcpServer;
49
+ constructor(mcpServer) {
50
+ super(mcpServer);
51
+ }
52
+ async handler({ name, destination, projectBase, distribution, auth, features, absoluteCurrentWorkingDirectory, }) {
53
+ let command = (0, command_1.addFlag)('hs project create', 'platform-version', '2025.2');
54
+ const content = [];
55
+ if (name) {
56
+ command = (0, command_1.addFlag)(command, 'name', name);
57
+ }
58
+ if (destination) {
59
+ command = (0, command_1.addFlag)(command, 'dest', destination);
60
+ }
61
+ if (projectBase) {
62
+ command = (0, command_1.addFlag)(command, 'project-base', projectBase);
63
+ }
64
+ if (distribution) {
65
+ command = (0, command_1.addFlag)(command, 'distribution', distribution);
66
+ }
67
+ 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
+ });
72
+ }
73
+ if (auth) {
74
+ command = (0, command_1.addFlag)(command, 'auth', auth);
75
+ }
76
+ 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
+ });
81
+ }
82
+ if (content.length > 0) {
83
+ return {
84
+ content,
85
+ };
86
+ }
87
+ if (features && features.length) {
88
+ command = (0, command_1.addFlag)(command, 'features', features);
89
+ }
90
+ try {
91
+ const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteCurrentWorkingDirectory, command);
92
+ return {
93
+ content: [
94
+ {
95
+ type: 'text',
96
+ text: stdout,
97
+ },
98
+ {
99
+ type: 'text',
100
+ text: stderr,
101
+ },
102
+ ],
103
+ };
104
+ }
105
+ catch (error) {
106
+ return {
107
+ content: [
108
+ {
109
+ type: 'text',
110
+ text: error instanceof Error ? error.message : `${error}`,
111
+ },
112
+ ],
113
+ };
114
+ }
115
+ }
116
+ register() {
15
117
  return this.mcpServer.registerTool('create-hubspot-project', {
16
118
  title: 'Create HubSpot Project',
17
119
  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
- });
120
+ inputSchema,
121
+ }, this.handler);
117
122
  }
118
123
  }
119
124
  exports.CreateProjectTool = CreateProjectTool;
@@ -1,6 +1,20 @@
1
- import { Tool } from '../../types';
1
+ import { TextContentResponse, Tool } from '../../types';
2
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;
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteProjectPath: z.ZodString;
6
+ buildNumber: z.ZodOptional<z.ZodNumber>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ absoluteProjectPath: string;
9
+ buildNumber?: number | undefined;
10
+ }, {
11
+ absoluteProjectPath: string;
12
+ buildNumber?: number | undefined;
13
+ }>;
14
+ type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
+ export declare class DeployProject extends Tool<InputSchemaType> {
16
+ constructor(mcpServer: McpServer);
17
+ handler({ absoluteProjectPath, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
18
+ register(): RegisteredTool;
6
19
  }
20
+ export {};
@@ -6,45 +6,52 @@ 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 inputSchema = {
10
+ absoluteProjectPath: constants_1.absoluteProjectPath,
11
+ buildNumber: zod_1.z
12
+ .optional(zod_1.z.number())
13
+ .describe('The build number to be deployed. This can be found in the project details page using `hs project open`. If no build number is specified, the most recent build is deployed'),
14
+ };
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ const inputSchemaZodObject = zod_1.z.object({
17
+ ...inputSchema,
18
+ });
9
19
  class DeployProject extends types_1.Tool {
10
- static mcpServer;
11
- static register(mcpServer) {
12
- this.mcpServer = mcpServer;
20
+ constructor(mcpServer) {
21
+ super(mcpServer);
22
+ }
23
+ async handler({ absoluteProjectPath, buildNumber, }) {
24
+ let command = `hs project deploy`;
25
+ const content = [];
26
+ if (!buildNumber) {
27
+ 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
+ });
32
+ }
33
+ else {
34
+ command = (0, command_1.addFlag)(command, 'build', buildNumber);
35
+ }
36
+ if (content.length) {
37
+ return {
38
+ content,
39
+ };
40
+ }
41
+ 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
+ };
48
+ }
49
+ register() {
13
50
  return this.mcpServer.registerTool('deploy-hubspot-project', {
14
51
  title: 'Deploy a build of HubSpot Project',
15
52
  description: 'Takes a build number and a project name and deploys that build of the project',
16
- inputSchema: {
17
- absoluteProjectPath: constants_1.absoluteProjectPath,
18
- buildNumber: zod_1.z
19
- .optional(zod_1.z.number())
20
- .describe('The build number to be deployed. This can be found in the project details page using `hs project open`. If no build number is specified, the most recent build is deployed'),
21
- },
22
- }, async ({ absoluteProjectPath, buildNumber }) => {
23
- let command = `hs project deploy`;
24
- const content = [];
25
- if (!buildNumber) {
26
- const { stdout } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project list-builds --limit 100`);
27
- content.push({
28
- type: 'text',
29
- text: `Ask the user which build number they would like to deploy? Build information: ${stdout}`,
30
- });
31
- }
32
- else {
33
- command = (0, command_1.addFlag)(command, 'build', buildNumber);
34
- }
35
- if (content.length) {
36
- return {
37
- content,
38
- };
39
- }
40
- const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
41
- return {
42
- content: [
43
- { type: 'text', text: stdout },
44
- { type: 'text', text: stderr },
45
- ],
46
- };
47
- });
53
+ inputSchema,
54
+ }, this.handler);
48
55
  }
49
56
  }
50
57
  exports.DeployProject = DeployProject;
@@ -1,6 +1,17 @@
1
- import { Tool } from '../../types';
1
+ import { TextContentResponse, Tool } from '../../types';
2
2
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- export declare class GuidedWalkthroughTool extends Tool {
4
- private static mcpServer;
5
- static register(mcpServer: McpServer): RegisteredTool;
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ command: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"hs init">, z.ZodLiteral<"hs auth">, z.ZodLiteral<"hs project create">, z.ZodLiteral<"hs project upload">]>>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ command?: "hs auth" | "hs project upload" | "hs project create" | "hs init" | undefined;
8
+ }, {
9
+ command?: "hs auth" | "hs project upload" | "hs project create" | "hs init" | undefined;
10
+ }>;
11
+ type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
+ export declare class GuidedWalkthroughTool extends Tool<InputSchemaType> {
13
+ constructor(mcpServer: McpServer);
14
+ handler({ command }: InputSchemaType): Promise<TextContentResponse>;
15
+ register(): RegisteredTool;
6
16
  }
17
+ export {};
@@ -10,10 +10,47 @@ const nextCommands = {
10
10
  'hs project create': 'hs project upload',
11
11
  'hs project upload': 'hs project dev',
12
12
  };
13
+ const inputSchema = {
14
+ command: zod_1.z
15
+ .union([
16
+ zod_1.z.literal('hs init'),
17
+ zod_1.z.literal('hs auth'),
18
+ zod_1.z.literal('hs project create'),
19
+ zod_1.z.literal('hs project upload'),
20
+ ])
21
+ .describe('The command to learn more about. Start with `hs init`')
22
+ .optional(),
23
+ };
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ const inputSchemaZodObject = zod_1.z.object({
26
+ ...inputSchema,
27
+ });
13
28
  class GuidedWalkthroughTool extends types_1.Tool {
14
- static mcpServer;
15
- static register(mcpServer) {
16
- this.mcpServer = mcpServer;
29
+ constructor(mcpServer) {
30
+ super(mcpServer);
31
+ }
32
+ async handler({ command }) {
33
+ if (command) {
34
+ 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
+ };
43
+ }
44
+ return {
45
+ content: [
46
+ {
47
+ type: 'text',
48
+ text: 'Is there another command you would like to learn more about?',
49
+ },
50
+ ],
51
+ };
52
+ }
53
+ register() {
17
54
  return this.mcpServer.registerTool('guided-walkthrough-hubspot-cli', {
18
55
  title: 'Guided walkthrough of the CLI',
19
56
  description: 'Give the user a guided walkthrough of the HubSpot CLI.',
@@ -28,27 +65,7 @@ class GuidedWalkthroughTool extends types_1.Tool {
28
65
  .describe('The command to learn more about. Start with `hs init`')
29
66
  .optional(),
30
67
  },
31
- }, async ({ command }) => {
32
- if (command) {
33
- const { stdout } = await (0, command_1.execAsync)(`${command} --help`);
34
- return {
35
- content: [
36
- {
37
- type: 'text',
38
- 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]}` : ''}`,
39
- },
40
- ],
41
- };
42
- }
43
- return {
44
- content: [
45
- {
46
- type: 'text',
47
- text: 'Is there another command you would like to learn more about?',
48
- },
49
- ],
50
- };
51
- });
68
+ }, this.handler);
52
69
  }
53
70
  }
54
71
  exports.GuidedWalkthroughTool = GuidedWalkthroughTool;
@@ -1,5 +1,17 @@
1
- import { Tool } from '../../types';
1
+ import { TextContentResponse, Tool } from '../../types';
2
2
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- export declare class UploadProjectTools extends Tool {
4
- static register(mcpServer: McpServer): RegisteredTool;
3
+ import z from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteProjectPath: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ absoluteProjectPath: string;
8
+ }, {
9
+ absoluteProjectPath: string;
10
+ }>;
11
+ type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
+ export declare class UploadProjectTools extends Tool<InputSchemaType> {
13
+ constructor(mcpServer: McpServer);
14
+ handler({ absoluteProjectPath, }: InputSchemaType): Promise<TextContentResponse>;
15
+ register(): RegisteredTool;
5
16
  }
17
+ export {};
@@ -1,26 +1,39 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.UploadProjectTools = void 0;
4
7
  const types_1 = require("../../types");
5
8
  const project_1 = require("../../utils/project");
6
9
  const constants_1 = require("./constants");
10
+ const zod_1 = __importDefault(require("zod"));
11
+ const inputSchema = {
12
+ absoluteProjectPath: constants_1.absoluteProjectPath,
13
+ };
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ const inputSchemaZodObject = zod_1.default.object({
16
+ ...inputSchema,
17
+ });
7
18
  class UploadProjectTools extends types_1.Tool {
8
- static register(mcpServer) {
9
- return mcpServer.registerTool('upload-hubspot-project', {
19
+ constructor(mcpServer) {
20
+ super(mcpServer);
21
+ }
22
+ async handler({ absoluteProjectPath, }) {
23
+ 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
+ };
30
+ }
31
+ register() {
32
+ return this.mcpServer.registerTool('upload-hubspot-project', {
10
33
  title: 'Upload HubSpot Project',
11
34
  description: 'Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.',
12
- inputSchema: {
13
- absoluteProjectPath: constants_1.absoluteProjectPath,
14
- },
15
- }, async ({ absoluteProjectPath }) => {
16
- const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project upload --force-create`);
17
- return {
18
- content: [
19
- { type: 'text', text: stdout },
20
- { type: 'text', text: stderr },
21
- ],
22
- };
23
- });
35
+ inputSchema,
36
+ }, this.handler);
24
37
  }
25
38
  }
26
39
  exports.UploadProjectTools = UploadProjectTools;
@@ -1,8 +1,14 @@
1
1
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare class Tool {
3
- static register: (McpServer: McpServer) => RegisteredTool;
2
+ export declare class Tool<InputSchema, ResponseType = TextContentResponse> {
3
+ protected mcpServer: McpServer;
4
+ constructor(mcpServer: McpServer);
5
+ register(): RegisteredTool;
6
+ handler(input: InputSchema): ResponseType | Promise<ResponseType>;
4
7
  }
5
- export type Content = {
8
+ export type TextContent = {
6
9
  type: 'text';
7
10
  text: string;
8
11
  };
12
+ export type TextContentResponse = {
13
+ content: TextContent[];
14
+ };
@@ -2,6 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Tool = void 0;
4
4
  class Tool {
5
- static register;
5
+ mcpServer;
6
+ constructor(mcpServer) {
7
+ this.mcpServer = mcpServer;
8
+ }
9
+ register() {
10
+ throw new Error('Must implement register');
11
+ }
12
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13
+ handler(input) {
14
+ throw new Error('Must implement handler');
15
+ }
6
16
  }
7
17
  exports.Tool = Tool;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.7.6-experimental.0",
3
+ "version": "7.7.8-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",
@@ -10,7 +10,6 @@
10
10
  "@hubspot/serverless-dev-runtime": "7.0.6",
11
11
  "@hubspot/theme-preview-dev-server": "0.0.10",
12
12
  "@hubspot/ui-extensions-dev-server": "0.9.2",
13
- "@modelcontextprotocol/sdk": "1.13.3",
14
13
  "archiver": "7.0.1",
15
14
  "boxen": "8.0.1",
16
15
  "chalk": "4.1.2",
@@ -62,7 +61,8 @@
62
61
  "typescript": "^5.6.2"
63
62
  },
64
63
  "optionalDependencies": {
65
- "@hubspot/cms-dev-server": "^1.0.9"
64
+ "@hubspot/cms-dev-server": "^1.0.9",
65
+ "@modelcontextprotocol/sdk": "1.13.3"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "ts-node ./scripts/build.ts",