@hubspot/cli 8.10.0 → 8.12.0-beta.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 (110) hide show
  1. package/commands/account/clean.js +1 -1
  2. package/commands/account/list.js +1 -1
  3. package/commands/account/remove.js +1 -1
  4. package/commands/account/rename.js +1 -1
  5. package/commands/account/use.js +1 -1
  6. package/commands/api.d.ts +4 -1
  7. package/commands/api.js +11 -14
  8. package/commands/hubdb/list.d.ts +1 -1
  9. package/commands/mcp/setup.d.ts +2 -0
  10. package/commands/mcp/setup.js +29 -1
  11. package/commands/project/appInstallStatus.d.ts +2 -1
  12. package/commands/project/appInstallStatus.js +19 -17
  13. package/commands/project/create.js +0 -1
  14. package/commands/project/deploy.d.ts +2 -1
  15. package/commands/project/deploy.js +31 -14
  16. package/commands/project/dev/index.js +18 -64
  17. package/commands/project/info.d.ts +2 -1
  18. package/commands/project/info.js +8 -6
  19. package/commands/project/installApp.d.ts +2 -1
  20. package/commands/project/installApp.js +25 -26
  21. package/commands/project/list.js +1 -1
  22. package/commands/project/profile/add.d.ts +2 -1
  23. package/commands/project/profile/add.js +22 -1
  24. package/commands/project/profile/delete.d.ts +1 -1
  25. package/commands/project/release/create.d.ts +2 -1
  26. package/commands/project/release/create.js +68 -69
  27. package/commands/project/release/info.d.ts +2 -1
  28. package/commands/project/release/info.js +7 -6
  29. package/commands/project/release/list.d.ts +2 -1
  30. package/commands/project/release/list.js +14 -6
  31. package/commands/project/upload.d.ts +2 -1
  32. package/commands/project/upload.js +23 -17
  33. package/commands/project/validate.d.ts +1 -1
  34. package/commands/project.js +2 -2
  35. package/commands/testAccount/create.d.ts +2 -1
  36. package/commands/testAccount/create.js +7 -7
  37. package/commands/testAccount/importData.d.ts +1 -1
  38. package/lang/en.d.ts +32 -28
  39. package/lang/en.js +41 -37
  40. package/lib/commonOpts.js +6 -1
  41. package/lib/constants.d.ts +1 -0
  42. package/lib/constants.js +1 -0
  43. package/lib/jsonOutput.d.ts +109 -0
  44. package/lib/jsonOutput.js +94 -0
  45. package/lib/mcp/setup.d.ts +6 -1
  46. package/lib/mcp/setup.js +29 -18
  47. package/lib/projects/builds.d.ts +2 -0
  48. package/lib/projects/builds.js +22 -0
  49. package/lib/projects/localDev/DevSessionManager.d.ts +2 -2
  50. package/lib/projects/localDev/DevSessionManager.js +4 -30
  51. package/lib/projects/localDev/helpers/account.d.ts +0 -7
  52. package/lib/projects/localDev/helpers/account.js +1 -80
  53. package/lib/projects/release.d.ts +4 -0
  54. package/lib/projects/release.js +90 -0
  55. package/lib/projects/upload.d.ts +1 -1
  56. package/lib/projects/upload.js +4 -8
  57. package/lib/yargs/makeWrappedYargsHandler.d.ts +5 -1
  58. package/lib/yargs/makeWrappedYargsHandler.js +123 -65
  59. package/mcp-server/Tool.d.ts +8 -3
  60. package/mcp-server/Tool.js +58 -32
  61. package/mcp-server/server.js +10 -6
  62. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +2 -2
  63. package/mcp-server/tools/cms/HsCreateFunctionTool.js +10 -17
  64. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  65. package/mcp-server/tools/cms/HsCreateModuleTool.js +12 -18
  66. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +2 -2
  67. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -10
  68. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +2 -2
  69. package/mcp-server/tools/cms/HsFunctionLogsTool.js +9 -9
  70. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +2 -2
  71. package/mcp-server/tools/cms/HsListFunctionsTool.js +7 -7
  72. package/mcp-server/tools/cms/HsListTool.d.ts +2 -2
  73. package/mcp-server/tools/cms/HsListTool.js +6 -10
  74. package/mcp-server/tools/index.js +2 -0
  75. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
  76. package/mcp-server/tools/project/AddFeatureToProjectTool.js +8 -9
  77. package/mcp-server/tools/project/AuthAccountTool.d.ts +18 -0
  78. package/mcp-server/tools/project/AuthAccountTool.js +78 -0
  79. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  80. package/mcp-server/tools/project/CreateProjectTool.js +13 -12
  81. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +2 -2
  82. package/mcp-server/tools/project/CreateTestAccountTool.js +14 -15
  83. package/mcp-server/tools/project/DeployProjectTool.d.ts +2 -2
  84. package/mcp-server/tools/project/DeployProjectTool.js +10 -10
  85. package/mcp-server/tools/project/DocFetchTool.js +1 -1
  86. package/mcp-server/tools/project/DocsSearchTool.js +9 -3
  87. package/mcp-server/tools/project/FindProjectsTool.js +1 -1
  88. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -4
  89. package/mcp-server/tools/project/GetApplicationInfoTool.js +9 -4
  90. package/mcp-server/tools/project/GetBuildLogsTool.js +9 -3
  91. package/mcp-server/tools/project/GetBuildStatusTool.js +9 -3
  92. package/mcp-server/tools/project/GetConfigValuesTool.js +9 -3
  93. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  94. package/mcp-server/tools/project/UploadProjectTools.d.ts +2 -2
  95. package/mcp-server/tools/project/UploadProjectTools.js +9 -8
  96. package/mcp-server/tools/project/ValidateProjectTool.d.ts +2 -2
  97. package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
  98. package/mcp-server/utils/command.d.ts +25 -2
  99. package/mcp-server/utils/command.js +67 -17
  100. package/mcp-server/utils/logger.d.ts +3 -0
  101. package/mcp-server/utils/logger.js +30 -0
  102. package/mcp-server/utils/toolUsageTracking.js +8 -1
  103. package/package.json +10 -8
  104. package/types/Yargs.d.ts +3 -1
  105. package/commands/project/dev/deprecatedFlow.d.ts +0 -11
  106. package/commands/project/dev/deprecatedFlow.js +0 -180
  107. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +0 -43
  108. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +0 -128
  109. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +0 -64
  110. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +0 -394
@@ -1,5 +1,6 @@
1
1
  import os from 'os';
2
2
  import path from 'path';
3
+ import { z } from 'zod';
3
4
  import { getConfig } from '@hubspot/local-dev-lib/config';
4
5
  import { getStateValue, setStateValue, } from '@hubspot/local-dev-lib/config/state';
5
6
  import { STATE_FLAGS } from '@hubspot/local-dev-lib/constants/config';
@@ -11,7 +12,8 @@ import { lib } from '../../lang/en.js';
11
12
  import { EXIT_CODES } from '../enums/exitCodes.js';
12
13
  import { isPromptExitError } from '../errors/PromptExitError.js';
13
14
  import { debugError } from '../errorHandlers/index.js';
14
- const HANDLER_LOG_DIR = path.join(os.homedir(), '.hscli', 'logs');
15
+ import { MAX_LOG_FILES } from '../constants.js';
16
+ const HANDLER_LOG_DIR = path.join(os.homedir(), '.hscli', 'logs', 'cli');
15
17
  function logUsageTrackingMessage(isJsonOutput) {
16
18
  if (isJsonOutput) {
17
19
  return;
@@ -32,90 +34,146 @@ function logUsageTrackingMessage(isJsonOutput) {
32
34
  return;
33
35
  }
34
36
  }
35
- export function makeWrappedYargsHandler(trackingName, handler) {
36
- return async (args) => {
37
- const startTime = Date.now();
38
- const meta = {};
39
- let trackingFired = false;
40
- const trackingArgs = args;
41
- const addUsageMetadata = (newMeta) => {
42
- Object.assign(meta, newMeta);
43
- };
44
- trackingArgs.addUsageMetadata = addUsageMetadata;
45
- const trackCommandUsage = async (successful) => {
46
- if (trackingFired) {
47
- return;
48
- }
49
- trackingFired = true;
50
- try {
51
- const { accountId: overrideAccountId, ...trackingMeta } = meta;
52
- trackingMeta.successful = successful;
53
- trackingMeta.executionTime = Date.now() - startTime;
54
- await _trackCommandUsage(trackingName, trackingMeta, overrideAccountId ?? args.derivedAccountId);
37
+ function createUsageTracker(trackingName, derivedAccountId) {
38
+ const startTime = Date.now();
39
+ const meta = {};
40
+ let fired = false;
41
+ const addMetadata = (newMeta) => {
42
+ Object.assign(meta, newMeta);
43
+ };
44
+ const track = async (successful) => {
45
+ if (fired) {
46
+ return;
47
+ }
48
+ fired = true;
49
+ try {
50
+ const { accountId: overrideAccountId, ...trackingMeta } = meta;
51
+ trackingMeta.successful = successful;
52
+ trackingMeta.executionTime = Date.now() - startTime;
53
+ await _trackCommandUsage(trackingName, trackingMeta, overrideAccountId ?? derivedAccountId);
54
+ }
55
+ catch (_e) { }
56
+ };
57
+ const onForcedExit = () => {
58
+ process.exit(EXIT_CODES.SUCCESS);
59
+ };
60
+ const onSigint = async () => {
61
+ process.removeListener('SIGINT', onSigint);
62
+ process.on('SIGINT', onForcedExit);
63
+ try {
64
+ await track(false);
65
+ }
66
+ catch (_e) { }
67
+ process.removeListener('SIGINT', onForcedExit);
68
+ process.exit(EXIT_CODES.SUCCESS);
69
+ };
70
+ process.on('SIGINT', onSigint);
71
+ const trackAndCleanup = async (successful) => {
72
+ await track(successful);
73
+ process.removeListener('SIGINT', onSigint);
74
+ process.removeListener('SIGINT', onForcedExit);
75
+ };
76
+ return { addMetadata, trackAndCleanup };
77
+ }
78
+ function createJsonOutputManager(isJsonOutput, schema) {
79
+ const data = {};
80
+ let emitted = false;
81
+ const add = (newData) => {
82
+ Object.assign(data, newData);
83
+ };
84
+ const emit = () => {
85
+ if (!emitted && isJsonOutput && Object.keys(data).length > 0) {
86
+ emitted = true;
87
+ if (schema) {
88
+ const result = schema.safeParse(data);
89
+ if (!result.success) {
90
+ uiLogger.json(data);
91
+ uiLogger.warn(lib.jsonSchema.validationFailed);
92
+ return false;
93
+ }
55
94
  }
56
- catch (_e) { }
57
- };
58
- const onForcedExit = () => {
59
- process.exit(EXIT_CODES.SUCCESS);
60
- };
61
- const onSigint = async () => {
62
- process.removeListener('SIGINT', onSigint);
63
- process.on('SIGINT', onForcedExit);
64
- try {
65
- await trackCommandUsage(false);
95
+ uiLogger.json(data);
96
+ }
97
+ return true;
98
+ };
99
+ return { add, emit };
100
+ }
101
+ function createLogFileWriter(trackingName, isJsonOutput) {
102
+ const writeLogFile = () => {
103
+ if (isJsonOutput) {
104
+ return null;
105
+ }
106
+ return ldlLogger.writeBufferedLogsToFile({
107
+ dir: HANDLER_LOG_DIR,
108
+ filenamePrefix: trackingName,
109
+ maxFiles: MAX_LOG_FILES,
110
+ });
111
+ };
112
+ const writeFailureLogFile = () => {
113
+ const savedPath = writeLogFile();
114
+ if (savedPath) {
115
+ uiLogger.log('');
116
+ uiLogger.error(lib.handlerLogFile.saved(savedPath));
117
+ }
118
+ };
119
+ return { writeLogFile, writeFailureLogFile };
120
+ }
121
+ export function makeWrappedYargsHandler(trackingName, handler, options) {
122
+ return async (args) => {
123
+ const wrappedHandlerArgs = args;
124
+ const isJsonOutput = Boolean(wrappedHandlerArgs.json || wrappedHandlerArgs.formatOutputAsJson);
125
+ const schema = options?.jsonOutputSchema;
126
+ const tracker = createUsageTracker(trackingName, args.derivedAccountId);
127
+ if (wrappedHandlerArgs.jsonSchema) {
128
+ if (schema) {
129
+ uiLogger.json(z.toJSONSchema(schema));
66
130
  }
67
- catch (_e) { }
68
- process.removeListener('SIGINT', onForcedExit);
69
- process.exit(EXIT_CODES.SUCCESS);
70
- };
71
- process.on('SIGINT', onSigint);
72
- const trackCommandUsageAndRemoveListeners = async (successful) => {
73
- await trackCommandUsage(successful);
74
- process.removeListener('SIGINT', onSigint);
75
- process.removeListener('SIGINT', onForcedExit);
76
- };
77
- const jsonArgs = args;
78
- const isJsonOutput = Boolean(jsonArgs.json || jsonArgs.formatOutputAsJson);
79
- const writeFailureLogFile = () => {
80
- // Skip in JSON output modes so the side effect + stderr message don't
81
- // interfere with structured output consumers.
82
- if (isJsonOutput) {
83
- return;
131
+ else {
132
+ uiLogger.json({ error: lib.jsonSchema.noSchemaForCommand });
84
133
  }
85
- const savedPath = ldlLogger.writeBufferedLogsToFile({
86
- dir: HANDLER_LOG_DIR,
87
- filenamePrefix: trackingName,
88
- });
89
- if (savedPath) {
90
- uiLogger.log('');
91
- uiLogger.error(lib.handlerLogFile.saved(savedPath));
134
+ await tracker.trackAndCleanup(true);
135
+ return process.exit(EXIT_CODES.SUCCESS);
136
+ }
137
+ const json = createJsonOutputManager(isJsonOutput, schema);
138
+ const logs = createLogFileWriter(trackingName, isJsonOutput);
139
+ wrappedHandlerArgs.addUsageMetadata = tracker.addMetadata;
140
+ wrappedHandlerArgs.addJsonOutput = json.add;
141
+ wrappedHandlerArgs.exit = async (code) => {
142
+ const jsonValid = json.emit();
143
+ const exitCode = !jsonValid && code === EXIT_CODES.SUCCESS ? EXIT_CODES.WARNING : code;
144
+ await tracker.trackAndCleanup(exitCode !== EXIT_CODES.ERROR);
145
+ if (exitCode === EXIT_CODES.ERROR) {
146
+ logs.writeFailureLogFile();
92
147
  }
93
- };
94
- trackingArgs.exit = async (code) => {
95
- await trackCommandUsageAndRemoveListeners(code !== EXIT_CODES.ERROR);
96
- if (code === EXIT_CODES.ERROR) {
97
- writeFailureLogFile();
148
+ else {
149
+ logs.writeLogFile();
98
150
  }
99
- return process.exit(code);
151
+ return process.exit(exitCode);
100
152
  };
101
153
  logUsageTrackingMessage(isJsonOutput);
102
154
  try {
103
- await handler(trackingArgs);
155
+ await handler(wrappedHandlerArgs);
104
156
  }
105
157
  catch (e) {
106
158
  const isSuccessfulPromptExit = isPromptExitError(e)
107
159
  ? e.exitCode !== EXIT_CODES.ERROR
108
160
  : false;
109
- await trackCommandUsageAndRemoveListeners(isSuccessfulPromptExit);
161
+ await tracker.trackAndCleanup(isSuccessfulPromptExit);
110
162
  if (isPromptExitError(e)) {
163
+ logs.writeLogFile();
111
164
  return process.exit(e.exitCode);
112
165
  }
113
166
  else {
114
167
  debugError(e);
115
- writeFailureLogFile();
168
+ logs.writeFailureLogFile();
116
169
  return process.exit(EXIT_CODES.ERROR);
117
170
  }
118
171
  }
119
- await trackCommandUsageAndRemoveListeners(true);
172
+ const jsonValid = json.emit();
173
+ await tracker.trackAndCleanup(true);
174
+ logs.writeLogFile();
175
+ if (!jsonValid) {
176
+ return process.exit(EXIT_CODES.WARNING);
177
+ }
120
178
  };
121
179
  }
@@ -1,15 +1,20 @@
1
1
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
3
+ import { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js';
2
4
  import { McpLogger } from './utils/logger.js';
3
5
  import { TextContentResponse } from './types.js';
6
+ import { CommandResults, Command } from './utils/command.js';
7
+ export type ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;
4
8
  export declare class Tool<InputSchema> {
5
9
  protected mcpServer: McpServer;
6
10
  protected logger: McpLogger;
7
11
  protected toolName: string;
8
12
  constructor(mcpServer: McpServer, logger: McpLogger, toolName: string);
9
13
  register(): RegisteredTool;
10
- handler(input: InputSchema): TextContentResponse | Promise<TextContentResponse>;
11
- protected getTrackingMeta(input: InputSchema): {
14
+ handler(_input: InputSchema): TextContentResponse | Promise<TextContentResponse>;
15
+ protected runCommand(directory: string, command: Command, extra?: ToolExtra): Promise<CommandResults>;
16
+ protected getTrackingMeta(_input: InputSchema): {
12
17
  [key: string]: string;
13
18
  } | undefined;
14
- protected wrappedHandler(input: InputSchema): Promise<TextContentResponse>;
19
+ protected wrappedHandler(input: InputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
15
20
  }
@@ -1,6 +1,7 @@
1
1
  import { formatTextContents } from './utils/content.js';
2
2
  import { getErrorMessage } from '../lib/errorHandlers/index.js';
3
3
  import { trackToolUsage } from './utils/toolUsageTracking.js';
4
+ import { runCommandInDir } from './utils/command.js';
4
5
  export class Tool {
5
6
  mcpServer;
6
7
  logger;
@@ -13,41 +14,66 @@ export class Tool {
13
14
  register() {
14
15
  throw new Error('Must implement register');
15
16
  }
16
- handler(
17
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
18
- input) {
17
+ handler(_input) {
19
18
  throw new Error('Must implement handler');
20
19
  }
21
- getTrackingMeta(
22
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
- input) {
20
+ runCommand(directory, command, extra) {
21
+ let progressCount = 0;
22
+ return runCommandInDir(directory, command, async (chunk) => {
23
+ try {
24
+ const message = `${chunk.trimEnd()}`;
25
+ this.logger.debug(this.toolName, message);
26
+ const token = extra?._meta?.progressToken;
27
+ if (token !== undefined && extra?.sendNotification) {
28
+ progressCount++;
29
+ await extra.sendNotification({
30
+ method: 'notifications/progress',
31
+ params: {
32
+ progressToken: token,
33
+ progress: progressCount,
34
+ message,
35
+ },
36
+ });
37
+ }
38
+ }
39
+ catch {
40
+ // Swallow notification errors so a failed notification never crashes the tool
41
+ }
42
+ });
43
+ }
44
+ getTrackingMeta(_input) {
24
45
  return undefined;
25
46
  }
26
- async wrappedHandler(input) {
27
- const startTime = Date.now();
28
- try {
29
- // `input` is logged unredacted. Tool input schemas MUST NOT include
30
- // credentials or other sensitive values, since MCP clients (Claude
31
- // Desktop, Inspector, etc.) will display these logs.
32
- this.logger.debug(this.toolName, {
33
- message: 'Tool invoked',
34
- args: input,
35
- });
36
- await trackToolUsage(this.toolName, this.getTrackingMeta(input));
37
- const result = await this.handler(input);
38
- this.logger.debug(this.toolName, {
39
- message: 'Tool completed',
40
- durationMs: Date.now() - startTime,
41
- });
42
- return result;
43
- }
44
- catch (error) {
45
- this.logger.error(this.toolName, {
46
- message: 'Tool failed',
47
- error: error instanceof Error ? error.message : String(error),
48
- durationMs: Date.now() - startTime,
49
- });
50
- return formatTextContents(getErrorMessage(error));
51
- }
47
+ wrappedHandler(input, extra) {
48
+ return this.logger.runWithBuffer(async () => {
49
+ const startTime = Date.now();
50
+ try {
51
+ // `input` is logged unredacted. Tool input schemas MUST NOT include
52
+ // credentials or other sensitive values, since MCP clients (Claude
53
+ // Desktop, Inspector, etc.) will display these logs.
54
+ this.logger.debug(this.toolName, {
55
+ message: 'Tool invoked',
56
+ args: input,
57
+ });
58
+ await trackToolUsage(this.toolName, this.getTrackingMeta(input));
59
+ const result = await this.handler(input, extra);
60
+ this.logger.debug(this.toolName, {
61
+ message: 'Tool completed',
62
+ durationMs: Date.now() - startTime,
63
+ });
64
+ return result;
65
+ }
66
+ catch (error) {
67
+ this.logger.error(this.toolName, {
68
+ message: 'Tool failed',
69
+ error: error instanceof Error ? error.message : String(error),
70
+ durationMs: Date.now() - startTime,
71
+ });
72
+ return formatTextContents(getErrorMessage(error));
73
+ }
74
+ finally {
75
+ this.logger.flushLogsToFile(this.toolName);
76
+ }
77
+ });
52
78
  }
53
79
  }
@@ -17,22 +17,26 @@ WHEN TO USE THIS SERVER
17
17
  via \`search-docs\` + \`fetch-doc\` rather than from prior knowledge.
18
18
 
19
19
  REQUIRED WORKFLOWS
20
- 1. Documentation lookup: always call \`search-docs\` first, then
20
+ 1. Authentication: call \`auth-account\` when the user wants to connect a
21
+ HubSpot account, when any tool reports "No account ID found", or when
22
+ there are no authenticated accounts. The tool opens a browser tab for
23
+ the user to authorize — no Personal Access Key is required up front.
24
+ 2. Documentation lookup: always call \`search-docs\` first, then
21
25
  \`fetch-doc\` on the most relevant result(s) before planning, writing
22
26
  code, or answering platform/API questions. Do not answer from memory.
23
- 2. Locating a HubSpot project: when the current working directory is not
27
+ 3. Locating a HubSpot project: when the current working directory is not
24
28
  a HubSpot project (no \`hsproject.json\`) or you need to determine
25
29
  whether a directory contains one, call \`find-projects\` before
26
30
  running any tool that requires a project path.
27
- 3. Editing \`*-hsmeta.json\`: call \`get-feature-config-schema\` for that
31
+ 4. Editing \`*-hsmeta.json\`: call \`get-feature-config-schema\` for that
28
32
  feature type first to learn the allowed fields and values.
29
- 4. Debugging a failed build: start with \`get-build-status\` to surface
33
+ 5. Debugging a failed build: start with \`get-build-status\` to surface
30
34
  error messages, and only reach for \`get-build-logs\` for deeper
31
35
  troubleshooting or warnings.
32
- 5. Reading serverless function logs: call \`list-cms-serverless-functions\`
36
+ 6. Reading serverless function logs: call \`list-cms-serverless-functions\`
33
37
  first to discover the endpoint path, then
34
38
  \`get-cms-serverless-function-logs\`.
35
- 6. App analytics: call \`get-apps-info\` to discover \`appId\` values
39
+ 7. App analytics: call \`get-apps-info\` to discover \`appId\` values
36
40
  before \`get-api-usage-patterns-by-app-id\`.
37
41
 
38
42
  OUTPUT
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -20,7 +20,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
20
20
  export type HsCreateFunctionInputSchema = z.infer<typeof inputSchemaZodObject>;
21
21
  export declare class HsCreateFunctionTool extends Tool<HsCreateFunctionInputSchema> {
22
22
  constructor(mcpServer: McpServer, logger: McpLogger);
23
- handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }: HsCreateFunctionInputSchema): Promise<TextContentResponse>;
23
+ handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }: HsCreateFunctionInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
24
24
  register(): RegisteredTool;
25
25
  }
26
26
  export {};
@@ -1,10 +1,9 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/command.js';
5
4
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
- import { addFlag } from '../../utils/command.js';
7
5
  import { HTTP_METHODS } from '../../../types/Cms.js';
6
+ import { HubSpotCommand } from '../../utils/command.js';
8
7
  import { setupHubSpotConfig } from '../../utils/config.js';
9
8
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
10
9
  const inputSchema = {
@@ -37,7 +36,7 @@ export class HsCreateFunctionTool extends Tool {
37
36
  constructor(mcpServer, logger) {
38
37
  super(mcpServer, logger, toolName);
39
38
  }
40
- async handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }) {
39
+ async handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }, extra) {
41
40
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
42
41
  const content = [];
43
42
  // Require functions folder
@@ -59,28 +58,22 @@ export class HsCreateFunctionTool extends Tool {
59
58
  };
60
59
  }
61
60
  // Build the command
62
- let command = 'hs cms function create';
61
+ const command = new HubSpotCommand('cms function create');
63
62
  if (dest) {
64
- command += ` "${dest}"`;
63
+ command.addArg(dest);
65
64
  }
66
- // Add function-specific flags
67
65
  if (functionsFolder) {
68
- command = addFlag(command, 'functions-folder', functionsFolder);
66
+ command.addFlag('functions-folder', functionsFolder);
69
67
  }
70
68
  if (filename) {
71
- command = addFlag(command, 'filename', filename);
72
- }
73
- if (endpointMethod) {
74
- command = addFlag(command, 'endpoint-method', endpointMethod);
75
- }
76
- else {
77
- command = addFlag(command, 'endpoint-method', 'GET');
69
+ command.addFlag('filename', filename);
78
70
  }
71
+ command.addFlag('endpoint-method', endpointMethod ?? 'GET');
79
72
  if (endpointPath) {
80
- command = addFlag(command, 'endpoint-path', endpointPath);
73
+ command.addFlag('endpoint-path', endpointPath);
81
74
  }
82
75
  try {
83
- const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
76
+ const { stdout, stderr } = await this.runCommand(absoluteCurrentWorkingDirectory, command, extra);
84
77
  return formatTextContents(stdout, stderr);
85
78
  }
86
79
  catch (error) {
@@ -102,6 +95,6 @@ export class HsCreateFunctionTool extends Tool {
102
95
  idempotentHint: false,
103
96
  openWorldHint: false,
104
97
  },
105
- }, input => this.wrappedHandler(input));
98
+ }, (input, extra) => this.wrappedHandler(input, extra));
106
99
  }
107
100
  }
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -16,7 +16,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
16
16
  export type HsCreateModuleInputSchema = z.infer<typeof inputSchemaZodObject>;
17
17
  export declare class HsCreateModuleTool extends Tool<HsCreateModuleInputSchema> {
18
18
  constructor(mcpServer: McpServer, logger: McpLogger);
19
- handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }: HsCreateModuleInputSchema): Promise<TextContentResponse>;
19
+ handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }: HsCreateModuleInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
20
20
  register(): RegisteredTool;
21
21
  }
22
22
  export {};
@@ -1,9 +1,9 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { addFlag, runCommandInDir } from '../../utils/command.js';
5
4
  import { formatTextContent, formatTextContents } from '../../utils/content.js';
6
5
  import { CONTENT_TYPES } from '../../../types/Cms.js';
6
+ import { HubSpotCommand } from '../../utils/command.js';
7
7
  import { setupHubSpotConfig } from '../../utils/config.js';
8
8
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
9
9
  const inputSchema = {
@@ -52,7 +52,7 @@ export class HsCreateModuleTool extends Tool {
52
52
  constructor(mcpServer, logger) {
53
53
  super(mcpServer, logger, toolName);
54
54
  }
55
- async handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }) {
55
+ async handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }, extra) {
56
56
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
57
57
  const content = [];
58
58
  // Always require a name
@@ -74,34 +74,28 @@ export class HsCreateModuleTool extends Tool {
74
74
  };
75
75
  }
76
76
  // Build the command
77
- let command = 'hs cms module create';
77
+ const command = new HubSpotCommand('cms module create');
78
78
  if (userSuppliedName) {
79
- command += ` "${userSuppliedName}"`;
79
+ command.addArg(userSuppliedName);
80
80
  }
81
81
  if (dest) {
82
- command += ` "${dest}"`;
82
+ command.addArg(dest);
83
83
  }
84
- // Add module-specific flags
85
84
  if (moduleLabel) {
86
- command = addFlag(command, 'module-label', moduleLabel);
85
+ command.addFlag('module-label', moduleLabel);
87
86
  }
88
87
  if (reactType !== undefined) {
89
- command = addFlag(command, 'react-type', reactType);
90
- }
91
- if (contentTypes) {
92
- command = addFlag(command, 'content-types', contentTypes);
93
- }
94
- else {
95
- command = addFlag(command, 'content-types', 'ANY');
88
+ command.addFlag('react-type', reactType);
96
89
  }
90
+ command.addFlag('content-types', contentTypes ?? 'ANY');
97
91
  if (global !== undefined) {
98
- command = addFlag(command, 'global', global);
92
+ command.addFlag('global', global);
99
93
  }
100
94
  if (availableForNewContent !== undefined) {
101
- command = addFlag(command, 'available-for-new-content', availableForNewContent);
95
+ command.addFlag('available-for-new-content', availableForNewContent);
102
96
  }
103
97
  try {
104
- const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
98
+ const { stdout, stderr } = await this.runCommand(absoluteCurrentWorkingDirectory, command, extra);
105
99
  return formatTextContents(stdout, stderr);
106
100
  }
107
101
  catch (error) {
@@ -123,6 +117,6 @@ export class HsCreateModuleTool extends Tool {
123
117
  idempotentHint: false,
124
118
  openWorldHint: false,
125
119
  },
126
- }, input => this.wrappedHandler(input));
120
+ }, (input, extra) => this.wrappedHandler(input, extra));
127
121
  }
128
122
  }
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -21,7 +21,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
21
21
  export type HsCreateTemplateInputSchema = z.infer<typeof inputSchemaZodObject>;
22
22
  export declare class HsCreateTemplateTool extends Tool<HsCreateTemplateInputSchema> {
23
23
  constructor(mcpServer: McpServer, logger: McpLogger);
24
- handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }: HsCreateTemplateInputSchema): Promise<TextContentResponse>;
24
+ handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }: HsCreateTemplateInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
25
25
  register(): RegisteredTool;
26
26
  }
27
27
  export {};
@@ -1,10 +1,9 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/command.js';
5
4
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
- import { addFlag } from '../../utils/command.js';
7
5
  import { TEMPLATE_TYPES } from '../../../types/Cms.js';
6
+ import { HubSpotCommand } from '../../utils/command.js';
8
7
  import { setupHubSpotConfig } from '../../utils/config.js';
9
8
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
10
9
  const inputSchema = {
@@ -29,7 +28,7 @@ export class HsCreateTemplateTool extends Tool {
29
28
  constructor(mcpServer, logger) {
30
29
  super(mcpServer, logger, toolName);
31
30
  }
32
- async handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }) {
31
+ async handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }, extra) {
33
32
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
34
33
  const content = [];
35
34
  // Always require a name
@@ -47,19 +46,18 @@ export class HsCreateTemplateTool extends Tool {
47
46
  };
48
47
  }
49
48
  // Build the command
50
- let command = 'hs cms template create';
49
+ const command = new HubSpotCommand('cms template create');
51
50
  if (userSuppliedName) {
52
- command += ` "${userSuppliedName}"`;
51
+ command.addArg(userSuppliedName);
53
52
  }
54
53
  if (dest) {
55
- command += ` "${dest}"`;
54
+ command.addArg(dest);
56
55
  }
57
- // Add template type flag
58
56
  if (templateType) {
59
- command = addFlag(command, 'template-type', templateType);
57
+ command.addFlag('template-type', templateType);
60
58
  }
61
59
  try {
62
- const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
60
+ const { stdout, stderr } = await this.runCommand(absoluteCurrentWorkingDirectory, command, extra);
63
61
  return formatTextContents(stdout, stderr);
64
62
  }
65
63
  catch (error) {
@@ -81,6 +79,6 @@ export class HsCreateTemplateTool extends Tool {
81
79
  idempotentHint: false,
82
80
  openWorldHint: false,
83
81
  },
84
- }, input => this.wrappedHandler(input));
82
+ }, (input, extra) => this.wrappedHandler(input, extra));
85
83
  }
86
84
  }
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -14,7 +14,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
14
14
  export type HsFunctionLogsInputSchema = z.infer<typeof inputSchemaZodObject>;
15
15
  export declare class HsFunctionLogsTool extends Tool<HsFunctionLogsInputSchema> {
16
16
  constructor(mcpServer: McpServer, logger: McpLogger);
17
- handler({ endpoint, account, latest, compact, limit, absoluteCurrentWorkingDirectory, }: HsFunctionLogsInputSchema): Promise<TextContentResponse>;
17
+ handler({ endpoint, account, latest, compact, limit, absoluteCurrentWorkingDirectory, }: HsFunctionLogsInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
18
18
  register(): RegisteredTool;
19
19
  }
20
20
  export {};