@hubspot/cli 7.7.30-experimental.0 → 7.7.32-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.
- package/lang/en.d.ts +3 -1
- package/lang/en.js +4 -2
- package/lib/projects/__tests__/AppDevModeInterface.test.js +10 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -1
- package/lib/projects/localDev/AppDevModeInterface.js +33 -10
- package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
- package/mcp-server/tools/index.js +6 -0
- package/package.json +2 -1
- package/ui/components/BoxWithTitle.d.ts +8 -0
- package/ui/components/BoxWithTitle.js +9 -0
- package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
- package/ui/components/HorizontalSelectPrompt.js +30 -0
- package/ui/components/StatusMessageBoxes.d.ts +12 -0
- package/ui/components/StatusMessageBoxes.js +31 -0
- package/ui/lib/ui-testing-utils.d.ts +9 -0
- package/ui/lib/ui-testing-utils.js +47 -0
- package/ui/lib/useTerminalSize.d.ts +13 -0
- package/ui/lib/useTerminalSize.js +31 -0
- package/ui/styles.d.ts +18 -0
- package/ui/styles.js +18 -0
- package/ui/views/UiSandbox.d.ts +5 -0
- package/ui/views/UiSandbox.js +25 -0
package/lang/en.d.ts
CHANGED
|
@@ -2569,6 +2569,8 @@ export declare const lib: {
|
|
|
2569
2569
|
readonly activeInstallations: (appName: string, installCount: number) => string;
|
|
2570
2570
|
readonly error: "An error occurred while checking installations for your app";
|
|
2571
2571
|
};
|
|
2572
|
+
readonly distributionChanged: `Your app's distribution type has been changed from private to marketplace. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's distribution type. This will uninstall your app from all accounts.`;
|
|
2573
|
+
readonly authTypeChanged: `Your app's auth type has been changed from static to oauth. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's auth type. This will uninstall your app from all accounts.`;
|
|
2572
2574
|
};
|
|
2573
2575
|
readonly LocalDevWebsocketServer: {
|
|
2574
2576
|
readonly errors: {
|
|
@@ -3380,7 +3382,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3380
3382
|
readonly componentsToBeMigrated: (components: string) => string;
|
|
3381
3383
|
readonly componentsThatWillNotBeMigrated: (components: string) => string;
|
|
3382
3384
|
readonly sourceContentsMoved: (newLocation: string) => string;
|
|
3383
|
-
readonly projectMigrationWarningTitle: "
|
|
3385
|
+
readonly projectMigrationWarningTitle: "Important: Migrating to platformVersion 2025.2 is irreversible";
|
|
3384
3386
|
readonly projectMigrationWarning: string;
|
|
3385
3387
|
readonly success: {
|
|
3386
3388
|
readonly downloadedProject: (projectName: string, projectDest: string) => string;
|
package/lang/en.js
CHANGED
|
@@ -5,7 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
|
|
|
5
5
|
import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
|
|
6
6
|
import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
|
|
7
7
|
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
|
|
8
|
-
import { PROJECT_CONFIG_FILE, PROJECT_WITH_APP } from '../lib/constants.js';
|
|
8
|
+
import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
|
|
9
9
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
10
10
|
export const commands = {
|
|
11
11
|
generalErrors: {
|
|
@@ -2566,6 +2566,8 @@ export const lib = {
|
|
|
2566
2566
|
activeInstallations: (appName, installCount) => `[WARNING] Your app ${chalk.bold(appName)} is installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}`,
|
|
2567
2567
|
error: 'An error occurred while checking installations for your app',
|
|
2568
2568
|
},
|
|
2569
|
+
distributionChanged: `Your app's distribution type has been changed from ${APP_DISTRIBUTION_TYPES.PRIVATE} to ${APP_DISTRIBUTION_TYPES.MARKETPLACE}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's distribution type. This will uninstall your app from all accounts.`,
|
|
2570
|
+
authTypeChanged: `Your app's auth type has been changed from ${APP_AUTH_TYPES.STATIC} to ${APP_AUTH_TYPES.OAUTH}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's auth type. This will uninstall your app from all accounts.`,
|
|
2569
2571
|
},
|
|
2570
2572
|
LocalDevWebsocketServer: {
|
|
2571
2573
|
errors: {
|
|
@@ -3374,7 +3376,7 @@ export const lib = {
|
|
|
3374
3376
|
componentsToBeMigrated: (components) => `The following features will be migrated: ${components}`,
|
|
3375
3377
|
componentsThatWillNotBeMigrated: (components) => `[NOTE] These features are not yet supported for migration but will be available later: ${components}`,
|
|
3376
3378
|
sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
|
|
3377
|
-
projectMigrationWarningTitle: '
|
|
3379
|
+
projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
|
|
3378
3380
|
projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion 2025.2. This action cannot be undone. To ensure you have access to your original files, they will be copied to a new directory (archive) for safekeeping.\n\nThis command will guide you through the process, prompting you to enter the required fields and will download the new project source code into your project source directory.`, false),
|
|
3379
3381
|
success: {
|
|
3380
3382
|
downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
|
|
@@ -100,6 +100,7 @@ describe('AppDevModeInterface', () => {
|
|
|
100
100
|
setAppDataForUid: vi.fn(),
|
|
101
101
|
addListener: vi.fn(),
|
|
102
102
|
addUploadWarning: vi.fn(),
|
|
103
|
+
removeListener: vi.fn(),
|
|
103
104
|
};
|
|
104
105
|
mockLocalDevLogger = {};
|
|
105
106
|
// Mock constructors
|
|
@@ -387,6 +388,15 @@ describe('AppDevModeInterface', () => {
|
|
|
387
388
|
await appDevModeInterface.cleanup();
|
|
388
389
|
expect(UIEDevModeInterface.cleanup).toHaveBeenCalled();
|
|
389
390
|
});
|
|
391
|
+
it('should remove state listeners', async () => {
|
|
392
|
+
await appDevModeInterface.cleanup();
|
|
393
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('devServerMessage',
|
|
394
|
+
// @ts-expect-error access private method for testing
|
|
395
|
+
appDevModeInterface.onDevServerMessage);
|
|
396
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('projectNodes',
|
|
397
|
+
// @ts-expect-error
|
|
398
|
+
appDevModeInterface.onChangeProjectNodes);
|
|
399
|
+
});
|
|
390
400
|
});
|
|
391
401
|
describe('isAutomaticallyInstallable()', () => {
|
|
392
402
|
it('should return true for static auth app on test account with correct parent', () => {
|
|
@@ -11,6 +11,7 @@ declare class AppDevModeInterface {
|
|
|
11
11
|
_appNode?: AppIRNode | null;
|
|
12
12
|
marketplaceAppInstalls?: number;
|
|
13
13
|
constructor(options: AppDevModeInterfaceConstructorOptions);
|
|
14
|
+
private getAppNodeFromProjectNodes;
|
|
14
15
|
private get appNode();
|
|
15
16
|
private get appData();
|
|
16
17
|
private set appData(value);
|
|
@@ -21,7 +22,10 @@ declare class AppDevModeInterface {
|
|
|
21
22
|
private autoInstallStaticAuthApp;
|
|
22
23
|
private installAppOrOpenInstallUrl;
|
|
23
24
|
private checkTestAccountAppInstallation;
|
|
24
|
-
private
|
|
25
|
+
private onDevServerMessage;
|
|
26
|
+
private onChangeProjectNodes;
|
|
27
|
+
private setUpStateListeners;
|
|
28
|
+
private removeStateListeners;
|
|
25
29
|
setup(args: any): Promise<void>;
|
|
26
30
|
start(): Promise<void>;
|
|
27
31
|
fileChange(filePath: string, event: string): Promise<void>;
|
|
@@ -30,12 +30,13 @@ class AppDevModeInterface {
|
|
|
30
30
|
process.exit(EXIT_CODES.ERROR);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
getAppNodeFromProjectNodes(projectNodes) {
|
|
34
|
+
return Object.values(projectNodes).find(isAppIRNode) || null;
|
|
35
|
+
}
|
|
33
36
|
// Assumes only one app per project
|
|
34
37
|
get appNode() {
|
|
35
38
|
if (this._appNode === undefined) {
|
|
36
|
-
this._appNode =
|
|
37
|
-
Object.values(this.localDevState.projectNodes).find(isAppIRNode) ||
|
|
38
|
-
null;
|
|
39
|
+
this._appNode = this.getAppNodeFromProjectNodes(this.localDevState.projectNodes);
|
|
39
40
|
}
|
|
40
41
|
return this._appNode;
|
|
41
42
|
}
|
|
@@ -175,12 +176,33 @@ class AppDevModeInterface {
|
|
|
175
176
|
}
|
|
176
177
|
return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
|
|
177
178
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
onDevServerMessage = (message) => {
|
|
180
|
+
if (message === LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED) {
|
|
181
|
+
this.checkTestAccountAppInstallation();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
onChangeProjectNodes = (nodes) => {
|
|
185
|
+
const newAppNode = this.getAppNodeFromProjectNodes(nodes);
|
|
186
|
+
const oldDistribution = this.appNode?.config.distribution;
|
|
187
|
+
const newDistribution = newAppNode?.config.distribution;
|
|
188
|
+
const oldAuthType = this.appNode?.config.auth.type;
|
|
189
|
+
const newAuthType = newAppNode?.config.auth.type;
|
|
190
|
+
if (newDistribution === APP_DISTRIBUTION_TYPES.MARKETPLACE &&
|
|
191
|
+
oldDistribution !== APP_DISTRIBUTION_TYPES.MARKETPLACE) {
|
|
192
|
+
this.localDevState.addUploadWarning(lib.AppDevModeInterface.distributionChanged);
|
|
193
|
+
}
|
|
194
|
+
else if (newAuthType === APP_AUTH_TYPES.OAUTH &&
|
|
195
|
+
oldAuthType !== APP_AUTH_TYPES.OAUTH) {
|
|
196
|
+
this.localDevState.addUploadWarning(lib.AppDevModeInterface.authTypeChanged);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
setUpStateListeners() {
|
|
200
|
+
this.localDevState.addListener('devServerMessage', this.onDevServerMessage);
|
|
201
|
+
this.localDevState.addListener('projectNodes', this.onChangeProjectNodes);
|
|
202
|
+
}
|
|
203
|
+
removeStateListeners() {
|
|
204
|
+
this.localDevState.removeListener('devServerMessage', this.onDevServerMessage);
|
|
205
|
+
this.localDevState.removeListener('projectNodes', this.onChangeProjectNodes);
|
|
184
206
|
}
|
|
185
207
|
// @ts-expect-error TODO: reconcile types between CLI and UIE Dev Server
|
|
186
208
|
// In the future, update UIE Dev Server to use LocalDevState
|
|
@@ -215,7 +237,7 @@ class AppDevModeInterface {
|
|
|
215
237
|
catch (e) {
|
|
216
238
|
logError(e);
|
|
217
239
|
}
|
|
218
|
-
this.
|
|
240
|
+
this.setUpStateListeners();
|
|
219
241
|
return UIEDevModeInterface.setup(args);
|
|
220
242
|
}
|
|
221
243
|
async start() {
|
|
@@ -239,6 +261,7 @@ class AppDevModeInterface {
|
|
|
239
261
|
if (!this.appNode) {
|
|
240
262
|
return;
|
|
241
263
|
}
|
|
264
|
+
this.removeStateListeners();
|
|
242
265
|
return UIEDevModeInterface.cleanup();
|
|
243
266
|
}
|
|
244
267
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
6
|
+
dest: z.ZodOptional<z.ZodString>;
|
|
7
|
+
functionsFolder: z.ZodOptional<z.ZodString>;
|
|
8
|
+
filename: z.ZodOptional<z.ZodString>;
|
|
9
|
+
endpointMethod: z.ZodOptional<z.ZodEnum<["DELETE", "GET", "PATCH", "POST", "PUT"]>>;
|
|
10
|
+
endpointPath: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
absoluteCurrentWorkingDirectory: string;
|
|
13
|
+
dest?: string | undefined;
|
|
14
|
+
functionsFolder?: string | undefined;
|
|
15
|
+
filename?: string | undefined;
|
|
16
|
+
endpointMethod?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT" | undefined;
|
|
17
|
+
endpointPath?: string | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
absoluteCurrentWorkingDirectory: string;
|
|
20
|
+
dest?: string | undefined;
|
|
21
|
+
functionsFolder?: string | undefined;
|
|
22
|
+
filename?: string | undefined;
|
|
23
|
+
endpointMethod?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT" | undefined;
|
|
24
|
+
endpointPath?: string | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
export type HsCreateFunctionInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
27
|
+
export declare class HsCreateFunctionTool extends Tool<HsCreateFunctionInputSchema> {
|
|
28
|
+
constructor(mcpServer: McpServer);
|
|
29
|
+
handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }: HsCreateFunctionInputSchema): Promise<TextContentResponse>;
|
|
30
|
+
register(): RegisteredTool;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Tool } from '../../types.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
|
|
4
|
+
import { runCommandInDir } from '../../utils/project.js';
|
|
5
|
+
import { formatTextContents, formatTextContent } from '../../utils/content.js';
|
|
6
|
+
import { trackToolUsage } from '../../utils/toolUsageTracking.js';
|
|
7
|
+
import { addFlag } from '../../utils/command.js';
|
|
8
|
+
import { HTTP_METHODS } from '../../../types/Cms.js';
|
|
9
|
+
const inputSchema = {
|
|
10
|
+
absoluteCurrentWorkingDirectory,
|
|
11
|
+
dest: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe('The destination path where the function should be created on the current computer.')
|
|
14
|
+
.optional(),
|
|
15
|
+
functionsFolder: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe('Folder name for function creation. Required for non-interactive function creation. If the user has not specified the folder name, ask them to provide it.')
|
|
18
|
+
.optional(),
|
|
19
|
+
filename: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe('Function filename. Required for non-interactive function creation. If the user has not specified the filename, ask them to provide it.')
|
|
22
|
+
.optional(),
|
|
23
|
+
endpointMethod: z
|
|
24
|
+
.enum(HTTP_METHODS)
|
|
25
|
+
.describe(`HTTP method for the function endpoint. Must be one of: ${HTTP_METHODS.join(', ')}. Defaults to GET.`)
|
|
26
|
+
.optional(),
|
|
27
|
+
endpointPath: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe('API endpoint path for the function. Required for non-interactive function creation. If the user has not specified the endpoint path, ask them to provide it.')
|
|
30
|
+
.optional(),
|
|
31
|
+
};
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
33
|
+
const inputSchemaZodObject = z.object({ ...inputSchema });
|
|
34
|
+
const toolName = 'create-hubspot-cms-function';
|
|
35
|
+
export class HsCreateFunctionTool extends Tool {
|
|
36
|
+
constructor(mcpServer) {
|
|
37
|
+
super(mcpServer);
|
|
38
|
+
}
|
|
39
|
+
async handler({ dest, functionsFolder, filename, endpointMethod, endpointPath, absoluteCurrentWorkingDirectory, }) {
|
|
40
|
+
await trackToolUsage(toolName);
|
|
41
|
+
const content = [];
|
|
42
|
+
// Require functions folder
|
|
43
|
+
if (!functionsFolder) {
|
|
44
|
+
content.push(formatTextContent(`Ask the user to provide the folder name for the function.`));
|
|
45
|
+
}
|
|
46
|
+
// Require filename
|
|
47
|
+
if (!filename) {
|
|
48
|
+
content.push(formatTextContent(`Ask the user to provide the filename for the function.`));
|
|
49
|
+
}
|
|
50
|
+
// Require endpoint path
|
|
51
|
+
if (!endpointPath) {
|
|
52
|
+
content.push(formatTextContent(`Ask the user to provide the API endpoint path for the function.`));
|
|
53
|
+
}
|
|
54
|
+
// If we have missing required information, return the prompts
|
|
55
|
+
if (content.length > 0) {
|
|
56
|
+
return {
|
|
57
|
+
content,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Build the command
|
|
61
|
+
let command = 'hs create function';
|
|
62
|
+
if (dest) {
|
|
63
|
+
command += ` "${dest}"`;
|
|
64
|
+
}
|
|
65
|
+
// Add function-specific flags
|
|
66
|
+
if (functionsFolder) {
|
|
67
|
+
command = addFlag(command, 'functions-folder', functionsFolder);
|
|
68
|
+
}
|
|
69
|
+
if (filename) {
|
|
70
|
+
command = addFlag(command, 'filename', filename);
|
|
71
|
+
}
|
|
72
|
+
if (endpointMethod) {
|
|
73
|
+
command = addFlag(command, 'endpoint-method', endpointMethod);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
command = addFlag(command, 'endpoint-method', 'GET');
|
|
77
|
+
}
|
|
78
|
+
if (endpointPath) {
|
|
79
|
+
command = addFlag(command, 'endpoint-path', endpointPath);
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
|
|
83
|
+
return formatTextContents(stdout, stderr);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return formatTextContents(error instanceof Error ? error.message : `${error}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
register() {
|
|
90
|
+
return this.mcpServer.registerTool(toolName, {
|
|
91
|
+
title: 'Create HubSpot CMS Serverless Function',
|
|
92
|
+
description: `Creates a new HubSpot CMS serverless function using the hs create function command. Functions can be created non-interactively by specifying functionsFolder, filename, and endpointPath. Supports all HTTP methods (${HTTP_METHODS.join(', ')}).`,
|
|
93
|
+
inputSchema,
|
|
94
|
+
}, this.handler);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
6
|
+
userSuppliedName: z.ZodOptional<z.ZodString>;
|
|
7
|
+
dest: z.ZodOptional<z.ZodString>;
|
|
8
|
+
templateType: z.ZodOptional<z.ZodEnum<["page-template", "email-template", "partial", "global-partial", "blog-listing-template", "blog-post-template", "search-template", "section"]>>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
absoluteCurrentWorkingDirectory: string;
|
|
11
|
+
dest?: string | undefined;
|
|
12
|
+
templateType?: "page-template" | "email-template" | "partial" | "global-partial" | "blog-listing-template" | "blog-post-template" | "search-template" | "section" | undefined;
|
|
13
|
+
userSuppliedName?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
absoluteCurrentWorkingDirectory: string;
|
|
16
|
+
dest?: string | undefined;
|
|
17
|
+
templateType?: "page-template" | "email-template" | "partial" | "global-partial" | "blog-listing-template" | "blog-post-template" | "search-template" | "section" | undefined;
|
|
18
|
+
userSuppliedName?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export type HsCreateTemplateInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
21
|
+
export declare class HsCreateTemplateTool extends Tool<HsCreateTemplateInputSchema> {
|
|
22
|
+
constructor(mcpServer: McpServer);
|
|
23
|
+
handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }: HsCreateTemplateInputSchema): Promise<TextContentResponse>;
|
|
24
|
+
register(): RegisteredTool;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Tool } from '../../types.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
|
|
4
|
+
import { runCommandInDir } from '../../utils/project.js';
|
|
5
|
+
import { formatTextContents, formatTextContent } from '../../utils/content.js';
|
|
6
|
+
import { trackToolUsage } from '../../utils/toolUsageTracking.js';
|
|
7
|
+
import { addFlag } from '../../utils/command.js';
|
|
8
|
+
import { TEMPLATE_TYPES } from '../../../types/Cms.js';
|
|
9
|
+
const inputSchema = {
|
|
10
|
+
absoluteCurrentWorkingDirectory,
|
|
11
|
+
userSuppliedName: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe('REQUIRED - If not specified by the user, DO NOT choose. Ask the user to specify the name of the template they want to create.')
|
|
14
|
+
.optional(),
|
|
15
|
+
dest: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe('The destination path where the template should be created on the current computer.')
|
|
18
|
+
.optional(),
|
|
19
|
+
templateType: z
|
|
20
|
+
.enum(TEMPLATE_TYPES)
|
|
21
|
+
.describe(`Template type for template creation. Must be one of: ${TEMPLATE_TYPES.join(', ')}`)
|
|
22
|
+
.optional(),
|
|
23
|
+
};
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
+
const inputSchemaZodObject = z.object({ ...inputSchema });
|
|
26
|
+
const toolName = 'create-hubspot-cms-template';
|
|
27
|
+
export class HsCreateTemplateTool extends Tool {
|
|
28
|
+
constructor(mcpServer) {
|
|
29
|
+
super(mcpServer);
|
|
30
|
+
}
|
|
31
|
+
async handler({ userSuppliedName, dest, templateType, absoluteCurrentWorkingDirectory, }) {
|
|
32
|
+
await trackToolUsage(toolName);
|
|
33
|
+
const content = [];
|
|
34
|
+
// Always require a name
|
|
35
|
+
if (!userSuppliedName) {
|
|
36
|
+
content.push(formatTextContent(`Ask the user to specify the name of the template they want to create.`));
|
|
37
|
+
}
|
|
38
|
+
// Require template type
|
|
39
|
+
if (!templateType) {
|
|
40
|
+
content.push(formatTextContent(`Ask the user what template type they want to create. Options are: ${TEMPLATE_TYPES.join(', ')}`));
|
|
41
|
+
}
|
|
42
|
+
// If we have missing required information, return the prompts
|
|
43
|
+
if (content.length > 0) {
|
|
44
|
+
return {
|
|
45
|
+
content,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Build the command
|
|
49
|
+
let command = 'hs create template';
|
|
50
|
+
if (userSuppliedName) {
|
|
51
|
+
command += ` "${userSuppliedName}"`;
|
|
52
|
+
}
|
|
53
|
+
if (dest) {
|
|
54
|
+
command += ` "${dest}"`;
|
|
55
|
+
}
|
|
56
|
+
// Add template type flag
|
|
57
|
+
if (templateType) {
|
|
58
|
+
command = addFlag(command, 'template-type', templateType);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
|
|
62
|
+
return formatTextContents(stdout, stderr);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return formatTextContents(error instanceof Error ? error.message : `${error}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
register() {
|
|
69
|
+
return this.mcpServer.registerTool(toolName, {
|
|
70
|
+
title: 'Create HubSpot CMS Template',
|
|
71
|
+
description: `Creates a new HubSpot CMS template using the hs create template command. Templates can be created non-interactively by specifying templateType. Supports all template types including: ${TEMPLATE_TYPES.join(', ')}.`,
|
|
72
|
+
inputSchema,
|
|
73
|
+
}, this.handler);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
6
|
+
account: z.ZodOptional<z.ZodString>;
|
|
7
|
+
json: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
absoluteCurrentWorkingDirectory: string;
|
|
10
|
+
account?: string | undefined;
|
|
11
|
+
json?: boolean | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
absoluteCurrentWorkingDirectory: string;
|
|
14
|
+
account?: string | undefined;
|
|
15
|
+
json?: boolean | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
export type HsListFunctionsInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
18
|
+
export declare class HsListFunctionsTool extends Tool<HsListFunctionsInputSchema> {
|
|
19
|
+
constructor(mcpServer: McpServer);
|
|
20
|
+
handler({ account, json, absoluteCurrentWorkingDirectory, }: HsListFunctionsInputSchema): Promise<TextContentResponse>;
|
|
21
|
+
register(): RegisteredTool;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Tool } from '../../types.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { addFlag } from '../../utils/command.js';
|
|
4
|
+
import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
|
|
5
|
+
import { runCommandInDir } from '../../utils/project.js';
|
|
6
|
+
import { formatTextContents } from '../../utils/content.js';
|
|
7
|
+
import { trackToolUsage } from '../../utils/toolUsageTracking.js';
|
|
8
|
+
const inputSchema = {
|
|
9
|
+
absoluteCurrentWorkingDirectory,
|
|
10
|
+
account: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe('The HubSpot account id or name from the HubSpot config file to use for the operation.')
|
|
13
|
+
.optional(),
|
|
14
|
+
json: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.describe('Return raw JSON output instead of formatted table. Useful for programmatic access.')
|
|
17
|
+
.optional(),
|
|
18
|
+
};
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20
|
+
const inputSchemaZodObject = z.object({ ...inputSchema });
|
|
21
|
+
const toolName = 'list-hubspot-cms-serverless-functions';
|
|
22
|
+
export class HsListFunctionsTool extends Tool {
|
|
23
|
+
constructor(mcpServer) {
|
|
24
|
+
super(mcpServer);
|
|
25
|
+
}
|
|
26
|
+
async handler({ account, json, absoluteCurrentWorkingDirectory, }) {
|
|
27
|
+
await trackToolUsage(toolName);
|
|
28
|
+
let command = 'hs function list';
|
|
29
|
+
if (json) {
|
|
30
|
+
command += ' --json';
|
|
31
|
+
}
|
|
32
|
+
if (account) {
|
|
33
|
+
command = addFlag(command, 'account', account);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
|
|
37
|
+
return formatTextContents(stdout, stderr);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: `Error executing hs function list command: ${errorMessage}`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
register() {
|
|
52
|
+
return this.mcpServer.registerTool(toolName, {
|
|
53
|
+
title: 'List HubSpot CMS Serverless Functions',
|
|
54
|
+
description: 'Get a list of all serverless functions deployed in a HubSpot portal/account. Shows function routes, HTTP methods, secrets, and timestamps.',
|
|
55
|
+
inputSchema,
|
|
56
|
+
}, this.handler);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|