UNPKG

create-mcp-i-app

Version:

Bootstrap MCP applications with identity features (temporary - use create-mcpi-app after Oct 7)

322 lines 13.1 kB
#!/usr/bin/env node import path from "path"; import fs from "fs-extra"; import chalk from "chalk"; import { Command } from "commander"; import inquirer from "inquirer"; import ora from "ora"; import { fileURLToPath } from "url"; import { checkNodeVersion } from "./utils/check-node.js"; import { createProject } from "./helpers/create.js"; import { createKYAOSBanner } from "./effects/index.js"; import { validateProjectName, validateDirectoryAvailability, } from "./utils/validate-project-name.js"; checkNodeVersion(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), "utf8")); const program = new Command() .name("create-mcpi-app") .description("Create a new xmcp application with identity features built-in") .version(packageJson.version, "-v, --version", "Output the current version of create-mcpi-app.") .argument("[directory]") .usage("[directory] [options]") .helpOption("-h, --help", "Display help message.") .option("-y, --yes", "Skip confirmation prompt", false) .option("--use-npm", "Use npm as package manager (default: use npm)") .option("--use-yarn", "Use yarn as package manager") .option("--use-pnpm", "Use pnpm as package manager") .option("--skip-install", "Skip installing dependencies", false) .option("--skip-identity", "Skip identity generation", false) .option("--no-identity", "Create plain XMCP project without identity features") .option("--vercel", "Add Vercel support for deployment", false) .option("--http", "Enable HTTP transport", false) .option("--stdio", "Enable STDIO transport", false) .option("--local", "Use local xmcp-i dependency", false) .option("--xmcp-version <version>", "Specify XMCP version (e.g., 0.3.1)") .option("--xmcp-channel <channel>", "Use dist-tag (latest|next)", "latest") .option("--no-animation", "Skip the black hole animation", false) .option("--fast", "Use shorter animation duration", false) .action(async (projectDir, options) => { console.log(chalk.bold(`\ncreate-mcpi-app@${packageJson.version}`)); // Show KYA-OS banner const banner = await createKYAOSBanner(); console.log(chalk.cyan(banner)); console.log(chalk.dim("\nEnhanced with identity features by MCP-I\n")); // If project directory wasn't specified, ask for it if (!projectDir) { const answers = await inquirer.prompt([ { type: "input", name: "projectDir", message: "What is your project named?", default: "my-mcpi-app", }, ]); projectDir = answers.projectDir; } // Normalize project directory const resolvedProjectPath = path.resolve(process.cwd(), projectDir); const projectName = path.basename(resolvedProjectPath); // Validate project name const nameValidation = validateProjectName(projectName); if (!nameValidation.valid) { console.error(chalk.red("Invalid project name:")); for (const issue of nameValidation.issues) { console.error(chalk.red(` - ${issue}`)); } process.exit(1); } // Validate directory availability const dirValidation = validateDirectoryAvailability(resolvedProjectPath); if (!dirValidation.valid) { console.error(chalk.red("Directory validation failed:")); for (const issue of dirValidation.issues) { console.error(chalk.red(` - ${issue}`)); } process.exit(1); } let packageManager = "npm"; let useLocalXmcp = options.local; let xmcpVersion = options.xmcpVersion; let xmcpChannel = options.xmcpChannel; let deployToVercel = options.vercel; let skipInstall = options.skipInstall; let skipIdentity = options.skipIdentity || !options.identity; let transports = ["http"]; let agentName = projectName; let agentDescription = "XMCP-I server with identity features"; let agentRepository = ""; // Handle transport selection from CLI options if (options.http || options.stdio) { transports = []; if (options.http) transports.push("http"); if (options.stdio) transports.push("stdio"); } if (!options.yes) { // Package manager selection if (options.useYarn) packageManager = "yarn"; if (options.usePnpm) packageManager = "pnpm"; if (!options.useYarn && !options.usePnpm && !options.useNpm) { const pmAnswers = await inquirer.prompt([ { type: "list", name: "packageManager", message: "Select a package manager:", choices: [ { name: "npm", value: "npm" }, { name: "yarn", value: "yarn" }, { name: "pnpm", value: "pnpm" }, ], default: "npm", }, ]); packageManager = pmAnswers.packageManager; } // Transport selection (skip if already specified via CLI options) if (!options.http && !options.stdio) { const transportAnswers = await inquirer.prompt([ { type: "checkbox", name: "transports", message: "Select the transports you want to use:", choices: [ { name: "HTTP (runs on a server)", value: "http", checked: true, }, { name: "STDIO (runs on the user's machine)", value: "stdio", checked: false, }, ], validate: (input) => { if (input.length === 0) { return "You must select at least one transport."; } return true; }, }, ]); transports = transportAnswers.transports; } // Vercel deployment option if (!options.vercel && transports.includes("http")) { const vercelAnswers = await inquirer.prompt([ { type: "confirm", name: "deployToVercel", message: "Add Vercel deployment support?", default: true, }, ]); deployToVercel = vercelAnswers.deployToVercel; } // Agent information for identity generation if (!skipIdentity) { console.log(chalk.blue("\n🤖 Agent Configuration:")); const agentInfo = await inquirer.prompt([ { type: "input", name: "name", message: "What's your agent name?", default: projectName, }, { type: "input", name: "description", message: "Brief description of your agent:", default: "XMCP-I server with identity features", }, { type: "input", name: "repository", message: "Repository URL (optional):", default: "", }, ]); agentName = agentInfo.name; agentDescription = agentInfo.description; agentRepository = agentInfo.repository; } console.log(); console.log(`Creating a new xmcp app in ${chalk.green(resolvedProjectPath)}.\n`); const { confirmed } = await inquirer.prompt([ { type: "confirm", name: "confirmed", message: "Ok to continue?", default: true, }, ]); if (!confirmed) { console.log(chalk.yellow("Aborting installation.")); process.exit(0); } } else { // Use command-line options when --yes is provided if (options.useYarn) packageManager = "yarn"; if (options.usePnpm) packageManager = "pnpm"; } const spinner = ora("Creating your xmcp-i app with identity features...").start(); try { spinner.text = "Scaffolding project structure..."; const result = await createProject({ projectPath: resolvedProjectPath, projectName, packageManager, transports: transports, packageVersion: packageJson.version, useLocalXmcp, xmcpVersion, xmcpChannel, deployToVercel, skipInstall, agentName, agentDescription, agentRepository, skipIdentity, skipAnimation: options.noAnimation === false ? true : false, // Handle --no-animation flag fastAnimation: options.fast, spinner, // Pass spinner to stop it before animation }); if (!result.success) { spinner.fail(chalk.red("Failed to create the project.")); if (result.warnings) { for (const warning of result.warnings) { console.error(chalk.red(` ${warning}`)); } } process.exit(1); } if (result.warnings && result.warnings.length > 0) { spinner.warn(chalk.yellow("Your xmcp-i app is ready with warnings")); for (const warning of result.warnings) { console.warn(chalk.yellow(` ⚠️ ${warning}`)); } } else { spinner.succeed(chalk.green("Your xmcp-i app is ready")); } console.log(); console.log("Next Steps:"); // Show agent management with claim URL first if (!skipIdentity) { console.log("Agent management:"); console.log(" Follow the Claim URL above to claim your agent"); if (resolvedProjectPath !== process.cwd()) { console.log(` cd ${chalk.cyan(projectDir)}`); } console.log(` ${chalk.cyan(`${packageManager} run status`)} - Check agent status`); console.log(` ${chalk.cyan(`${packageManager} run register`)} - Register with registry`); console.log(); } console.log("Start agent:"); if (resolvedProjectPath !== process.cwd()) { console.log(` cd ${chalk.cyan(projectDir)}`); } if (skipInstall) { if (packageManager === "yarn") { console.log(` ${chalk.cyan("yarn install")}`); } else if (packageManager === "pnpm") { console.log(` ${chalk.cyan("pnpm install")}`); } else { console.log(` ${chalk.cyan("npm install")}`); } } // Show appropriate command based on transport if (transports.includes("stdio")) { console.log(` ${chalk.cyan("npx xmcp build")}`); } else { // HTTP transport - show dev command if (packageManager === "yarn") { console.log(` ${chalk.cyan("yarn dev")}`); } else if (packageManager === "pnpm") { console.log(` ${chalk.cyan("pnpm dev")}`); } else { console.log(` ${chalk.cyan("npm run dev")}`); } } console.log(); console.log("Manage identity:"); if (resolvedProjectPath !== process.cwd()) { console.log(` cd ${chalk.cyan(projectDir)}`); } // Show identity management commands if (packageManager === "yarn") { console.log(` ${chalk.cyan("yarn keys:rotate")} - Rotate cryptographic keys`); console.log(` ${chalk.cyan("yarn identity:clean")} - Clean identity data`); } else if (packageManager === "pnpm") { console.log(` ${chalk.cyan("pnpm keys:rotate")} - Rotate cryptographic keys`); console.log(` ${chalk.cyan("pnpm identity:clean")} - Clean identity data`); } else { console.log(` ${chalk.cyan("npm run keys:rotate")} - Rotate cryptographic keys`); console.log(` ${chalk.cyan("npm run identity:clean")} - Clean identity data`); } console.log(); // Explicitly exit after successful completion process.exit(0); } catch (error) { spinner.fail(chalk.red("Failed to create the project.")); console.error(error); process.exit(1); } }); program.parse(process.argv); //# sourceMappingURL=index.js.map