@realeng/maestro
Version:
Easy setup and management for local MCP servers
283 lines • 14.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCommand = initCommand;
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
const config_1 = require("../utils/config");
const servers_1 = require("../servers");
const docker_1 = require("../utils/docker");
const sync_1 = require("./sync");
function displayWelcomeBanner() {
console.clear();
console.log(chalk_1.default.cyan(`
███╗ ███╗ █████╗ ███████╗███████╗████████╗██████╗ ██████╗
████╗ ████║██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔══██╗██╔═══██╗
██╔████╔██║███████║█████╗ ███████╗ ██║ ██████╔╝██║ ██║
██║╚██╔╝██║██╔══██║██╔══╝ ╚════██║ ██║ ██╔══██╗██║ ██║
██║ ╚═╝ ██║██║ ██║███████╗███████║ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
`));
console.log(chalk_1.default.blue(` ${chalk_1.default.bold('MCP Server Manager')}`));
console.log(chalk_1.default.gray(' ══════════════════════════════════════════════════════'));
console.log(chalk_1.default.yellow(` 🎭 Making AI assistants more capable!`));
console.log(chalk_1.default.gray(' ══════════════════════════════════════════════════════\n'));
}
async function initCommand() {
const config = (0, config_1.loadConfig)();
const isFirstTime = Object.keys(config.servers).length === 0;
if (isFirstTime) {
displayWelcomeBanner();
console.log(chalk_1.default.green(' 🎉 Welcome to your first Maestro setup! Let\'s get started!\n'));
// Add Claude Desktop requirement message
console.log(chalk_1.default.yellow(' ⚠️ Prerequisites:\n'));
console.log(chalk_1.default.white(' You\'ll need Claude Desktop to use these MCP servers.'));
console.log(chalk_1.default.white(' Download it from: ') + chalk_1.default.cyan.underline('https://claude.ai/download'));
console.log(chalk_1.default.gray(' Available for macOS, Windows, and Linux\n'));
console.log(chalk_1.default.gray(' ──────────────────────────────────────────────────────\n'));
}
else {
console.log(chalk_1.default.blue('\n🚀 Welcome back to Maestro MCP Setup!\n'));
}
const servers = (0, servers_1.listActiveServers)();
const { selectedServer } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'selectedServer',
message: 'Which MCP server would you like to configure?',
choices: [
...servers.map(s => ({
name: `${s.displayName} - ${s.description}`,
value: s.name
})),
new inquirer_1.default.Separator(),
{ name: 'Exit', value: 'exit' }
]
}
]);
if (selectedServer === 'exit') {
console.log(chalk_1.default.gray('\nSetup cancelled.'));
return;
}
const serverDef = (0, servers_1.getServer)(selectedServer);
if (!serverDef) {
console.error(chalk_1.default.red('Server definition not found.'));
return;
}
// Check if Docker is required and available
if (serverDef.requiresDocker && !(0, docker_1.checkDockerAvailable)()) {
console.log(chalk_1.default.red('\n❌ Docker is not installed or not running!\n'));
console.log((0, docker_1.getDockerInstallInstructions)());
const { continueAnyway } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'continueAnyway',
message: 'Docker is required for this server. Would you like to continue setup anyway?',
default: false
}
]);
if (!continueAnyway) {
console.log(chalk_1.default.gray('\nSetup cancelled. Please install Docker and try again.'));
return;
}
console.log(chalk_1.default.yellow('\n⚠️ Warning: This server will not work without Docker installed.\n'));
}
console.log(chalk_1.default.cyan(`\nConfiguring ${serverDef.displayName}...`));
// Add fun server-specific messages
const serverMessages = {
youtrack: '🐛 Time to track those bugs like a pro!',
playwright: '🎭 Lights, camera, automation!',
github: '🐙 Let\'s connect to the world\'s largest code repository!',
teamcity: '🏗️ Ready to build, test, and deploy like a champion!',
datadog: '📊 Ready to monitor and observe everything!'
};
if (serverMessages[selectedServer]) {
console.log(chalk_1.default.magenta(`\n${serverMessages[selectedServer]}\n`));
}
// Special handling for Datadog setup instructions
if (selectedServer === 'datadog') {
console.log(chalk_1.default.yellow('⚠️ IMPORTANT: Before continuing, you must set up the Datadog MCP CLI tool.\n'));
console.log(chalk_1.default.white('Please follow the instructions at:'));
console.log(chalk_1.default.blue('https://realbrokerage.youtrack.cloud/articles/RV2-A-1231160967/Setting-up-Datadog-MCP\n'));
const { setupComplete } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'setupComplete',
message: 'Have you completed the Datadog MCP CLI setup?',
default: false
}
]);
if (!setupComplete) {
console.log(chalk_1.default.gray('\nPlease complete the setup first, then run "maestro init" again.'));
return;
}
}
const existingConfig = config.servers[selectedServer];
if (existingConfig && existingConfig.enabled) {
const { overwrite } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `${serverDef.displayName} is already configured. Do you want to reconfigure it?`,
default: false
}
]);
if (!overwrite) {
console.log(chalk_1.default.gray('\nConfiguration unchanged.'));
return;
}
}
const authQuestions = serverDef.requiredAuth.map(field => {
const question = {
type: field.type === 'password' ? 'password' : 'input',
name: field.key,
message: field.label,
validate: (input, answers) => {
// Special handling for Slack server
if (selectedServer === 'slack') {
const authMethod = answers?.authMethod;
// Skip validation for tokens not needed by selected auth method
if (field.key === 'xoxpToken' && authMethod === 'browser') {
return true; // Allow empty for browser method
}
if ((field.key === 'xoxcToken' || field.key === 'xoxdToken') && authMethod === 'oauth') {
return true; // Allow empty for oauth method
}
// Validate auth method input
if (field.key === 'authMethod' && input.trim()) {
const method = input.trim().toLowerCase();
if (method !== 'oauth' && method !== 'browser') {
return 'Please enter either "oauth" or "browser"';
}
}
// Require tokens based on auth method
if (authMethod === 'oauth' && field.key === 'xoxpToken' && !input.trim()) {
return 'User OAuth Token is required for oauth method';
}
if (authMethod === 'browser' && field.key === 'xoxcToken' && !input.trim()) {
return 'Browser Token is required for browser method';
}
if (authMethod === 'browser' && field.key === 'xoxdToken' && !input.trim()) {
return 'Browser Cookie Token is required for browser method';
}
}
else {
// Default validation for non-Slack servers
if (!input.trim()) {
return `${field.label} is required`;
}
}
if (field.type === 'url' && !input.match(/^https?:\/\//)) {
return 'Please enter a valid URL starting with http:// or https://';
}
return true;
},
default: existingConfig?.auth?.[field.key]
};
// Add description as a prefix to the message if available
if (field.description) {
question.prefix = chalk_1.default.gray(`\n${field.description}\n`);
}
return question;
});
const authAnswers = await inquirer_1.default.prompt(authQuestions);
let settings = {};
if (serverDef.optionalSettings && serverDef.optionalSettings.length > 0) {
const { configureSettings } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'configureSettings',
message: 'Would you like to configure optional settings?',
default: false
}
]);
if (configureSettings) {
const settingsQuestions = serverDef.optionalSettings.map(field => {
const question = {
name: field.key,
message: field.label,
default: existingConfig?.settings?.[field.key] || field.default
};
switch (field.type) {
case 'boolean':
question.type = 'confirm';
break;
case 'select':
question.type = 'list';
question.choices = field.options;
break;
case 'number':
question.type = 'number';
break;
default:
question.type = 'input';
}
return question;
});
settings = await inquirer_1.default.prompt(settingsQuestions);
}
}
const serverConfig = {
type: selectedServer,
enabled: true,
auth: authAnswers,
settings
};
// Add a small loading animation
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
let i = 0;
process.stdout.write('\n' + chalk_1.default.yellow('Saving configuration '));
const loadingInterval = setInterval(() => {
process.stdout.write(`\r${chalk_1.default.yellow('Saving configuration')} ${chalk_1.default.cyan(spinner[i])}`);
i = (i + 1) % spinner.length;
}, 80);
(0, config_1.updateServerConfig)(selectedServer, serverConfig);
// Stop the spinner after a short delay to make it visible
await new Promise((resolve) => {
setTimeout(async () => {
clearInterval(loadingInterval);
process.stdout.write('\r' + chalk_1.default.green(`✅ ${serverDef.displayName} configured successfully!`) + ' \n');
// Add some fun celebration for first-time setup
if (isFirstTime) {
console.log(chalk_1.default.yellow('\n🎊 Congratulations on setting up your first MCP server! 🎊'));
}
console.log(chalk_1.default.gray('\nNext steps:'));
console.log(chalk_1.default.green.bold(' 🚀 Run: maestro sync') + chalk_1.default.white(' - Automatically sync to Claude Desktop'));
console.log(chalk_1.default.gray('\n Or manually:'));
console.log(chalk_1.default.white(' 1. Run: ') + chalk_1.default.green('maestro mcp-config') + chalk_1.default.white(' - Generate Claude configuration'));
console.log(chalk_1.default.white(' 2. Copy the generated JSON to Claude Desktop settings'));
console.log(chalk_1.default.gray('\nOther commands:'));
console.log(chalk_1.default.white(' maestro list - See all your configured servers'));
console.log(chalk_1.default.white(' maestro enable/disable <name> - Toggle servers on/off'));
const { configureAnother } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'configureAnother',
message: 'Would you like to configure another server?',
default: false
}
]);
if (configureAnother) {
await initCommand();
}
else {
// Ask if they want to sync to Claude Desktop
const { runSync } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'runSync',
message: 'Would you like to sync your configuration to Claude Desktop now?',
default: true
}
]);
if (runSync) {
await (0, sync_1.syncCommand)();
}
}
resolve();
}, 800);
});
}
//# sourceMappingURL=init.js.map