@hubspot/cli 7.8.0-experimental.0 → 7.8.1-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/bin/cli.js +0 -2
- package/commands/getStarted.d.ts +1 -1
- package/commands/getStarted.js +64 -16
- package/commands/mcp/setup.js +8 -0
- package/commands/project/dev/unifiedFlow.js +1 -1
- package/commands/project/migrate.js +30 -21
- package/lang/en.d.ts +4 -1
- package/lang/en.js +5 -1
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/app/__tests__/migrate.test.js +14 -51
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +5 -80
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +3 -0
- package/lib/dependencyManagement.d.ts +0 -5
- package/lib/dependencyManagement.js +0 -9
- package/lib/hasFeature.js +6 -0
- package/lib/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.js +1 -1
- package/lib/projects/create/v3.js +3 -2
- package/lib/projects/localDev/helpers/project.d.ts +2 -2
- package/lib/projects/localDev/helpers/project.js +5 -6
- package/lib/theme/__tests__/migrate.test.d.ts +1 -0
- package/lib/theme/__tests__/migrate.test.js +233 -0
- package/lib/theme/migrate.d.ts +13 -0
- package/lib/theme/migrate.js +90 -0
- package/lib/ui/SpinniesManager.js +105 -8
- package/lib/usageTracking.js +2 -2
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
- package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/CreateProjectTool.js +5 -5
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/DocFetchTool.js +2 -2
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +7 -7
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +11 -5
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +2 -2
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
- package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
- package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/tools/project/constants.js +9 -3
- package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
- package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
- package/mcp-server/utils/cliConfig.d.ts +1 -0
- package/mcp-server/utils/cliConfig.js +12 -0
- package/package.json +2 -7
- package/ui/components/HorizontalSelectPrompt.js +1 -1
- package/commands/getStartedV2.d.ts +0 -9
- package/commands/getStartedV2.js +0 -39
- package/ui/components/Ascii.d.ts +0 -10
- package/ui/components/Ascii.js +0 -11
- package/ui/views/GetStarted.d.ts +0 -7
- package/ui/views/GetStarted.js +0 -157
|
@@ -6,19 +6,19 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
6
6
|
addApp: z.ZodBoolean;
|
|
7
7
|
distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
|
|
8
8
|
auth: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>;
|
|
9
|
-
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
9
|
+
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>, "many">>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
11
|
absoluteProjectPath: string;
|
|
12
12
|
addApp: boolean;
|
|
13
13
|
auth?: "oauth" | "static" | undefined;
|
|
14
14
|
distribution?: "marketplace" | "private" | undefined;
|
|
15
|
-
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
15
|
+
features?: ("card" | "settings" | "page" | "app-event" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
16
16
|
}, {
|
|
17
17
|
absoluteProjectPath: string;
|
|
18
18
|
addApp: boolean;
|
|
19
19
|
auth?: "oauth" | "static" | undefined;
|
|
20
20
|
distribution?: "marketplace" | "private" | undefined;
|
|
21
|
-
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
21
|
+
features?: ("card" | "settings" | "page" | "app-event" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
22
22
|
}>;
|
|
23
23
|
export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
24
24
|
export declare class AddFeatureToProjectTool extends Tool<AddFeatureInputSchema> {
|
|
@@ -16,20 +16,20 @@ const inputSchema = {
|
|
|
16
16
|
z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
|
|
17
17
|
z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
|
|
18
18
|
]))
|
|
19
|
-
.describe('
|
|
19
|
+
.describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your application on the HubSpot marketplace. '),
|
|
20
20
|
auth: z
|
|
21
21
|
.optional(z.union([
|
|
22
22
|
z.literal(APP_AUTH_TYPES.STATIC),
|
|
23
23
|
z.literal(APP_AUTH_TYPES.OAUTH),
|
|
24
24
|
]))
|
|
25
|
-
.describe('
|
|
25
|
+
.describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. '),
|
|
26
26
|
features,
|
|
27
27
|
};
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
29
29
|
const inputSchemaZodObject = z.object({
|
|
30
30
|
...inputSchema,
|
|
31
31
|
});
|
|
32
|
-
const toolName = 'add-feature-to-
|
|
32
|
+
const toolName = 'add-feature-to-project';
|
|
33
33
|
export class AddFeatureToProjectTool extends Tool {
|
|
34
34
|
constructor(mcpServer) {
|
|
35
35
|
super(mcpServer);
|
|
@@ -8,7 +8,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
8
8
|
projectBase: z.ZodUnion<[z.ZodLiteral<"empty">, z.ZodLiteral<"app">]>;
|
|
9
9
|
distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
|
|
10
10
|
auth: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>>;
|
|
11
|
-
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
11
|
+
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>, "many">>;
|
|
12
12
|
}, "strip", z.ZodTypeAny, {
|
|
13
13
|
projectBase: "app" | "empty";
|
|
14
14
|
absoluteCurrentWorkingDirectory: string;
|
|
@@ -16,7 +16,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
16
16
|
name?: string | undefined;
|
|
17
17
|
auth?: "oauth" | "static" | undefined;
|
|
18
18
|
distribution?: "marketplace" | "private" | undefined;
|
|
19
|
-
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
19
|
+
features?: ("card" | "settings" | "page" | "app-event" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
20
20
|
}, {
|
|
21
21
|
projectBase: "app" | "empty";
|
|
22
22
|
absoluteCurrentWorkingDirectory: string;
|
|
@@ -24,7 +24,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
24
24
|
name?: string | undefined;
|
|
25
25
|
auth?: "oauth" | "static" | undefined;
|
|
26
26
|
distribution?: "marketplace" | "private" | undefined;
|
|
27
|
-
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
27
|
+
features?: ("card" | "settings" | "page" | "app-event" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
28
28
|
}>;
|
|
29
29
|
export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
30
30
|
export declare class CreateProjectTool extends Tool<CreateProjectInputSchema> {
|
|
@@ -10,11 +10,11 @@ const inputSchema = {
|
|
|
10
10
|
absoluteCurrentWorkingDirectory,
|
|
11
11
|
name: z
|
|
12
12
|
.string()
|
|
13
|
-
.describe('The name of the project to be created. This name is how your project will appear in HubSpot.
|
|
13
|
+
.describe('If not specified by the user, DO NOT choose for them. Changing this is potentially destructive.The name of the project to be created. This name is how your project will appear in HubSpot. ')
|
|
14
14
|
.optional(),
|
|
15
15
|
destination: z
|
|
16
16
|
.string()
|
|
17
|
-
.describe('
|
|
17
|
+
.describe('DO NOT use the current directory unless the user has explicitly stated to do so. Relative path to the directory the project will be created in.'),
|
|
18
18
|
projectBase: z
|
|
19
19
|
.union([z.literal(EMPTY_PROJECT), z.literal(PROJECT_WITH_APP)])
|
|
20
20
|
.describe('Empty will create an empty project, and app will create a project with an app inside of it.'),
|
|
@@ -23,19 +23,19 @@ const inputSchema = {
|
|
|
23
23
|
z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
|
|
24
24
|
z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
|
|
25
25
|
]))
|
|
26
|
-
.describe('
|
|
26
|
+
.describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your application on the HubSpot marketplace. '),
|
|
27
27
|
auth: z
|
|
28
28
|
.optional(z.union([
|
|
29
29
|
z.literal(APP_AUTH_TYPES.STATIC),
|
|
30
30
|
z.literal(APP_AUTH_TYPES.OAUTH),
|
|
31
31
|
]))
|
|
32
|
-
.describe('
|
|
32
|
+
.describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. ')
|
|
33
33
|
.optional(),
|
|
34
34
|
features,
|
|
35
35
|
};
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
37
37
|
const inputSchemaZodObject = z.object({ ...inputSchema });
|
|
38
|
-
const toolName = 'create-
|
|
38
|
+
const toolName = 'create-project';
|
|
39
39
|
export class CreateProjectTool extends Tool {
|
|
40
40
|
constructor(mcpServer) {
|
|
41
41
|
super(mcpServer);
|
|
@@ -15,7 +15,7 @@ const inputSchema = {
|
|
|
15
15
|
const inputSchemaZodObject = z.object({
|
|
16
16
|
...inputSchema,
|
|
17
17
|
});
|
|
18
|
-
const toolName = 'deploy-
|
|
18
|
+
const toolName = 'deploy-project';
|
|
19
19
|
export class DeployProjectTool extends Tool {
|
|
20
20
|
constructor(mcpServer) {
|
|
21
21
|
super(mcpServer);
|
|
@@ -12,7 +12,7 @@ const inputSchema = {
|
|
|
12
12
|
const inputSchemaZodObject = z.object({
|
|
13
13
|
...inputSchema,
|
|
14
14
|
});
|
|
15
|
-
const toolName = 'fetch-
|
|
15
|
+
const toolName = 'fetch-doc';
|
|
16
16
|
export class DocFetchTool extends Tool {
|
|
17
17
|
constructor(mcpServer) {
|
|
18
18
|
super(mcpServer);
|
|
@@ -42,7 +42,7 @@ export class DocFetchTool extends Tool {
|
|
|
42
42
|
register() {
|
|
43
43
|
return this.mcpServer.registerTool(toolName, {
|
|
44
44
|
title: 'Fetch HubSpot Developer Documentation (single file)',
|
|
45
|
-
description: 'Always use this immediately after `search-
|
|
45
|
+
description: 'Always use this immediately after `search-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
|
|
46
46
|
inputSchema,
|
|
47
47
|
}, this.handler);
|
|
48
48
|
}
|
|
@@ -2,10 +2,13 @@ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.
|
|
|
2
2
|
import z from 'zod';
|
|
3
3
|
import { TextContentResponse, Tool } from '../../types.js';
|
|
4
4
|
declare const inputSchemaZodObject: z.ZodObject<{
|
|
5
|
+
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
5
6
|
docsSearchQuery: z.ZodString;
|
|
6
7
|
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
absoluteCurrentWorkingDirectory: string;
|
|
7
9
|
docsSearchQuery: string;
|
|
8
10
|
}, {
|
|
11
|
+
absoluteCurrentWorkingDirectory: string;
|
|
9
12
|
docsSearchQuery: string;
|
|
10
13
|
}>;
|
|
11
14
|
export interface DocsSearchResponse {
|
|
@@ -20,7 +23,7 @@ export interface DocsSearchResponse {
|
|
|
20
23
|
type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
|
|
21
24
|
export declare class DocsSearchTool extends Tool<InputSchemaType> {
|
|
22
25
|
constructor(mcpServer: McpServer);
|
|
23
|
-
handler({ docsSearchQuery, }: InputSchemaType): Promise<TextContentResponse>;
|
|
26
|
+
handler({ absoluteCurrentWorkingDirectory, docsSearchQuery, }: InputSchemaType): Promise<TextContentResponse>;
|
|
24
27
|
register(): RegisteredTool;
|
|
25
28
|
}
|
|
26
29
|
export {};
|
|
@@ -3,25 +3,25 @@ import z from 'zod';
|
|
|
3
3
|
import { Tool } from '../../types.js';
|
|
4
4
|
import { formatTextContents } from '../../utils/content.js';
|
|
5
5
|
import { trackToolUsage } from '../../utils/toolUsageTracking.js';
|
|
6
|
-
import { docsSearchQuery } from './constants.js';
|
|
7
|
-
import { getAccountId, getConfigPath, loadConfig, } from '@hubspot/local-dev-lib/config';
|
|
6
|
+
import { absoluteCurrentWorkingDirectory, docsSearchQuery, } from './constants.js';
|
|
8
7
|
import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
|
|
8
|
+
import { getAccountIdFromCliConfig } from '../../utils/cliConfig.js';
|
|
9
9
|
const inputSchema = {
|
|
10
|
+
absoluteCurrentWorkingDirectory,
|
|
10
11
|
docsSearchQuery,
|
|
11
12
|
};
|
|
12
13
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13
14
|
const inputSchemaZodObject = z.object({
|
|
14
15
|
...inputSchema,
|
|
15
16
|
});
|
|
16
|
-
const toolName = 'search-
|
|
17
|
+
const toolName = 'search-docs';
|
|
17
18
|
export class DocsSearchTool extends Tool {
|
|
18
19
|
constructor(mcpServer) {
|
|
19
20
|
super(mcpServer);
|
|
20
21
|
}
|
|
21
|
-
async handler({ docsSearchQuery, }) {
|
|
22
|
+
async handler({ absoluteCurrentWorkingDirectory, docsSearchQuery, }) {
|
|
22
23
|
await trackToolUsage(toolName, { mode: docsSearchQuery });
|
|
23
|
-
|
|
24
|
-
const accountId = getAccountId();
|
|
24
|
+
const accountId = getAccountIdFromCliConfig(absoluteCurrentWorkingDirectory);
|
|
25
25
|
if (!accountId) {
|
|
26
26
|
const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
|
|
27
27
|
return formatTextContents(authErrorMessage);
|
|
@@ -55,7 +55,7 @@ export class DocsSearchTool extends Tool {
|
|
|
55
55
|
register() {
|
|
56
56
|
return this.mcpServer.registerTool(toolName, {
|
|
57
57
|
title: 'Search HubSpot Developer Documentation',
|
|
58
|
-
description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-
|
|
58
|
+
description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
|
|
59
59
|
inputSchema,
|
|
60
60
|
}, this.handler);
|
|
61
61
|
}
|
|
@@ -2,19 +2,22 @@ import { TextContentResponse, Tool } from '../../types.js';
|
|
|
2
2
|
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
declare const inputSchemaZodObject: z.ZodObject<{
|
|
5
|
+
absoluteCurrentWorkingDirectory: z.ZodString;
|
|
5
6
|
platformVersion: z.ZodString;
|
|
6
7
|
featureType: z.ZodString;
|
|
7
8
|
}, "strip", z.ZodTypeAny, {
|
|
8
9
|
platformVersion: string;
|
|
10
|
+
absoluteCurrentWorkingDirectory: string;
|
|
9
11
|
featureType: string;
|
|
10
12
|
}, {
|
|
11
13
|
platformVersion: string;
|
|
14
|
+
absoluteCurrentWorkingDirectory: string;
|
|
12
15
|
featureType: string;
|
|
13
16
|
}>;
|
|
14
17
|
type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
|
|
15
18
|
export declare class GetConfigValuesTool extends Tool<InputSchemaType> {
|
|
16
19
|
constructor(mcpServer: McpServer);
|
|
17
|
-
handler({ platformVersion, featureType, }: InputSchemaType): Promise<TextContentResponse>;
|
|
20
|
+
handler({ absoluteCurrentWorkingDirectory, platformVersion, featureType, }: InputSchemaType): Promise<TextContentResponse>;
|
|
18
21
|
register(): RegisteredTool;
|
|
19
22
|
}
|
|
20
23
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Tool } from '../../types.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { formatTextContents } from '../../utils/content.js';
|
|
4
|
+
import { absoluteCurrentWorkingDirectory } from './constants.js';
|
|
4
5
|
import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
|
|
5
|
-
import { getAccountId, getConfigPath, loadConfig, } from '@hubspot/local-dev-lib/config';
|
|
6
6
|
import { useV3Api } from '../../../lib/projects/platformVersion.js';
|
|
7
|
+
import { getAccountIdFromCliConfig } from '../../utils/cliConfig.js';
|
|
7
8
|
const inputSchema = {
|
|
9
|
+
absoluteCurrentWorkingDirectory,
|
|
8
10
|
platformVersion: z
|
|
9
11
|
.string()
|
|
10
12
|
.describe('The platform version for the project. Located in the hsproject.json file.'),
|
|
@@ -16,21 +18,25 @@ const inputSchema = {
|
|
|
16
18
|
const inputSchemaZodObject = z.object({
|
|
17
19
|
...inputSchema,
|
|
18
20
|
});
|
|
19
|
-
const toolName = 'get-
|
|
21
|
+
const toolName = 'get-feature-config-schema';
|
|
20
22
|
export class GetConfigValuesTool extends Tool {
|
|
21
23
|
constructor(mcpServer) {
|
|
22
24
|
super(mcpServer);
|
|
23
25
|
}
|
|
24
|
-
async handler({ platformVersion, featureType, }) {
|
|
26
|
+
async handler({ absoluteCurrentWorkingDirectory, platformVersion, featureType, }) {
|
|
25
27
|
try {
|
|
26
28
|
if (!useV3Api(platformVersion)) {
|
|
27
29
|
return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
|
|
28
30
|
}
|
|
29
|
-
|
|
31
|
+
const accountId = getAccountIdFromCliConfig(absoluteCurrentWorkingDirectory);
|
|
32
|
+
if (!accountId) {
|
|
33
|
+
const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
|
|
34
|
+
return formatTextContents(authErrorMessage);
|
|
35
|
+
}
|
|
30
36
|
const schema = await getIntermediateRepresentationSchema({
|
|
31
37
|
platformVersion,
|
|
32
38
|
projectSourceDir: '',
|
|
33
|
-
accountId
|
|
39
|
+
accountId,
|
|
34
40
|
});
|
|
35
41
|
const internalComponentType = mapToInternalType(featureType);
|
|
36
42
|
if (schema[internalComponentType]) {
|
|
@@ -24,7 +24,7 @@ const inputSchema = {
|
|
|
24
24
|
const inputSchemaZodObject = z.object({
|
|
25
25
|
...inputSchema,
|
|
26
26
|
});
|
|
27
|
-
const toolName = 'guided-walkthrough-
|
|
27
|
+
const toolName = 'guided-walkthrough-cli';
|
|
28
28
|
export class GuidedWalkthroughTool extends Tool {
|
|
29
29
|
constructor(mcpServer) {
|
|
30
30
|
super(mcpServer);
|
|
@@ -11,7 +11,7 @@ const inputSchema = {
|
|
|
11
11
|
const inputSchemaZodObject = z.object({
|
|
12
12
|
...inputSchema,
|
|
13
13
|
});
|
|
14
|
-
const toolName = 'upload-
|
|
14
|
+
const toolName = 'upload-project';
|
|
15
15
|
export class UploadProjectTools extends Tool {
|
|
16
16
|
constructor(mcpServer) {
|
|
17
17
|
super(mcpServer);
|
|
@@ -24,7 +24,7 @@ export class UploadProjectTools extends Tool {
|
|
|
24
24
|
register() {
|
|
25
25
|
return this.mcpServer.registerTool(toolName, {
|
|
26
26
|
title: 'Upload HubSpot Project',
|
|
27
|
-
description: 'Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.
|
|
27
|
+
description: 'DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive. Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.',
|
|
28
28
|
inputSchema,
|
|
29
29
|
}, this.handler);
|
|
30
30
|
}
|
|
@@ -9,7 +9,7 @@ const inputSchema = {
|
|
|
9
9
|
};
|
|
10
10
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
11
|
const inputSchemaZodObject = z.object({ ...inputSchema });
|
|
12
|
-
const toolName = 'validate-
|
|
12
|
+
const toolName = 'validate-project';
|
|
13
13
|
export class ValidateProjectTool extends Tool {
|
|
14
14
|
constructor(mcpServer) {
|
|
15
15
|
super(mcpServer);
|
|
@@ -28,7 +28,7 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
|
|
|
28
28
|
describe('register', () => {
|
|
29
29
|
it('should register tool with correct parameters', () => {
|
|
30
30
|
const result = tool.register();
|
|
31
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-
|
|
31
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-project', expect.objectContaining({
|
|
32
32
|
title: 'Add feature to HubSpot Project',
|
|
33
33
|
description: expect.stringContaining('Adds a feature to an existing HubSpot project'),
|
|
34
34
|
inputSchema: expect.any(Object),
|
|
@@ -29,7 +29,7 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
|
|
|
29
29
|
describe('register', () => {
|
|
30
30
|
it('should register tool with correct parameters', () => {
|
|
31
31
|
const result = tool.register();
|
|
32
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-
|
|
32
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-project', {
|
|
33
33
|
title: 'Create HubSpot Project',
|
|
34
34
|
description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
|
|
35
35
|
inputSchema: expect.any(Object),
|
|
@@ -26,7 +26,7 @@ describe('mcp-server/tools/project/DeployProject', () => {
|
|
|
26
26
|
describe('register', () => {
|
|
27
27
|
it('should register tool with correct parameters', () => {
|
|
28
28
|
const result = tool.register();
|
|
29
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-
|
|
29
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-project', {
|
|
30
30
|
title: 'Deploy a build of HubSpot Project',
|
|
31
31
|
description: expect.stringContaining('Takes a build number and a project name and deploys that build of the project'),
|
|
32
32
|
inputSchema: expect.any(Object),
|
|
@@ -24,9 +24,9 @@ describe('mcp-server/tools/project/DocFetchTool', () => {
|
|
|
24
24
|
describe('register', () => {
|
|
25
25
|
it('should register tool with correct parameters', () => {
|
|
26
26
|
const result = tool.register();
|
|
27
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-
|
|
27
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-doc', {
|
|
28
28
|
title: 'Fetch HubSpot Developer Documentation (single file)',
|
|
29
|
-
description: 'Always use this immediately after `search-
|
|
29
|
+
description: 'Always use this immediately after `search-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
|
|
30
30
|
inputSchema: expect.any(Object),
|
|
31
31
|
}, tool.handler);
|
|
32
32
|
expect(result).toBe(mockRegisteredTool);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { DocsSearchTool } from '../DocsSearchTool.js';
|
|
2
2
|
import { http } from '@hubspot/local-dev-lib/http';
|
|
3
|
-
import { getAccountId } from '@hubspot/local-dev-lib/config';
|
|
4
3
|
import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
|
|
4
|
+
import { getAccountIdFromCliConfig } from '../../../utils/cliConfig.js';
|
|
5
5
|
vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
|
|
6
6
|
vi.mock('@hubspot/local-dev-lib/http');
|
|
7
|
-
vi.mock('@hubspot/local-dev-lib/config');
|
|
8
7
|
vi.mock('@hubspot/local-dev-lib/errors/index');
|
|
9
8
|
vi.mock('../../../utils/toolUsageTracking');
|
|
9
|
+
vi.mock('../../../utils/cliConfig.js');
|
|
10
10
|
const mockHttp = http;
|
|
11
|
-
const
|
|
11
|
+
const mockGetAccountIdFromCliConfig = getAccountIdFromCliConfig;
|
|
12
12
|
const mockIsHubSpotHttpError = vi.mocked(isHubSpotHttpError);
|
|
13
13
|
describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
14
14
|
let mockMcpServer;
|
|
@@ -27,9 +27,9 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
27
27
|
describe('register', () => {
|
|
28
28
|
it('should register tool with correct parameters', () => {
|
|
29
29
|
const result = tool.register();
|
|
30
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-
|
|
30
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-docs', {
|
|
31
31
|
title: 'Search HubSpot Developer Documentation',
|
|
32
|
-
description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-
|
|
32
|
+
description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
|
|
33
33
|
inputSchema: expect.any(Object),
|
|
34
34
|
}, tool.handler);
|
|
35
35
|
expect(result).toBe(mockRegisteredTool);
|
|
@@ -38,9 +38,10 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
38
38
|
describe('handler', () => {
|
|
39
39
|
const mockInput = {
|
|
40
40
|
docsSearchQuery: 'test query',
|
|
41
|
+
absoluteCurrentWorkingDirectory: '/foo',
|
|
41
42
|
};
|
|
42
43
|
it('should return auth error message when no account ID is found', async () => {
|
|
43
|
-
|
|
44
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(null);
|
|
44
45
|
const result = await tool.handler(mockInput);
|
|
45
46
|
expect(result).toEqual({
|
|
46
47
|
content: [
|
|
@@ -52,7 +53,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
it('should return successful results when docs are found', async () => {
|
|
55
|
-
|
|
56
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
56
57
|
const mockResponse = {
|
|
57
58
|
results: [
|
|
58
59
|
{
|
|
@@ -76,6 +77,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
76
77
|
data: mockResponse,
|
|
77
78
|
});
|
|
78
79
|
const result = await tool.handler(mockInput);
|
|
80
|
+
expect(mockGetAccountIdFromCliConfig).toHaveBeenCalledWith('/foo');
|
|
79
81
|
expect(mockHttp.post).toHaveBeenCalledWith(12345, {
|
|
80
82
|
url: 'dev/docs/llms/v1/docs-search',
|
|
81
83
|
data: {
|
|
@@ -103,7 +105,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
103
105
|
expect(resultText).toContain('Test content 2');
|
|
104
106
|
});
|
|
105
107
|
it('should return no results message when no documentation is found', async () => {
|
|
106
|
-
|
|
108
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
107
109
|
const mockResponse = {
|
|
108
110
|
results: [],
|
|
109
111
|
};
|
|
@@ -122,7 +124,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
122
124
|
});
|
|
123
125
|
});
|
|
124
126
|
it('should return no results message when results is null', async () => {
|
|
125
|
-
|
|
127
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
126
128
|
const mockResponse = {
|
|
127
129
|
results: null,
|
|
128
130
|
};
|
|
@@ -141,7 +143,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
141
143
|
});
|
|
142
144
|
});
|
|
143
145
|
it('should handle HubSpot HTTP errors', async () => {
|
|
144
|
-
|
|
146
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
145
147
|
const mockError = {
|
|
146
148
|
toString: () => 'HubSpot API Error: 404 Not Found',
|
|
147
149
|
};
|
|
@@ -158,7 +160,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
158
160
|
});
|
|
159
161
|
});
|
|
160
162
|
it('should handle generic errors', async () => {
|
|
161
|
-
|
|
163
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
162
164
|
const mockError = new Error('Network error');
|
|
163
165
|
mockHttp.post.mockRejectedValue(mockError);
|
|
164
166
|
mockIsHubSpotHttpError.mockReturnValue(false);
|
|
@@ -173,7 +175,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
|
|
|
173
175
|
});
|
|
174
176
|
});
|
|
175
177
|
it('should handle non-Error rejections', async () => {
|
|
176
|
-
|
|
178
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(12345);
|
|
177
179
|
mockHttp.post.mockRejectedValue('String error');
|
|
178
180
|
mockIsHubSpotHttpError.mockReturnValue(false);
|
|
179
181
|
const result = await tool.handler(mockInput);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { GetConfigValuesTool } from '../GetConfigValuesTool.js';
|
|
2
2
|
import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
|
|
3
|
-
import {
|
|
3
|
+
import { getAccountIdFromCliConfig } from '../../../utils/cliConfig.js';
|
|
4
4
|
vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
|
|
5
5
|
vi.mock('@hubspot/project-parsing-lib');
|
|
6
|
-
vi.mock('
|
|
6
|
+
vi.mock('../../../utils/cliConfig.js');
|
|
7
7
|
vi.mock('../../../utils/toolUsageTracking');
|
|
8
8
|
const mockGetIntermediateRepresentationSchema = getIntermediateRepresentationSchema;
|
|
9
9
|
const mockMapToInternalType = mapToInternalType;
|
|
10
|
-
const
|
|
10
|
+
const mockGetAccountIdFromCliConfig = getAccountIdFromCliConfig;
|
|
11
11
|
describe('mcp-server/tools/project/GetConfigValuesTool', () => {
|
|
12
12
|
let mockMcpServer;
|
|
13
13
|
let tool;
|
|
@@ -25,7 +25,7 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
|
|
|
25
25
|
describe('register', () => {
|
|
26
26
|
it('should register tool with correct parameters', () => {
|
|
27
27
|
const result = tool.register();
|
|
28
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-
|
|
28
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-feature-config-schema', {
|
|
29
29
|
title: 'Fetch the JSON Schema for component',
|
|
30
30
|
description: expect.stringContaining('Fetches and returns the JSON schema for the provided feature'),
|
|
31
31
|
inputSchema: expect.objectContaining({
|
|
@@ -44,9 +44,10 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
|
|
|
44
44
|
const input = {
|
|
45
45
|
platformVersion: '2025.2',
|
|
46
46
|
featureType: 'card',
|
|
47
|
+
absoluteCurrentWorkingDirectory: '/foo',
|
|
47
48
|
};
|
|
48
49
|
beforeEach(() => {
|
|
49
|
-
|
|
50
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(123456789);
|
|
50
51
|
});
|
|
51
52
|
it('should return config schema when component type exists', async () => {
|
|
52
53
|
const mockSchema = {
|
|
@@ -61,6 +62,7 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
|
|
|
61
62
|
mockGetIntermediateRepresentationSchema.mockResolvedValue(mockSchema);
|
|
62
63
|
mockMapToInternalType.mockReturnValue('internal-card-type');
|
|
63
64
|
const result = await tool.handler(input);
|
|
65
|
+
expect(mockGetAccountIdFromCliConfig).toHaveBeenCalledWith('/foo');
|
|
64
66
|
expect(mockGetIntermediateRepresentationSchema).toHaveBeenCalledWith({
|
|
65
67
|
platformVersion: '2025.2',
|
|
66
68
|
projectSourceDir: '',
|
|
@@ -133,14 +135,13 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
|
|
|
133
135
|
});
|
|
134
136
|
});
|
|
135
137
|
it('should handle null account id', async () => {
|
|
136
|
-
|
|
137
|
-
mockGetIntermediateRepresentationSchema.mockRejectedValue(new Error('No account ID'));
|
|
138
|
+
mockGetAccountIdFromCliConfig.mockReturnValue(null);
|
|
138
139
|
const result = await tool.handler(input);
|
|
139
140
|
expect(result).toEqual({
|
|
140
141
|
content: [
|
|
141
142
|
{
|
|
142
143
|
type: 'text',
|
|
143
|
-
text: '
|
|
144
|
+
text: 'No account ID found. Please run `hs account auth` to configure an account, or set a default account with `hs account use <account>`',
|
|
144
145
|
},
|
|
145
146
|
],
|
|
146
147
|
});
|
|
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/GuidedWalkthroughTool', () => {
|
|
|
21
21
|
describe('register', () => {
|
|
22
22
|
it('should register tool with correct parameters', () => {
|
|
23
23
|
const result = tool.register();
|
|
24
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('guided-walkthrough-
|
|
24
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('guided-walkthrough-cli', {
|
|
25
25
|
title: 'Guided walkthrough of the CLI',
|
|
26
26
|
description: 'Give the user a guided walkthrough of the HubSpot CLI.',
|
|
27
27
|
inputSchema: expect.any(Object),
|
|
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/UploadProjectTools', () => {
|
|
|
21
21
|
describe('register', () => {
|
|
22
22
|
it('should register tool with correct parameters', () => {
|
|
23
23
|
const result = tool.register();
|
|
24
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('upload-
|
|
24
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('upload-project', {
|
|
25
25
|
title: 'Upload HubSpot Project',
|
|
26
26
|
description: expect.stringContaining('Uploads the HubSpot project in current working directory.'),
|
|
27
27
|
inputSchema: expect.any(Object),
|
|
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/ValidateProjectTool', () => {
|
|
|
21
21
|
describe('register', () => {
|
|
22
22
|
it('should register tool with correct parameters', () => {
|
|
23
23
|
const result = tool.register();
|
|
24
|
-
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('validate-
|
|
24
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('validate-project', {
|
|
25
25
|
title: expect.stringContaining('Validate HubSpot Project'),
|
|
26
26
|
description: expect.stringContaining('Validates the HubSpot project and its configuration files.'),
|
|
27
27
|
inputSchema: expect.any(Object),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
2
|
export declare const absoluteProjectPath: z.ZodString;
|
|
3
3
|
export declare const absoluteCurrentWorkingDirectory: z.ZodString;
|
|
4
|
-
export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
4
|
+
export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>, "many">>;
|
|
5
5
|
export declare const docsSearchQuery: z.ZodString;
|
|
6
6
|
export declare const docUrl: z.ZodString;
|
|
@@ -9,12 +9,18 @@ export const features = z
|
|
|
9
9
|
.array(z.union([
|
|
10
10
|
z.literal('card'),
|
|
11
11
|
z.literal('settings'),
|
|
12
|
-
z
|
|
12
|
+
z
|
|
13
|
+
.literal('app-function')
|
|
14
|
+
.describe('Also known as a public serverless function'),
|
|
13
15
|
z.literal('webhooks'),
|
|
14
|
-
z
|
|
15
|
-
|
|
16
|
+
z
|
|
17
|
+
.literal('workflow-action')
|
|
18
|
+
.describe('Also known as a custom workflow action.'),
|
|
19
|
+
z.literal('workflow-action-tool').describe('Also known as agent tools.'),
|
|
16
20
|
z.literal('app-object'),
|
|
21
|
+
z.literal('app-event'),
|
|
17
22
|
z.literal('scim'),
|
|
23
|
+
z.literal('page'),
|
|
18
24
|
]))
|
|
19
25
|
.describe('The features to include in the project, multiple options can be selected')
|
|
20
26
|
.optional();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|