@hubspot/cli 7.7.4-experimental.0 → 7.7.5-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 +2 -0
- package/commands/mcp/setup.d.ts +13 -0
- package/commands/mcp/setup.js +248 -0
- package/commands/mcp/start.d.ts +10 -0
- package/commands/mcp/start.js +66 -0
- package/commands/mcp.d.ts +10 -0
- package/commands/mcp.js +20 -0
- package/commands/project/listBuilds.js +2 -5
- package/lang/en.d.ts +1 -1
- package/lang/en.js +1 -1
- package/lib/prompts/projectAddPrompt.js +1 -1
- package/lib/prompts/promptUtils.d.ts +2 -1
- package/lib/prompts/promptUtils.js +2 -1
- package/mcp-server/server.d.ts +1 -0
- package/mcp-server/server.js +18 -0
- package/mcp-server/tools/index.d.ts +2 -0
- package/mcp-server/tools/index.js +17 -0
- package/mcp-server/tools/project/AddFeatureToProject.d.ts +6 -0
- package/mcp-server/tools/project/AddFeatureToProject.js +100 -0
- package/mcp-server/tools/project/CreateProjectTool.d.ts +6 -0
- package/mcp-server/tools/project/CreateProjectTool.js +119 -0
- package/mcp-server/tools/project/DeployProject.d.ts +6 -0
- package/mcp-server/tools/project/DeployProject.js +50 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +6 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +54 -0
- package/mcp-server/tools/project/UploadProjectTools.d.ts +5 -0
- package/mcp-server/tools/project/UploadProjectTools.js +26 -0
- package/mcp-server/tools/project/constants.d.ts +3 -0
- package/mcp-server/tools/project/constants.js +13 -0
- package/mcp-server/types.d.ts +8 -0
- package/mcp-server/types.js +7 -0
- package/mcp-server/utils/command.d.ts +3 -0
- package/mcp-server/utils/command.js +16 -0
- package/mcp-server/utils/project.d.ts +5 -0
- package/mcp-server/utils/project.js +17 -0
- package/package.json +4 -2
package/bin/cli.js
CHANGED
|
@@ -44,6 +44,7 @@ const feedback_1 = __importDefault(require("../commands/feedback"));
|
|
|
44
44
|
const doctor_1 = __importDefault(require("../commands/doctor"));
|
|
45
45
|
const completion_1 = __importDefault(require("../commands/completion"));
|
|
46
46
|
const app_1 = __importDefault(require("../commands/app"));
|
|
47
|
+
const mcp_1 = __importDefault(require("../commands/mcp"));
|
|
47
48
|
function getTerminalWidth() {
|
|
48
49
|
const width = yargs_1.default.terminalWidth();
|
|
49
50
|
if (width >= 100)
|
|
@@ -122,6 +123,7 @@ const argv = yargs_1.default
|
|
|
122
123
|
.command(doctor_1.default)
|
|
123
124
|
.command(completion_1.default)
|
|
124
125
|
.command(app_1.default)
|
|
126
|
+
.command(mcp_1.default)
|
|
125
127
|
.help()
|
|
126
128
|
.alias('h', 'help')
|
|
127
129
|
.recommendCommands()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ArgumentsCamelCase, Argv } from 'yargs';
|
|
2
|
+
interface MCPSetupArgs {
|
|
3
|
+
targets?: string[];
|
|
4
|
+
}
|
|
5
|
+
declare function builder(yargs: Argv): Argv;
|
|
6
|
+
declare function handler(argv: ArgumentsCamelCase<MCPSetupArgs>): Promise<void>;
|
|
7
|
+
declare const _default: {
|
|
8
|
+
command: string[];
|
|
9
|
+
describe: string;
|
|
10
|
+
builder: typeof builder;
|
|
11
|
+
handler: typeof handler;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,248 @@
|
|
|
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 logger_1 = require("@hubspot/local-dev-lib/logger");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
|
|
13
|
+
const exitCodes_1 = require("../../lib/enums/exitCodes");
|
|
14
|
+
const promptUtils_1 = require("../../lib/prompts/promptUtils");
|
|
15
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
16
|
+
const command = ['setup', 'update'];
|
|
17
|
+
const describe = 'Add or update MCP server configuration to claude and cursor';
|
|
18
|
+
const mcpServerPath = path_1.default.join(os_1.default.homedir(), 'src', 'hubspot-cli', 'dist', 'mcp-server', 'server.js');
|
|
19
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
20
|
+
const supportedTools = [
|
|
21
|
+
{ name: 'Claude', value: 'claude' },
|
|
22
|
+
{ name: 'Claude Desktop', value: 'claude-desktop' },
|
|
23
|
+
{ name: 'Cursor', value: 'cursor' },
|
|
24
|
+
];
|
|
25
|
+
function builder(yargs) {
|
|
26
|
+
yargs.option('targets', {
|
|
27
|
+
describe: 'Target application to configure',
|
|
28
|
+
type: 'array',
|
|
29
|
+
choices: [...supportedTools.map(tool => tool.value)],
|
|
30
|
+
});
|
|
31
|
+
return yargs;
|
|
32
|
+
}
|
|
33
|
+
async function handler(argv) {
|
|
34
|
+
await addMcpServerToConfig(argv.targets);
|
|
35
|
+
}
|
|
36
|
+
exports.default = { command, describe, builder, handler };
|
|
37
|
+
async function addMcpServerToConfig(targets) {
|
|
38
|
+
try {
|
|
39
|
+
let derivedTargets = [];
|
|
40
|
+
if (!targets || targets.length === 0) {
|
|
41
|
+
const { selectedTargets } = await (0, promptUtils_1.promptUser)({
|
|
42
|
+
name: 'selectedTargets',
|
|
43
|
+
type: 'checkbox',
|
|
44
|
+
message: '[--targets] Which tools would you like to add the HubSpot CLI MCP server to?',
|
|
45
|
+
choices: supportedTools,
|
|
46
|
+
validate: (choices) => {
|
|
47
|
+
return choices.length === 0 ? 'Must choose at least one tool' : true;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
derivedTargets = selectedTargets;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
derivedTargets = targets;
|
|
54
|
+
}
|
|
55
|
+
SpinniesManager_1.default.init();
|
|
56
|
+
if (derivedTargets.includes('claude-desktop')) {
|
|
57
|
+
await runSetupFunction(setupClaudeDesktop);
|
|
58
|
+
}
|
|
59
|
+
if (derivedTargets.includes('claude')) {
|
|
60
|
+
await runSetupFunction(setupClaudeCode);
|
|
61
|
+
}
|
|
62
|
+
if (derivedTargets.includes('cursor')) {
|
|
63
|
+
await runSetupFunction(setupCursor);
|
|
64
|
+
}
|
|
65
|
+
logger_1.logger.info(`You can now use the HubSpot CLI tools in ${derivedTargets.join(', ')}. ${chalk_1.default.bold('You may need to restart these tools to apply the changes')}.`);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
SpinniesManager_1.default.fail('mcpSetup', {
|
|
69
|
+
text: 'Failed to configure HubSpot CLI MCP server.',
|
|
70
|
+
});
|
|
71
|
+
logger_1.logger.error(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function runSetupFunction(func) {
|
|
75
|
+
const result = await func();
|
|
76
|
+
if (!result) {
|
|
77
|
+
process.exit(exitCodes_1.EXIT_CODES.ERROR);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function setupClaudeDesktop() {
|
|
81
|
+
try {
|
|
82
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
83
|
+
SpinniesManager_1.default.add('claudeDesktop', {
|
|
84
|
+
text: 'Configuring Claude Desktop...',
|
|
85
|
+
});
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
let config = {};
|
|
88
|
+
// Read existing config if it exists
|
|
89
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
90
|
+
try {
|
|
91
|
+
const configContent = fs_1.default.readFileSync(configPath, 'utf8');
|
|
92
|
+
config = JSON.parse(configContent);
|
|
93
|
+
logger_1.logger.debug(`Found existing Claude Desktop config at ${configPath}`);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger_1.logger.debug(`Could not parse existing Claude Desktop config, creating new one: ${error}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Create config directory if it doesn't exist
|
|
101
|
+
const configDir = path_1.default.dirname(configPath);
|
|
102
|
+
fs_1.default.mkdirSync(configDir, { recursive: true });
|
|
103
|
+
logger_1.logger.debug(`Created Claude Desktop config directory at ${configDir}`);
|
|
104
|
+
}
|
|
105
|
+
// Initialize mcpServers if it doesn't exist
|
|
106
|
+
if (!config.mcpServers) {
|
|
107
|
+
config.mcpServers = {};
|
|
108
|
+
}
|
|
109
|
+
// Add or update HubSpot CLI MCP server
|
|
110
|
+
config.mcpServers['hubspot-cli-mcp'] = {
|
|
111
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
112
|
+
command: 'node',
|
|
113
|
+
args: [mcpServerPath],
|
|
114
|
+
};
|
|
115
|
+
// Write the updated config
|
|
116
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
117
|
+
SpinniesManager_1.default.succeed('claudeDesktop', {
|
|
118
|
+
text: 'Configured Claude Desktop',
|
|
119
|
+
});
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
SpinniesManager_1.default.fail('claudeDesktop', {
|
|
124
|
+
text: 'Failed to configure Claude Desktop',
|
|
125
|
+
});
|
|
126
|
+
logger_1.logger.debug(`Failed to configure Claude Desktop: ${error}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function setupClaudeCode() {
|
|
131
|
+
try {
|
|
132
|
+
SpinniesManager_1.default.add('claudeCode', {
|
|
133
|
+
text: 'Configuring Claude Code...',
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
// Check if claude command is available
|
|
137
|
+
await execAsync('claude --version');
|
|
138
|
+
// Run claude mcp add command
|
|
139
|
+
const mcpConfig = JSON.stringify({
|
|
140
|
+
type: 'stdio',
|
|
141
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
142
|
+
command: 'node',
|
|
143
|
+
args: [mcpServerPath],
|
|
144
|
+
});
|
|
145
|
+
const { stdout } = await execAsync('claude mcp list');
|
|
146
|
+
if (stdout.includes('hubspot-cli-mcp')) {
|
|
147
|
+
SpinniesManager_1.default.update('claudeCode', {
|
|
148
|
+
text: 'CLI mcp server already installed, reinstalling',
|
|
149
|
+
});
|
|
150
|
+
try {
|
|
151
|
+
await execAsync('claude mcp remove "hubspot-cli-mcp" --scope user');
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
logger_1.logger.warn(error.stderr);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
await execAsync(`claude mcp add-json "hubspot-cli-mcp" '${mcpConfig}' --scope user`);
|
|
159
|
+
SpinniesManager_1.default.succeed('claudeCode', {
|
|
160
|
+
text: 'Configured Claude Code',
|
|
161
|
+
});
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof Error &&
|
|
166
|
+
error.message.includes('claude: command not found')) {
|
|
167
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
168
|
+
text: 'Claude Code CLI not found - skipping configuration',
|
|
169
|
+
});
|
|
170
|
+
logger_1.logger.info(' Install Claude Code CLI to enable this configuration');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
174
|
+
text: 'Claude Code CLI not working - skipping configuration',
|
|
175
|
+
});
|
|
176
|
+
logger_1.logger.error(`Error: ${error}`);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
183
|
+
text: 'Failed to configure Claude Code',
|
|
184
|
+
});
|
|
185
|
+
logger_1.logger.error(`Failed to configure Claude Code: ${error}`);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function setupCursor() {
|
|
190
|
+
try {
|
|
191
|
+
SpinniesManager_1.default.add('cursor', {
|
|
192
|
+
text: 'Configuring Cursor...',
|
|
193
|
+
});
|
|
194
|
+
const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
|
|
195
|
+
if (!fs_1.default.existsSync(cursorConfigPath)) {
|
|
196
|
+
SpinniesManager_1.default.succeed('cursor', {
|
|
197
|
+
text: 'No .cursor/mcp.json file found - skipping Cursor configuration',
|
|
198
|
+
});
|
|
199
|
+
logger_1.logger.info(' Create a .cursor/mcp.json file in your project to enable Cursor MCP support');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
|
+
let config = {};
|
|
204
|
+
// Read existing config
|
|
205
|
+
try {
|
|
206
|
+
const configContent = fs_1.default.readFileSync(cursorConfigPath, 'utf8');
|
|
207
|
+
config = JSON.parse(configContent);
|
|
208
|
+
logger_1.logger.debug(`Found existing Cursor config at ${cursorConfigPath}`);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
logger_1.logger.warn(`Could not parse existing Cursor config, creating new one: ${error}`);
|
|
212
|
+
}
|
|
213
|
+
// Initialize mcpServers if it doesn't exist
|
|
214
|
+
if (!config.mcpServers) {
|
|
215
|
+
config.mcpServers = {};
|
|
216
|
+
}
|
|
217
|
+
// Add or update HubSpot CLI MCP server
|
|
218
|
+
config.mcpServers['hubspot-cli-mcp'] = {
|
|
219
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
220
|
+
command: 'node',
|
|
221
|
+
args: [mcpServerPath],
|
|
222
|
+
};
|
|
223
|
+
// Write the updated config
|
|
224
|
+
fs_1.default.writeFileSync(cursorConfigPath, JSON.stringify(config, null, 2));
|
|
225
|
+
SpinniesManager_1.default.succeed('cursor', {
|
|
226
|
+
text: 'Configured Cursor',
|
|
227
|
+
});
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
SpinniesManager_1.default.fail('cursor', {
|
|
232
|
+
text: 'Failed to configure Cursor',
|
|
233
|
+
});
|
|
234
|
+
logger_1.logger.error(`Failed to configure Cursor: ${error}`);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function getClaudeDesktopConfigPath() {
|
|
239
|
+
const platform = os_1.default.platform();
|
|
240
|
+
const homeDir = os_1.default.homedir();
|
|
241
|
+
if (platform === 'win32') {
|
|
242
|
+
const appData = process.env.APPDATA || path_1.default.join(homeDir, 'AppData', 'Roaming');
|
|
243
|
+
return path_1.default.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return path_1.default.join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Argv } from 'yargs';
|
|
2
|
+
declare function builder(yargs: Argv): Argv;
|
|
3
|
+
declare function handler(): Promise<void>;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
command: string;
|
|
6
|
+
describe: undefined;
|
|
7
|
+
builder: typeof builder;
|
|
8
|
+
handler: typeof handler;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,66 @@
|
|
|
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 logger_1 = require("@hubspot/local-dev-lib/logger");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const command = 'start';
|
|
11
|
+
const describe = undefined; // Keep this command hidden, doesn't seem useful to expose
|
|
12
|
+
function builder(yargs) {
|
|
13
|
+
return yargs;
|
|
14
|
+
}
|
|
15
|
+
async function handler() {
|
|
16
|
+
await startMcpServer();
|
|
17
|
+
}
|
|
18
|
+
exports.default = { command, describe, builder, handler };
|
|
19
|
+
async function startMcpServer() {
|
|
20
|
+
try {
|
|
21
|
+
const serverPath = path_1.default.join(__dirname, '..', '..', 'mcp-server', 'server.js');
|
|
22
|
+
// Check if server file exists
|
|
23
|
+
if (!fs_1.default.existsSync(serverPath)) {
|
|
24
|
+
logger_1.logger.error(`MCP server file not found at ${serverPath}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
logger_1.logger.info('Starting HubSpot CLI MCP server...');
|
|
28
|
+
logger_1.logger.info(`Server path: ${serverPath}`);
|
|
29
|
+
logger_1.logger.info('The server will run in stdio mode for MCP client connections');
|
|
30
|
+
logger_1.logger.info('Press Ctrl+C to stop the server');
|
|
31
|
+
// Start the server using ts-node
|
|
32
|
+
const child = (0, child_process_1.spawn)('node', [serverPath], {
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Handle server process events
|
|
39
|
+
child.on('error', error => {
|
|
40
|
+
logger_1.logger.error(error);
|
|
41
|
+
logger_1.logger.error('Failed to start MCP server:', error.message);
|
|
42
|
+
});
|
|
43
|
+
child.on('close', code => {
|
|
44
|
+
if (code === 0) {
|
|
45
|
+
logger_1.logger.info('MCP server stopped gracefully');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
logger_1.logger.error(`MCP server exited with code ${code}`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Handle graceful shutdown
|
|
52
|
+
process.on('SIGINT', () => {
|
|
53
|
+
logger_1.logger.info('Shutting down MCP server...');
|
|
54
|
+
child.kill('SIGTERM');
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
process.on('SIGTERM', () => {
|
|
58
|
+
logger_1.logger.info('Shutting down MCP server...');
|
|
59
|
+
child.kill('SIGTERM');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
logger_1.logger.error('Error starting MCP server:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Argv } from 'yargs';
|
|
2
|
+
declare function builder(yargs: Argv): Argv;
|
|
3
|
+
declare function handler(): void;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
command: string;
|
|
6
|
+
describe: string;
|
|
7
|
+
builder: typeof builder;
|
|
8
|
+
handler: typeof handler;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
package/commands/mcp.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 start_1 = __importDefault(require("./mcp/start"));
|
|
7
|
+
const setup_1 = __importDefault(require("./mcp/setup"));
|
|
8
|
+
const command = 'mcp';
|
|
9
|
+
const describe = 'Manage the Model Context Protocol server';
|
|
10
|
+
function builder(yargs) {
|
|
11
|
+
return yargs
|
|
12
|
+
.command(start_1.default)
|
|
13
|
+
.command(setup_1.default)
|
|
14
|
+
.demandCommand(1, 'You must specify a subcommand')
|
|
15
|
+
.help();
|
|
16
|
+
}
|
|
17
|
+
function handler() {
|
|
18
|
+
// This function is required by yargs but not used since we have subcommands
|
|
19
|
+
}
|
|
20
|
+
exports.default = { command, describe, builder, handler };
|
|
@@ -17,6 +17,7 @@ const lang_1 = require("../../lib/lang");
|
|
|
17
17
|
const index_2 = require("../../lib/errorHandlers/index");
|
|
18
18
|
const exitCodes_1 = require("../../lib/enums/exitCodes");
|
|
19
19
|
const yargsUtils_1 = require("../../lib/yargsUtils");
|
|
20
|
+
const en_1 = require("../../lang/en");
|
|
20
21
|
const command = 'list-builds';
|
|
21
22
|
const describe = (0, ui_1.uiBetaTag)((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.describe`), false);
|
|
22
23
|
async function fetchAndDisplayBuilds(accountId, project, options) {
|
|
@@ -29,11 +30,7 @@ async function fetchAndDisplayBuilds(accountId, project, options) {
|
|
|
29
30
|
}));
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
|
-
logger_1.logger.log((0,
|
|
33
|
-
count: results.length,
|
|
34
|
-
projectName: project.name,
|
|
35
|
-
viewBuildsLink: (0, ui_1.uiLink)((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.logs.viewAllBuildsLink`), (0, urls_1.getProjectDetailUrl)(project.name, accountId)),
|
|
36
|
-
}));
|
|
33
|
+
logger_1.logger.log(en_1.commands.project.listBuilds.showingRecentBuilds(results.length, project.name, (0, ui_1.uiLink)(en_1.commands.project.listBuilds.viewAllBuildsLink, (0, urls_1.getProjectDetailUrl)(project.name, accountId))));
|
|
37
34
|
}
|
|
38
35
|
if (results.length === 0) {
|
|
39
36
|
logger_1.logger.log((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.errors.noBuilds`));
|
package/lang/en.d.ts
CHANGED
|
@@ -1081,7 +1081,7 @@ ${string}`;
|
|
|
1081
1081
|
readonly continueOrExitPrompt: "Press <enter> to load more, or ctrl+c to exit";
|
|
1082
1082
|
readonly viewAllBuildsLink: "View all builds";
|
|
1083
1083
|
readonly showingNextBuilds: (count: string, projectName: string) => string;
|
|
1084
|
-
readonly showingRecentBuilds: (count:
|
|
1084
|
+
readonly showingRecentBuilds: (count: number, projectName: string, viewAllBuildsLink: string) => string;
|
|
1085
1085
|
readonly errors: {
|
|
1086
1086
|
readonly noBuilds: "No builds for this project were found.";
|
|
1087
1087
|
readonly projectNotFound: (projectName: string) => string;
|
package/lang/en.js
CHANGED
|
@@ -1036,7 +1036,7 @@ exports.commands = {
|
|
|
1036
1036
|
},
|
|
1037
1037
|
},
|
|
1038
1038
|
creatingComponent: (projectName) => `\nAdding a new component to ${chalk_1.default.bold(projectName)}\n`,
|
|
1039
|
-
success: (componentName, multiple = false) => `${componentName} ${multiple ? 'were' : 'was'} successfully added to your app.`,
|
|
1039
|
+
success: (componentName, multiple = false) => `${componentName || 'An app'} ${multiple ? 'were' : 'was'} successfully added to your ${componentName ? 'app' : 'project'}.`,
|
|
1040
1040
|
error: {
|
|
1041
1041
|
failedToDownloadComponent: 'Failed to download project component. Please try again later.',
|
|
1042
1042
|
maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
|
|
@@ -66,7 +66,7 @@ async function projectAddPromptV3(components, selectedFeatures) {
|
|
|
66
66
|
{
|
|
67
67
|
name: 'componentTemplate',
|
|
68
68
|
message: en_1.lib.prompts.projectAddPrompt.selectType,
|
|
69
|
-
when: selectedComponents.length === 0,
|
|
69
|
+
when: !selectedFeatures && selectedComponents.length === 0,
|
|
70
70
|
type: 'checkbox',
|
|
71
71
|
choices: components,
|
|
72
72
|
},
|
|
@@ -4,10 +4,11 @@ export declare function confirmPrompt(message: string, options?: {
|
|
|
4
4
|
defaultAnswer?: boolean;
|
|
5
5
|
when?: PromptWhen;
|
|
6
6
|
}): Promise<boolean>;
|
|
7
|
-
export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, }: {
|
|
7
|
+
export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, validate, }: {
|
|
8
8
|
choices: PromptChoices<T>;
|
|
9
9
|
when?: PromptWhen;
|
|
10
10
|
defaultAnswer?: string | number | boolean;
|
|
11
|
+
validate?: (input: T[]) => (boolean | string) | Promise<boolean | string>;
|
|
11
12
|
}): Promise<T>;
|
|
12
13
|
export declare function inputPrompt(message: string, { when, validate, defaultAnswer, }?: {
|
|
13
14
|
when?: boolean | (() => boolean);
|
|
@@ -22,7 +22,7 @@ async function confirmPrompt(message, options = {}) {
|
|
|
22
22
|
]);
|
|
23
23
|
return choice;
|
|
24
24
|
}
|
|
25
|
-
async function listPrompt(message, { choices, when, defaultAnswer, }) {
|
|
25
|
+
async function listPrompt(message, { choices, when, defaultAnswer, validate, }) {
|
|
26
26
|
const { choice } = await promptUser([
|
|
27
27
|
{
|
|
28
28
|
name: 'choice',
|
|
@@ -31,6 +31,7 @@ async function listPrompt(message, { choices, when, defaultAnswer, }) {
|
|
|
31
31
|
choices,
|
|
32
32
|
when,
|
|
33
33
|
default: defaultAnswer,
|
|
34
|
+
validate,
|
|
34
35
|
},
|
|
35
36
|
]);
|
|
36
37
|
return choice;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
4
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
5
|
+
const tools_1 = require("./tools");
|
|
6
|
+
const server = new mcp_js_1.McpServer({
|
|
7
|
+
name: 'HubSpot CLI MCP Server',
|
|
8
|
+
version: '0.0.1',
|
|
9
|
+
description: 'Helps perform tasks for local development of HubSpot projects.',
|
|
10
|
+
capabilities: {
|
|
11
|
+
tools: {},
|
|
12
|
+
prompts: {},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
(0, tools_1.registerProjectTools)(server);
|
|
16
|
+
// Start receiving messages on stdin and sending messages on stdout
|
|
17
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
18
|
+
server.connect(transport);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerProjectTools = registerProjectTools;
|
|
4
|
+
const UploadProjectTools_1 = require("./project/UploadProjectTools");
|
|
5
|
+
const CreateProjectTool_1 = require("./project/CreateProjectTool");
|
|
6
|
+
const GuidedWalkthroughTool_1 = require("./project/GuidedWalkthroughTool");
|
|
7
|
+
const DeployProject_1 = require("./project/DeployProject");
|
|
8
|
+
const AddFeatureToProject_1 = require("./project/AddFeatureToProject");
|
|
9
|
+
function registerProjectTools(mcpServer) {
|
|
10
|
+
return [
|
|
11
|
+
UploadProjectTools_1.UploadProjectTools.register(mcpServer),
|
|
12
|
+
CreateProjectTool_1.CreateProjectTool.register(mcpServer),
|
|
13
|
+
GuidedWalkthroughTool_1.GuidedWalkthroughTool.register(mcpServer),
|
|
14
|
+
DeployProject_1.DeployProject.register(mcpServer),
|
|
15
|
+
AddFeatureToProject_1.AddFeatureToProject.register(mcpServer),
|
|
16
|
+
];
|
|
17
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AddFeatureToProject = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const constants_1 = require("../../../lib/constants");
|
|
7
|
+
const command_1 = require("../../utils/command");
|
|
8
|
+
const constants_2 = require("./constants");
|
|
9
|
+
const project_1 = require("../../utils/project");
|
|
10
|
+
class AddFeatureToProject extends types_1.Tool {
|
|
11
|
+
static mcpServer;
|
|
12
|
+
static register(mcpServer) {
|
|
13
|
+
this.mcpServer = mcpServer;
|
|
14
|
+
return this.mcpServer.registerTool('add-feature-to-hubspot-project', {
|
|
15
|
+
title: 'Add feature to HubSpot Project',
|
|
16
|
+
description: 'Adds a feature to an existing HubSpot project',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
absoluteProjectPath: constants_2.absoluteProjectPath,
|
|
19
|
+
addApp: zod_1.z.boolean().describe('Would you like to add an app?'),
|
|
20
|
+
distribution: zod_1.z
|
|
21
|
+
.optional(zod_1.z.union([
|
|
22
|
+
zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
|
|
23
|
+
zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
|
|
24
|
+
]))
|
|
25
|
+
.describe('Private is used if you do not wish to distribute your application on the HubSpot marketplace. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.'),
|
|
26
|
+
auth: zod_1.z
|
|
27
|
+
.optional(zod_1.z.union([
|
|
28
|
+
zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
|
|
29
|
+
zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
|
|
30
|
+
]))
|
|
31
|
+
.describe('Static uses a static non changing authentication token, and is only available for private distribution. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.')
|
|
32
|
+
.optional(),
|
|
33
|
+
features: zod_1.z
|
|
34
|
+
.array(zod_1.z
|
|
35
|
+
.union([
|
|
36
|
+
zod_1.z.literal('card'),
|
|
37
|
+
zod_1.z.literal('settings'),
|
|
38
|
+
zod_1.z.literal('app-function'),
|
|
39
|
+
zod_1.z.literal('webhooks'),
|
|
40
|
+
])
|
|
41
|
+
.describe('The features to include in the project, multiple options can be selected'))
|
|
42
|
+
.optional(),
|
|
43
|
+
},
|
|
44
|
+
}, async ({ absoluteProjectPath, distribution, auth, features, addApp }) => {
|
|
45
|
+
try {
|
|
46
|
+
let command = `hs project add`;
|
|
47
|
+
const content = [];
|
|
48
|
+
if (distribution) {
|
|
49
|
+
command = (0, command_1.addFlag)(command, 'distribution', distribution);
|
|
50
|
+
}
|
|
51
|
+
else if (addApp) {
|
|
52
|
+
content.push({
|
|
53
|
+
type: 'text',
|
|
54
|
+
text: `Ask the user how they would you like to distribute the application? Options are ${constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${constants_1.APP_DISTRIBUTION_TYPES.PRIVATE}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (auth) {
|
|
58
|
+
command = (0, command_1.addFlag)(command, 'auth', auth);
|
|
59
|
+
}
|
|
60
|
+
else if (addApp) {
|
|
61
|
+
content.push({
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: `Ask the user which auth type they would like to use? Options are ${constants_1.APP_AUTH_TYPES.STATIC} and ${constants_1.APP_AUTH_TYPES.OAUTH}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (content.length > 0) {
|
|
67
|
+
return {
|
|
68
|
+
content,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// If features isn't provided, pass an empty array to bypass the prompt
|
|
72
|
+
command = (0, command_1.addFlag)(command, 'features', features || []);
|
|
73
|
+
const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
|
|
74
|
+
return {
|
|
75
|
+
content: [
|
|
76
|
+
{
|
|
77
|
+
type: 'text',
|
|
78
|
+
text: stdout,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: stderr,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: error instanceof Error ? error.message : `${error}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.AddFeatureToProject = AddFeatureToProject;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateProjectTool = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const constants_1 = require("../../../lib/constants");
|
|
7
|
+
const command_1 = require("../../utils/command");
|
|
8
|
+
const v3_1 = require("../../../lib/projects/create/v3");
|
|
9
|
+
const constants_2 = require("./constants");
|
|
10
|
+
const project_1 = require("../../utils/project");
|
|
11
|
+
class CreateProjectTool extends types_1.Tool {
|
|
12
|
+
static mcpServer;
|
|
13
|
+
static register(mcpServer) {
|
|
14
|
+
this.mcpServer = mcpServer;
|
|
15
|
+
return this.mcpServer.registerTool('create-hubspot-project', {
|
|
16
|
+
title: 'Create HubSpot Project',
|
|
17
|
+
description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
absoluteCurrentWorkingDirectory: constants_2.absoluteCurrentWorkingDirectory,
|
|
20
|
+
name: zod_1.z
|
|
21
|
+
.string()
|
|
22
|
+
.describe('The name of the project to be created. This name is how your project will appear in HubSpot.'),
|
|
23
|
+
destination: zod_1.z
|
|
24
|
+
.string()
|
|
25
|
+
.describe('Relative path to the directory the project will be created in. It is not recommended to use the current directory unless it is an empty directory'),
|
|
26
|
+
projectBase: zod_1.z
|
|
27
|
+
.union([zod_1.z.literal(v3_1.EMPTY_PROJECT), zod_1.z.literal(v3_1.PROJECT_WITH_APP)])
|
|
28
|
+
.describe('Empty will create and empty project, and app will create a project with an app inside of it.'),
|
|
29
|
+
distribution: zod_1.z
|
|
30
|
+
.optional(zod_1.z.union([
|
|
31
|
+
zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE),
|
|
32
|
+
zod_1.z.literal(constants_1.APP_DISTRIBUTION_TYPES.PRIVATE),
|
|
33
|
+
]))
|
|
34
|
+
.describe('Private is used if you do not wish to distribute your application on the HubSpot marketplace. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.'),
|
|
35
|
+
auth: zod_1.z
|
|
36
|
+
.optional(zod_1.z.union([
|
|
37
|
+
zod_1.z.literal(constants_1.APP_AUTH_TYPES.STATIC),
|
|
38
|
+
zod_1.z.literal(constants_1.APP_AUTH_TYPES.OAUTH),
|
|
39
|
+
]))
|
|
40
|
+
.describe('Static uses a static non changing authentication token, and is only available for private distribution. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.')
|
|
41
|
+
.optional(),
|
|
42
|
+
features: zod_1.z
|
|
43
|
+
.array(zod_1.z
|
|
44
|
+
.union([
|
|
45
|
+
zod_1.z.literal('card'),
|
|
46
|
+
zod_1.z.literal('settings'),
|
|
47
|
+
zod_1.z.literal('app-function'),
|
|
48
|
+
zod_1.z.literal('webhooks'),
|
|
49
|
+
])
|
|
50
|
+
.describe('The features to include in the project, multiple options can be selected'))
|
|
51
|
+
.optional(),
|
|
52
|
+
},
|
|
53
|
+
}, async ({ name, destination, projectBase, distribution, auth, features, absoluteCurrentWorkingDirectory, }) => {
|
|
54
|
+
let command = (0, command_1.addFlag)('hs project create', 'platform-version', '2025.2');
|
|
55
|
+
const content = [];
|
|
56
|
+
if (name) {
|
|
57
|
+
command = (0, command_1.addFlag)(command, 'name', name);
|
|
58
|
+
}
|
|
59
|
+
if (destination) {
|
|
60
|
+
command = (0, command_1.addFlag)(command, 'dest', destination);
|
|
61
|
+
}
|
|
62
|
+
if (projectBase) {
|
|
63
|
+
command = (0, command_1.addFlag)(command, 'project-base', projectBase);
|
|
64
|
+
}
|
|
65
|
+
if (distribution) {
|
|
66
|
+
command = (0, command_1.addFlag)(command, 'distribution', distribution);
|
|
67
|
+
}
|
|
68
|
+
else if (projectBase === v3_1.PROJECT_WITH_APP) {
|
|
69
|
+
content.push({
|
|
70
|
+
type: 'text',
|
|
71
|
+
text: `Ask the user how they would you like to distribute the application? Options are ${constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${constants_1.APP_DISTRIBUTION_TYPES.PRIVATE}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (auth) {
|
|
75
|
+
command = (0, command_1.addFlag)(command, 'auth', auth);
|
|
76
|
+
}
|
|
77
|
+
else if (projectBase === v3_1.PROJECT_WITH_APP) {
|
|
78
|
+
content.push({
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `Ask the user which auth type they would like to use? Options are ${constants_1.APP_AUTH_TYPES.STATIC} and ${constants_1.APP_AUTH_TYPES.OAUTH}`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (content.length > 0) {
|
|
84
|
+
return {
|
|
85
|
+
content,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (features && features.length) {
|
|
89
|
+
command = (0, command_1.addFlag)(command, 'features', features);
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteCurrentWorkingDirectory, command);
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: 'text',
|
|
97
|
+
text: stdout,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'text',
|
|
101
|
+
text: stderr,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: error instanceof Error ? error.message : `${error}`,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.CreateProjectTool = CreateProjectTool;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeployProject = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const command_1 = require("../../utils/command");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
const project_1 = require("../../utils/project");
|
|
9
|
+
class DeployProject extends types_1.Tool {
|
|
10
|
+
static mcpServer;
|
|
11
|
+
static register(mcpServer) {
|
|
12
|
+
this.mcpServer = mcpServer;
|
|
13
|
+
return this.mcpServer.registerTool('deploy-hubspot-project', {
|
|
14
|
+
title: 'Deploy a build of HubSpot Project',
|
|
15
|
+
description: 'Takes a build number and a project name and deploys that build of the project',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
absoluteProjectPath: constants_1.absoluteProjectPath,
|
|
18
|
+
buildNumber: zod_1.z
|
|
19
|
+
.optional(zod_1.z.number())
|
|
20
|
+
.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'),
|
|
21
|
+
},
|
|
22
|
+
}, async ({ absoluteProjectPath, buildNumber }) => {
|
|
23
|
+
let command = `hs project deploy`;
|
|
24
|
+
const content = [];
|
|
25
|
+
if (!buildNumber) {
|
|
26
|
+
const { stdout } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project list-builds --limit 100`);
|
|
27
|
+
content.push({
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: `Ask the user which build number they would like to deploy? Build information: ${stdout}`,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
command = (0, command_1.addFlag)(command, 'build', buildNumber);
|
|
34
|
+
}
|
|
35
|
+
if (content.length) {
|
|
36
|
+
return {
|
|
37
|
+
content,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, command);
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{ type: 'text', text: stdout },
|
|
44
|
+
{ type: 'text', text: stderr },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.DeployProject = DeployProject;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Tool } from '../../types';
|
|
2
|
+
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
export declare class GuidedWalkthroughTool extends Tool {
|
|
4
|
+
private static mcpServer;
|
|
5
|
+
static register(mcpServer: McpServer): RegisteredTool;
|
|
6
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GuidedWalkthroughTool = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const command_1 = require("../../utils/command");
|
|
7
|
+
const nextCommands = {
|
|
8
|
+
'hs init': 'hs auth',
|
|
9
|
+
'hs auth': 'hs project create',
|
|
10
|
+
'hs project create': 'hs project upload',
|
|
11
|
+
'hs project upload': 'hs project dev',
|
|
12
|
+
};
|
|
13
|
+
class GuidedWalkthroughTool extends types_1.Tool {
|
|
14
|
+
static mcpServer;
|
|
15
|
+
static register(mcpServer) {
|
|
16
|
+
this.mcpServer = mcpServer;
|
|
17
|
+
return this.mcpServer.registerTool('guided-walkthrough-hubspot-cli', {
|
|
18
|
+
title: 'Guided walkthrough of the CLI',
|
|
19
|
+
description: 'Give the user a guided walkthrough of the HubSpot CLI.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
command: zod_1.z
|
|
22
|
+
.union([
|
|
23
|
+
zod_1.z.literal('hs init'),
|
|
24
|
+
zod_1.z.literal('hs auth'),
|
|
25
|
+
zod_1.z.literal('hs project create'),
|
|
26
|
+
zod_1.z.literal('hs project upload'),
|
|
27
|
+
])
|
|
28
|
+
.describe('The command to learn more about. Start with `hs init`')
|
|
29
|
+
.optional(),
|
|
30
|
+
},
|
|
31
|
+
}, async ({ command }) => {
|
|
32
|
+
if (command) {
|
|
33
|
+
const { stdout } = await (0, command_1.execAsync)(`${command} --help`);
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: `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]}` : ''}`,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: 'Is there another command you would like to learn more about?',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.GuidedWalkthroughTool = GuidedWalkthroughTool;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UploadProjectTools = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const project_1 = require("../../utils/project");
|
|
6
|
+
const constants_1 = require("./constants");
|
|
7
|
+
class UploadProjectTools extends types_1.Tool {
|
|
8
|
+
static register(mcpServer) {
|
|
9
|
+
return mcpServer.registerTool('upload-hubspot-project', {
|
|
10
|
+
title: 'Upload HubSpot Project',
|
|
11
|
+
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.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
absoluteProjectPath: constants_1.absoluteProjectPath,
|
|
14
|
+
},
|
|
15
|
+
}, async ({ absoluteProjectPath }) => {
|
|
16
|
+
const { stdout, stderr } = await (0, project_1.runCommandInDir)(absoluteProjectPath, `hs project upload --force-create`);
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{ type: 'text', text: stdout },
|
|
20
|
+
{ type: 'text', text: stderr },
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.UploadProjectTools = UploadProjectTools;
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
exports.absoluteCurrentWorkingDirectory = exports.absoluteProjectPath = void 0;
|
|
7
|
+
const zod_1 = __importDefault(require("zod"));
|
|
8
|
+
exports.absoluteProjectPath = zod_1.default
|
|
9
|
+
.string()
|
|
10
|
+
.describe('The absolute path to the project directory.');
|
|
11
|
+
exports.absoluteCurrentWorkingDirectory = zod_1.default
|
|
12
|
+
.string()
|
|
13
|
+
.describe('The absolute path to the current working directory.');
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
exports.execAsync = void 0;
|
|
7
|
+
exports.addFlag = addFlag;
|
|
8
|
+
const util_1 = __importDefault(require("util"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
exports.execAsync = util_1.default.promisify(child_process_1.exec);
|
|
11
|
+
function addFlag(command, flagName, value) {
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return `${command} --${flagName} ${value.map(item => `"${item}"`).join(' ')}`;
|
|
14
|
+
}
|
|
15
|
+
return `${command} --${flagName} "${value}"`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
exports.runCommandInDir = runCommandInDir;
|
|
7
|
+
const command_1 = require("./command");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
async function runCommandInDir(directory, command) {
|
|
11
|
+
if (!fs_1.default.existsSync(directory)) {
|
|
12
|
+
fs_1.default.mkdirSync(directory);
|
|
13
|
+
}
|
|
14
|
+
return (0, command_1.execAsync)(command, {
|
|
15
|
+
cwd: path_1.default.resolve(directory),
|
|
16
|
+
});
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.5-experimental.0",
|
|
4
4
|
"description": "The official CLI for developing on HubSpot",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": "https://github.com/HubSpot/hubspot-cli",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@hubspot/local-dev-lib": "3.7.1",
|
|
9
|
-
"@hubspot/project-parsing-lib": "0.3.
|
|
9
|
+
"@hubspot/project-parsing-lib": "0.3.0",
|
|
10
10
|
"@hubspot/serverless-dev-runtime": "7.0.6",
|
|
11
11
|
"@hubspot/theme-preview-dev-server": "0.0.10",
|
|
12
12
|
"@hubspot/ui-extensions-dev-server": "0.9.2",
|
|
13
|
+
"@modelcontextprotocol/sdk": "1.13.3",
|
|
13
14
|
"archiver": "7.0.1",
|
|
14
15
|
"boxen": "8.0.1",
|
|
15
16
|
"chalk": "4.1.2",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"build": "ts-node ./scripts/build.ts",
|
|
69
|
+
"debug-mcp": "yarn build && npx @modelcontextprotocol/inspector node dist/mcp-server/server.js",
|
|
68
70
|
"lint": "echo 'Linting is disabled for Blazar'",
|
|
69
71
|
"lint:local": "eslint . && prettier --list-different './**/*.{ts,js,json}'",
|
|
70
72
|
"list-all-commands": "yarn ts-node ./scripts/get-all-commands.ts",
|