@hubspot/cli 8.10.0 → 8.11.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.
- package/commands/hubdb/list.d.ts +1 -1
- package/commands/mcp/setup.d.ts +2 -0
- package/commands/mcp/setup.js +29 -1
- package/commands/project/appInstallStatus.d.ts +1 -1
- package/commands/project/info.d.ts +1 -1
- package/commands/project/installApp.d.ts +1 -1
- package/commands/project/profile/add.d.ts +2 -1
- package/commands/project/profile/add.js +22 -1
- package/commands/project/profile/delete.d.ts +1 -1
- package/commands/project/upload.js +4 -1
- package/commands/project/validate.d.ts +1 -1
- package/commands/testAccount/importData.d.ts +1 -1
- package/lang/en.d.ts +11 -0
- package/lang/en.js +11 -0
- package/lib/mcp/setup.d.ts +6 -1
- package/lib/mcp/setup.js +29 -18
- package/lib/projects/upload.d.ts +1 -1
- package/lib/projects/upload.js +4 -8
- package/mcp-server/Tool.d.ts +8 -3
- package/mcp-server/Tool.js +28 -8
- package/mcp-server/server.js +10 -6
- package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +10 -17
- package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsCreateModuleTool.js +12 -18
- package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -10
- package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +9 -9
- package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +7 -7
- package/mcp-server/tools/cms/HsListTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsListTool.js +6 -10
- package/mcp-server/tools/index.js +2 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +8 -9
- package/mcp-server/tools/project/AuthAccountTool.d.ts +18 -0
- package/mcp-server/tools/project/AuthAccountTool.js +78 -0
- package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/CreateProjectTool.js +13 -12
- package/mcp-server/tools/project/CreateTestAccountTool.d.ts +2 -2
- package/mcp-server/tools/project/CreateTestAccountTool.js +14 -15
- package/mcp-server/tools/project/DeployProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/DeployProjectTool.js +10 -10
- package/mcp-server/tools/project/DocFetchTool.js +1 -1
- package/mcp-server/tools/project/DocsSearchTool.js +9 -3
- package/mcp-server/tools/project/FindProjectsTool.js +1 -1
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -4
- package/mcp-server/tools/project/GetApplicationInfoTool.js +9 -4
- package/mcp-server/tools/project/GetBuildLogsTool.js +9 -3
- package/mcp-server/tools/project/GetBuildStatusTool.js +9 -3
- package/mcp-server/tools/project/GetConfigValuesTool.js +9 -3
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.d.ts +2 -2
- package/mcp-server/tools/project/UploadProjectTools.js +9 -8
- package/mcp-server/tools/project/ValidateProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
- package/mcp-server/utils/command.d.ts +25 -2
- package/mcp-server/utils/command.js +67 -17
- package/mcp-server/utils/toolUsageTracking.js +8 -1
- package/package.json +3 -3
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { formatTextContents, formatTextContent } from '../../utils/content.js';
|
|
4
|
-
import {
|
|
5
|
-
import { runCommandInDir } from '../../utils/command.js';
|
|
4
|
+
import { HubSpotCommand } from '../../utils/command.js';
|
|
6
5
|
import { ACCOUNT_LEVELS, ACCOUNT_LEVEL_CHOICES, } from '../../../lib/constants.js';
|
|
7
6
|
import { Tool } from '../../Tool.js';
|
|
8
7
|
import { absoluteCurrentWorkingDirectory } from './constants.js';
|
|
@@ -70,9 +69,9 @@ export class CreateTestAccountTool extends Tool {
|
|
|
70
69
|
constructor(mcpServer, logger) {
|
|
71
70
|
super(mcpServer, logger, toolName);
|
|
72
71
|
}
|
|
73
|
-
async handler({ absoluteCurrentWorkingDirectory, name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, commerceLevel, configPath, }) {
|
|
72
|
+
async handler({ absoluteCurrentWorkingDirectory, name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, commerceLevel, configPath, }, extra) {
|
|
74
73
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
75
|
-
|
|
74
|
+
const command = new HubSpotCommand('test-account create');
|
|
76
75
|
const content = [];
|
|
77
76
|
// Use config file if provided (LLM should check for config first)
|
|
78
77
|
if (configPath) {
|
|
@@ -105,7 +104,7 @@ export class CreateTestAccountTool extends Tool {
|
|
|
105
104
|
});
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
|
-
command
|
|
107
|
+
command.addFlag('config-path', configPath);
|
|
109
108
|
}
|
|
110
109
|
// Use flags if name is provided (when no config used)
|
|
111
110
|
else if (name) {
|
|
@@ -120,14 +119,14 @@ export class CreateTestAccountTool extends Tool {
|
|
|
120
119
|
error: e instanceof Error ? e.message : String(e),
|
|
121
120
|
});
|
|
122
121
|
}
|
|
123
|
-
command
|
|
124
|
-
command
|
|
125
|
-
command
|
|
126
|
-
command
|
|
127
|
-
command
|
|
128
|
-
command
|
|
129
|
-
command
|
|
130
|
-
command
|
|
122
|
+
command.addFlag('name', name);
|
|
123
|
+
command.addFlag('description', description || name);
|
|
124
|
+
command.addFlag('marketing-level', marketingLevel || 'ENTERPRISE');
|
|
125
|
+
command.addFlag('ops-level', opsLevel || 'ENTERPRISE');
|
|
126
|
+
command.addFlag('service-level', serviceLevel || 'ENTERPRISE');
|
|
127
|
+
command.addFlag('sales-level', salesLevel || 'ENTERPRISE');
|
|
128
|
+
command.addFlag('content-level', contentLevel || 'ENTERPRISE');
|
|
129
|
+
command.addFlag('commerce-level', commerceLevel || 'ENTERPRISE');
|
|
131
130
|
}
|
|
132
131
|
else {
|
|
133
132
|
content.push(formatTextContent(`Ask the user for the account config JSON path or the name of the test account to create.`));
|
|
@@ -139,7 +138,7 @@ export class CreateTestAccountTool extends Tool {
|
|
|
139
138
|
}
|
|
140
139
|
// No flags or config - command will prompt user interactively
|
|
141
140
|
try {
|
|
142
|
-
const { stdout, stderr } = await
|
|
141
|
+
const { stdout, stderr } = await this.runCommand(absoluteCurrentWorkingDirectory, command, extra);
|
|
143
142
|
return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
|
|
144
143
|
}
|
|
145
144
|
catch (error) {
|
|
@@ -177,6 +176,6 @@ export class CreateTestAccountTool extends Tool {
|
|
|
177
176
|
idempotentHint: false,
|
|
178
177
|
openWorldHint: true,
|
|
179
178
|
},
|
|
180
|
-
}, input => this.wrappedHandler(input));
|
|
179
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
181
180
|
}
|
|
182
181
|
}
|
|
@@ -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';
|
|
@@ -11,7 +11,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
11
11
|
type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
|
|
12
12
|
export declare class DeployProjectTool extends Tool<InputSchemaType> {
|
|
13
13
|
constructor(mcpServer: McpServer, logger: McpLogger);
|
|
14
|
-
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
|
|
14
|
+
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }: InputSchemaType, extra?: ToolExtra): Promise<TextContentResponse>;
|
|
15
15
|
register(): RegisteredTool;
|
|
16
16
|
}
|
|
17
17
|
export {};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Tool } from '../../Tool.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
3
|
+
import { HubSpotCommand } from '../../utils/command.js';
|
|
4
4
|
import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
|
|
5
|
-
import { runCommandInDir } from '../../utils/command.js';
|
|
6
5
|
import { formatTextContents, formatTextContent } from '../../utils/content.js';
|
|
7
6
|
import { setupHubSpotConfig } from '../../utils/config.js';
|
|
8
7
|
const inputSchema = {
|
|
@@ -21,23 +20,24 @@ export class DeployProjectTool extends Tool {
|
|
|
21
20
|
constructor(mcpServer, logger) {
|
|
22
21
|
super(mcpServer, logger, toolName);
|
|
23
22
|
}
|
|
24
|
-
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }) {
|
|
23
|
+
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }, extra) {
|
|
25
24
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
26
|
-
let command = `hs project deploy`;
|
|
27
25
|
const content = [];
|
|
28
26
|
if (!buildNumber) {
|
|
29
|
-
const { stdout } = await
|
|
27
|
+
const { stdout } = await this.runCommand(absoluteProjectPath, new HubSpotCommand('project list-builds', [
|
|
28
|
+
{ name: 'limit', value: 100 },
|
|
29
|
+
]), extra);
|
|
30
30
|
content.push(formatTextContent(`Ask the user which build number they would like to deploy? Build information: ${stdout}`));
|
|
31
31
|
}
|
|
32
|
-
else {
|
|
33
|
-
command = addFlag(command, 'build', buildNumber);
|
|
34
|
-
}
|
|
35
32
|
if (content.length) {
|
|
36
33
|
return {
|
|
37
34
|
content,
|
|
38
35
|
};
|
|
39
36
|
}
|
|
40
|
-
const
|
|
37
|
+
const command = new HubSpotCommand('project deploy', [
|
|
38
|
+
{ name: 'build', value: buildNumber },
|
|
39
|
+
]);
|
|
40
|
+
const { stdout, stderr } = await this.runCommand(absoluteProjectPath, command, extra);
|
|
41
41
|
return formatTextContents(stdout, stderr);
|
|
42
42
|
}
|
|
43
43
|
register() {
|
|
@@ -51,6 +51,6 @@ export class DeployProjectTool extends Tool {
|
|
|
51
51
|
idempotentHint: true,
|
|
52
52
|
openWorldHint: true,
|
|
53
53
|
},
|
|
54
|
-
}, input => this.wrappedHandler(input));
|
|
54
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -33,9 +33,15 @@ export class DocsSearchTool extends Tool {
|
|
|
33
33
|
}
|
|
34
34
|
async handler({ docsSearchQuery, docsSearchLimit, absoluteCurrentWorkingDirectory, }) {
|
|
35
35
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
36
|
-
|
|
36
|
+
let accountId;
|
|
37
|
+
try {
|
|
38
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Config file does not exist
|
|
42
|
+
}
|
|
37
43
|
if (!accountId) {
|
|
38
|
-
const authErrorMessage = `No account ID found.
|
|
44
|
+
const authErrorMessage = `No account ID found. Call the auth-account tool to authenticate a HubSpot account.`;
|
|
39
45
|
return formatTextContents(authErrorMessage);
|
|
40
46
|
}
|
|
41
47
|
try {
|
|
@@ -87,6 +93,6 @@ export class DocsSearchTool extends Tool {
|
|
|
87
93
|
readOnlyHint: true,
|
|
88
94
|
openWorldHint: true,
|
|
89
95
|
},
|
|
90
|
-
}, input => this.wrappedHandler(input));
|
|
96
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
91
97
|
}
|
|
92
98
|
}
|
|
@@ -31,10 +31,15 @@ export class GetApiUsagePatternsByAppIdTool extends Tool {
|
|
|
31
31
|
async handler({ appId, startDate, endDate, absoluteCurrentWorkingDirectory, }) {
|
|
32
32
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
33
33
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
let accountId;
|
|
35
|
+
try {
|
|
36
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Config file does not exist
|
|
40
|
+
}
|
|
36
41
|
if (!accountId) {
|
|
37
|
-
const authErrorMessage = `No account ID found.
|
|
42
|
+
const authErrorMessage = `No account ID found. Call the auth-account tool to authenticate a HubSpot account.`;
|
|
38
43
|
return formatTextContents(authErrorMessage);
|
|
39
44
|
}
|
|
40
45
|
const response = await http.get(accountId, {
|
|
@@ -70,6 +75,6 @@ export class GetApiUsagePatternsByAppIdTool extends Tool {
|
|
|
70
75
|
readOnlyHint: true,
|
|
71
76
|
openWorldHint: true,
|
|
72
77
|
},
|
|
73
|
-
}, input => this.wrappedHandler(input));
|
|
78
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
74
79
|
}
|
|
75
80
|
}
|
|
@@ -18,10 +18,15 @@ export class GetApplicationInfoTool extends Tool {
|
|
|
18
18
|
async handler({ absoluteCurrentWorkingDirectory, }) {
|
|
19
19
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
20
20
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
let accountId;
|
|
22
|
+
try {
|
|
23
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Config file does not exist
|
|
27
|
+
}
|
|
23
28
|
if (!accountId) {
|
|
24
|
-
const authErrorMessage = `No account ID found.
|
|
29
|
+
const authErrorMessage = `No account ID found. Call the auth-account tool to authenticate a HubSpot account.`;
|
|
25
30
|
return formatTextContents(authErrorMessage);
|
|
26
31
|
}
|
|
27
32
|
const response = await http.get(accountId, {
|
|
@@ -53,6 +58,6 @@ export class GetApplicationInfoTool extends Tool {
|
|
|
53
58
|
readOnlyHint: true,
|
|
54
59
|
openWorldHint: true,
|
|
55
60
|
},
|
|
56
|
-
}, input => this.wrappedHandler(input));
|
|
61
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
57
62
|
}
|
|
58
63
|
}
|
|
@@ -64,9 +64,15 @@ export class GetBuildLogsTool extends Tool {
|
|
|
64
64
|
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildId, logLevel, }) {
|
|
65
65
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
let accountId;
|
|
68
|
+
try {
|
|
69
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Config file does not exist
|
|
73
|
+
}
|
|
68
74
|
if (!accountId) {
|
|
69
|
-
return formatTextContents(absoluteCurrentWorkingDirectory, 'No account ID found.
|
|
75
|
+
return formatTextContents(absoluteCurrentWorkingDirectory, 'No account ID found. Call the auth-account tool to authenticate a HubSpot account.');
|
|
70
76
|
}
|
|
71
77
|
const { projectConfig, projectDir } = await getProjectConfig(absoluteProjectPath);
|
|
72
78
|
validateProjectConfig(projectConfig, projectDir);
|
|
@@ -121,6 +127,6 @@ export class GetBuildLogsTool extends Tool {
|
|
|
121
127
|
openWorldHint: true,
|
|
122
128
|
idempotentHint: true,
|
|
123
129
|
},
|
|
124
|
-
}, input => this.wrappedHandler(input));
|
|
130
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
125
131
|
}
|
|
126
132
|
}
|
|
@@ -109,9 +109,15 @@ export class GetBuildStatusTool extends Tool {
|
|
|
109
109
|
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildId, limit, }) {
|
|
110
110
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
111
111
|
try {
|
|
112
|
-
|
|
112
|
+
let accountId;
|
|
113
|
+
try {
|
|
114
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Config file does not exist
|
|
118
|
+
}
|
|
113
119
|
if (!accountId) {
|
|
114
|
-
return formatTextContents(absoluteCurrentWorkingDirectory, 'No account ID found.
|
|
120
|
+
return formatTextContents(absoluteCurrentWorkingDirectory, 'No account ID found. Call the auth-account tool to authenticate a HubSpot account.');
|
|
115
121
|
}
|
|
116
122
|
const { projectConfig, projectDir } = await getProjectConfig(absoluteProjectPath);
|
|
117
123
|
validateProjectConfig(projectConfig, projectDir);
|
|
@@ -162,6 +168,6 @@ export class GetBuildStatusTool extends Tool {
|
|
|
162
168
|
openWorldHint: true,
|
|
163
169
|
idempotentHint: true,
|
|
164
170
|
},
|
|
165
|
-
}, input => this.wrappedHandler(input));
|
|
171
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
166
172
|
}
|
|
167
173
|
}
|
|
@@ -31,9 +31,15 @@ export class GetConfigValuesTool extends Tool {
|
|
|
31
31
|
if (isLegacyProject(platformVersion)) {
|
|
32
32
|
return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
let accountId;
|
|
35
|
+
try {
|
|
36
|
+
accountId = getConfigDefaultAccountIfExists()?.accountId;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Config file does not exist
|
|
40
|
+
}
|
|
35
41
|
if (!accountId) {
|
|
36
|
-
const authErrorMessage = `No account ID found.
|
|
42
|
+
const authErrorMessage = `No account ID found. Call the auth-account tool to authenticate a HubSpot account.`;
|
|
37
43
|
return formatTextContents(authErrorMessage);
|
|
38
44
|
}
|
|
39
45
|
const schema = await getIntermediateRepresentationSchema({
|
|
@@ -65,6 +71,6 @@ export class GetConfigValuesTool extends Tool {
|
|
|
65
71
|
readOnlyHint: true,
|
|
66
72
|
openWorldHint: false,
|
|
67
73
|
},
|
|
68
|
-
}, input => this.wrappedHandler(input));
|
|
74
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
69
75
|
}
|
|
70
76
|
}
|
|
@@ -2,7 +2,7 @@ import z from 'zod';
|
|
|
2
2
|
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { McpLogger } from '../../utils/logger.js';
|
|
4
4
|
import { TextContentResponse } from '../../types.js';
|
|
5
|
-
import { Tool } from '../../Tool.js';
|
|
5
|
+
import { Tool, ToolExtra } from '../../Tool.js';
|
|
6
6
|
declare const inputSchemaZodObject: z.ZodObject<{
|
|
7
7
|
absoluteProjectPath: z.ZodString;
|
|
8
8
|
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
@@ -12,7 +12,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
12
12
|
type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
|
|
13
13
|
export declare class UploadProjectTools extends Tool<InputSchemaType> {
|
|
14
14
|
constructor(mcpServer: McpServer, logger: McpLogger);
|
|
15
|
-
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }: InputSchemaType): Promise<TextContentResponse>;
|
|
15
|
+
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }: InputSchemaType, extra?: ToolExtra): Promise<TextContentResponse>;
|
|
16
16
|
register(): RegisteredTool;
|
|
17
17
|
}
|
|
18
18
|
export {};
|
|
@@ -3,10 +3,9 @@ import z from 'zod';
|
|
|
3
3
|
import { getAllHsProfiles } from '@hubspot/project-parsing-lib/profiles';
|
|
4
4
|
import { getProjectConfig } from '../../../lib/projects/config.js';
|
|
5
5
|
import { Tool } from '../../Tool.js';
|
|
6
|
-
import { runCommandInDir } from '../../utils/command.js';
|
|
7
6
|
import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
|
|
8
7
|
import { formatTextContent, formatTextContents } from '../../utils/content.js';
|
|
9
|
-
import {
|
|
8
|
+
import { HubSpotCommand } from '../../utils/command.js';
|
|
10
9
|
import { setupHubSpotConfig } from '../../utils/config.js';
|
|
11
10
|
const inputSchema = {
|
|
12
11
|
absoluteProjectPath,
|
|
@@ -27,15 +26,17 @@ export class UploadProjectTools extends Tool {
|
|
|
27
26
|
constructor(mcpServer, logger) {
|
|
28
27
|
super(mcpServer, logger, toolName);
|
|
29
28
|
}
|
|
30
|
-
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }) {
|
|
29
|
+
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }, extra) {
|
|
31
30
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
32
|
-
|
|
31
|
+
const command = new HubSpotCommand('project upload', [
|
|
32
|
+
{ name: 'force', value: true },
|
|
33
|
+
]);
|
|
33
34
|
const content = [];
|
|
34
35
|
if (uploadMessage) {
|
|
35
|
-
command
|
|
36
|
+
command.addFlag('message', uploadMessage);
|
|
36
37
|
}
|
|
37
38
|
if (profile) {
|
|
38
|
-
command
|
|
39
|
+
command.addFlag('profile', profile);
|
|
39
40
|
}
|
|
40
41
|
else {
|
|
41
42
|
let hasProfiles = false;
|
|
@@ -63,7 +64,7 @@ export class UploadProjectTools extends Tool {
|
|
|
63
64
|
content,
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
|
-
const { stdout, stderr } = await
|
|
67
|
+
const { stdout, stderr } = await this.runCommand(absoluteProjectPath, command, extra);
|
|
67
68
|
const response = await formatTextContents(stdout, stderr);
|
|
68
69
|
// Add reminder about cards needing to be added to views
|
|
69
70
|
response.content.push(formatTextContent('\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.'));
|
|
@@ -80,6 +81,6 @@ export class UploadProjectTools extends Tool {
|
|
|
80
81
|
idempotentHint: true,
|
|
81
82
|
openWorldHint: true,
|
|
82
83
|
},
|
|
83
|
-
}, input => this.wrappedHandler(input));
|
|
84
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -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';
|
|
@@ -10,7 +10,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
10
10
|
export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
11
11
|
export declare class ValidateProjectTool extends Tool<CreateProjectInputSchema> {
|
|
12
12
|
constructor(mcpServer: McpServer, logger: McpLogger);
|
|
13
|
-
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema): Promise<TextContentResponse>;
|
|
13
|
+
handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
|
|
14
14
|
register(): RegisteredTool;
|
|
15
15
|
}
|
|
16
16
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Tool } from '../../Tool.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
|
|
4
|
-
import { runCommandInDir } from '../../utils/command.js';
|
|
5
4
|
import { formatTextContents } from '../../utils/content.js';
|
|
6
5
|
import { setupHubSpotConfig } from '../../utils/config.js';
|
|
7
6
|
import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
|
|
7
|
+
import { HubSpotCommand } from '../../utils/command.js';
|
|
8
8
|
const inputSchema = {
|
|
9
9
|
absoluteProjectPath,
|
|
10
10
|
absoluteCurrentWorkingDirectory,
|
|
@@ -16,10 +16,11 @@ export class ValidateProjectTool extends Tool {
|
|
|
16
16
|
constructor(mcpServer, logger) {
|
|
17
17
|
super(mcpServer, logger, toolName);
|
|
18
18
|
}
|
|
19
|
-
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }) {
|
|
19
|
+
async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }, extra) {
|
|
20
20
|
setupHubSpotConfig(absoluteCurrentWorkingDirectory);
|
|
21
21
|
try {
|
|
22
|
-
const
|
|
22
|
+
const command = new HubSpotCommand('project validate');
|
|
23
|
+
const { stdout, stderr } = await this.runCommand(absoluteProjectPath, command, extra);
|
|
23
24
|
return formatTextContents(stdout, stderr);
|
|
24
25
|
}
|
|
25
26
|
catch (error) {
|
|
@@ -39,6 +40,6 @@ export class ValidateProjectTool extends Tool {
|
|
|
39
40
|
readOnlyHint: true,
|
|
40
41
|
openWorldHint: false,
|
|
41
42
|
},
|
|
42
|
-
}, input => this.wrappedHandler(input));
|
|
43
|
+
}, (input, extra) => this.wrappedHandler(input, extra));
|
|
43
44
|
}
|
|
44
45
|
}
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
import { exec } from 'node:child_process';
|
|
2
|
+
export interface Command {
|
|
3
|
+
executable: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
}
|
|
6
|
+
export type FlagValue = string | number | boolean | string[];
|
|
7
|
+
export interface Flag {
|
|
8
|
+
name: string;
|
|
9
|
+
value: FlagValue;
|
|
10
|
+
}
|
|
11
|
+
export declare class HubSpotCommand implements Command {
|
|
12
|
+
readonly executable = "hs";
|
|
13
|
+
private readonly subcommandArgs;
|
|
14
|
+
private readonly positionalArgs;
|
|
15
|
+
private readonly flags;
|
|
16
|
+
constructor(subcommand: string, flags?: Flag[]);
|
|
17
|
+
addArg(value: string): this;
|
|
18
|
+
addFlag(name: string, value: FlagValue): this;
|
|
19
|
+
get args(): string[];
|
|
20
|
+
}
|
|
21
|
+
export declare class NpxHubSpotCommand implements Command {
|
|
22
|
+
readonly executable = "npx";
|
|
23
|
+
readonly args: string[];
|
|
24
|
+
constructor(command: Command, cliPackage?: string);
|
|
25
|
+
}
|
|
2
26
|
export declare const execAsync: typeof exec.__promisify__;
|
|
3
|
-
export declare function addFlag(command: string, flagName: string, value: string | number | boolean | string[]): string;
|
|
4
27
|
export interface CommandResults {
|
|
5
28
|
stderr: string;
|
|
6
29
|
stdout: string;
|
|
7
30
|
}
|
|
8
|
-
export declare function runCommandInDir(directory: string, command: string): Promise<CommandResults>;
|
|
31
|
+
export declare function runCommandInDir(directory: string, command: Command, onData?: (chunk: string, source: 'stdout' | 'stderr') => void): Promise<CommandResults>;
|
|
@@ -1,35 +1,85 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import util from 'util';
|
|
4
|
-
import { exec } from 'node:child_process';
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import { exec, spawn } from 'node:child_process';
|
|
5
|
+
export class HubSpotCommand {
|
|
6
|
+
executable = 'hs';
|
|
7
|
+
subcommandArgs;
|
|
8
|
+
positionalArgs;
|
|
9
|
+
flags;
|
|
10
|
+
constructor(subcommand, flags = []) {
|
|
11
|
+
this.subcommandArgs = subcommand.trim().split(/\s+/);
|
|
12
|
+
this.positionalArgs = [];
|
|
13
|
+
this.flags = [...flags];
|
|
14
|
+
}
|
|
15
|
+
addArg(value) {
|
|
16
|
+
this.positionalArgs.push(value);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
addFlag(name, value) {
|
|
20
|
+
this.flags.push({ name, value });
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
get args() {
|
|
24
|
+
return [
|
|
25
|
+
...this.subcommandArgs,
|
|
26
|
+
...this.positionalArgs,
|
|
27
|
+
...this.flags.flatMap(({ name, value }) => Array.isArray(value)
|
|
28
|
+
? [`--${name}`, ...value.map(String)]
|
|
29
|
+
: [`--${name}`, String(value)]),
|
|
30
|
+
];
|
|
9
31
|
}
|
|
10
|
-
return `${command} --${flagName} "${value}"`;
|
|
11
32
|
}
|
|
12
|
-
export
|
|
33
|
+
export class NpxHubSpotCommand {
|
|
34
|
+
executable = 'npx';
|
|
35
|
+
args;
|
|
36
|
+
constructor(command, cliPackage = '@hubspot/cli') {
|
|
37
|
+
this.args = ['-y', '-p', cliPackage, 'hs', ...command.args];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const execAsync = util.promisify(exec);
|
|
41
|
+
export async function runCommandInDir(directory, command, onData) {
|
|
13
42
|
if (!fs.existsSync(directory)) {
|
|
14
43
|
fs.mkdirSync(directory);
|
|
15
44
|
}
|
|
16
45
|
let finalCommand = command;
|
|
17
|
-
if (command
|
|
18
|
-
|
|
46
|
+
if (command instanceof HubSpotCommand) {
|
|
47
|
+
command.addFlag('disable-usage-tracking', 'true');
|
|
19
48
|
if (process.env.HUBSPOT_MCP_STANDALONE === 'true') {
|
|
20
49
|
const cliPackage = process.env.HUBSPOT_CLI_VERSION
|
|
21
50
|
? `@hubspot/cli@${process.env.HUBSPOT_CLI_VERSION}`
|
|
22
51
|
: '@hubspot/cli';
|
|
23
|
-
finalCommand = command
|
|
52
|
+
finalCommand = new NpxHubSpotCommand(command, cliPackage);
|
|
24
53
|
}
|
|
25
|
-
finalCommand = addFlag(finalCommand, 'disable-usage-tracking', true);
|
|
26
54
|
}
|
|
27
55
|
const resolvedDir = path.resolve(directory);
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const stdoutChunks = [];
|
|
58
|
+
const stderrChunks = [];
|
|
59
|
+
const child = spawn(finalCommand.executable, finalCommand.args, {
|
|
60
|
+
cwd: resolvedDir,
|
|
61
|
+
env: { ...process.env, INIT_CWD: resolvedDir },
|
|
62
|
+
});
|
|
63
|
+
child.stdout.on('data', (chunk) => {
|
|
64
|
+
const text = chunk.toString();
|
|
65
|
+
stdoutChunks.push(text);
|
|
66
|
+
onData?.(text, 'stdout');
|
|
67
|
+
});
|
|
68
|
+
child.stderr.on('data', (chunk) => {
|
|
69
|
+
const text = chunk.toString();
|
|
70
|
+
stderrChunks.push(text);
|
|
71
|
+
onData?.(text, 'stderr');
|
|
72
|
+
});
|
|
73
|
+
child.on('error', reject);
|
|
74
|
+
child.on('close', code => {
|
|
75
|
+
const stdout = stdoutChunks.join('');
|
|
76
|
+
const stderr = stderrChunks.join('');
|
|
77
|
+
if (code !== 0) {
|
|
78
|
+
reject(Object.assign(new Error(`Command failed: ${finalCommand.executable} ${finalCommand.args.join(' ')}\n${stderr}`), { code, stdout, stderr }));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
resolve({ stdout, stderr });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
34
84
|
});
|
|
35
85
|
}
|
|
@@ -2,7 +2,14 @@ import { EventClass, getExecutionEnvironmentMeta, } from '../../lib/usageTrackin
|
|
|
2
2
|
import { getConfig, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
|
|
3
3
|
import { sendUsageEvent } from '../../lib/api/usageTracking.js';
|
|
4
4
|
export async function trackToolUsage(toolName, meta) {
|
|
5
|
-
|
|
5
|
+
let config;
|
|
6
|
+
try {
|
|
7
|
+
config = getConfig();
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// No config file exists yet; skip tracking
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
6
13
|
if (config?.allowUsageTracking === false) {
|
|
7
14
|
return;
|
|
8
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.11.0-beta.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,7 @@
|
|
|
10
10
|
"!**/__tests__/**"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@hubspot/local-dev-lib": "5.
|
|
13
|
+
"@hubspot/local-dev-lib": "5.10.0",
|
|
14
14
|
"@hubspot/project-parsing-lib": "0.19.1",
|
|
15
15
|
"@hubspot/serverless-dev-runtime": "7.0.7",
|
|
16
16
|
"@hubspot/ui-extensions-dev-server": "2.0.10",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
|
61
61
|
"@typescript-eslint/parser": "^8.11.0",
|
|
62
62
|
"@vitest/coverage-v8": "^2.1.9",
|
|
63
|
-
"axios": "1.
|
|
63
|
+
"axios": "1.16.0",
|
|
64
64
|
"eslint": "^8.56.0",
|
|
65
65
|
"eslint-plugin-import": "^2.31.0",
|
|
66
66
|
"husky": "^4.3.8",
|