@hubspot/cli 7.9.0-beta.0 → 7.9.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/commands/project/__tests__/deploy.test.js +4 -3
  2. package/lang/en.d.ts +11 -10
  3. package/lang/en.js +14 -13
  4. package/lib/__tests__/http.test.d.ts +1 -0
  5. package/lib/__tests__/http.test.js +40 -0
  6. package/lib/__tests__/npm.test.js +1 -1
  7. package/lib/__tests__/sandboxSync.test.js +1 -1
  8. package/lib/__tests__/usageTracking.test.js +2 -2
  9. package/lib/configMigrate.js +3 -3
  10. package/lib/doctor/DiagnosticInfoBuilder.js +1 -1
  11. package/lib/doctor/Doctor.js +1 -1
  12. package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +4 -2
  13. package/lib/doctor/__tests__/Doctor.test.js +1 -1
  14. package/lib/http.d.ts +1 -0
  15. package/lib/http.js +26 -0
  16. package/lib/jsonLoader.d.ts +14 -0
  17. package/lib/jsonLoader.js +60 -0
  18. package/lib/middleware/__test__/requestMiddleware.test.js +1 -1
  19. package/lib/middleware/autoUpdateMiddleware.js +1 -1
  20. package/lib/middleware/commandTargetingUtils.js +1 -0
  21. package/lib/middleware/fireAlarmMiddleware.js +1 -1
  22. package/lib/middleware/notificationsMiddleware.js +1 -1
  23. package/lib/middleware/requestMiddleware.js +1 -1
  24. package/lib/npm.js +1 -1
  25. package/lib/projects/__tests__/AppDevModeInterface.test.js +3 -0
  26. package/lib/projects/create/__tests__/v2.test.js +20 -14
  27. package/lib/projects/create/v2.js +8 -13
  28. package/lib/projects/localDev/AppDevModeInterface.d.ts +1 -0
  29. package/lib/projects/localDev/AppDevModeInterface.js +16 -0
  30. package/lib/projects/localDev/LocalDevLogger.js +2 -2
  31. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
  32. package/lib/projects/localDev/LocalDevWebsocketServer.js +1 -1
  33. package/lib/prompts/promptUtils.d.ts +8 -0
  34. package/lib/prompts/promptUtils.js +7 -1
  35. package/lib/prompts/selectProjectTemplatePrompt.js +4 -0
  36. package/lib/sandboxSync.js +1 -1
  37. package/lib/usageTracking.js +2 -2
  38. package/mcp-server/tools/cms/HsCreateFunctionTool.js +2 -2
  39. package/mcp-server/tools/cms/HsCreateModuleTool.js +2 -2
  40. package/mcp-server/tools/cms/HsCreateTemplateTool.js +2 -2
  41. package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -9
  42. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  43. package/mcp-server/tools/cms/HsListTool.js +1 -1
  44. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +7 -4
  45. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +7 -3
  46. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +7 -4
  47. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +5 -1
  48. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +8 -3
  49. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +8 -3
  50. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +4 -1
  51. package/mcp-server/tools/project/AddFeatureToProjectTool.js +6 -5
  52. package/mcp-server/tools/project/CreateProjectTool.js +2 -2
  53. package/mcp-server/tools/project/DeployProjectTool.d.ts +4 -1
  54. package/mcp-server/tools/project/DeployProjectTool.js +4 -3
  55. package/mcp-server/tools/project/DocFetchTool.d.ts +4 -1
  56. package/mcp-server/tools/project/DocFetchTool.js +7 -6
  57. package/mcp-server/tools/project/DocsSearchTool.js +5 -5
  58. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +4 -1
  59. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +7 -5
  60. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +8 -2
  61. package/mcp-server/tools/project/GetApplicationInfoTool.js +7 -6
  62. package/mcp-server/tools/project/GetConfigValuesTool.js +4 -4
  63. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +4 -1
  64. package/mcp-server/tools/project/GuidedWalkthroughTool.js +6 -14
  65. package/mcp-server/tools/project/UploadProjectTools.d.ts +4 -1
  66. package/mcp-server/tools/project/UploadProjectTools.js +4 -3
  67. package/mcp-server/tools/project/ValidateProjectTool.d.ts +4 -1
  68. package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
  69. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +6 -1
  70. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +7 -3
  71. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +7 -2
  72. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +8 -3
  73. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +6 -2
  74. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +8 -3
  75. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +9 -5
  76. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +6 -2
  77. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +43 -13
  78. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +8 -2
  79. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +8 -2
  80. package/mcp-server/utils/__tests__/content.test.d.ts +1 -0
  81. package/mcp-server/utils/__tests__/content.test.js +166 -0
  82. package/mcp-server/utils/__tests__/feedbackTracking.test.d.ts +1 -0
  83. package/mcp-server/utils/__tests__/feedbackTracking.test.js +121 -0
  84. package/mcp-server/utils/content.d.ts +1 -1
  85. package/mcp-server/utils/content.js +8 -1
  86. package/mcp-server/utils/feedbackTracking.d.ts +1 -0
  87. package/mcp-server/utils/feedbackTracking.js +41 -0
  88. package/package.json +2 -2
  89. package/commands/project/__tests__/fixtures/exampleProject.json +0 -33
@@ -3,18 +3,21 @@ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.
3
3
  import { z } from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
7
  buildNumber: z.ZodOptional<z.ZodNumber>;
7
8
  }, "strip", z.ZodTypeAny, {
8
9
  absoluteProjectPath: string;
10
+ absoluteCurrentWorkingDirectory: string;
9
11
  buildNumber?: number | undefined;
10
12
  }, {
11
13
  absoluteProjectPath: string;
14
+ absoluteCurrentWorkingDirectory: string;
12
15
  buildNumber?: number | undefined;
13
16
  }>;
14
17
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
15
18
  export declare class DeployProjectTool extends Tool<InputSchemaType> {
16
19
  constructor(mcpServer: McpServer);
17
- handler({ absoluteProjectPath, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
20
+ handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }: InputSchemaType): Promise<TextContentResponse>;
18
21
  register(): RegisteredTool;
19
22
  }
20
23
  export {};
@@ -1,12 +1,13 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
- import { absoluteProjectPath } from './constants.js';
4
+ import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
5
5
  import { runCommandInDir } from '../../utils/project.js';
6
6
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  const inputSchema = {
9
9
  absoluteProjectPath,
10
+ absoluteCurrentWorkingDirectory,
10
11
  buildNumber: z
11
12
  .optional(z.number())
12
13
  .describe('The build number to be deployed. This can be found in the project details page using `hs project open`. If no build number is specified, the most recent build is deployed'),
@@ -20,7 +21,7 @@ export class DeployProjectTool extends Tool {
20
21
  constructor(mcpServer) {
21
22
  super(mcpServer);
22
23
  }
23
- async handler({ absoluteProjectPath, buildNumber, }) {
24
+ async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, buildNumber, }) {
24
25
  await trackToolUsage(toolName);
25
26
  let command = `hs project deploy`;
26
27
  const content = [];
@@ -37,7 +38,7 @@ export class DeployProjectTool extends Tool {
37
38
  };
38
39
  }
39
40
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, command);
40
- return formatTextContents(stdout, stderr);
41
+ return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
41
42
  }
42
43
  register() {
43
44
  return this.mcpServer.registerTool(toolName, {
@@ -3,15 +3,18 @@ import z from 'zod';
3
3
  import { TextContentResponse, Tool } from '../../types.js';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  docUrl: z.ZodString;
6
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
7
  }, "strip", z.ZodTypeAny, {
8
+ absoluteCurrentWorkingDirectory: string;
7
9
  docUrl: string;
8
10
  }, {
11
+ absoluteCurrentWorkingDirectory: string;
9
12
  docUrl: string;
10
13
  }>;
11
14
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
15
  export declare class DocFetchTool extends Tool<InputSchemaType> {
13
16
  constructor(mcpServer: McpServer);
14
- handler({ docUrl }: InputSchemaType): Promise<TextContentResponse>;
17
+ handler({ docUrl, absoluteCurrentWorkingDirectory, }: InputSchemaType): Promise<TextContentResponse>;
15
18
  register(): RegisteredTool;
16
19
  }
17
20
  export {};
@@ -2,11 +2,12 @@ import z from 'zod';
2
2
  import { Tool } from '../../types.js';
3
3
  import { formatTextContents } from '../../utils/content.js';
4
4
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
5
- import { docUrl } from './constants.js';
5
+ import { absoluteCurrentWorkingDirectory, docUrl } from './constants.js';
6
6
  import { http } from '@hubspot/local-dev-lib/http/unauthed';
7
7
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
8
8
  const inputSchema = {
9
9
  docUrl,
10
+ absoluteCurrentWorkingDirectory,
10
11
  };
11
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
13
  const inputSchemaZodObject = z.object({
@@ -17,7 +18,7 @@ export class DocFetchTool extends Tool {
17
18
  constructor(mcpServer) {
18
19
  super(mcpServer);
19
20
  }
20
- async handler({ docUrl }) {
21
+ async handler({ docUrl, absoluteCurrentWorkingDirectory, }) {
21
22
  await trackToolUsage(toolName);
22
23
  try {
23
24
  // Append .md extension to the URL
@@ -27,16 +28,16 @@ export class DocFetchTool extends Tool {
27
28
  });
28
29
  const content = response.data;
29
30
  if (!content || content.trim().length === 0) {
30
- return formatTextContents('Document is empty or contains no content.');
31
+ return formatTextContents(absoluteCurrentWorkingDirectory, 'Document is empty or contains no content.');
31
32
  }
32
- return formatTextContents(content);
33
+ return formatTextContents(absoluteCurrentWorkingDirectory, content);
33
34
  }
34
35
  catch (error) {
35
36
  if (isHubSpotHttpError(error)) {
36
- return formatTextContents(error.toString());
37
+ return formatTextContents(absoluteCurrentWorkingDirectory, error.toString());
37
38
  }
38
39
  const errorMessage = `Error fetching documentation: ${error instanceof Error ? error.message : String(error)}`;
39
- return formatTextContents(errorMessage);
40
+ return formatTextContents(absoluteCurrentWorkingDirectory, errorMessage);
40
41
  }
41
42
  }
42
43
  register() {
@@ -24,7 +24,7 @@ export class DocsSearchTool extends Tool {
24
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
- return formatTextContents(authErrorMessage);
27
+ return formatTextContents(absoluteCurrentWorkingDirectory, authErrorMessage);
28
28
  }
29
29
  try {
30
30
  const response = await http.post(accountId, {
@@ -35,21 +35,21 @@ export class DocsSearchTool extends Tool {
35
35
  });
36
36
  const results = response.data.results;
37
37
  if (!results || results.length === 0) {
38
- return formatTextContents('No documentation found for your query.');
38
+ return formatTextContents(absoluteCurrentWorkingDirectory, 'No documentation found for your query.');
39
39
  }
40
40
  const formattedResults = results
41
41
  .map(result => `**${result.title}**\n${result.description}\nURL: ${result.url}\nScore: ${result.score}\n\n${result.content}\n---\n`)
42
42
  .join('\n');
43
43
  const successMessage = `Found ${results.length} documentation results:\n\n${formattedResults}`;
44
- return formatTextContents(successMessage);
44
+ return formatTextContents(absoluteCurrentWorkingDirectory, successMessage);
45
45
  }
46
46
  catch (error) {
47
47
  if (isHubSpotHttpError(error)) {
48
48
  // Handle different status codes
49
- return formatTextContents(error.toString());
49
+ return formatTextContents(absoluteCurrentWorkingDirectory, error.toString());
50
50
  }
51
51
  const errorMessage = `Error searching documentation: ${error instanceof Error ? error.message : String(error)}`;
52
- return formatTextContents(errorMessage);
52
+ return formatTextContents(absoluteCurrentWorkingDirectory, errorMessage);
53
53
  }
54
54
  }
55
55
  register() {
@@ -2,22 +2,25 @@ 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
  appId: z.ZodString;
6
7
  startDate: z.ZodOptional<z.ZodString>;
7
8
  endDate: z.ZodOptional<z.ZodString>;
8
9
  }, "strip", z.ZodTypeAny, {
9
10
  appId: string;
11
+ absoluteCurrentWorkingDirectory: string;
10
12
  startDate?: string | undefined;
11
13
  endDate?: string | undefined;
12
14
  }, {
13
15
  appId: string;
16
+ absoluteCurrentWorkingDirectory: string;
14
17
  startDate?: string | undefined;
15
18
  endDate?: string | undefined;
16
19
  }>;
17
20
  export type GetApiUsagePatternsByAppIdInputSchema = z.infer<typeof inputSchemaZodObject>;
18
21
  export declare class GetApiUsagePatternsByAppIdTool extends Tool<GetApiUsagePatternsByAppIdInputSchema> {
19
22
  constructor(mcpServer: McpServer);
20
- handler({ appId, startDate, endDate, }: GetApiUsagePatternsByAppIdInputSchema): Promise<TextContentResponse>;
23
+ handler({ absoluteCurrentWorkingDirectory, appId, startDate, endDate, }: GetApiUsagePatternsByAppIdInputSchema): Promise<TextContentResponse>;
21
24
  register(): RegisteredTool;
22
25
  }
23
26
  export {};
@@ -5,7 +5,9 @@ import { http } from '@hubspot/local-dev-lib/http';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
7
7
  import { getAccountId } from '@hubspot/local-dev-lib/config';
8
+ import { absoluteCurrentWorkingDirectory } from './constants.js';
8
9
  const inputSchema = {
10
+ absoluteCurrentWorkingDirectory,
9
11
  appId: z
10
12
  .string()
11
13
  .regex(/^\d+$/, 'App ID must be a numeric string')
@@ -28,14 +30,14 @@ export class GetApiUsagePatternsByAppIdTool extends Tool {
28
30
  constructor(mcpServer) {
29
31
  super(mcpServer);
30
32
  }
31
- async handler({ appId, startDate, endDate, }) {
33
+ async handler({ absoluteCurrentWorkingDirectory, appId, startDate, endDate, }) {
32
34
  await trackToolUsage(toolName);
33
35
  try {
34
36
  // Get account ID from CLI config
35
37
  const accountId = getAccountId();
36
38
  if (!accountId) {
37
39
  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>\``;
38
- return formatTextContents(authErrorMessage);
40
+ return formatTextContents(absoluteCurrentWorkingDirectory, authErrorMessage);
39
41
  }
40
42
  const response = await http.get(accountId, {
41
43
  url: `app/feature/utilization/public/v3/insights/app/${appId}/usage-patterns`,
@@ -47,15 +49,15 @@ export class GetApiUsagePatternsByAppIdTool extends Tool {
47
49
  // Format the response for display
48
50
  const { data } = response;
49
51
  const formattedResult = JSON.stringify(data, null, 2);
50
- return formatTextContents(formattedResult);
52
+ return formatTextContents(absoluteCurrentWorkingDirectory, formattedResult);
51
53
  }
52
54
  catch (error) {
53
55
  if (isHubSpotHttpError(error)) {
54
56
  // Handle HubSpot-specific HTTP errors
55
- return formatTextContents(error.toString());
57
+ return formatTextContents(absoluteCurrentWorkingDirectory, error.toString());
56
58
  }
57
59
  const errorMessage = `${error instanceof Error ? error.message : String(error)}`;
58
- return formatTextContents(errorMessage);
60
+ return formatTextContents(absoluteCurrentWorkingDirectory, errorMessage);
59
61
  }
60
62
  }
61
63
  register() {
@@ -1,11 +1,17 @@
1
1
  import { TextContentResponse, Tool } from '../../types.js';
2
2
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { z } from 'zod';
4
- declare const inputSchemaZodObject: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ absoluteCurrentWorkingDirectory: string;
8
+ }, {
9
+ absoluteCurrentWorkingDirectory: string;
10
+ }>;
5
11
  export type GetApplicationInfoInputSchema = z.infer<typeof inputSchemaZodObject>;
6
12
  export declare class GetApplicationInfoTool extends Tool<GetApplicationInfoInputSchema> {
7
13
  constructor(mcpServer: McpServer);
8
- handler({}: GetApplicationInfoInputSchema): Promise<TextContentResponse>;
14
+ handler({ absoluteCurrentWorkingDirectory, }: GetApplicationInfoInputSchema): Promise<TextContentResponse>;
9
15
  register(): RegisteredTool;
10
16
  }
11
17
  export {};
@@ -5,7 +5,8 @@ import { http } from '@hubspot/local-dev-lib/http';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
7
7
  import { getAccountId } from '@hubspot/local-dev-lib/config';
8
- const inputSchema = {};
8
+ import { absoluteCurrentWorkingDirectory } from './constants.js';
9
+ const inputSchema = { absoluteCurrentWorkingDirectory };
9
10
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
11
  const inputSchemaZodObject = z.object({ ...inputSchema });
11
12
  const toolName = 'get-applications-info';
@@ -13,14 +14,14 @@ export class GetApplicationInfoTool extends Tool {
13
14
  constructor(mcpServer) {
14
15
  super(mcpServer);
15
16
  }
16
- async handler({}) {
17
+ async handler({ absoluteCurrentWorkingDirectory, }) {
17
18
  await trackToolUsage(toolName);
18
19
  try {
19
20
  // Get account ID from CLI config
20
21
  const accountId = getAccountId();
21
22
  if (!accountId) {
22
23
  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>\``;
23
- return formatTextContents(authErrorMessage);
24
+ return formatTextContents(absoluteCurrentWorkingDirectory, authErrorMessage);
24
25
  }
25
26
  const response = await http.get(accountId, {
26
27
  url: `app/feature/utilization/public/v3/insights/apps`,
@@ -28,15 +29,15 @@ export class GetApplicationInfoTool extends Tool {
28
29
  // Format the response for display
29
30
  const { data } = response;
30
31
  const formattedResult = JSON.stringify(data, null, 2);
31
- return formatTextContents(formattedResult);
32
+ return formatTextContents(absoluteCurrentWorkingDirectory, formattedResult);
32
33
  }
33
34
  catch (error) {
34
35
  if (isHubSpotHttpError(error)) {
35
36
  // Handle HubSpot-specific HTTP errors
36
- return formatTextContents(error.toString());
37
+ return formatTextContents(absoluteCurrentWorkingDirectory, error.toString());
37
38
  }
38
39
  const errorMessage = `${error instanceof Error ? error.message : String(error)}`;
39
- return formatTextContents(errorMessage);
40
+ return formatTextContents(absoluteCurrentWorkingDirectory, errorMessage);
40
41
  }
41
42
  }
42
43
  register() {
@@ -26,12 +26,12 @@ export class GetConfigValuesTool extends Tool {
26
26
  async handler({ absoluteCurrentWorkingDirectory, platformVersion, featureType, }) {
27
27
  try {
28
28
  if (!isV2Project(platformVersion)) {
29
- return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
29
+ return formatTextContents(absoluteCurrentWorkingDirectory, `Can only be used on projects with a minimum platformVersion of 2025.2`);
30
30
  }
31
31
  const accountId = getAccountIdFromCliConfig(absoluteCurrentWorkingDirectory);
32
32
  if (!accountId) {
33
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);
34
+ return formatTextContents(absoluteCurrentWorkingDirectory, authErrorMessage);
35
35
  }
36
36
  const schema = await getIntermediateRepresentationSchema({
37
37
  platformVersion,
@@ -40,11 +40,11 @@ export class GetConfigValuesTool extends Tool {
40
40
  });
41
41
  const internalComponentType = mapToInternalType(featureType);
42
42
  if (schema[internalComponentType]) {
43
- return formatTextContents(JSON.stringify({ config: schema[internalComponentType] }));
43
+ return formatTextContents(absoluteCurrentWorkingDirectory, JSON.stringify({ config: schema[internalComponentType] }));
44
44
  }
45
45
  }
46
46
  catch (error) { }
47
- return formatTextContents(`Unable to locate JSON schema for type ${featureType}`);
47
+ return formatTextContents(absoluteCurrentWorkingDirectory, `Unable to locate JSON schema for type ${featureType}`);
48
48
  }
49
49
  register() {
50
50
  return this.mcpServer.registerTool(toolName, {
@@ -2,16 +2,19 @@ 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
  command: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"hs init">, z.ZodLiteral<"hs auth">, z.ZodLiteral<"hs project create">, z.ZodLiteral<"hs project upload">]>>;
6
7
  }, "strip", z.ZodTypeAny, {
8
+ absoluteCurrentWorkingDirectory: string;
7
9
  command?: "hs auth" | "hs project create" | "hs project upload" | "hs init" | undefined;
8
10
  }, {
11
+ absoluteCurrentWorkingDirectory: string;
9
12
  command?: "hs auth" | "hs project create" | "hs project upload" | "hs init" | undefined;
10
13
  }>;
11
14
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
15
  export declare class GuidedWalkthroughTool extends Tool<InputSchemaType> {
13
16
  constructor(mcpServer: McpServer);
14
- handler({ command }: InputSchemaType): Promise<TextContentResponse>;
17
+ handler({ absoluteCurrentWorkingDirectory, command, }: InputSchemaType): Promise<TextContentResponse>;
15
18
  register(): RegisteredTool;
16
19
  }
17
20
  export {};
@@ -3,6 +3,7 @@ import { z } from 'zod';
3
3
  import { execAsync } from '../../utils/command.js';
4
4
  import { formatTextContents } from '../../utils/content.js';
5
5
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
6
+ import { absoluteCurrentWorkingDirectory } from './constants.js';
6
7
  const nextCommands = {
7
8
  'hs init': 'hs auth',
8
9
  'hs auth': 'hs project create',
@@ -10,6 +11,7 @@ const nextCommands = {
10
11
  'hs project upload': 'hs project dev',
11
12
  };
12
13
  const inputSchema = {
14
+ absoluteCurrentWorkingDirectory,
13
15
  command: z
14
16
  .union([
15
17
  z.literal('hs init'),
@@ -29,29 +31,19 @@ export class GuidedWalkthroughTool extends Tool {
29
31
  constructor(mcpServer) {
30
32
  super(mcpServer);
31
33
  }
32
- async handler({ command }) {
34
+ async handler({ absoluteCurrentWorkingDirectory, command, }) {
33
35
  await trackToolUsage(toolName);
34
36
  if (command) {
35
37
  const { stdout } = await execAsync(`${command} --help`);
36
- return formatTextContents(`Display this help output for the user amd wait for them to acknowledge: ${stdout}. ${nextCommands[command] ? `Once they are ready, A good command to look at next is ${nextCommands[command]}` : ''}`);
38
+ return formatTextContents(absoluteCurrentWorkingDirectory, `Display this help output for the user amd wait for them to acknowledge: ${stdout}. ${nextCommands[command] ? `Once they are ready, A good command to look at next is ${nextCommands[command]}` : ''}`);
37
39
  }
38
- return formatTextContents('Is there another command you would like to learn more about?');
40
+ return formatTextContents(absoluteCurrentWorkingDirectory, 'Is there another command you would like to learn more about?');
39
41
  }
40
42
  register() {
41
43
  return this.mcpServer.registerTool(toolName, {
42
44
  title: 'Guided walkthrough of the CLI',
43
45
  description: 'Give the user a guided walkthrough of the HubSpot CLI.',
44
- inputSchema: {
45
- command: z
46
- .union([
47
- z.literal('hs init'),
48
- z.literal('hs auth'),
49
- z.literal('hs project create'),
50
- z.literal('hs project upload'),
51
- ])
52
- .describe('The command to learn more about. Start with `hs init`')
53
- .optional(),
54
- },
46
+ inputSchema,
55
47
  }, this.handler);
56
48
  }
57
49
  }
@@ -3,15 +3,18 @@ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.
3
3
  import z from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
7
  }, "strip", z.ZodTypeAny, {
7
8
  absoluteProjectPath: string;
9
+ absoluteCurrentWorkingDirectory: string;
8
10
  }, {
9
11
  absoluteProjectPath: string;
12
+ absoluteCurrentWorkingDirectory: string;
10
13
  }>;
11
14
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
12
15
  export declare class UploadProjectTools extends Tool<InputSchemaType> {
13
16
  constructor(mcpServer: McpServer);
14
- handler({ absoluteProjectPath, }: InputSchemaType): Promise<TextContentResponse>;
17
+ handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: InputSchemaType): Promise<TextContentResponse>;
15
18
  register(): RegisteredTool;
16
19
  }
17
20
  export {};
@@ -1,11 +1,12 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { runCommandInDir } from '../../utils/project.js';
3
- import { absoluteProjectPath } from './constants.js';
3
+ import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
4
4
  import z from 'zod';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  const inputSchema = {
8
8
  absoluteProjectPath,
9
+ absoluteCurrentWorkingDirectory,
9
10
  };
10
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
12
  const inputSchemaZodObject = z.object({
@@ -16,10 +17,10 @@ export class UploadProjectTools extends Tool {
16
17
  constructor(mcpServer) {
17
18
  super(mcpServer);
18
19
  }
19
- async handler({ absoluteProjectPath, }) {
20
+ async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }) {
20
21
  await trackToolUsage(toolName);
21
22
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, `hs project upload --force-create`);
22
- return formatTextContents(stdout, stderr);
23
+ return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
23
24
  }
24
25
  register() {
25
26
  return this.mcpServer.registerTool(toolName, {
@@ -3,15 +3,18 @@ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.
3
3
  import { z } from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
7
  }, "strip", z.ZodTypeAny, {
7
8
  absoluteProjectPath: string;
9
+ absoluteCurrentWorkingDirectory: string;
8
10
  }, {
9
11
  absoluteProjectPath: string;
12
+ absoluteCurrentWorkingDirectory: string;
10
13
  }>;
11
14
  export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
12
15
  export declare class ValidateProjectTool extends Tool<CreateProjectInputSchema> {
13
16
  constructor(mcpServer: McpServer);
14
- handler({ absoluteProjectPath, }: CreateProjectInputSchema): Promise<TextContentResponse>;
17
+ handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema): Promise<TextContentResponse>;
15
18
  register(): RegisteredTool;
16
19
  }
17
20
  export {};
@@ -1,11 +1,12 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
- import { absoluteProjectPath } from './constants.js';
3
+ import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
4
4
  import { runCommandInDir } from '../../utils/project.js';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  const inputSchema = {
8
8
  absoluteProjectPath,
9
+ absoluteCurrentWorkingDirectory,
9
10
  };
10
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
12
  const inputSchemaZodObject = z.object({ ...inputSchema });
@@ -14,14 +15,14 @@ export class ValidateProjectTool extends Tool {
14
15
  constructor(mcpServer) {
15
16
  super(mcpServer);
16
17
  }
17
- async handler({ absoluteProjectPath, }) {
18
+ async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }) {
18
19
  await trackToolUsage(toolName);
19
20
  try {
20
21
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, 'hs project validate');
21
- return formatTextContents(stdout, stderr);
22
+ return formatTextContents(absoluteCurrentWorkingDirectory, stdout, stderr);
22
23
  }
23
24
  catch (error) {
24
- return formatTextContents(error instanceof Error ? error.message : `${error}`);
25
+ return formatTextContents(absoluteCurrentWorkingDirectory, error instanceof Error ? error.message : `${error}`);
25
26
  }
26
27
  }
27
28
  register() {
@@ -2,11 +2,14 @@ import { AddFeatureToProjectTool, } from '../AddFeatureToProjectTool.js';
2
2
  import { runCommandInDir } from '../../../utils/project.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../../lib/constants.js';
5
+ import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
5
6
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
7
  vi.mock('../../../utils/project');
7
8
  vi.mock('../../../utils/command');
8
9
  vi.mock('../../../../lib/constants');
9
10
  vi.mock('../../../utils/toolUsageTracking');
11
+ vi.mock('../../../utils/feedbackTracking');
12
+ const mockMcpFeedbackRequest = mcpFeedbackRequest;
10
13
  const mockRunCommandInDir = runCommandInDir;
11
14
  const mockAddFlag = addFlag;
12
15
  describe('mcp-server/tools/project/AddFeatureToProject', () => {
@@ -21,6 +24,7 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
21
24
  };
22
25
  mockRegisteredTool = {};
23
26
  mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
27
+ mockMcpFeedbackRequest.mockResolvedValue('');
24
28
  tool = new AddFeatureToProjectTool(mockMcpServer);
25
29
  // Mock addFlag to simulate command building
26
30
  mockAddFlag.mockImplementation((command, flag, value) => `${command} --${flag} "${value}"`);
@@ -32,12 +36,13 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
32
36
  title: 'Add feature to HubSpot Project',
33
37
  description: expect.stringContaining('Adds a feature to an existing HubSpot project'),
34
38
  inputSchema: expect.any(Object),
35
- }), tool.handler);
39
+ }), expect.any(Function));
36
40
  expect(result).toBe(mockRegisteredTool);
37
41
  });
38
42
  });
39
43
  describe('handler', () => {
40
44
  const baseInput = {
45
+ absoluteCurrentWorkingDirectory: '/test/dir',
41
46
  absoluteProjectPath: '/test/project',
42
47
  addApp: false,
43
48
  };
@@ -2,12 +2,15 @@ import { CreateProjectTool, } from '../CreateProjectTool.js';
2
2
  import { runCommandInDir } from '../../../utils/project.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { APP_DISTRIBUTION_TYPES, EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../../../lib/constants.js';
5
+ import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
5
6
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
7
  vi.mock('../../../utils/project');
7
8
  vi.mock('../../../utils/command');
8
9
  vi.mock('../../../../lib/constants');
9
10
  vi.mock('../../../../lib/projects/create/v2');
10
11
  vi.mock('../../../utils/toolUsageTracking');
12
+ vi.mock('../../../utils/feedbackTracking');
13
+ const mockMcpFeedbackRequest = mcpFeedbackRequest;
11
14
  const mockRunCommandInDir = runCommandInDir;
12
15
  const mockAddFlag = addFlag;
13
16
  describe('mcp-server/tools/project/CreateProjectTool', () => {
@@ -22,6 +25,7 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
22
25
  };
23
26
  mockRegisteredTool = {};
24
27
  mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
28
+ mockMcpFeedbackRequest.mockResolvedValue('');
25
29
  tool = new CreateProjectTool(mockMcpServer);
26
30
  // Mock addFlag to simulate command building
27
31
  mockAddFlag.mockImplementation((command, flag, value) => `${command} --${flag} "${value}"`);
@@ -29,11 +33,11 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
29
33
  describe('register', () => {
30
34
  it('should register tool with correct parameters', () => {
31
35
  const result = tool.register();
32
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-project', {
36
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-project', expect.objectContaining({
33
37
  title: 'Create HubSpot Project',
34
- description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
38
+ description: expect.stringContaining('Creates a HubSpot project with the provided name'),
35
39
  inputSchema: expect.any(Object),
36
- }, tool.handler);
40
+ }), expect.any(Function));
37
41
  expect(result).toBe(mockRegisteredTool);
38
42
  });
39
43
  });
@@ -1,10 +1,13 @@
1
1
  import { DeployProjectTool } from '../DeployProjectTool.js';
2
2
  import { runCommandInDir } from '../../../utils/project.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
+ import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
4
5
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
5
6
  vi.mock('../../../utils/project');
6
7
  vi.mock('../../../utils/command');
7
8
  vi.mock('../../../utils/toolUsageTracking');
9
+ vi.mock('../../../utils/feedbackTracking');
10
+ const mockMcpFeedbackRequest = mcpFeedbackRequest;
8
11
  const mockRunCommandInDir = runCommandInDir;
9
12
  const mockAddFlag = addFlag;
10
13
  describe('mcp-server/tools/project/DeployProject', () => {
@@ -19,6 +22,7 @@ describe('mcp-server/tools/project/DeployProject', () => {
19
22
  };
20
23
  mockRegisteredTool = {};
21
24
  mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
25
+ mockMcpFeedbackRequest.mockResolvedValue('');
22
26
  tool = new DeployProjectTool(mockMcpServer);
23
27
  // Mock addFlag to simulate command building
24
28
  mockAddFlag.mockImplementation((command, flag, value) => `${command} --${flag} "${value}"`);
@@ -26,16 +30,17 @@ describe('mcp-server/tools/project/DeployProject', () => {
26
30
  describe('register', () => {
27
31
  it('should register tool with correct parameters', () => {
28
32
  const result = tool.register();
29
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-project', {
33
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-project', expect.objectContaining({
30
34
  title: 'Deploy a build of HubSpot Project',
31
35
  description: expect.stringContaining('Takes a build number and a project name and deploys that build of the project'),
32
36
  inputSchema: expect.any(Object),
33
- }, tool.handler);
37
+ }), expect.any(Function));
34
38
  expect(result).toBe(mockRegisteredTool);
35
39
  });
36
40
  });
37
41
  describe('handler', () => {
38
42
  const baseInput = {
43
+ absoluteCurrentWorkingDirectory: '/test/dir',
39
44
  absoluteProjectPath: '/test/project',
40
45
  };
41
46
  it('should deploy project with specified build number', async () => {