=== NEW === - studio/ — MCPEngine Studio scaffold (Next.js monorepo, build plan) - docs/FACTORY-V2.md — Factory v2 architecture doc - docs/CALENDLY_MCP_BUILD_SUMMARY.md — Calendly MCP build report === UPDATED SERVERS === - fieldedge: Added jobs-tools, UI build script, main entry update - lightspeed: Updated main + server entry points - squarespace: Added collection-browser + page-manager apps - toast: Added main + server entry points === INFRA === - infra/command-center/state.json — Updated pipeline state - infra/command-center/FACTORY-V2.md — Factory v2 operator playbook
119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
/**
|
|
* MCPEngine Studio — Server Compiler
|
|
*
|
|
* Takes a ServerBundle of generated TypeScript files and produces a single
|
|
* bundled JavaScript string ready for Cloudflare Workers deployment.
|
|
*
|
|
* NOTE: This is Phase-1 concatenation. Real esbuild bundling will be added later.
|
|
*/
|
|
|
|
import type { ServerBundle, GeneratedFile } from '@mcpengine/ai-pipeline/types';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Strip TypeScript type annotations in a best-effort way (imports, type-only
|
|
* exports, interface/type blocks). Good enough for simple generated code. */
|
|
function stripTypes(src: string): string {
|
|
return src
|
|
// Remove `import type …` lines
|
|
.replace(/^import\s+type\s+.*$/gm, '')
|
|
// Remove `export type …` and `export interface …` blocks (single-line + multi-line)
|
|
.replace(/^export\s+(type|interface)\s+\w+[^{]*\{[^}]*\}/gm, '')
|
|
.replace(/^export\s+(type|interface)\s+.*$/gm, '')
|
|
// Remove inline type annotations `: Foo` after parameter names (rough)
|
|
.replace(/:\s*[A-Z]\w+(\[\])?\s*(,|\)|\s*=>)/g, (_m, _arr, tail) => tail)
|
|
// Remove `as Type` casts
|
|
.replace(/\s+as\s+\w+/g, '')
|
|
// Collapse blank lines
|
|
.replace(/\n{3,}/g, '\n\n');
|
|
}
|
|
|
|
/** Convert TS import paths to inline references (they all live in the same
|
|
* concatenated scope so we just drop the import statements). */
|
|
function stripImports(src: string): string {
|
|
return src.replace(/^import\s+.*$/gm, '');
|
|
}
|
|
|
|
/** Wrap file content in an IIFE-scoped module to avoid collisions. */
|
|
function wrapModule(name: string, code: string): string {
|
|
return [
|
|
`// ── ${name} ${'─'.repeat(Math.max(0, 60 - name.length))}`,
|
|
`const __mod_${safeId(name)} = (() => {`,
|
|
` const exports = {};`,
|
|
` const module = { exports };`,
|
|
code,
|
|
` return module.exports;`,
|
|
`})();`,
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
function safeId(path: string): string {
|
|
return path.replace(/[^a-zA-Z0-9]/g, '_').replace(/_+/g, '_');
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main compiler
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function compileServer(bundle: ServerBundle): string {
|
|
const sections: string[] = [];
|
|
|
|
// 1. Banner
|
|
sections.push(
|
|
'// ═══════════════════════════════════════════════════════════',
|
|
'// MCPEngine Studio — Compiled MCP Server',
|
|
`// Generated at ${new Date().toISOString()}`,
|
|
`// Tools: ${bundle.toolCount}`,
|
|
'// ═══════════════════════════════════════════════════════════',
|
|
'',
|
|
);
|
|
|
|
// 2. Sort files: put the entry point last
|
|
const sorted = [...bundle.files].sort((a, b) => {
|
|
if (a.path === bundle.entryPoint) return 1;
|
|
if (b.path === bundle.entryPoint) return -1;
|
|
// Utility / shared modules first
|
|
if (a.path.includes('utils') || a.path.includes('shared')) return -1;
|
|
if (b.path.includes('utils') || b.path.includes('shared')) return 1;
|
|
return a.path.localeCompare(b.path);
|
|
});
|
|
|
|
// 3. Process each file
|
|
for (const file of sorted) {
|
|
if (file.language === 'json' || file.language === 'markdown') continue;
|
|
let code = file.content;
|
|
code = stripTypes(code);
|
|
code = stripImports(code);
|
|
sections.push(wrapModule(file.path, code));
|
|
}
|
|
|
|
// 4. Export a fetch handler that the Worker template will consume
|
|
sections.push(
|
|
'// ── Fetch handler export ──────────────────────────────────',
|
|
`const __entryModule = __mod_${safeId(bundle.entryPoint)};`,
|
|
'export const serverModule = __entryModule;',
|
|
'',
|
|
);
|
|
|
|
return sections.join('\n');
|
|
}
|
|
|
|
/** Compile and return metadata useful for deploy targets. */
|
|
export function compileWithMeta(bundle: ServerBundle) {
|
|
const compiledCode = compileServer(bundle);
|
|
const toolNames = bundle.files
|
|
.filter((f) => f.path.includes('tools/'))
|
|
.map((f) => f.path.replace(/^.*tools\//, '').replace(/\.ts$/, ''));
|
|
|
|
return {
|
|
code: compiledCode,
|
|
toolNames,
|
|
toolCount: bundle.toolCount,
|
|
fileCount: bundle.files.length,
|
|
sizeBytes: new TextEncoder().encode(compiledCode).byteLength,
|
|
};
|
|
}
|