@hubspot/cli 7.7.23-experimental.0 → 7.7.24-experimental.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/commands/account/auth.js +15 -4
  2. package/commands/auth.js +1 -1
  3. package/commands/mcp/start.d.ts +1 -0
  4. package/commands/mcp/start.js +12 -4
  5. package/commands/project/create.js +2 -2
  6. package/commands/project/validate.js +1 -0
  7. package/commands/sandbox/__tests__/create.test.js +207 -0
  8. package/commands/sandbox/create.d.ts +1 -1
  9. package/commands/sandbox/create.js +31 -16
  10. package/lang/en.d.ts +7 -3
  11. package/lang/en.js +12 -5
  12. package/lang/en.lyaml +4 -2
  13. package/lib/__tests__/buildAccount.test.js +62 -4
  14. package/lib/buildAccount.d.ts +4 -1
  15. package/lib/buildAccount.js +57 -2
  16. package/lib/commonOpts.js +25 -0
  17. package/lib/constants.d.ts +4 -0
  18. package/lib/constants.js +4 -0
  19. package/lib/errorHandlers/index.js +1 -3
  20. package/lib/errors/ProjectValidationError.d.ts +4 -0
  21. package/lib/errors/ProjectValidationError.js +9 -0
  22. package/lib/mcp/setup.d.ts +4 -0
  23. package/lib/mcp/setup.js +36 -0
  24. package/lib/projects/__tests__/LocalDevProcess.test.js +35 -0
  25. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +170 -1
  26. package/lib/projects/add/v3AddComponent.js +2 -1
  27. package/lib/projects/create/index.js +2 -2
  28. package/lib/projects/create/v3.d.ts +0 -2
  29. package/lib/projects/create/v3.js +1 -3
  30. package/lib/projects/localDev/LocalDevProcess.d.ts +1 -0
  31. package/lib/projects/localDev/LocalDevProcess.js +3 -0
  32. package/lib/projects/localDev/LocalDevState.d.ts +1 -0
  33. package/lib/projects/localDev/LocalDevState.js +5 -0
  34. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -2
  35. package/lib/projects/localDev/LocalDevWebsocketServer.js +35 -29
  36. package/lib/projects/upload.js +5 -12
  37. package/lib/sandboxes.d.ts +4 -0
  38. package/lib/sandboxes.js +4 -0
  39. package/lib/ui/index.d.ts +6 -0
  40. package/lib/ui/index.js +3 -5
  41. package/mcp-server/tools/index.js +6 -4
  42. package/mcp-server/tools/project/{AddFeatureToProject.d.ts → AddFeatureToProjectTool.d.ts} +4 -4
  43. package/mcp-server/tools/project/{AddFeatureToProject.js → AddFeatureToProjectTool.js} +6 -14
  44. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  45. package/mcp-server/tools/project/CreateProjectTool.js +4 -14
  46. package/mcp-server/tools/project/{DeployProject.d.ts → DeployProjectTool.d.ts} +1 -1
  47. package/mcp-server/tools/project/{DeployProject.js → DeployProjectTool.js} +2 -2
  48. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +20 -0
  49. package/mcp-server/tools/project/GetConfigValuesTool.js +51 -0
  50. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  51. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  52. package/mcp-server/tools/project/__tests__/{AddFeatureToProject.test.js → AddFeatureToProjectTool.test.js} +7 -7
  53. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +3 -4
  54. package/mcp-server/tools/project/__tests__/{DeployProject.test.js → DeployProjectTool.test.js} +4 -4
  55. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +198 -0
  56. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +2 -2
  57. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +2 -2
  58. package/mcp-server/tools/project/constants.d.ts +1 -0
  59. package/mcp-server/tools/project/constants.js +11 -0
  60. package/mcp-server/utils/__tests__/command.test.js +76 -3
  61. package/mcp-server/utils/command.d.ts +6 -0
  62. package/mcp-server/utils/command.js +19 -0
  63. package/package.json +2 -2
  64. package/mcp-server/utils/__tests__/project.test.js +0 -79
  65. package/mcp-server/utils/project.d.ts +0 -5
  66. package/mcp-server/utils/project.js +0 -14
  67. /package/mcp-server/tools/project/__tests__/{AddFeatureToProject.test.d.ts → AddFeatureToProjectTool.test.d.ts} +0 -0
  68. /package/mcp-server/tools/project/__tests__/{DeployProject.test.d.ts → DeployProjectTool.test.d.ts} +0 -0
  69. /package/mcp-server/{utils/__tests__/project.test.d.ts → tools/project/__tests__/GetConfigValuesTool.test.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { Separator } from '@inquirer/prompts';
2
- import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../constants.js';
2
+ import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../constants.js';
3
3
  import { commands, lib } from '../../../lang/en.js';
4
4
  import { listPrompt } from '../../prompts/promptUtils.js';
5
5
  import chalk from 'chalk';
@@ -8,8 +8,6 @@ import path from 'path';
8
8
  import { getConfigForPlatformVersion } from './legacy.js';
9
9
  import { logError } from '../../errorHandlers/index.js';
10
10
  import { EXIT_CODES } from '../../enums/exitCodes.js';
11
- export const EMPTY_PROJECT = 'empty';
12
- export const PROJECT_WITH_APP = 'app';
13
11
  export async function createV3App(providedAuth, providedDistribution) {
14
12
  let authType;
15
13
  if (providedAuth &&
@@ -32,5 +32,6 @@ declare class LocalDevProcess {
32
32
  uploadProject(): Promise<boolean>;
33
33
  addStateListener<K extends keyof LocalDevState>(key: K, listener: LocalDevStateListener<K>, callOnInit?: boolean): void;
34
34
  sendDevServerMessage(message: LocalDevServerMessage): void;
35
+ removeStateListener<K extends keyof LocalDevState>(key: K, listener: LocalDevStateListener<K>): void;
35
36
  }
36
37
  export default LocalDevProcess;
@@ -208,5 +208,8 @@ class LocalDevProcess {
208
208
  sendDevServerMessage(message) {
209
209
  this.state.devServerMessage = message;
210
210
  }
211
+ removeStateListener(key, listener) {
212
+ this.state.removeListener(key, listener);
213
+ }
211
214
  }
212
215
  export default LocalDevProcess;
@@ -51,5 +51,6 @@ declare class LocalDevState {
51
51
  get devServerMessage(): string;
52
52
  set devServerMessage(message: LocalDevServerMessage);
53
53
  addListener<K extends keyof LocalDevState>(key: K, listener: LocalDevStateListener<K>, callOnInit?: boolean): void;
54
+ removeListener<K extends keyof LocalDevState>(key: K, listener: LocalDevStateListener<K>): void;
54
55
  }
55
56
  export default LocalDevState;
@@ -111,5 +111,10 @@ class LocalDevState {
111
111
  listener(this[key]);
112
112
  }
113
113
  }
114
+ removeListener(key, listener) {
115
+ if (this._listeners[key]) {
116
+ this._listeners[key].splice(this._listeners[key].indexOf(listener), 1);
117
+ }
118
+ }
114
119
  }
115
120
  export default LocalDevState;
@@ -1,18 +1,18 @@
1
1
  import LocalDevProcess from './LocalDevProcess.js';
2
2
  declare class LocalDevWebsocketServer {
3
3
  private server?;
4
- private _websocket?;
5
4
  private debug?;
6
5
  private localDevProcess;
7
6
  private ALLOWED_ORIGINS;
8
7
  constructor(localDevProcess: LocalDevProcess, debug?: boolean);
9
- private websocket;
10
8
  private log;
11
9
  private logError;
12
10
  private sendMessage;
13
11
  private handleUpload;
14
12
  private setupMessageHandlers;
15
13
  private sendProjectData;
14
+ private setupProjectNodesListener;
15
+ private setupAppDataListener;
16
16
  private setupStateListeners;
17
17
  start(): Promise<void>;
18
18
  shutdown(): void;
@@ -8,7 +8,6 @@ const SERVER_INSTANCE_ID = 'local-dev-ui-websocket-server';
8
8
  const LOG_PREFIX = '[LocalDevWebsocketServer]';
9
9
  class LocalDevWebsocketServer {
10
10
  server;
11
- _websocket;
12
11
  debug;
13
12
  localDevProcess;
14
13
  ALLOWED_ORIGINS = [
@@ -21,12 +20,6 @@ class LocalDevWebsocketServer {
21
20
  this.localDevProcess = localDevProcess;
22
21
  this.debug = debug;
23
22
  }
24
- websocket() {
25
- if (!this._websocket) {
26
- throw new Error(lib.LocalDevWebsocketServer.errors.notInitialized(LOG_PREFIX));
27
- }
28
- return this._websocket;
29
- }
30
23
  log(message) {
31
24
  if (this.debug) {
32
25
  logger.log(LOG_PREFIX, message);
@@ -37,24 +30,24 @@ class LocalDevWebsocketServer {
37
30
  logger.error(LOG_PREFIX, message);
38
31
  }
39
32
  }
40
- sendMessage(message) {
41
- this.websocket().send(JSON.stringify(message));
33
+ sendMessage(websocket, message) {
34
+ websocket.send(JSON.stringify(message));
42
35
  }
43
- async handleUpload() {
36
+ async handleUpload(websocket) {
44
37
  const uploadSuccess = await this.localDevProcess.uploadProject();
45
38
  if (uploadSuccess) {
46
- this.sendMessage({
39
+ this.sendMessage(websocket, {
47
40
  type: LOCAL_DEV_UI_MESSAGE_SEND_TYPES.UPLOAD_SUCCESS,
48
41
  });
49
42
  }
50
43
  else {
51
- this.sendMessage({
44
+ this.sendMessage(websocket, {
52
45
  type: LOCAL_DEV_UI_MESSAGE_SEND_TYPES.UPLOAD_FAILURE,
53
46
  });
54
47
  }
55
48
  }
56
- setupMessageHandlers() {
57
- this.websocket().on('message', data => {
49
+ setupMessageHandlers(websocket) {
50
+ websocket.on('message', data => {
58
51
  try {
59
52
  const message = JSON.parse(data.toString());
60
53
  if (!message.type) {
@@ -63,7 +56,7 @@ class LocalDevWebsocketServer {
63
56
  }
64
57
  switch (message.type) {
65
58
  case LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES.UPLOAD:
66
- this.handleUpload();
59
+ this.handleUpload(websocket);
67
60
  break;
68
61
  case LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES.VIEWED_WELCOME_SCREEN:
69
62
  addLocalStateFlag(CONFIG_LOCAL_STATE_FLAGS.LOCAL_DEV_UI_WELCOME);
@@ -77,8 +70,8 @@ class LocalDevWebsocketServer {
77
70
  }
78
71
  });
79
72
  }
80
- sendProjectData() {
81
- this.sendMessage({
73
+ sendProjectData(websocket) {
74
+ this.sendMessage(websocket, {
82
75
  type: LOCAL_DEV_UI_MESSAGE_SEND_TYPES.UPDATE_PROJECT_DATA,
83
76
  data: {
84
77
  projectName: this.localDevProcess.projectName,
@@ -88,19 +81,33 @@ class LocalDevWebsocketServer {
88
81
  },
89
82
  });
90
83
  }
91
- setupStateListeners() {
92
- this.localDevProcess.addStateListener('projectNodes', nodes => {
93
- this.sendMessage({
84
+ setupProjectNodesListener(websocket) {
85
+ const listener = (nodes) => {
86
+ this.sendMessage(websocket, {
94
87
  type: LOCAL_DEV_UI_MESSAGE_SEND_TYPES.UPDATE_PROJECT_NODES,
95
88
  data: nodes,
96
89
  });
97
- }, true);
98
- this.localDevProcess.addStateListener('appData', appData => {
99
- this.sendMessage({
90
+ };
91
+ this.localDevProcess.addStateListener('projectNodes', listener, true);
92
+ websocket.on('close', () => {
93
+ this.localDevProcess.removeStateListener('projectNodes', listener);
94
+ });
95
+ }
96
+ setupAppDataListener(websocket) {
97
+ const listener = (appData) => {
98
+ this.sendMessage(websocket, {
100
99
  type: LOCAL_DEV_UI_MESSAGE_SEND_TYPES.UPDATE_APP_DATA,
101
100
  data: appData,
102
101
  });
103
- }, true);
102
+ };
103
+ this.localDevProcess.addStateListener('appData', listener, true);
104
+ websocket.on('close', () => {
105
+ this.localDevProcess.removeStateListener('appData', listener);
106
+ });
107
+ }
108
+ setupStateListeners(websocket) {
109
+ this.setupProjectNodesListener(websocket);
110
+ this.setupAppDataListener(websocket);
104
111
  }
105
112
  async start() {
106
113
  const portManagerIsRunning = await isPortManagerServerRunning();
@@ -117,17 +124,16 @@ class LocalDevWebsocketServer {
117
124
  ws.close(1008, lib.LocalDevWebsocketServer.errors.originNotAllowed(origin));
118
125
  return;
119
126
  }
120
- this._websocket = ws;
121
- this.sendProjectData();
122
- this.setupMessageHandlers();
123
- this.setupStateListeners();
127
+ this.sendProjectData(ws);
128
+ this.setupMessageHandlers(ws);
129
+ this.setupStateListeners(ws);
124
130
  this.localDevProcess.sendDevServerMessage(LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED);
125
131
  });
132
+ this.server.on('close', () => { });
126
133
  }
127
134
  shutdown() {
128
135
  this.server?.close();
129
136
  this.server = undefined;
130
- this._websocket = undefined;
131
137
  }
132
138
  }
133
139
  export default LocalDevWebsocketServer;
@@ -14,6 +14,7 @@ import { ensureProjectExists } from './ensureProjectExists.js';
14
14
  import { uiLogger } from '../ui/logger.js';
15
15
  import { useV3Api } from './buildAndDeploy.js';
16
16
  import { EXIT_CODES } from '../enums/exitCodes.js';
17
+ import ProjectValidationError from '../errors/ProjectValidationError.js';
17
18
  async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
18
19
  SpinniesManager.init({});
19
20
  const accountIdentifier = uiAccountDescription(accountId) || `${accountId}`;
@@ -107,17 +108,13 @@ export async function handleProjectUpload({ accountId, projectConfig, projectDir
107
108
  export function validateSourceDirectory(srcDir, projectConfig) {
108
109
  const filenames = fs.readdirSync(srcDir);
109
110
  if (!filenames || filenames.length === 0) {
110
- const validationError = new Error(lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
111
- validationError.name = 'ProjectValidationError';
112
- throw validationError;
111
+ throw new ProjectValidationError(lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
113
112
  }
114
113
  }
115
114
  export async function validateNoHSMetaMismatch(srcDir, projectConfig) {
116
115
  const hasHsMetaFiles = await projectContainsHsMetaFiles(srcDir);
117
116
  if (!useV3Api(projectConfig.platformVersion) && hasHsMetaFiles) {
118
- const validationError = new Error(lib.projectUpload.wrongPlatformVersionMetaFiles);
119
- validationError.name = 'ProjectValidationError';
120
- throw validationError;
117
+ throw new ProjectValidationError(lib.projectUpload.wrongPlatformVersionMetaFiles);
121
118
  }
122
119
  }
123
120
  export async function handleTranslate(projectDir, projectConfig, accountId, skipValidation, profile) {
@@ -132,12 +129,8 @@ export async function handleTranslate(projectDir, projectConfig, accountId, skip
132
129
  }
133
130
  catch (e) {
134
131
  if (isTranslationError(e)) {
135
- const validationError = new Error(e.toString());
136
- validationError.name = 'ProjectValidationError';
137
- throw validationError;
138
- }
139
- else {
140
- logError(e);
132
+ throw new ProjectValidationError(e.toString(), { cause: e });
141
133
  }
134
+ throw e;
142
135
  }
143
136
  }
@@ -11,6 +11,10 @@ export declare const SANDBOX_API_TYPE_MAP: {
11
11
  readonly STANDARD_SANDBOX: 1;
12
12
  readonly DEVELOPMENT_SANDBOX: 2;
13
13
  };
14
+ export declare const SANDBOX_TYPE_MAP_V2: {
15
+ readonly STANDARD_SANDBOX: "STANDARD";
16
+ readonly DEVELOPMENT_SANDBOX: "DEVELOPER";
17
+ };
14
18
  export declare function getSandboxTypeAsString(accountType?: AccountType): string;
15
19
  export declare function getHasSandboxesByType(parentAccountConfig: CLIAccount, type: AccountType): boolean;
16
20
  export declare function getAvailableSyncTypes(parentAccountConfig: CLIAccount, config: CLIAccount): Promise<Array<SandboxSyncTask>>;
package/lib/sandboxes.js CHANGED
@@ -22,6 +22,10 @@ export const SANDBOX_API_TYPE_MAP = {
22
22
  [HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX]: 1,
23
23
  [HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX]: 2,
24
24
  };
25
+ export const SANDBOX_TYPE_MAP_V2 = {
26
+ [HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX]: 'STANDARD',
27
+ [HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX]: 'DEVELOPER',
28
+ };
25
29
  export function getSandboxTypeAsString(accountType) {
26
30
  if (accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX) {
27
31
  return 'development'; // Only place we're using this specific name
package/lib/ui/index.d.ts CHANGED
@@ -1,9 +1,14 @@
1
+ type TerminalSupport = {
2
+ hyperlinks: boolean;
3
+ color: boolean;
4
+ };
1
5
  export declare const UI_COLORS: {
2
6
  SORBET: string;
3
7
  MARIGOLD: string;
4
8
  MARIGOLD_DARK: string;
5
9
  };
6
10
  export declare function uiLine(): void;
11
+ export declare function getTerminalUISupport(): TerminalSupport;
7
12
  export declare function uiLink(linkText: string, url: string): string;
8
13
  export declare function uiAccountDescription(accountId?: number | null, bold?: boolean): string;
9
14
  export declare function uiInfoSection(title: string, logContent: () => void): void;
@@ -17,3 +22,4 @@ export declare function uiCommandDisabledBanner(command: string, url?: string, m
17
22
  export declare function uiDeprecatedDescription(message: string, command: string, url?: string): undefined;
18
23
  export declare function uiDeprecatedMessage(command: string, url?: string, message?: string): void;
19
24
  export declare function indent(level: number): string;
25
+ export {};
package/lib/ui/index.js CHANGED
@@ -13,7 +13,7 @@ export const UI_COLORS = {
13
13
  export function uiLine() {
14
14
  logger.log('-'.repeat(50));
15
15
  }
16
- function getTerminalUISupport() {
16
+ export function getTerminalUISupport() {
17
17
  return {
18
18
  hyperlinks: supportsHyperlinkModule.stdout,
19
19
  color: supportsColor.stdout.hasBasic,
@@ -79,9 +79,8 @@ export function uiFeatureHighlight(features, title) {
79
79
  });
80
80
  }
81
81
  export function uiBetaTag(message, log = true) {
82
- const terminalUISupport = getTerminalUISupport();
83
82
  const tag = i18n(`lib.ui.betaTag`);
84
- const result = `${terminalUISupport.color ? chalk.hex(UI_COLORS.SORBET)(tag) : tag} ${message}`;
83
+ const result = `${tag} ${message}`;
85
84
  if (log) {
86
85
  logger.log(result);
87
86
  return;
@@ -89,9 +88,8 @@ export function uiBetaTag(message, log = true) {
89
88
  return result;
90
89
  }
91
90
  export function uiDeprecatedTag(message, log = true) {
92
- const terminalUISupport = getTerminalUISupport();
93
91
  const tag = i18n(`lib.ui.deprecatedTag`);
94
- const result = `${terminalUISupport.color ? chalk.yellow(tag) : tag} ${message}`;
92
+ const result = `${tag} ${message}`;
95
93
  if (log) {
96
94
  logger.log(result);
97
95
  return;
@@ -1,16 +1,18 @@
1
1
  import { UploadProjectTools } from './project/UploadProjectTools.js';
2
2
  import { CreateProjectTool } from './project/CreateProjectTool.js';
3
3
  import { GuidedWalkthroughTool } from './project/GuidedWalkthroughTool.js';
4
- import { DeployProject } from './project/DeployProject.js';
5
- import { AddFeatureToProject } from './project/AddFeatureToProject.js';
4
+ import { DeployProjectTool } from './project/DeployProjectTool.js';
5
+ import { AddFeatureToProjectTool } from './project/AddFeatureToProjectTool.js';
6
6
  import { ValidateProjectTool } from './project/ValidateProjectTool.js';
7
+ import { GetConfigValuesTool } from './project/GetConfigValuesTool.js';
7
8
  export function registerProjectTools(mcpServer) {
8
9
  return [
9
10
  new UploadProjectTools(mcpServer).register(),
10
11
  new CreateProjectTool(mcpServer).register(),
11
12
  new GuidedWalkthroughTool(mcpServer).register(),
12
- new DeployProject(mcpServer).register(),
13
- new AddFeatureToProject(mcpServer).register(),
13
+ new DeployProjectTool(mcpServer).register(),
14
+ new AddFeatureToProjectTool(mcpServer).register(),
14
15
  new ValidateProjectTool(mcpServer).register(),
16
+ new GetConfigValuesTool(mcpServer).register(),
15
17
  ];
16
18
  }
@@ -6,22 +6,22 @@ declare const inputSchemaZodObject: z.ZodObject<{
6
6
  addApp: z.ZodBoolean;
7
7
  distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
8
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">>;
9
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">]>, "many">>;
10
10
  }, "strip", z.ZodTypeAny, {
11
11
  absoluteProjectPath: string;
12
12
  addApp: boolean;
13
13
  auth?: "oauth" | "static" | undefined;
14
14
  distribution?: "marketplace" | "private" | undefined;
15
- features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
15
+ features?: ("card" | "settings" | "app-function" | "webhooks" | "workflow-action")[] | undefined;
16
16
  }, {
17
17
  absoluteProjectPath: string;
18
18
  addApp: boolean;
19
19
  auth?: "oauth" | "static" | undefined;
20
20
  distribution?: "marketplace" | "private" | undefined;
21
- features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
21
+ features?: ("card" | "settings" | "app-function" | "webhooks" | "workflow-action")[] | undefined;
22
22
  }>;
23
23
  export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
24
- export declare class AddFeatureToProject extends Tool<AddFeatureInputSchema> {
24
+ export declare class AddFeatureToProjectTool extends Tool<AddFeatureInputSchema> {
25
25
  constructor(mcpServer: McpServer);
26
26
  handler({ absoluteProjectPath, distribution, auth, features, addApp, }: AddFeatureInputSchema): Promise<TextContentResponse>;
27
27
  register(): RegisteredTool;
@@ -2,8 +2,8 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../lib/constants.js';
4
4
  import { addFlag } from '../../utils/command.js';
5
- import { absoluteProjectPath } from './constants.js';
6
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { absoluteProjectPath, features } from './constants.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
7
7
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
8
8
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
9
9
  const inputSchema = {
@@ -23,23 +23,14 @@ const inputSchema = {
23
23
  z.literal(APP_AUTH_TYPES.OAUTH),
24
24
  ]))
25
25
  .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.'),
26
- features: z
27
- .array(z
28
- .union([
29
- z.literal('card'),
30
- z.literal('settings'),
31
- z.literal('app-function'),
32
- z.literal('webhooks'),
33
- ])
34
- .describe('The features to include in the project, multiple options can be selected'))
35
- .optional(),
26
+ features,
36
27
  };
37
28
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
29
  const inputSchemaZodObject = z.object({
39
30
  ...inputSchema,
40
31
  });
41
32
  const toolName = 'add-feature-to-hubspot-project';
42
- export class AddFeatureToProject extends Tool {
33
+ export class AddFeatureToProjectTool extends Tool {
43
34
  constructor(mcpServer) {
44
35
  super(mcpServer);
45
36
  }
@@ -77,7 +68,8 @@ export class AddFeatureToProject extends Tool {
77
68
  register() {
78
69
  return this.mcpServer.registerTool(toolName, {
79
70
  title: 'Add feature to HubSpot Project',
80
- description: 'Adds a feature to an existing HubSpot project',
71
+ description: `Adds a feature to an existing HubSpot project.
72
+ Only works for projects with platformVersion '2025.2' and beyond`,
81
73
  inputSchema,
82
74
  }, this.handler);
83
75
  }
@@ -8,7 +8,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
8
8
  projectBase: z.ZodUnion<[z.ZodLiteral<"empty">, z.ZodLiteral<"app">]>;
9
9
  distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
10
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">>;
11
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">]>, "many">>;
12
12
  }, "strip", z.ZodTypeAny, {
13
13
  projectBase: "app" | "empty";
14
14
  absoluteCurrentWorkingDirectory: string;
@@ -16,7 +16,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
16
16
  name?: string | undefined;
17
17
  auth?: "oauth" | "static" | undefined;
18
18
  distribution?: "marketplace" | "private" | undefined;
19
- features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
19
+ features?: ("card" | "settings" | "app-function" | "webhooks" | "workflow-action")[] | undefined;
20
20
  }, {
21
21
  projectBase: "app" | "empty";
22
22
  absoluteCurrentWorkingDirectory: string;
@@ -24,7 +24,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
24
24
  name?: string | undefined;
25
25
  auth?: "oauth" | "static" | undefined;
26
26
  distribution?: "marketplace" | "private" | undefined;
27
- features?: ("card" | "settings" | "app-function" | "webhooks")[] | undefined;
27
+ features?: ("card" | "settings" | "app-function" | "webhooks" | "workflow-action")[] | undefined;
28
28
  }>;
29
29
  export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
30
30
  export declare class CreateProjectTool extends Tool<CreateProjectInputSchema> {
@@ -1,10 +1,9 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
- import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../lib/constants.js';
3
+ import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../../lib/constants.js';
4
4
  import { addFlag } from '../../utils/command.js';
5
- import { EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../../lib/projects/create/v3.js';
6
- import { absoluteCurrentWorkingDirectory } from './constants.js';
7
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { absoluteCurrentWorkingDirectory, features } from './constants.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
8
7
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
9
8
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
10
9
  const inputSchema = {
@@ -32,16 +31,7 @@ const inputSchema = {
32
31
  ]))
33
32
  .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
33
  .optional(),
35
- features: z
36
- .array(z
37
- .union([
38
- z.literal('card'),
39
- z.literal('settings'),
40
- z.literal('app-function'),
41
- z.literal('webhooks'),
42
- ])
43
- .describe('The features to include in the project, multiple options can be selected'))
44
- .optional(),
34
+ features,
45
35
  };
46
36
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
37
  const inputSchemaZodObject = z.object({ ...inputSchema });
@@ -12,7 +12,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
12
12
  buildNumber?: number | undefined;
13
13
  }>;
14
14
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
- export declare class DeployProject extends Tool<InputSchemaType> {
15
+ export declare class DeployProjectTool extends Tool<InputSchemaType> {
16
16
  constructor(mcpServer: McpServer);
17
17
  handler({ absoluteProjectPath, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
18
18
  register(): RegisteredTool;
@@ -2,7 +2,7 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
4
  import { absoluteProjectPath } from './constants.js';
5
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { runCommandInDir } from '../../utils/command.js';
6
6
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  const inputSchema = {
@@ -16,7 +16,7 @@ const inputSchemaZodObject = z.object({
16
16
  ...inputSchema,
17
17
  });
18
18
  const toolName = 'deploy-hubspot-project';
19
- export class DeployProject extends Tool {
19
+ export class DeployProjectTool extends Tool {
20
20
  constructor(mcpServer) {
21
21
  super(mcpServer);
22
22
  }
@@ -0,0 +1,20 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ platformVersion: z.ZodString;
6
+ featureType: z.ZodString;
7
+ }, "strip", z.ZodTypeAny, {
8
+ platformVersion: string;
9
+ featureType: string;
10
+ }, {
11
+ platformVersion: string;
12
+ featureType: string;
13
+ }>;
14
+ type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
+ export declare class GetConfigValuesTool extends Tool<InputSchemaType> {
16
+ constructor(mcpServer: McpServer);
17
+ handler({ platformVersion, featureType, }: InputSchemaType): Promise<TextContentResponse>;
18
+ register(): RegisteredTool;
19
+ }
20
+ export {};
@@ -0,0 +1,51 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { formatTextContents } from '../../utils/content.js';
4
+ import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
5
+ import { getAccountId } from '@hubspot/local-dev-lib/config';
6
+ import { useV3Api } from '../../../lib/projects/buildAndDeploy.js';
7
+ const inputSchema = {
8
+ platformVersion: z
9
+ .string()
10
+ .describe('The platform version for the project. Located in the hsproject.json file.'),
11
+ featureType: z
12
+ .string()
13
+ .describe('The type of the component to fetch the JSON schema for. This will be the `type` field in the -hsmeta.json file'),
14
+ };
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ const inputSchemaZodObject = z.object({
17
+ ...inputSchema,
18
+ });
19
+ const toolName = 'get-hubspot-project-feature-config-schema';
20
+ export class GetConfigValuesTool extends Tool {
21
+ constructor(mcpServer) {
22
+ super(mcpServer);
23
+ }
24
+ async handler({ platformVersion, featureType, }) {
25
+ try {
26
+ if (!useV3Api(platformVersion)) {
27
+ return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
28
+ }
29
+ const schema = await getIntermediateRepresentationSchema({
30
+ platformVersion,
31
+ projectSourceDir: '',
32
+ accountId: getAccountId(),
33
+ });
34
+ const internalComponentType = mapToInternalType(featureType);
35
+ if (schema[internalComponentType]) {
36
+ return formatTextContents(JSON.stringify({ config: schema[internalComponentType] }));
37
+ }
38
+ }
39
+ catch (error) { }
40
+ return formatTextContents(`Unable to locate JSON schema for type ${featureType}`);
41
+ }
42
+ register() {
43
+ return this.mcpServer.registerTool(toolName, {
44
+ title: 'Fetch the JSON Schema for component',
45
+ description: `Fetches and returns the JSON schema for the provided feature 'type' found in -hsmeta.json file.
46
+ This should be called before editing a '-hsmeta.json' file to get the list of possible values and restrictions on those values.
47
+ This will only work for projects with platformVersion 2025.2 and beyond`,
48
+ inputSchema,
49
+ }, this.handler);
50
+ }
51
+ }
@@ -1,5 +1,5 @@
1
1
  import { Tool } from '../../types.js';
2
- import { runCommandInDir } from '../../utils/project.js';
2
+ import { runCommandInDir } from '../../utils/command.js';
3
3
  import { absoluteProjectPath } from './constants.js';
4
4
  import z from 'zod';
5
5
  import { formatTextContents } from '../../utils/content.js';
@@ -1,7 +1,7 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteProjectPath } from './constants.js';
4
- import { runCommandInDir } from '../../utils/project.js';
4
+ import { runCommandInDir } from '../../utils/command.js';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  const inputSchema = {
@@ -1,9 +1,9 @@
1
- import { AddFeatureToProject, } from '../AddFeatureToProject.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
1
+ import { AddFeatureToProjectTool, } from '../AddFeatureToProjectTool.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../../lib/constants.js';
5
5
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
- vi.mock('../../../utils/project');
6
+ vi.mock('../../../utils/command');
7
7
  vi.mock('../../../utils/command');
8
8
  vi.mock('../../../../lib/constants');
9
9
  vi.mock('../../../utils/toolUsageTracking');
@@ -21,18 +21,18 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
21
21
  };
22
22
  mockRegisteredTool = {};
23
23
  mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
24
- tool = new AddFeatureToProject(mockMcpServer);
24
+ tool = new AddFeatureToProjectTool(mockMcpServer);
25
25
  // Mock addFlag to simulate command building
26
26
  mockAddFlag.mockImplementation((command, flag, value) => `${command} --${flag} "${value}"`);
27
27
  });
28
28
  describe('register', () => {
29
29
  it('should register tool with correct parameters', () => {
30
30
  const result = tool.register();
31
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-hubspot-project', {
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-hubspot-project', expect.objectContaining({
32
32
  title: 'Add feature to HubSpot Project',
33
- description: 'Adds a feature to an existing HubSpot project',
33
+ description: expect.stringContaining('Adds a feature to an existing HubSpot project'),
34
34
  inputSchema: expect.any(Object),
35
- }, tool.handler);
35
+ }), tool.handler);
36
36
  expect(result).toBe(mockRegisteredTool);
37
37
  });
38
38
  });