/** * MCPEngine Studio — Deploy to MCPEngine (Cloudflare Workers) * * Phase 1: Simulates the Cloudflare Workers upload flow. * Compile → Package → "Upload" (writes to local output dir). * TODO: Real Wrangler API integration. */ import { promises as fs } from 'fs'; import path from 'path'; import type { ServerBundle, DeployConfig, DeployResult, } from '@mcpengine/ai-pipeline/types'; import { compileWithMeta } from '../compiler'; import { generateWorkerScript } from '../worker-template'; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface DeployProgress { step: string; message: string; percent: number; level: 'info' | 'success' | 'warning' | 'error'; } // --------------------------------------------------------------------------- // Config // --------------------------------------------------------------------------- const OUTPUT_BASE = path.join(process.cwd(), '.mcpengine-output', 'workers'); // --------------------------------------------------------------------------- // Main // --------------------------------------------------------------------------- export async function* deployToMCPEngine( projectId: string, bundle: ServerBundle, config: DeployConfig, ): AsyncGenerator { const slug = config.slug ?? projectId.slice(0, 8); const deployId = crypto.randomUUID(); const startedAt = new Date().toISOString(); const logs: string[] = []; const log = (msg: string) => { logs.push(`[${new Date().toISOString()}] ${msg}`); }; // ── Step 1: Compile ──────────────────────────────────────────────────── yield { step: 'compile', message: 'Compiling server bundle…', percent: 10, level: 'info', }; log('Starting compilation…'); const compiled = compileWithMeta(bundle); log(`Compiled ${compiled.fileCount} files (${(compiled.sizeBytes / 1024).toFixed(1)} KB)`); yield { step: 'compile', message: `Compiled ${compiled.fileCount} files (${compiled.toolCount} tools)`, percent: 25, level: 'success', }; // ── Step 2: Generate Worker script ───────────────────────────────────── yield { step: 'package', message: 'Generating Cloudflare Worker script…', percent: 35, level: 'info', }; log('Generating worker script…'); const workerScript = generateWorkerScript({ serverName: config.slug ?? 'MCP Server', serverSlug: slug, compiledCode: compiled.code, toolNames: compiled.toolNames, toolCount: compiled.toolCount, }); const workerSize = new TextEncoder().encode(workerScript).byteLength; log(`Worker script generated (${(workerSize / 1024).toFixed(1)} KB)`); yield { step: 'package', message: `Worker packaged (${(workerSize / 1024).toFixed(1)} KB)`, percent: 50, level: 'success', }; // ── Step 3: "Upload" to local output (simulated) ────────────────────── yield { step: 'deploy', message: 'Deploying to MCPEngine…', percent: 60, level: 'info', }; log('Uploading worker to MCPEngine…'); const outputDir = path.join(OUTPUT_BASE, slug); await fs.mkdir(outputDir, { recursive: true }); // Write the worker script await fs.writeFile(path.join(outputDir, 'worker.js'), workerScript, 'utf-8'); // Write wrangler.toml (for future real deploys) const wranglerToml = ` # MCPEngine Studio — Auto-generated Wrangler config name = "${slug}" main = "worker.js" compatibility_date = "${new Date().toISOString().split('T')[0]}" compatibility_flags = ["nodejs_compat"] [vars] SERVER_NAME = "${slug}" DEPLOY_ID = "${deployId}" # TODO: Add KV/D1/R2 bindings as needed # [[kv_namespaces]] # binding = "CACHE" # id = "xxx" `.trimStart(); await fs.writeFile(path.join(outputDir, 'wrangler.toml'), wranglerToml, 'utf-8'); // Write deploy metadata const metadata = { deployId, projectId, slug, target: 'mcpengine' as const, toolCount: compiled.toolCount, toolNames: compiled.toolNames, sizeBytes: workerSize, createdAt: startedAt, url: `https://${slug}.mcpengine.run`, endpoint: `https://${slug}.mcpengine.run/mcp`, }; await fs.writeFile( path.join(outputDir, 'deploy-meta.json'), JSON.stringify(metadata, null, 2), 'utf-8', ); log(`Worker written to ${outputDir}`); yield { step: 'deploy', message: 'Worker deployed successfully', percent: 85, level: 'success', }; // ── Step 4: Verify ───────────────────────────────────────────────────── yield { step: 'verify', message: 'Verifying deployment…', percent: 90, level: 'info', }; // TODO: Hit the real health endpoint once we have actual CF Workers deploy // For now, simulate verification log('Health check: OK (simulated)'); log(`Server live at https://${slug}.mcpengine.run`); yield { step: 'verify', message: `Live at https://${slug}.mcpengine.run`, percent: 100, level: 'success', }; // ── Return result ────────────────────────────────────────────────────── const result: DeployResult = { id: deployId, target: 'mcpengine', status: 'live', url: `https://${slug}.mcpengine.run`, endpoint: `https://${slug}.mcpengine.run/mcp`, logs, createdAt: startedAt, }; return result; }