UNPKG

create-mcp-i-app

Version:

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

171 lines (170 loc) 8.18 kB
import fs from "fs-extra"; import path from "path"; import chalk from "chalk"; /** * Apply identity preset to an XMCP project * This enhances the upstream XMCP template with identity features * If noIdentity is true, leaves the XMCP project unchanged */ export async function applyIdentityPreset(options) { const { projectPath, projectName, transports, noIdentity = false } = options; if (noIdentity) { console.log(chalk.blue("📦 Keeping plain XMCP project (--no-identity flag)")); return; } console.log(chalk.blue("🔐 Applying identity preset...")); const srcDir = path.join(projectPath, "src"); // Check if upstream XMCP structure exists const middlewarePath = path.join(srcDir, "middleware.ts"); const toolsDir = path.join(srcDir, "tools"); if (await fs.pathExists(middlewarePath)) { // Enhance existing middleware.ts with identity features console.log(chalk.gray("📝 Enhancing existing middleware.ts with identity features...")); let middlewareContent = await fs.readFile(middlewarePath, "utf-8"); // Add identity imports at the top const identityImports = `import { createMCPIRuntime } from "@kya-os/mcp-i";\n`; // Add identity middleware before existing middleware const identityMiddleware = `\n// XMCP-I Identity Middleware\nconst mcpiRuntime = await createMCPIRuntime({\n identity: {\n environment: process.env.NODE_ENV === "production" ? "production" : "development",\n devIdentityPath: ".mcp-i/identity.json",\n },\n wellKnown: {\n environment: process.env.NODE_ENV === "production" ? "production" : "development",\n baseUrl: process.env.BASE_URL || "http://localhost:3000",\n },\n});\n\n// Add identity verification to all requests\nexport const identityMiddleware = mcpiRuntime.middleware;\n\n`; // Insert identity imports at the beginning middlewareContent = identityImports + middlewareContent; // Insert identity middleware before the last export const lastExportIndex = middlewareContent.lastIndexOf("export"); if (lastExportIndex !== -1) { middlewareContent = middlewareContent.slice(0, lastExportIndex) + identityMiddleware + middlewareContent.slice(lastExportIndex); } else { middlewareContent += identityMiddleware; } await fs.writeFile(middlewarePath, middlewareContent); } else { // Fallback: create our own structure if upstream doesn't exist console.log(chalk.yellow("⚠️ No upstream middleware.ts found, creating mcpi structure...")); await fs.ensureDir(srcDir); await fs.ensureDir(toolsDir); // Create middleware.ts with identity features const middlewareContent = `import { createMCPIRuntime } from "@kya-os/mcp-i";\n\n// Create XMCP-I server with identity features\nconst runtime = await createMCPIRuntime({\n identity: {\n environment: process.env.NODE_ENV === "production" ? "production" : "development",\n devIdentityPath: ".mcp-i/identity.json",\n },\n wellKnown: {\n environment: process.env.NODE_ENV === "production" ? "production" : "development",\n baseUrl: process.env.BASE_URL || "http://localhost:3000",\n },\n});\n\n// Export identity middleware\nexport const identityMiddleware = runtime.middleware;\n`; await fs.writeFile(middlewarePath, middlewareContent); } // Enhance existing tools or create identity-aware tools if (await fs.pathExists(toolsDir)) { console.log(chalk.gray("🔧 Enhancing existing tools with identity features...")); // Check for existing tools and enhance them const toolFiles = await fs.readdir(toolsDir); const tsFiles = toolFiles.filter((f) => f.endsWith(".ts") && f !== "index.ts"); if (tsFiles.length > 0) { // Enhance existing tools for (const toolFile of tsFiles) { const toolPath = path.join(toolsDir, toolFile); let toolContent = await fs.readFile(toolPath, "utf-8"); // Add identity comment to existing tools const identityComment = `// Enhanced with XMCP-I identity features\n`; toolContent = identityComment + toolContent; await fs.writeFile(toolPath, toolContent); } } else { // Create identity-aware hello tool if no tools exist await createIdentityHelloTool(toolsDir); } } else { // Create tools directory and identity-aware tools await fs.ensureDir(toolsDir); await createIdentityHelloTool(toolsDir); } // Update package.json with identity dependencies and scripts await updatePackageJsonForIdentity(projectPath, projectName, transports); // Create .mcp-i directory for identity files const mcpiDir = path.join(projectPath, ".mcp-i"); await fs.ensureDir(mcpiDir); // Create .gitignore with identity-specific entries await updateGitignore(projectPath); console.log(chalk.green("✅ Identity preset applied")); } async function createIdentityHelloTool(toolsDir) { // Create tools/hello.ts with identity features const helloToolContent = `// Enhanced with XMCP-I identity features import { z } from "zod"; export const hello = { name: "hello", description: "Say hello with identity verification", inputSchema: z.object({ name: z.string().describe("Name to greet"), }), handler: async ({ name }: { name: string }) => { return { content: [ { type: "text" as const, text: \`Hello, \${name}! This message is cryptographically signed.\`, }, ], }; }, }; `; await fs.writeFile(path.join(toolsDir, "hello.ts"), helloToolContent); // Create or update tools/index.ts const indexPath = path.join(toolsDir, "index.ts"); let indexContent = ""; if (await fs.pathExists(indexPath)) { indexContent = await fs.readFile(indexPath, "utf-8"); } // Add hello export if not already present if (!indexContent.includes("hello")) { indexContent += `\nexport * from "./hello.js";`; await fs.writeFile(indexPath, indexContent); } } async function updatePackageJsonForIdentity(projectPath, projectName, transports) { const packageJsonPath = path.join(projectPath, "package.json"); const packageJson = await fs.readJson(packageJsonPath); // Add identity dependencies // Use workspace dependencies for local testing, published versions for production const useWorkspace = process.env.MCPI_USE_WORKSPACE === "true"; packageJson.dependencies = { ...packageJson.dependencies, "@kya-os/mcp-i": useWorkspace ? "workspace:*" : "^1.2.0", "@kya-os/cli": useWorkspace ? "workspace:*" : "^1.2.2", }; // Add exactly 8 scripts as required by spec packageJson.scripts = { dev: "mcpi dev", build: "mcpi build", start: "mcpi start", init: "mcpi init", register: "mcpi register", "keys:rotate": "mcpi keys rotate", "identity:clean": "mcpi identity clean", status: "mcpi status", }; await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); } async function updateGitignore(projectPath) { const gitignorePath = path.join(projectPath, ".gitignore"); let gitignoreContent = ""; if (await fs.pathExists(gitignorePath)) { gitignoreContent = await fs.readFile(gitignorePath, "utf-8"); } // Add identity-specific gitignore entries if not already present const identityEntries = [ "# MCP-I Identity", ".mcp-i/", ".mcpi/", // Legacy support ".xmcpi/", // Legacy support for old naming ".env.local", "generated-identity.json", ".mcp-identity.json", ]; for (const entry of identityEntries) { if (!gitignoreContent.includes(entry)) { gitignoreContent += `\n${entry}`; } } await fs.writeFile(gitignorePath, gitignoreContent); } //# sourceMappingURL=apply-identity-preset.js.map