@hubspot/cli 7.7.0-experimental.2 → 7.7.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 +96 -94
- package/commands/account.js +0 -2
- package/commands/app.js +0 -2
- package/commands/auth.js +0 -2
- package/commands/cms.js +0 -2
- package/commands/completion.js +0 -2
- package/commands/config.js +0 -2
- package/commands/create.js +0 -2
- package/commands/customObject.js +0 -2
- package/commands/doctor.js +0 -2
- package/commands/feedback.js +0 -2
- package/commands/filemanager.js +0 -2
- package/commands/function.js +0 -2
- package/commands/hubdb.js +0 -2
- package/commands/init.js +0 -2
- package/commands/lint.js +0 -2
- package/commands/list.js +0 -2
- package/commands/mv.js +0 -2
- package/commands/open.js +0 -2
- package/commands/project/dev/deprecatedFlow.d.ts +8 -2
- package/commands/project/dev/deprecatedFlow.js +9 -1
- package/commands/project/dev/index.js +59 -34
- package/commands/project/dev/unifiedFlow.d.ts +10 -2
- package/commands/project/dev/unifiedFlow.js +27 -41
- package/commands/project.js +0 -4
- package/commands/remove.js +0 -2
- package/commands/sandbox.js +0 -2
- package/commands/secret.js +0 -2
- package/commands/theme.js +0 -2
- package/commands/upload.js +0 -2
- package/lang/en.d.ts +14 -54
- package/lang/en.js +14 -54
- package/lib/accountTypes.js +1 -3
- package/lib/commonOpts.d.ts +1 -3
- package/lib/commonOpts.js +1 -1
- package/lib/middleware/fireAlarmMiddleware.d.ts +2 -2
- package/lib/middleware/fireAlarmMiddleware.js +5 -3
- package/lib/projects/localDev/AppDevModeInterface.js +0 -6
- package/lib/projects/upload.js +6 -0
- package/package.json +4 -7
- package/types/Yargs.d.ts +2 -0
- package/bin/hsmcp.d.ts +0 -2
- package/bin/hsmcp.js +0 -13
- package/commands/project/validate.d.ts +0 -4
- package/commands/project/validate.js +0 -53
- package/commands/setupMcp.d.ts +0 -8
- package/commands/setupMcp.js +0 -229
- package/mcp-server/index.d.ts +0 -1
- package/mcp-server/index.js +0 -17
- package/mcp-server/mcpLoader.d.ts +0 -5
- package/mcp-server/mcpLoader.js +0 -24
- package/mcp-server/tools/ExplainProjectStructureTool.d.ts +0 -33
- package/mcp-server/tools/ExplainProjectStructureTool.js +0 -266
- package/mcp-server/tools/GenerateAppComponentTool.d.ts +0 -99
- package/mcp-server/tools/GenerateAppComponentTool.js +0 -193
- package/mcp-server/tools/GenerateCardComponentTool.d.ts +0 -74
- package/mcp-server/tools/GenerateCardComponentTool.js +0 -146
- package/mcp-server/tools/GenerateProjectConfigTool.d.ts +0 -32
- package/mcp-server/tools/GenerateProjectConfigTool.js +0 -40
- package/mcp-server/tools/HubSpotCLIHelper.d.ts +0 -24
- package/mcp-server/tools/HubSpotCLIHelper.js +0 -110
- package/mcp-server/tools/UploadProjectTool.d.ts +0 -44
- package/mcp-server/tools/UploadProjectTool.js +0 -166
- package/mcp-server/tools/ValidateProjectTool.d.ts +0 -62
- package/mcp-server/tools/ValidateProjectTool.js +0 -336
package/commands/setupMcp.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const exitCodes_1 = require("../lib/enums/exitCodes");
|
|
7
|
-
const en_1 = require("../lang/en");
|
|
8
|
-
const yargsUtils_1 = require("../lib/yargsUtils");
|
|
9
|
-
const logger_1 = require("@hubspot/local-dev-lib/logger");
|
|
10
|
-
const promptUtils_1 = require("../lib/prompts/promptUtils");
|
|
11
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
|
-
const path_1 = __importDefault(require("path"));
|
|
13
|
-
const os_1 = __importDefault(require("os"));
|
|
14
|
-
const command = 'setup-mcp';
|
|
15
|
-
const describe = en_1.commands.setupMcp.describe;
|
|
16
|
-
function getCursorMcpConfigPath(global = true) {
|
|
17
|
-
if (global) {
|
|
18
|
-
// Global configuration: ~/.cursor/mcp.json
|
|
19
|
-
const homeDir = os_1.default.homedir();
|
|
20
|
-
return path_1.default.join(homeDir, '.cursor', 'mcp.json');
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
// Project-specific configuration: .cursor/mcp.json in current directory
|
|
24
|
-
return path_1.default.join(process.cwd(), '.cursor', 'mcp.json');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
const MCP_SERVER_COMMAND = 'hsmcp';
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
async function readCursorConfig(global = true) {
|
|
30
|
-
const configPath = getCursorMcpConfigPath(global);
|
|
31
|
-
try {
|
|
32
|
-
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
33
|
-
const content = await fs_extra_1.default.readFile(configPath, 'utf8');
|
|
34
|
-
return JSON.parse(content);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
logger_1.logger.debug(`Error reading Cursor config: ${error}`);
|
|
39
|
-
}
|
|
40
|
-
return { mcpServers: {} };
|
|
41
|
-
}
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
-
async function writeCursorConfig(config, global = true) {
|
|
44
|
-
const configPath = getCursorMcpConfigPath(global);
|
|
45
|
-
const configDir = path_1.default.dirname(configPath);
|
|
46
|
-
// Ensure the directory exists
|
|
47
|
-
await fs_extra_1.default.ensureDir(configDir);
|
|
48
|
-
// Write the config with proper formatting
|
|
49
|
-
await fs_extra_1.default.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
50
|
-
}
|
|
51
|
-
async function setupMcpServer(global = true) {
|
|
52
|
-
try {
|
|
53
|
-
const config = await readCursorConfig(global);
|
|
54
|
-
// Check if hsmcp command is available
|
|
55
|
-
try {
|
|
56
|
-
require('child_process').execSync(`which ${MCP_SERVER_COMMAND} 2>/dev/null`, {
|
|
57
|
-
encoding: 'utf8',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
logger_1.logger.error(en_1.commands.setupMcp.errors.serverNotFound);
|
|
62
|
-
logger_1.logger.error(en_1.commands.setupMcp.errors.installHint);
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
// Check if already configured
|
|
66
|
-
if (config.mcpServers && config.mcpServers['hubspot-cli-mcp']) {
|
|
67
|
-
logger_1.logger.info(en_1.commands.setupMcp.warnings.alreadyConfigured);
|
|
68
|
-
const { shouldOverwrite } = await (0, promptUtils_1.promptUser)([
|
|
69
|
-
{
|
|
70
|
-
type: 'confirm',
|
|
71
|
-
name: 'shouldOverwrite',
|
|
72
|
-
message: en_1.commands.setupMcp.prompts.overwrite,
|
|
73
|
-
default: false,
|
|
74
|
-
},
|
|
75
|
-
]);
|
|
76
|
-
if (!shouldOverwrite) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// Configure the MCP server with the simple global command
|
|
81
|
-
if (!config.mcpServers) {
|
|
82
|
-
config.mcpServers = {};
|
|
83
|
-
}
|
|
84
|
-
config.mcpServers['hubspot-cli-mcp'] = {
|
|
85
|
-
command: MCP_SERVER_COMMAND,
|
|
86
|
-
args: [],
|
|
87
|
-
};
|
|
88
|
-
await writeCursorConfig(config, global);
|
|
89
|
-
const configType = global ? 'global' : 'project-specific';
|
|
90
|
-
logger_1.logger.success(en_1.commands.setupMcp.success.configured.replace('Claude', `Cursor (${configType})`));
|
|
91
|
-
logger_1.logger.info(en_1.commands.setupMcp.success.restartCursor);
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
logger_1.logger.error(`${en_1.commands.setupMcp.errors.configurationFailed}: ${error}`);
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function displayInstructions(global = true) {
|
|
100
|
-
const configPath = getCursorMcpConfigPath(global);
|
|
101
|
-
const configType = global ? 'global' : 'project-specific';
|
|
102
|
-
logger_1.logger.log(en_1.commands.setupMcp.instructions.title.replace('Claude', `Cursor (${configType})`));
|
|
103
|
-
logger_1.logger.log('');
|
|
104
|
-
logger_1.logger.log(en_1.commands.setupMcp.instructions.step1);
|
|
105
|
-
logger_1.logger.log(` ${configPath}`);
|
|
106
|
-
logger_1.logger.log('');
|
|
107
|
-
logger_1.logger.log(en_1.commands.setupMcp.instructions.step2);
|
|
108
|
-
logger_1.logger.log(' {');
|
|
109
|
-
logger_1.logger.log(' "mcpServers": {');
|
|
110
|
-
logger_1.logger.log(' "hubspot-cli-mcp": {');
|
|
111
|
-
logger_1.logger.log(` "command": "${MCP_SERVER_COMMAND}",`);
|
|
112
|
-
logger_1.logger.log(' "args": []');
|
|
113
|
-
logger_1.logger.log(' }');
|
|
114
|
-
logger_1.logger.log(' }');
|
|
115
|
-
logger_1.logger.log(' }');
|
|
116
|
-
logger_1.logger.log('');
|
|
117
|
-
logger_1.logger.log(en_1.commands.setupMcp.instructions.step3.replace('Claude', 'Cursor'));
|
|
118
|
-
logger_1.logger.log('');
|
|
119
|
-
logger_1.logger.log(en_1.commands.setupMcp.instructions.documentation);
|
|
120
|
-
}
|
|
121
|
-
async function handler(args) {
|
|
122
|
-
const useGlobal = args.global !== false; // Default to global unless explicitly set to false
|
|
123
|
-
if (args.showInstructions) {
|
|
124
|
-
displayInstructions(useGlobal);
|
|
125
|
-
process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (args.autoSetup) {
|
|
129
|
-
const success = await setupMcpServer(useGlobal);
|
|
130
|
-
process.exit(success ? exitCodes_1.EXIT_CODES.SUCCESS : exitCodes_1.EXIT_CODES.ERROR);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
// Interactive mode - determine scope first if not specified
|
|
134
|
-
let isGlobal = useGlobal;
|
|
135
|
-
if (args.global === undefined) {
|
|
136
|
-
const { scope } = await (0, promptUtils_1.promptUser)([
|
|
137
|
-
{
|
|
138
|
-
type: 'list',
|
|
139
|
-
name: 'scope',
|
|
140
|
-
message: en_1.commands.setupMcp.prompts.selectScope,
|
|
141
|
-
choices: [
|
|
142
|
-
{
|
|
143
|
-
name: en_1.commands.setupMcp.choices.global,
|
|
144
|
-
value: 'global',
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
name: en_1.commands.setupMcp.choices.project,
|
|
148
|
-
value: 'project',
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
]);
|
|
153
|
-
isGlobal = scope === 'global';
|
|
154
|
-
}
|
|
155
|
-
// Then ask what action to take
|
|
156
|
-
const { action } = await (0, promptUtils_1.promptUser)([
|
|
157
|
-
{
|
|
158
|
-
type: 'list',
|
|
159
|
-
name: 'action',
|
|
160
|
-
message: en_1.commands.setupMcp.prompts.selectAction,
|
|
161
|
-
choices: [
|
|
162
|
-
{
|
|
163
|
-
name: en_1.commands.setupMcp.choices.autoSetup,
|
|
164
|
-
value: 'auto',
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
name: en_1.commands.setupMcp.choices.showInstructions,
|
|
168
|
-
value: 'instructions',
|
|
169
|
-
},
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
]);
|
|
173
|
-
if (action === 'auto') {
|
|
174
|
-
const success = await setupMcpServer(isGlobal);
|
|
175
|
-
process.exit(success ? exitCodes_1.EXIT_CODES.SUCCESS : exitCodes_1.EXIT_CODES.ERROR);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
displayInstructions(isGlobal);
|
|
179
|
-
process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function setupMcpBuilder(yargs) {
|
|
183
|
-
yargs
|
|
184
|
-
.options({
|
|
185
|
-
'auto-setup': {
|
|
186
|
-
describe: en_1.commands.setupMcp.options.autoSetup.describe,
|
|
187
|
-
type: 'boolean',
|
|
188
|
-
default: false,
|
|
189
|
-
},
|
|
190
|
-
'show-instructions': {
|
|
191
|
-
describe: en_1.commands.setupMcp.options.showInstructions.describe,
|
|
192
|
-
type: 'boolean',
|
|
193
|
-
default: false,
|
|
194
|
-
},
|
|
195
|
-
global: {
|
|
196
|
-
describe: en_1.commands.setupMcp.options.global.describe,
|
|
197
|
-
type: 'boolean',
|
|
198
|
-
},
|
|
199
|
-
})
|
|
200
|
-
.example([
|
|
201
|
-
['$0 setup-mcp', en_1.commands.setupMcp.examples.default],
|
|
202
|
-
['$0 setup-mcp --auto-setup', en_1.commands.setupMcp.examples.autoSetup],
|
|
203
|
-
[
|
|
204
|
-
'$0 setup-mcp --auto-setup --global',
|
|
205
|
-
en_1.commands.setupMcp.examples.autoSetupGlobal,
|
|
206
|
-
],
|
|
207
|
-
[
|
|
208
|
-
'$0 setup-mcp --auto-setup --no-global',
|
|
209
|
-
en_1.commands.setupMcp.examples.autoSetupProject,
|
|
210
|
-
],
|
|
211
|
-
[
|
|
212
|
-
'$0 setup-mcp --show-instructions',
|
|
213
|
-
en_1.commands.setupMcp.examples.showInstructions,
|
|
214
|
-
],
|
|
215
|
-
]);
|
|
216
|
-
return yargs;
|
|
217
|
-
}
|
|
218
|
-
const builder = (0, yargsUtils_1.makeYargsBuilder)(setupMcpBuilder, command, describe, {
|
|
219
|
-
useGlobalOptions: true,
|
|
220
|
-
});
|
|
221
|
-
const setupMcpCommand = {
|
|
222
|
-
command,
|
|
223
|
-
describe,
|
|
224
|
-
handler,
|
|
225
|
-
builder,
|
|
226
|
-
};
|
|
227
|
-
exports.default = setupMcpCommand;
|
|
228
|
-
// Legacy export for compatibility
|
|
229
|
-
module.exports = setupMcpCommand;
|
package/mcp-server/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/mcp-server/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { MCPServer } from 'mcp-framework';
|
|
2
|
-
import { dirname, join } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = dirname(__filename);
|
|
6
|
-
// Calculate the correct base path for tools
|
|
7
|
-
// ToolLoader does: dirname(basePath) + "/tools"
|
|
8
|
-
// We want it to look in dist/mcp-server/tools
|
|
9
|
-
// So basePath should be dist/mcp-server/anything
|
|
10
|
-
// __filename is dist/mcp-server/index.js, so dirname is dist/mcp-server
|
|
11
|
-
// We need to provide a path like dist/mcp-server/main.js
|
|
12
|
-
const basePath = join(__dirname, 'main.js');
|
|
13
|
-
console.log('Debug: basePath:', basePath);
|
|
14
|
-
const server = new MCPServer({
|
|
15
|
-
basePath: basePath,
|
|
16
|
-
});
|
|
17
|
-
server.start();
|
package/mcp-server/mcpLoader.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Dynamic import loader for mcp-framework to handle ESM/CommonJS compatibility
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
-
let mcpFramework = null;
|
|
4
|
-
async function loadMCPFramework() {
|
|
5
|
-
if (!mcpFramework) {
|
|
6
|
-
mcpFramework = await import('mcp-framework');
|
|
7
|
-
}
|
|
8
|
-
return mcpFramework;
|
|
9
|
-
}
|
|
10
|
-
export async function getMCPServer() {
|
|
11
|
-
const framework = await loadMCPFramework();
|
|
12
|
-
return framework.MCPServer;
|
|
13
|
-
}
|
|
14
|
-
export async function getMCPTool() {
|
|
15
|
-
const framework = await loadMCPFramework();
|
|
16
|
-
return framework.MCPTool;
|
|
17
|
-
}
|
|
18
|
-
// Export a ready-to-use MCPTool base class
|
|
19
|
-
export class MCPToolBase {
|
|
20
|
-
static async create() {
|
|
21
|
-
const MCPTool = await getMCPTool();
|
|
22
|
-
return MCPTool;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { MCPTool } from "mcp-framework";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
interface ExplainProjectStructureInput {
|
|
4
|
-
section?: string;
|
|
5
|
-
}
|
|
6
|
-
declare class ExplainProjectStructureTool extends MCPTool<ExplainProjectStructureInput> {
|
|
7
|
-
name: string;
|
|
8
|
-
description: string;
|
|
9
|
-
schema: {
|
|
10
|
-
section: {
|
|
11
|
-
type: z.ZodOptional<z.ZodString>;
|
|
12
|
-
description: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
execute(input: ExplainProjectStructureInput): Promise<{
|
|
16
|
-
section: string;
|
|
17
|
-
title: string;
|
|
18
|
-
content: string;
|
|
19
|
-
message?: undefined;
|
|
20
|
-
sections?: undefined;
|
|
21
|
-
} | {
|
|
22
|
-
message: string;
|
|
23
|
-
sections: {
|
|
24
|
-
section: string;
|
|
25
|
-
title: string;
|
|
26
|
-
content: string;
|
|
27
|
-
}[];
|
|
28
|
-
section?: undefined;
|
|
29
|
-
title?: undefined;
|
|
30
|
-
content?: undefined;
|
|
31
|
-
}>;
|
|
32
|
-
}
|
|
33
|
-
export default ExplainProjectStructureTool;
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { MCPTool } from "mcp-framework";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
class ExplainProjectStructureTool extends MCPTool {
|
|
4
|
-
name = "explainProjectStructure";
|
|
5
|
-
description = "Explains the developer projects platform structure, conventions, and best practices";
|
|
6
|
-
schema = {
|
|
7
|
-
section: {
|
|
8
|
-
type: z.string().optional(),
|
|
9
|
-
description: "Specific section to explain (overview, projectFiles, components, hierarchy, fileNaming, bestPractices, envelope). If not provided, returns complete explanation",
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
async execute(input) {
|
|
13
|
-
const { section } = input;
|
|
14
|
-
const explanation = {
|
|
15
|
-
overview: {
|
|
16
|
-
title: "Developer Projects Platform Overview",
|
|
17
|
-
content: `
|
|
18
|
-
The developer projects platform allows users to build applications using a hierarchical component-based architecture with consistency and predictability as core principles.
|
|
19
|
-
|
|
20
|
-
Key Concepts:
|
|
21
|
-
- Projects are defined by hsproject.json files in the root directory
|
|
22
|
-
- Components follow a hierarchical structure with top-level and sub-components
|
|
23
|
-
- Apps are the primary top-level components that provide authentication
|
|
24
|
-
- Sub-components (features) live within apps and depend on app authentication
|
|
25
|
-
- All components live in the src directory with dedicated folders
|
|
26
|
-
- Component instances are declared via *-hsmeta.json files
|
|
27
|
-
- 1:1 relationship between components and their configuration files
|
|
28
|
-
- Envelope layer (uid, type) provides meta information about components
|
|
29
|
-
`,
|
|
30
|
-
},
|
|
31
|
-
projectFiles: {
|
|
32
|
-
title: "Project Configuration Files",
|
|
33
|
-
content: `
|
|
34
|
-
hsproject.json (Project Root):
|
|
35
|
-
- Designates a folder as a developer project
|
|
36
|
-
- Contains project metadata including name, source directory, and platform version
|
|
37
|
-
- Required fields:
|
|
38
|
-
* name: Project name (e.g., "North Star Project")
|
|
39
|
-
* srcDir: Source code directory (typically "src")
|
|
40
|
-
* platformVersion: Platform version (e.g., "2025.1", defaults to "2025.2")
|
|
41
|
-
|
|
42
|
-
Example:
|
|
43
|
-
{
|
|
44
|
-
"name": "North Star Project",
|
|
45
|
-
"srcDir": "src",
|
|
46
|
-
"platformVersion": "2025.1"
|
|
47
|
-
}
|
|
48
|
-
`,
|
|
49
|
-
},
|
|
50
|
-
components: {
|
|
51
|
-
title: "Component Architecture",
|
|
52
|
-
content: `
|
|
53
|
-
Components follow a structured, predictable architecture with two main categories:
|
|
54
|
-
|
|
55
|
-
**Top-Level Components:**
|
|
56
|
-
- Stand-alone components that provide core functionality
|
|
57
|
-
- Currently: Apps (and Themes)
|
|
58
|
-
- Located directly in src/[component-type]/
|
|
59
|
-
- Provide authentication and foundational services
|
|
60
|
-
|
|
61
|
-
**Sub-Components (Features):**
|
|
62
|
-
- Live within top-level components (primarily apps)
|
|
63
|
-
- Depend on parent component's authentication
|
|
64
|
-
- Located in src/app/[feature-type]/
|
|
65
|
-
- Include: cards, functions, settings, webhooks, marketing-events, timeline-events, etc.
|
|
66
|
-
|
|
67
|
-
Standard Component Structure (Envelope Layer):
|
|
68
|
-
{
|
|
69
|
-
"uid": "unique-component-id", // Unique identifier
|
|
70
|
-
"type": "component-type", // Component type
|
|
71
|
-
"config": { // Component-specific configuration
|
|
72
|
-
// Configuration details here
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
Component Characteristics:
|
|
77
|
-
- Each component has a dedicated folder and *-hsmeta.json file (1:1 relationship)
|
|
78
|
-
- Some components are singular (only one per app): settings, webhooks, calling
|
|
79
|
-
- Others can have multiple instances: cards, functions, marketing-events
|
|
80
|
-
- All follow the envelope layer structure for consistency
|
|
81
|
-
`,
|
|
82
|
-
},
|
|
83
|
-
hierarchy: {
|
|
84
|
-
title: "Component Hierarchy & Authentication",
|
|
85
|
-
content: `
|
|
86
|
-
The platform uses a hierarchical architecture where authentication flows from top-level components to sub-components.
|
|
87
|
-
|
|
88
|
-
**Top-Level Components:**
|
|
89
|
-
- App: Primary component providing OAuth authentication
|
|
90
|
-
- Theme: Styling and branding (top-level but may depend on app context)
|
|
91
|
-
|
|
92
|
-
**Sub-Components within Apps:**
|
|
93
|
-
Sub-components rely on their parent app's authentication and include:
|
|
94
|
-
|
|
95
|
-
- App Objects: Custom CRM objects
|
|
96
|
-
- App Object Associations: Relationships between objects
|
|
97
|
-
- Cards: UI cards displayed in HubSpot
|
|
98
|
-
- Functions: Serverless functions
|
|
99
|
-
- Settings: Configuration interfaces
|
|
100
|
-
- Marketing Events: Custom marketing event types
|
|
101
|
-
- Timeline Events: Custom timeline activities
|
|
102
|
-
- Webhooks: Event notification endpoints
|
|
103
|
-
- Workflow Actions: Custom workflow steps
|
|
104
|
-
- Calling: Phone/communication features
|
|
105
|
-
- Video Conferencing: Meeting integrations
|
|
106
|
-
- Media Bridge: Media handling capabilities
|
|
107
|
-
|
|
108
|
-
**Authentication Flow:**
|
|
109
|
-
1. App component defines OAuth scopes and authentication
|
|
110
|
-
2. Sub-components inherit and utilize app authentication
|
|
111
|
-
3. Sub-components cannot exist without a parent app
|
|
112
|
-
4. All API calls from sub-components use app credentials
|
|
113
|
-
`,
|
|
114
|
-
},
|
|
115
|
-
fileNaming: {
|
|
116
|
-
title: "File Naming Conventions & Structure",
|
|
117
|
-
content: `
|
|
118
|
-
Component Configuration Files (*-hsmeta.json):
|
|
119
|
-
- Must end with "-hsmeta.json"
|
|
120
|
-
- Prefix can be anything meaningful to the user
|
|
121
|
-
- Recommended: use component name as prefix for clarity
|
|
122
|
-
- Examples: "my-app-hsmeta.json", "contact-card-hsmeta.json"
|
|
123
|
-
- Maintain 1:1 relationship between components and configuration files
|
|
124
|
-
|
|
125
|
-
Complete Directory Structure:
|
|
126
|
-
my-project/
|
|
127
|
-
├── hsproject.json # Project configuration
|
|
128
|
-
└── src/ # Source code directory
|
|
129
|
-
├── app/ # App component directory (top-level)
|
|
130
|
-
│ ├── my-app-hsmeta.json # App component configuration
|
|
131
|
-
│ ├── cards/ # Cards sub-components
|
|
132
|
-
│ │ ├── contact-card-hsmeta.json
|
|
133
|
-
│ │ └── deal-card-hsmeta.json
|
|
134
|
-
│ ├── functions/ # Function sub-components
|
|
135
|
-
│ │ ├── data-sync-hsmeta.json
|
|
136
|
-
│ │ └── email-validator-hsmeta.json
|
|
137
|
-
│ ├── settings/ # Settings sub-component (singular)
|
|
138
|
-
│ │ └── app-settings-hsmeta.json
|
|
139
|
-
│ ├── webhooks/ # Webhooks sub-component (singular)
|
|
140
|
-
│ │ └── event-handler-hsmeta.json
|
|
141
|
-
│ └── [other-features]/ # Other sub-component directories
|
|
142
|
-
└── theme/ # Theme component (if applicable)
|
|
143
|
-
└── brand-theme-hsmeta.json
|
|
144
|
-
|
|
145
|
-
UIDs (Unique Identifiers):
|
|
146
|
-
- Must be unique within each project
|
|
147
|
-
- Use descriptive, kebab-case naming
|
|
148
|
-
- Examples: "my-unified-app", "contact-summary-card", "lead-scoring-function"
|
|
149
|
-
- Should clearly identify the component's purpose and type
|
|
150
|
-
`,
|
|
151
|
-
},
|
|
152
|
-
envelope: {
|
|
153
|
-
title: "Envelope Layer Architecture",
|
|
154
|
-
content: `
|
|
155
|
-
The envelope layer provides a consistent structure for all components, enabling the system to derive meta information automatically.
|
|
156
|
-
|
|
157
|
-
Envelope Structure:
|
|
158
|
-
{
|
|
159
|
-
"uid": "component-identifier", // Unique ID for component identification
|
|
160
|
-
"type": "component-type", // Type determines component behavior
|
|
161
|
-
"config": { // Component-specific configuration
|
|
162
|
-
// All component-specific settings go here
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
Benefits of Envelope Layer:
|
|
167
|
-
- Consistent metadata extraction across all component types
|
|
168
|
-
- Predictable structure for tooling and validation
|
|
169
|
-
- Clear separation between component identity (uid, type) and configuration
|
|
170
|
-
- Enables automatic component discovery and management
|
|
171
|
-
- Supports hierarchical relationships between components
|
|
172
|
-
|
|
173
|
-
Example App Component (Top-Level):
|
|
174
|
-
{
|
|
175
|
-
"uid": "my-unified-app",
|
|
176
|
-
"type": "app",
|
|
177
|
-
"config": {
|
|
178
|
-
"description": "An example to demonstrate how to build a public app with developer projects.",
|
|
179
|
-
"name": "My unified app",
|
|
180
|
-
"logo": "./app-logo.png",
|
|
181
|
-
"distribution": "A public app",
|
|
182
|
-
"auth": {
|
|
183
|
-
"type": "oauth",
|
|
184
|
-
"redirectUrls": ["http://localhost:3000/oauth-callback"],
|
|
185
|
-
"requiredScopes": ["crm.objects.contacts.read"]
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
Example Sub-Component (Card):
|
|
191
|
-
{
|
|
192
|
-
"uid": "contact-summary-card",
|
|
193
|
-
"type": "cards",
|
|
194
|
-
"config": {
|
|
195
|
-
"title": "Contact Summary",
|
|
196
|
-
"description": "Displays key contact information",
|
|
197
|
-
"displayOptions": {
|
|
198
|
-
"width": "medium",
|
|
199
|
-
"height": "auto"
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
`,
|
|
204
|
-
},
|
|
205
|
-
bestPractices: {
|
|
206
|
-
title: "Best Practices & Guidelines",
|
|
207
|
-
content: `
|
|
208
|
-
1. Hierarchy and Authentication:
|
|
209
|
-
- Always create an app component first (provides authentication)
|
|
210
|
-
- Sub-components must live within the app directory structure
|
|
211
|
-
- Ensure app has sufficient OAuth scopes for all sub-components
|
|
212
|
-
- Consider authentication requirements when planning components
|
|
213
|
-
|
|
214
|
-
2. Consistency and Predictability:
|
|
215
|
-
- Follow established folder structures exactly
|
|
216
|
-
- Maintain 1:1 relationship between components and configuration files
|
|
217
|
-
- Use consistent naming conventions across all components
|
|
218
|
-
- Ensure each component has a dedicated folder
|
|
219
|
-
|
|
220
|
-
3. Project Organization:
|
|
221
|
-
- Keep hsproject.json in project root
|
|
222
|
-
- Organize sub-components in their dedicated directories within src/app/
|
|
223
|
-
- Group related functionality (e.g., all cards in cards/ directory)
|
|
224
|
-
- Follow the hierarchical structure religiously
|
|
225
|
-
|
|
226
|
-
4. Component Configuration:
|
|
227
|
-
- Keep configurations clear and concise
|
|
228
|
-
- Focus on the rules established by the schema
|
|
229
|
-
- Use the envelope layer (uid, type, config) consistently
|
|
230
|
-
- Ensure UIDs are unique and descriptive across all component types
|
|
231
|
-
|
|
232
|
-
5. File Management:
|
|
233
|
-
- Use meaningful -hsmeta.json file names that reflect component purpose
|
|
234
|
-
- Place sub-component files in appropriate feature directories
|
|
235
|
-
- Include necessary assets (like logos) in component directories
|
|
236
|
-
- Follow kebab-case for consistency
|
|
237
|
-
|
|
238
|
-
6. Component Development:
|
|
239
|
-
- Start with app component to establish authentication foundation
|
|
240
|
-
- Plan OAuth scopes to cover all intended sub-components
|
|
241
|
-
- Consider component relationships and dependencies
|
|
242
|
-
- Validate component configurations against the schema
|
|
243
|
-
- Remember some components are singular (settings, webhooks) vs. multiple instances (cards, functions)
|
|
244
|
-
`,
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
if (section && explanation[section]) {
|
|
248
|
-
const selectedSection = explanation[section];
|
|
249
|
-
return {
|
|
250
|
-
section: section,
|
|
251
|
-
title: selectedSection.title,
|
|
252
|
-
content: selectedSection.content.trim(),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
// Return complete explanation
|
|
256
|
-
return {
|
|
257
|
-
message: "Complete Developer Projects Platform Structure Guide",
|
|
258
|
-
sections: Object.entries(explanation).map(([key, value]) => ({
|
|
259
|
-
section: key,
|
|
260
|
-
title: value.title,
|
|
261
|
-
content: value.content.trim(),
|
|
262
|
-
})),
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
export default ExplainProjectStructureTool;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { MCPTool } from 'mcp-framework';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
interface GenerateAppComponentInput {
|
|
4
|
-
appName: string;
|
|
5
|
-
uid: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
displayName?: string;
|
|
8
|
-
distribution?: string;
|
|
9
|
-
supportEmail?: string;
|
|
10
|
-
documentationUrl?: string;
|
|
11
|
-
supportUrl?: string;
|
|
12
|
-
supportPhone?: string;
|
|
13
|
-
redirectUrls?: string[];
|
|
14
|
-
requiredScopes?: string[];
|
|
15
|
-
optionalScopes?: string[];
|
|
16
|
-
permittedFetchUrls?: string[];
|
|
17
|
-
plannedFeatures?: string[];
|
|
18
|
-
}
|
|
19
|
-
declare class GenerateAppComponentTool extends MCPTool<GenerateAppComponentInput> {
|
|
20
|
-
name: string;
|
|
21
|
-
description: string;
|
|
22
|
-
schema: {
|
|
23
|
-
appName: {
|
|
24
|
-
type: z.ZodString;
|
|
25
|
-
description: string;
|
|
26
|
-
};
|
|
27
|
-
uid: {
|
|
28
|
-
type: z.ZodString;
|
|
29
|
-
description: string;
|
|
30
|
-
};
|
|
31
|
-
description: {
|
|
32
|
-
type: z.ZodOptional<z.ZodString>;
|
|
33
|
-
description: string;
|
|
34
|
-
};
|
|
35
|
-
displayName: {
|
|
36
|
-
type: z.ZodOptional<z.ZodString>;
|
|
37
|
-
description: string;
|
|
38
|
-
};
|
|
39
|
-
distribution: {
|
|
40
|
-
type: z.ZodOptional<z.ZodString>;
|
|
41
|
-
description: string;
|
|
42
|
-
};
|
|
43
|
-
supportEmail: {
|
|
44
|
-
type: z.ZodOptional<z.ZodString>;
|
|
45
|
-
description: string;
|
|
46
|
-
};
|
|
47
|
-
documentationUrl: {
|
|
48
|
-
type: z.ZodOptional<z.ZodString>;
|
|
49
|
-
description: string;
|
|
50
|
-
};
|
|
51
|
-
supportUrl: {
|
|
52
|
-
type: z.ZodOptional<z.ZodString>;
|
|
53
|
-
description: string;
|
|
54
|
-
};
|
|
55
|
-
supportPhone: {
|
|
56
|
-
type: z.ZodOptional<z.ZodString>;
|
|
57
|
-
description: string;
|
|
58
|
-
};
|
|
59
|
-
redirectUrls: {
|
|
60
|
-
type: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
61
|
-
description: string;
|
|
62
|
-
};
|
|
63
|
-
requiredScopes: {
|
|
64
|
-
type: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
65
|
-
description: string;
|
|
66
|
-
};
|
|
67
|
-
optionalScopes: {
|
|
68
|
-
type: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
69
|
-
description: string;
|
|
70
|
-
};
|
|
71
|
-
permittedFetchUrls: {
|
|
72
|
-
type: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
73
|
-
description: string;
|
|
74
|
-
};
|
|
75
|
-
plannedFeatures: {
|
|
76
|
-
type: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
77
|
-
description: string;
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
execute(input: GenerateAppComponentInput): Promise<{
|
|
81
|
-
message: string;
|
|
82
|
-
config: string;
|
|
83
|
-
folderPath: string;
|
|
84
|
-
fileName: string;
|
|
85
|
-
fullPath: string;
|
|
86
|
-
architecture: {
|
|
87
|
-
componentType: string;
|
|
88
|
-
role: string;
|
|
89
|
-
singularComponent: boolean;
|
|
90
|
-
dependents: string;
|
|
91
|
-
};
|
|
92
|
-
scopeRecommendations: string[];
|
|
93
|
-
nextSteps: string[];
|
|
94
|
-
instructions: string[];
|
|
95
|
-
}>;
|
|
96
|
-
private generateScopeRecommendations;
|
|
97
|
-
private generateNextSteps;
|
|
98
|
-
}
|
|
99
|
-
export default GenerateAppComponentTool;
|