frontmcp 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +5 -5
  2. package/src/commands/build/adapters/cloudflare.js +29 -1
  3. package/src/commands/build/adapters/cloudflare.js.map +1 -1
  4. package/src/commands/build/adapters/distributed.js +4 -1
  5. package/src/commands/build/adapters/distributed.js.map +1 -1
  6. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.d.ts +18 -0
  7. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js +58 -0
  8. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js.map +1 -0
  9. package/src/commands/build/exec/cli-runtime/generate-cli-entry.d.ts +2 -2
  10. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +165 -47
  11. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  12. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +7 -0
  13. package/src/commands/build/exec/cli-runtime/schema-extractor.js +17 -1
  14. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  15. package/src/commands/build/exec/index.d.ts +11 -0
  16. package/src/commands/build/exec/index.js +68 -20
  17. package/src/commands/build/exec/index.js.map +1 -1
  18. package/src/commands/build/exec/installer-script.d.ts +8 -2
  19. package/src/commands/build/exec/installer-script.js +34 -15
  20. package/src/commands/build/exec/installer-script.js.map +1 -1
  21. package/src/commands/build/exec/manifest.d.ts +16 -3
  22. package/src/commands/build/exec/manifest.js +17 -5
  23. package/src/commands/build/exec/manifest.js.map +1 -1
  24. package/src/commands/build/exec/runner-script.d.ts +9 -1
  25. package/src/commands/build/exec/runner-script.js +60 -2
  26. package/src/commands/build/exec/runner-script.js.map +1 -1
  27. package/src/commands/build/index.js +80 -18
  28. package/src/commands/build/index.js.map +1 -1
  29. package/src/commands/build/load-entry-config.d.ts +5 -0
  30. package/src/commands/build/load-entry-config.js +96 -0
  31. package/src/commands/build/load-entry-config.js.map +1 -0
  32. package/src/commands/build/mcpb/manifest.d.ts +14 -0
  33. package/src/commands/build/mcpb/manifest.js +29 -0
  34. package/src/commands/build/mcpb/manifest.js.map +1 -1
  35. package/src/commands/build/types.d.ts +19 -0
  36. package/src/commands/build/types.js.map +1 -1
  37. package/src/commands/dev/doctor.js +7 -3
  38. package/src/commands/dev/doctor.js.map +1 -1
  39. package/src/commands/package/install.d.ts +1 -1
  40. package/src/commands/package/install.js +10 -8
  41. package/src/commands/package/install.js.map +1 -1
  42. package/src/commands/package/types.d.ts +2 -1
  43. package/src/commands/package/types.js.map +1 -1
  44. package/src/config/frontmcp-config.loader.d.ts +20 -0
  45. package/src/config/frontmcp-config.loader.js +124 -5
  46. package/src/config/frontmcp-config.loader.js.map +1 -1
  47. package/src/config/index.d.ts +1 -1
  48. package/src/config/index.js +2 -1
  49. package/src/config/index.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"runner-script.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/runner-script.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAIH,oDA8FC;AA9FD,SAAgB,oBAAoB,CAAC,MAA0B,EAAE,OAAiB,EAAE,OAAiB;IACnG,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAEzB,0DAA0D;IAC1D,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC;QAC3D,MAAM,OAAO,GAAG,OAAO;YACrB,CAAC,CAAC,KAAK,IAAI,qCAAqC;YAChD,CAAC,CAAC,KAAK,IAAI,wCAAwC,CAAC;QAEtD,OAAO;;;EAGT,OAAO;yCACgC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;;;yBAGxC,MAAM;;;;;;;;;;;;;;;;;CAiB9B,CAAC;IACA,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC;IACrD,MAAM,YAAY,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,OAAO;QACpB,CAAC,CAAC,kBAAkB,IAAI,gBAAgB;QACxC,CAAC,CAAC,kBAAkB,IAAI,YAAY,CAAC;IAEvC,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,KAAK,IAAI,4BAA4B;QACvC,CAAC,CAAC,KAAK,IAAI,2BAA2B,CAAC;IAEzC,OAAO;;;EAGP,OAAO;yCACgC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;;;UAG5D,MAAM;;;;;0BAKU,WAAW;;;;;6BAKR,YAAY;yBAChB,WAAW;;;;;;;uCAOG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;;;;;;;;;;;;;;8CActB,IAAI;;;;;;CAMjD,CAAC;AACF,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * Bash runner script generation.\n * The runner checks Node.js, loads .env, and runs the bundle.\n */\n\nimport { FrontmcpExecConfig } from './config';\n\nexport function generateRunnerScript(config: FrontmcpExecConfig, cliMode?: boolean, seaMode?: boolean): string {\n const name = config.name;\n\n // SEA mode: binary is self-contained, no Node.js required\n if (seaMode) {\n const binary = cliMode ? `${name}-cli-bin` : `${name}-bin`;\n const comment = cliMode\n ? `# ${name} — FrontMCP CLI (single executable)`\n : `# ${name} — FrontMCP Server (single executable)`;\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n${comment}\n# Generated by frontmcp build --target ${cliMode ? 'cli' : 'node'}\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nBINARY=\"\\${SCRIPT_DIR}/${binary}\"\n\nif [ ! -f \"\\${BINARY}\" ]; then\n echo \"Error: Binary not found at \\${BINARY}\"\n exit 1\nfi\n\n# Load .env if present\nENV_FILE=\"\\${SCRIPT_DIR}/.env\"\nif [ -f \"\\${ENV_FILE}\" ]; then\n set -a\n # shellcheck disable=SC1090\n source \"\\${ENV_FILE}\"\n set +a\nfi\n\nexec \"\\${BINARY}\" \"$@\"\n`;\n }\n\n const nodeVersion = config.nodeVersion || '>=22.0.0';\n const minNodeMajor = extractMinMajor(nodeVersion);\n\n const bundle = cliMode\n ? `\\${SCRIPT_DIR}/${name}-cli.bundle.js`\n : `\\${SCRIPT_DIR}/${name}.bundle.js`;\n\n const comment = cliMode\n ? `# ${name} — FrontMCP CLI Executable`\n : `# ${name} — FrontMCP Server Runner`;\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n${comment}\n# Generated by frontmcp build --target ${cliMode ? 'cli --js' : 'node'}\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nBUNDLE=\"${bundle}\"\n\n# Check Node.js\nif ! command -v node &> /dev/null; then\n echo \"Error: Node.js is required but not installed.\"\n echo \"Install Node.js ${nodeVersion}: https://nodejs.org\"\n exit 1\nfi\n\nNODE_MAJOR=$(node -e \"console.log(process.versions.node.split('.')[0])\")\nif [ \"\\${NODE_MAJOR}\" -lt \"${minNodeMajor}\" ]; then\n echo \"Error: Node.js ${nodeVersion} required, found v$(node -v)\"\n exit 1\nfi\n\n# Check bundle exists\nif [ ! -f \"\\${BUNDLE}\" ]; then\n echo \"Error: Bundle not found at \\${BUNDLE}\"\n echo \"Run 'frontmcp build --target ${cliMode ? 'cli --js' : 'node'}' to create it.\"\n exit 1\nfi\n\n# Load .env if present\nENV_FILE=\"\\${SCRIPT_DIR}/.env\"\nif [ -f \"\\${ENV_FILE}\" ]; then\n set -a\n # shellcheck disable=SC1090\n source \"\\${ENV_FILE}\"\n set +a\nfi\n\n# Enable Node.js compile cache for faster startup on warm runs\nCOMPILE_CACHE_DIR=\"\\${HOME}/.cache/frontmcp/${name}\"\nmkdir -p \"\\${COMPILE_CACHE_DIR}\" 2>/dev/null || true\nexport NODE_COMPILE_CACHE=\"\\${COMPILE_CACHE_DIR}\"\n\n# Run\nexec node \"\\${BUNDLE}\" \"$@\"\n`;\n}\n\nfunction extractMinMajor(version: string): number {\n const match = version.match(/(\\d+)/);\n return match ? parseInt(match[1], 10) : 22;\n}\n"]}
1
+ {"version":3,"file":"runner-script.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/runner-script.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAWH,oDAGC;AAED,oDA6IC;AAzJD;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAAC,KAAa;IAChD,0EAA0E;IAC1E,OAAO,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,SAAgB,oBAAoB,CAAC,MAA0B,EAAE,OAAiB,EAAE,OAAiB;IACnG,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;IAEhE,yEAAyE;IACzE,6EAA6E;IAC7E,wEAAwE;IACxE,wEAAwE;IACxE,wBAAwB;IACxB,MAAM,eAAe,GAAG,CAAC,OAAO;QAC9B,CAAC,CAAC;;;;;EAKJ,IAAI,KAAK,OAAO;;;;;IAKd,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;;;;;;;;;;YAUI,IAAI,IAAI,OAAO;;;;0BAID,IAAI;;;;;;;;;;CAU7B;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,0DAA0D;IAC1D,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC;QAC3D,MAAM,OAAO,GAAG,OAAO;YACrB,CAAC,CAAC,KAAK,IAAI,qCAAqC;YAChD,CAAC,CAAC,KAAK,IAAI,wCAAwC,CAAC;QAEtD,OAAO;;;EAGT,OAAO;yCACgC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;;;yBAGxC,MAAM;;;;;;EAM7B,eAAe;;;;;;;;;;;CAWhB,CAAC;IACA,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC;IACrD,MAAM,YAAY,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,OAAO;QACpB,CAAC,CAAC,kBAAkB,IAAI,gBAAgB;QACxC,CAAC,CAAC,kBAAkB,IAAI,YAAY,CAAC;IAEvC,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,KAAK,IAAI,4BAA4B;QACvC,CAAC,CAAC,KAAK,IAAI,2BAA2B,CAAC;IAEzC,OAAO;;;EAGP,OAAO;yCACgC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;;;UAG5D,MAAM;EACd,eAAe;;;;0BAIS,WAAW;;;;;6BAKR,YAAY;yBAChB,WAAW;;;;;;;uCAOG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;;;;;;;;;;;;;;8CActB,IAAI;;;;;;CAMjD,CAAC;AACF,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * Bash runner script generation.\n * The runner checks Node.js, loads .env, and runs the bundle.\n */\n\nimport { type FrontmcpExecConfig } from './config';\n\n/**\n * Defense-in-depth: scrub anything outside `[a-zA-Z0-9._+-]` from values that\n * the runner / installer scripts interpolate into bash. The user owns\n * `frontmcp.config`, so a malicious value would be self-inflicted, but the\n * generated scripts are committed into repos and downloaded by end-users —\n * so we keep them safe to copy/paste regardless of upstream config hygiene.\n */\nexport function sanitizeShellLiteral(value: string): string {\n // `-` placed last in the character class is literal, so no escape needed.\n return value.replace(/[^A-Za-z0-9._+-]/g, '_');\n}\n\nexport function generateRunnerScript(config: FrontmcpExecConfig, cliMode?: boolean, seaMode?: boolean): string {\n const name = config.name;\n const version = sanitizeShellLiteral(config.version || '0.0.0');\n\n // #377 — `--target node` runner used to silently exec the bundle for any\n // flag, so `./frontegg-bin --help` quietly booted the HTTP server. Intercept\n // help/version here so server-mode runners behave like a normal CLI for\n // those flags. CLI-mode runners pass everything through to the bundle's\n // own commander parser.\n const helpInterceptor = !cliMode\n ? `\n# Intercept standard CLI flags before booting the long-running server.\ncase \"\\${1:-}\" in\n -h|--help)\n cat <<EOF\n${name} v${version} — FrontMCP server\n\nThis binary starts a long-running MCP HTTP server.\n\nUsage:\n ${name} Start the server\n ${name} --help Show this help\n ${name} --version Show version\n ${name} --print-manifest Print the deployment manifest as JSON\n\nConfigure via environment variables, .env, or frontmcp.config.\n\nFor a CLI-style binary that exposes tools/resources/prompts as subcommands,\nbuild with: frontmcp build --target cli\nEOF\n exit 0\n ;;\n --version)\n echo \"${name} ${version}\"\n exit 0\n ;;\n --print-manifest)\n cat \"\\${SCRIPT_DIR}/${name}.manifest.json\"\n exit 0\n ;;\n --*)\n echo \"Error: unsupported flag '\\${1}' on the server runner.\"\n echo \"This binary is a long-running HTTP server; flag-style invocation is reserved.\" >&2\n echo \"Run with no args to start, or build with --target cli for a CLI binary.\" >&2\n exit 2\n ;;\nesac\n`\n : '';\n\n // SEA mode: binary is self-contained, no Node.js required\n if (seaMode) {\n const binary = cliMode ? `${name}-cli-bin` : `${name}-bin`;\n const comment = cliMode\n ? `# ${name} — FrontMCP CLI (single executable)`\n : `# ${name} — FrontMCP Server (single executable)`;\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n${comment}\n# Generated by frontmcp build --target ${cliMode ? 'cli' : 'node'}\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nBINARY=\"\\${SCRIPT_DIR}/${binary}\"\n\nif [ ! -f \"\\${BINARY}\" ]; then\n echo \"Error: Binary not found at \\${BINARY}\"\n exit 1\nfi\n${helpInterceptor}\n# Load .env if present\nENV_FILE=\"\\${SCRIPT_DIR}/.env\"\nif [ -f \"\\${ENV_FILE}\" ]; then\n set -a\n # shellcheck disable=SC1090\n source \"\\${ENV_FILE}\"\n set +a\nfi\n\nexec \"\\${BINARY}\" \"$@\"\n`;\n }\n\n const nodeVersion = config.nodeVersion || '>=22.0.0';\n const minNodeMajor = extractMinMajor(nodeVersion);\n\n const bundle = cliMode\n ? `\\${SCRIPT_DIR}/${name}-cli.bundle.js`\n : `\\${SCRIPT_DIR}/${name}.bundle.js`;\n\n const comment = cliMode\n ? `# ${name} — FrontMCP CLI Executable`\n : `# ${name} — FrontMCP Server Runner`;\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n${comment}\n# Generated by frontmcp build --target ${cliMode ? 'cli --js' : 'node'}\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nBUNDLE=\"${bundle}\"\n${helpInterceptor}\n# Check Node.js\nif ! command -v node &> /dev/null; then\n echo \"Error: Node.js is required but not installed.\"\n echo \"Install Node.js ${nodeVersion}: https://nodejs.org\"\n exit 1\nfi\n\nNODE_MAJOR=$(node -e \"console.log(process.versions.node.split('.')[0])\")\nif [ \"\\${NODE_MAJOR}\" -lt \"${minNodeMajor}\" ]; then\n echo \"Error: Node.js ${nodeVersion} required, found v$(node -v)\"\n exit 1\nfi\n\n# Check bundle exists\nif [ ! -f \"\\${BUNDLE}\" ]; then\n echo \"Error: Bundle not found at \\${BUNDLE}\"\n echo \"Run 'frontmcp build --target ${cliMode ? 'cli --js' : 'node'}' to create it.\"\n exit 1\nfi\n\n# Load .env if present\nENV_FILE=\"\\${SCRIPT_DIR}/.env\"\nif [ -f \"\\${ENV_FILE}\" ]; then\n set -a\n # shellcheck disable=SC1090\n source \"\\${ENV_FILE}\"\n set +a\nfi\n\n# Enable Node.js compile cache for faster startup on warm runs\nCOMPILE_CACHE_DIR=\"\\${HOME}/.cache/frontmcp/${name}\"\nmkdir -p \"\\${COMPILE_CACHE_DIR}\" 2>/dev/null || true\nexport NODE_COMPILE_CACHE=\"\\${COMPILE_CACHE_DIR}\"\n\n# Run\nexec node \"\\${BUNDLE}\" \"$@\"\n`;\n}\n\nfunction extractMinMajor(version: string): number {\n const match = version.match(/(\\d+)/);\n return match ? parseInt(match[1], 10) : 22;\n}\n"]}
@@ -34,6 +34,15 @@ async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
34
34
  const entryPath = path.join(outDir, 'index.js');
35
35
  await fs_1.fsp.writeFile(entryPath, entryContent, 'utf8');
36
36
  console.log((0, colors_1.c)('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));
37
+ // ESM adapters (vercel, lambda) emit `import` syntax in the entry. The
38
+ // user's project may be `"type": "commonjs"`, in which case Node and
39
+ // rspack treat the .js file as CJS and parsing fails on `import`.
40
+ // Drop a sibling package.json with `"type": "module"` so the dist
41
+ // directory is always interpreted correctly regardless of the parent.
42
+ if (template.moduleFormat === 'esnext') {
43
+ const pkgPath = path.join(outDir, 'package.json');
44
+ await fs_1.fsp.writeFile(pkgPath, JSON.stringify({ type: 'module' }, null, 2), 'utf8');
45
+ }
37
46
  }
38
47
  // Bundle if adapter requires it (creates single CJS file for serverless)
39
48
  if (template.shouldBundle && template.bundleOutput) {
@@ -48,24 +57,34 @@ async function generateAdapterFiles(adapter, outDir, entryBasename, cwd) {
48
57
  console.log((0, colors_1.c)('green', ` Created deployment output structure`));
49
58
  }
50
59
  }
51
- // Generate config file if adapter has one (skip if already exists)
60
+ // Generate config file if adapter has one. By default we preserve an
61
+ // existing user-edited file. Adapters that mark `alwaysWriteConfig` (e.g.,
62
+ // cloudflare's wrangler.toml) overwrite it on every build so the
63
+ // generated `main = ...` path always matches the actual build output —
64
+ // see #374.
52
65
  if (template.getConfig && template.configFileName) {
53
66
  const configPath = path.join(cwd, template.configFileName);
54
- if (await (0, utils_1.fileExists)(configPath)) {
55
- console.log((0, colors_1.c)('yellow', ` ${template.configFileName} already exists (skipping)`));
56
- }
57
- else {
58
- const configContent = template.getConfig(cwd);
67
+ const exists = await (0, utils_1.fileExists)(configPath);
68
+ const configContent = template.getConfig(cwd);
69
+ const writeIt = async () => {
59
70
  if (typeof configContent === 'string') {
60
- // Write as plain text (e.g., TOML for wrangler.toml)
61
71
  await fs_1.fsp.writeFile(configPath, configContent, 'utf8');
62
72
  }
63
73
  else {
64
- // Write as JSON
65
74
  await (0, utils_1.writeJSON)(configPath, configContent);
66
75
  }
76
+ };
77
+ if (!exists) {
78
+ await writeIt();
67
79
  console.log((0, colors_1.c)('green', ` Generated ${template.configFileName}`));
68
80
  }
81
+ else if (template.alwaysWriteConfig) {
82
+ await writeIt();
83
+ console.log((0, colors_1.c)('green', ` Updated ${template.configFileName} (build output reference)`));
84
+ }
85
+ else {
86
+ console.log((0, colors_1.c)('yellow', ` ${template.configFileName} already exists (skipping)`));
87
+ }
69
88
  }
70
89
  }
71
90
  /** Map target names to internal adapter names. */
@@ -92,14 +111,17 @@ const TARGET_TO_ADAPTER = {
92
111
  */
93
112
  async function runBuild(opts) {
94
113
  const cwd = process.cwd();
95
- // Try loading frontmcp.config for multi-target support
96
- let config;
97
- try {
98
- config = await (0, config_1.loadFrontMcpConfig)(cwd);
99
- }
100
- catch {
101
- // No config file — fall back to CLI flags only
102
- }
114
+ // Try loading frontmcp.config for multi-target support.
115
+ //
116
+ // #365 — `tryLoadFrontMcpConfig` differentiates between recoverable cases:
117
+ // - no config file present → returns undefined
118
+ // - config exists but doesn't match the new schema → returns undefined
119
+ // (legacy top-level `cli`/`sea`/`esbuild` shape — picked up later by
120
+ // the exec-build's own `loadExecConfig`)
121
+ // …and hard failures:
122
+ // - file exists but can't be parsed (TS syntax error, broken require)
123
+ // → the error propagates, no silent-default regression.
124
+ const config = await (0, config_1.tryLoadFrontMcpConfig)(cwd);
103
125
  // If no -t flag and config has deployments, build all targets from config
104
126
  if (!opts.buildTarget && config && config.deployments.length > 0) {
105
127
  const targets = (0, config_1.getDeploymentTargets)(config);
@@ -128,14 +150,44 @@ async function buildSingleTarget(target, opts, config) {
128
150
  // Merge entry from config if not provided via CLI
129
151
  const entry = opts.entry || config?.entry;
130
152
  const targetOpts = { ...opts, outDir: targetOutDir, entry };
153
+ // #370: forward `build.storage` and per-deployment `cli.outputDefault` from
154
+ // the FrontMcp config into the exec build so the manifest reflects them.
155
+ // The exec build has its own loader (`loadExecConfig`) that doesn't see
156
+ // the deployment-level shape; passing these via opts merges them in
157
+ // `normalizeConfig` before the manifest is generated.
158
+ //
159
+ // Only the cli deployment shape carries a `cli` block; map it down to the
160
+ // narrow exec-config shape (outputDefault / description / authRequired) so
161
+ // the result is a clean object, never `false`.
162
+ const cliDeploymentConfig = deployment?.target === 'cli' ? deployment.cli : undefined;
163
+ const execOverrides = {
164
+ storage: config?.build?.storage,
165
+ cli: cliDeploymentConfig
166
+ ? {
167
+ ...(cliDeploymentConfig.outputDefault ? { outputDefault: cliDeploymentConfig.outputDefault } : {}),
168
+ ...(cliDeploymentConfig.description ? { description: cliDeploymentConfig.description } : {}),
169
+ ...(typeof cliDeploymentConfig.authRequired === 'boolean'
170
+ ? { authRequired: cliDeploymentConfig.authRequired }
171
+ : {}),
172
+ }
173
+ : undefined,
174
+ };
131
175
  switch (target) {
132
176
  case 'cli': {
133
177
  const { buildExec } = await import('./exec/index.js');
134
- return buildExec({ ...targetOpts, cli: true, sea: !opts.js });
178
+ return buildExec({
179
+ ...targetOpts,
180
+ cli: true,
181
+ sea: !opts.js,
182
+ execOverrides,
183
+ });
135
184
  }
136
185
  case 'node': {
137
186
  const { buildExec } = await import('./exec/index.js');
138
- return buildExec(targetOpts);
187
+ return buildExec({
188
+ ...targetOpts,
189
+ execOverrides,
190
+ });
139
191
  }
140
192
  case 'sdk': {
141
193
  const { buildSdk } = await import('./sdk/index.js');
@@ -176,6 +228,16 @@ async function runAdapterBuild(opts, adapter) {
176
228
  if (adapter === 'cloudflare') {
177
229
  console.log((0, colors_1.c)('yellow', 'Cloudflare Workers adapter is experimental. See docs for limitations.'));
178
230
  }
231
+ // #375 — adapter-level pre-validation: read the entry's @FrontMcp() config
232
+ // metadata and let the adapter reject incompatible features
233
+ // (sqlite/redis on Workers, etc.) before emitting an unrunnable bundle.
234
+ // `loadEntryDecoratorConfig` handles both compiled-JS and raw-TS entries
235
+ // (TS goes through esbuild + Module._compile so we don't need a tsc pass).
236
+ if (template.validate) {
237
+ const { loadEntryDecoratorConfig } = await import('./load-entry-config.js');
238
+ const decoratorConfig = await loadEntryDecoratorConfig(entry);
239
+ template.validate(decoratorConfig);
240
+ }
179
241
  const moduleFormat = template.moduleFormat;
180
242
  console.log(`${(0, colors_1.c)('cyan', '[build]')} entry: ${path.relative(cwd, entry)}`);
181
243
  console.log(`${(0, colors_1.c)('cyan', '[build]')} outDir: ${path.relative(cwd, outDir)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/build/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { type ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { ensureDir, fileExists, runCmd, writeJSON } from '@frontmcp/utils';\nimport { fsp, resolveEntry } from '../../shared/fs';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../core/tsconfig';\nimport { ADAPTERS } from './adapters';\nimport { type AdapterName } from './types';\nimport { bundleForServerless } from './bundler';\nimport { findDeployment, type FrontMcpConfigParsed, getDeploymentTargets, loadFrontMcpConfig } from '../../config';\n\nfunction isTsLike(p: string): boolean {\n return /\\.tsx?$/i.test(p);\n}\n\n/**\n * Generate adapter-specific entry point and config files.\n */\nasync function generateAdapterFiles(\n adapter: AdapterName,\n outDir: string,\n entryBasename: string,\n cwd: string,\n): Promise<void> {\n const template = ADAPTERS[adapter];\n\n // Generate serverless setup file first (if adapter has one)\n // This file sets FRONTMCP_SERVERLESS=1 before any imports run\n if (template.getSetupTemplate) {\n const setupContent = template.getSetupTemplate();\n const setupPath = path.join(outDir, 'serverless-setup.js');\n await fsp.writeFile(setupPath, setupContent, 'utf8');\n console.log(c('green', ` Generated serverless setup at ${path.relative(cwd, setupPath)}`));\n }\n\n // Generate index.js entry point\n const mainModuleName = entryBasename.replace(/\\.tsx?$/, '.js');\n const entryContent = template.getEntryTemplate(`./${mainModuleName}`);\n\n // Skip if no entry template (e.g., node adapter)\n if (entryContent) {\n const entryPath = path.join(outDir, 'index.js');\n await fsp.writeFile(entryPath, entryContent, 'utf8');\n console.log(c('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));\n }\n\n // Bundle if adapter requires it (creates single CJS file for serverless)\n if (template.shouldBundle && template.bundleOutput) {\n console.log(c('cyan', `[build] Bundling for ${adapter}...`));\n const entryPath = path.join(outDir, 'index.js');\n await bundleForServerless(entryPath, outDir, template.bundleOutput);\n console.log(c('green', ` Created bundle: ${template.bundleOutput}`));\n\n // Run post-bundle hook if defined (e.g., create Build Output API structure)\n if (template.postBundle) {\n console.log(c('cyan', `[build] Creating ${adapter} deployment structure...`));\n await template.postBundle(outDir, cwd, template.bundleOutput);\n console.log(c('green', ` Created deployment output structure`));\n }\n }\n\n // Generate config file if adapter has one (skip if already exists)\n if (template.getConfig && template.configFileName) {\n const configPath = path.join(cwd, template.configFileName);\n\n if (await fileExists(configPath)) {\n console.log(c('yellow', ` ${template.configFileName} already exists (skipping)`));\n } else {\n const configContent = template.getConfig(cwd);\n\n if (typeof configContent === 'string') {\n // Write as plain text (e.g., TOML for wrangler.toml)\n await fsp.writeFile(configPath, configContent, 'utf8');\n } else {\n // Write as JSON\n await writeJSON(configPath, configContent);\n }\n console.log(c('green', ` Generated ${template.configFileName}`));\n }\n }\n}\n\n/** Map target names to internal adapter names. */\nconst TARGET_TO_ADAPTER: Record<string, AdapterName> = {\n 'vercel': 'vercel',\n 'lambda': 'lambda',\n 'cloudflare': 'cloudflare',\n 'distributed': 'distributed',\n};\n\n/**\n * Build the FrontMCP server for a specific deployment target.\n *\n * @example\n * ```bash\n * frontmcp build --target node # Node.js server bundle\n * frontmcp build --target cli # CLI with SEA binary\n * frontmcp build --target cli --js # CLI without SEA\n * frontmcp build --target sdk # Library (CJS+ESM+types)\n * frontmcp build --target browser # Browser ESM bundle\n * frontmcp build --target vercel # Vercel serverless\n * frontmcp build --target lambda # AWS Lambda\n * frontmcp build --target cloudflare # Cloudflare Workers\n * ```\n */\nexport async function runBuild(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n\n // Try loading frontmcp.config for multi-target support\n let config: FrontMcpConfigParsed | undefined;\n try {\n config = await loadFrontMcpConfig(cwd);\n } catch {\n // No config file — fall back to CLI flags only\n }\n\n // If no -t flag and config has deployments, build all targets from config\n if (!opts.buildTarget && config && config.deployments.length > 0) {\n const targets = getDeploymentTargets(config);\n console.log(c('cyan', `[build] Building ${targets.length} target(s) from frontmcp.config: ${targets.join(', ')}`));\n\n for (const targetName of targets) {\n console.log(c('cyan', `\\n[build] ═══ ${targetName} ═══`));\n await buildSingleTarget(targetName, opts, config);\n }\n return;\n }\n\n // Single target build (from -t flag or default 'node')\n const target = opts.buildTarget ?? 'node';\n await buildSingleTarget(target, opts, config);\n}\n\n/**\n * Build a single deployment target.\n * Merges per-target config (from frontmcp.config) with CLI opts.\n */\nasync function buildSingleTarget(\n target: string,\n opts: ParsedArgs,\n config?: FrontMcpConfigParsed,\n): Promise<void> {\n const deployment = config ? findDeployment(config, target) : undefined;\n\n // Resolve output directory: deployment.outDir > CLI --out-dir > dist/{target}\n const baseOutDir = path.resolve(process.cwd(), opts.outDir || 'dist');\n const targetOutDir = deployment?.outDir\n ? path.resolve(process.cwd(), deployment.outDir)\n : path.join(baseOutDir, target);\n\n // Merge entry from config if not provided via CLI\n const entry = opts.entry || config?.entry;\n const targetOpts = { ...opts, outDir: targetOutDir, entry };\n\n switch (target) {\n case 'cli': {\n const { buildExec } = await import('./exec/index.js');\n return buildExec({ ...targetOpts, cli: true, sea: !opts.js } as ParsedArgs & { cli: boolean; sea: boolean });\n }\n case 'node': {\n const { buildExec } = await import('./exec/index.js');\n return buildExec(targetOpts);\n }\n case 'sdk': {\n const { buildSdk } = await import('./sdk/index.js');\n return buildSdk(targetOpts);\n }\n case 'browser': {\n const { buildBrowser } = await import('./browser/index.js');\n return buildBrowser(targetOpts);\n }\n case 'mcpb': {\n const { buildMcpb } = await import('./mcpb/index.js');\n return buildMcpb(targetOpts, config);\n }\n case 'vercel':\n case 'lambda':\n case 'cloudflare':\n case 'distributed': {\n const adapter = TARGET_TO_ADAPTER[target];\n return runAdapterBuild(targetOpts, adapter);\n }\n default:\n throw new Error(`Unknown build target: ${target}. Available: cli, node, sdk, browser, cloudflare, vercel, lambda, distributed, mcpb`);\n }\n}\n\n/**\n * Build using a deployment adapter (serverless platforms).\n */\nasync function runAdapterBuild(opts: ParsedArgs, adapter: AdapterName): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n await ensureDir(outDir);\n\n const template = ADAPTERS[adapter];\n if (!template) {\n const available = Object.keys(ADAPTERS).join(', ');\n throw new Error(`Unknown adapter: ${adapter}. Available: ${available}`);\n }\n\n if (adapter === 'cloudflare') {\n console.log(\n c('yellow', 'Cloudflare Workers adapter is experimental. See docs for limitations.'),\n );\n }\n\n const moduleFormat = template.moduleFormat;\n\n console.log(`${c('cyan', '[build]')} entry: ${path.relative(cwd, entry)}`);\n console.log(`${c('cyan', '[build]')} outDir: ${path.relative(cwd, outDir)}`);\n console.log(`${c('cyan', '[build]')} target: ${adapter} (${moduleFormat})`);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const args: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n console.log(c('gray', `[build] tsconfig.json detected — compiling with project settings`));\n args.push('--project', tsconfigPath);\n } else {\n args.push(entry);\n args.push('--rootDir', path.dirname(entry));\n if (!isTsLike(entry)) {\n args.push('--allowJs');\n console.log(c('yellow', '[build] Entry is not TypeScript; enabling --allowJs'));\n }\n args.push('--experimentalDecorators', '--emitDecoratorMetadata');\n args.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n args.push('--module', moduleFormat);\n args.push('--outDir', outDir);\n args.push('--skipLibCheck');\n\n await runCmd('npx', args);\n\n if (adapter !== 'node') {\n console.log(c('cyan', `[build] Generating ${adapter} deployment files...`));\n const entryBasename = path.basename(entry);\n await generateAdapterFiles(adapter, outDir, entryBasename, cwd);\n }\n\n console.log(c('green', 'Build completed.'));\n console.log(c('gray', `Output placed in ${path.relative(cwd, outDir)}`));\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/build/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { type ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { ensureDir, fileExists, runCmd, writeJSON } from '@frontmcp/utils';\nimport { fsp, resolveEntry } from '../../shared/fs';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../core/tsconfig';\nimport { ADAPTERS } from './adapters';\nimport { type AdapterName } from './types';\nimport { bundleForServerless } from './bundler';\nimport {\n type DeploymentTarget,\n findDeployment,\n type FrontMcpConfigParsed,\n getDeploymentTargets,\n tryLoadFrontMcpConfig,\n} from '../../config';\n\nfunction isTsLike(p: string): boolean {\n return /\\.tsx?$/i.test(p);\n}\n\n/**\n * Generate adapter-specific entry point and config files.\n */\nasync function generateAdapterFiles(\n adapter: AdapterName,\n outDir: string,\n entryBasename: string,\n cwd: string,\n): Promise<void> {\n const template = ADAPTERS[adapter];\n\n // Generate serverless setup file first (if adapter has one)\n // This file sets FRONTMCP_SERVERLESS=1 before any imports run\n if (template.getSetupTemplate) {\n const setupContent = template.getSetupTemplate();\n const setupPath = path.join(outDir, 'serverless-setup.js');\n await fsp.writeFile(setupPath, setupContent, 'utf8');\n console.log(c('green', ` Generated serverless setup at ${path.relative(cwd, setupPath)}`));\n }\n\n // Generate index.js entry point\n const mainModuleName = entryBasename.replace(/\\.tsx?$/, '.js');\n const entryContent = template.getEntryTemplate(`./${mainModuleName}`);\n\n // Skip if no entry template (e.g., node adapter)\n if (entryContent) {\n const entryPath = path.join(outDir, 'index.js');\n await fsp.writeFile(entryPath, entryContent, 'utf8');\n console.log(c('green', ` Generated ${adapter} entry at ${path.relative(cwd, entryPath)}`));\n\n // ESM adapters (vercel, lambda) emit `import` syntax in the entry. The\n // user's project may be `\"type\": \"commonjs\"`, in which case Node and\n // rspack treat the .js file as CJS and parsing fails on `import`.\n // Drop a sibling package.json with `\"type\": \"module\"` so the dist\n // directory is always interpreted correctly regardless of the parent.\n if (template.moduleFormat === 'esnext') {\n const pkgPath = path.join(outDir, 'package.json');\n await fsp.writeFile(pkgPath, JSON.stringify({ type: 'module' }, null, 2), 'utf8');\n }\n }\n\n // Bundle if adapter requires it (creates single CJS file for serverless)\n if (template.shouldBundle && template.bundleOutput) {\n console.log(c('cyan', `[build] Bundling for ${adapter}...`));\n const entryPath = path.join(outDir, 'index.js');\n await bundleForServerless(entryPath, outDir, template.bundleOutput);\n console.log(c('green', ` Created bundle: ${template.bundleOutput}`));\n\n // Run post-bundle hook if defined (e.g., create Build Output API structure)\n if (template.postBundle) {\n console.log(c('cyan', `[build] Creating ${adapter} deployment structure...`));\n await template.postBundle(outDir, cwd, template.bundleOutput);\n console.log(c('green', ` Created deployment output structure`));\n }\n }\n\n // Generate config file if adapter has one. By default we preserve an\n // existing user-edited file. Adapters that mark `alwaysWriteConfig` (e.g.,\n // cloudflare's wrangler.toml) overwrite it on every build so the\n // generated `main = ...` path always matches the actual build output —\n // see #374.\n if (template.getConfig && template.configFileName) {\n const configPath = path.join(cwd, template.configFileName);\n const exists = await fileExists(configPath);\n\n const configContent = template.getConfig(cwd);\n const writeIt = async (): Promise<void> => {\n if (typeof configContent === 'string') {\n await fsp.writeFile(configPath, configContent, 'utf8');\n } else {\n await writeJSON(configPath, configContent);\n }\n };\n\n if (!exists) {\n await writeIt();\n console.log(c('green', ` Generated ${template.configFileName}`));\n } else if (template.alwaysWriteConfig) {\n await writeIt();\n console.log(c('green', ` Updated ${template.configFileName} (build output reference)`));\n } else {\n console.log(c('yellow', ` ${template.configFileName} already exists (skipping)`));\n }\n }\n}\n\n/** Map target names to internal adapter names. */\nconst TARGET_TO_ADAPTER: Record<string, AdapterName> = {\n 'vercel': 'vercel',\n 'lambda': 'lambda',\n 'cloudflare': 'cloudflare',\n 'distributed': 'distributed',\n};\n\n/**\n * Build the FrontMCP server for a specific deployment target.\n *\n * @example\n * ```bash\n * frontmcp build --target node # Node.js server bundle\n * frontmcp build --target cli # CLI with SEA binary\n * frontmcp build --target cli --js # CLI without SEA\n * frontmcp build --target sdk # Library (CJS+ESM+types)\n * frontmcp build --target browser # Browser ESM bundle\n * frontmcp build --target vercel # Vercel serverless\n * frontmcp build --target lambda # AWS Lambda\n * frontmcp build --target cloudflare # Cloudflare Workers\n * ```\n */\nexport async function runBuild(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n\n // Try loading frontmcp.config for multi-target support.\n //\n // #365 — `tryLoadFrontMcpConfig` differentiates between recoverable cases:\n // - no config file present → returns undefined\n // - config exists but doesn't match the new schema → returns undefined\n // (legacy top-level `cli`/`sea`/`esbuild` shape — picked up later by\n // the exec-build's own `loadExecConfig`)\n // …and hard failures:\n // - file exists but can't be parsed (TS syntax error, broken require)\n // → the error propagates, no silent-default regression.\n const config: FrontMcpConfigParsed | undefined = await tryLoadFrontMcpConfig(cwd);\n\n // If no -t flag and config has deployments, build all targets from config\n if (!opts.buildTarget && config && config.deployments.length > 0) {\n const targets = getDeploymentTargets(config);\n console.log(c('cyan', `[build] Building ${targets.length} target(s) from frontmcp.config: ${targets.join(', ')}`));\n\n for (const targetName of targets) {\n console.log(c('cyan', `\\n[build] ═══ ${targetName} ═══`));\n await buildSingleTarget(targetName, opts, config);\n }\n return;\n }\n\n // Single target build (from -t flag or default 'node')\n const target = opts.buildTarget ?? 'node';\n await buildSingleTarget(target, opts, config);\n}\n\n/**\n * Build a single deployment target.\n * Merges per-target config (from frontmcp.config) with CLI opts.\n */\nasync function buildSingleTarget(\n target: string,\n opts: ParsedArgs,\n config?: FrontMcpConfigParsed,\n): Promise<void> {\n const deployment:DeploymentTarget | undefined = config ? findDeployment(config, target) : undefined;\n\n // Resolve output directory: deployment.outDir > CLI --out-dir > dist/{target}\n const baseOutDir = path.resolve(process.cwd(), opts.outDir || 'dist');\n const targetOutDir = deployment?.outDir\n ? path.resolve(process.cwd(), deployment.outDir)\n : path.join(baseOutDir, target);\n\n // Merge entry from config if not provided via CLI\n const entry = opts.entry || config?.entry;\n const targetOpts = { ...opts, outDir: targetOutDir, entry };\n\n // #370: forward `build.storage` and per-deployment `cli.outputDefault` from\n // the FrontMcp config into the exec build so the manifest reflects them.\n // The exec build has its own loader (`loadExecConfig`) that doesn't see\n // the deployment-level shape; passing these via opts merges them in\n // `normalizeConfig` before the manifest is generated.\n //\n // Only the cli deployment shape carries a `cli` block; map it down to the\n // narrow exec-config shape (outputDefault / description / authRequired) so\n // the result is a clean object, never `false`.\n const cliDeploymentConfig = deployment?.target === 'cli' ? deployment.cli : undefined;\n const execOverrides: {\n storage?: { type: 'sqlite' | 'redis' | 'none'; required?: boolean };\n cli?: { outputDefault?: 'text' | 'json'; description?: string; authRequired?: boolean };\n } = {\n storage: config?.build?.storage,\n cli: cliDeploymentConfig\n ? {\n ...(cliDeploymentConfig.outputDefault ? { outputDefault: cliDeploymentConfig.outputDefault } : {}),\n ...(cliDeploymentConfig.description ? { description: cliDeploymentConfig.description } : {}),\n ...(typeof cliDeploymentConfig.authRequired === 'boolean'\n ? { authRequired: cliDeploymentConfig.authRequired }\n : {}),\n }\n : undefined,\n };\n\n switch (target) {\n case 'cli': {\n const { buildExec } = await import('./exec/index.js');\n return buildExec({\n ...targetOpts,\n cli: true,\n sea: !opts.js,\n execOverrides,\n } as ParsedArgs & { cli: boolean; sea: boolean; execOverrides?: typeof execOverrides });\n }\n case 'node': {\n const { buildExec } = await import('./exec/index.js');\n return buildExec({\n ...targetOpts,\n execOverrides,\n } as ParsedArgs & { execOverrides?: typeof execOverrides });\n }\n case 'sdk': {\n const { buildSdk } = await import('./sdk/index.js');\n return buildSdk(targetOpts);\n }\n case 'browser': {\n const { buildBrowser } = await import('./browser/index.js');\n return buildBrowser(targetOpts);\n }\n case 'mcpb': {\n const { buildMcpb } = await import('./mcpb/index.js');\n return buildMcpb(targetOpts, config);\n }\n case 'vercel':\n case 'lambda':\n case 'cloudflare':\n case 'distributed': {\n const adapter = TARGET_TO_ADAPTER[target];\n return runAdapterBuild(targetOpts, adapter);\n }\n default:\n throw new Error(`Unknown build target: ${target}. Available: cli, node, sdk, browser, cloudflare, vercel, lambda, distributed, mcpb`);\n }\n}\n\n/**\n * Build using a deployment adapter (serverless platforms).\n */\nasync function runAdapterBuild(opts: ParsedArgs, adapter: AdapterName): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n await ensureDir(outDir);\n\n const template = ADAPTERS[adapter];\n if (!template) {\n const available = Object.keys(ADAPTERS).join(', ');\n throw new Error(`Unknown adapter: ${adapter}. Available: ${available}`);\n }\n\n if (adapter === 'cloudflare') {\n console.log(\n c('yellow', 'Cloudflare Workers adapter is experimental. See docs for limitations.'),\n );\n }\n\n // #375 — adapter-level pre-validation: read the entry's @FrontMcp() config\n // metadata and let the adapter reject incompatible features\n // (sqlite/redis on Workers, etc.) before emitting an unrunnable bundle.\n // `loadEntryDecoratorConfig` handles both compiled-JS and raw-TS entries\n // (TS goes through esbuild + Module._compile so we don't need a tsc pass).\n if (template.validate) {\n const { loadEntryDecoratorConfig } = await import('./load-entry-config.js');\n const decoratorConfig = await loadEntryDecoratorConfig(entry);\n template.validate(decoratorConfig);\n }\n\n const moduleFormat = template.moduleFormat;\n\n console.log(`${c('cyan', '[build]')} entry: ${path.relative(cwd, entry)}`);\n console.log(`${c('cyan', '[build]')} outDir: ${path.relative(cwd, outDir)}`);\n console.log(`${c('cyan', '[build]')} target: ${adapter} (${moduleFormat})`);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const args: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n console.log(c('gray', `[build] tsconfig.json detected — compiling with project settings`));\n args.push('--project', tsconfigPath);\n } else {\n args.push(entry);\n args.push('--rootDir', path.dirname(entry));\n if (!isTsLike(entry)) {\n args.push('--allowJs');\n console.log(c('yellow', '[build] Entry is not TypeScript; enabling --allowJs'));\n }\n args.push('--experimentalDecorators', '--emitDecoratorMetadata');\n args.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n args.push('--module', moduleFormat);\n args.push('--outDir', outDir);\n args.push('--skipLibCheck');\n\n await runCmd('npx', args);\n\n if (adapter !== 'node') {\n console.log(c('cyan', `[build] Generating ${adapter} deployment files...`));\n const entryBasename = path.basename(entry);\n await generateAdapterFiles(adapter, outDir, entryBasename, cwd);\n }\n\n console.log(c('green', 'Build completed.'));\n console.log(c('gray', `Output placed in ${path.relative(cwd, outDir)}`));\n}\n"]}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Load the entry, extract its `@FrontMcp` decorator config, and return it.
3
+ * Falls back to esbuild for TS entries.
4
+ */
5
+ export declare function loadEntryDecoratorConfig(entry: string): Promise<Record<string, unknown> | undefined>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadEntryDecoratorConfig = loadEntryDecoratorConfig;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Best-effort load of a user's `@FrontMcp()`-decorated entry to read its
7
+ * decorator config at build time.
8
+ *
9
+ * Why it exists: `runAdapterBuild` and the adapter `validate` hook need
10
+ * `@FrontMcp({ http, sqlite, redis })` *before* TypeScript compilation runs,
11
+ * but the entry itself is usually `./src/main.ts` — `require()` can't load it
12
+ * without a TS hook. Falling back to esbuild + `Module._compile` lets us
13
+ * read the metadata regardless of whether the entry is `.ts` or `.js`.
14
+ *
15
+ * Returns `undefined` when the entry can't be loaded or doesn't carry a
16
+ * decorator config (plain config object, missing decorator, etc.). Callers
17
+ * should treat that as "no metadata available" and not as a build failure.
18
+ */
19
+ const fs = tslib_1.__importStar(require("fs"));
20
+ const path = tslib_1.__importStar(require("path"));
21
+ const FRONTMCP_CONFIG_METADATA_KEY = '__frontmcp:config';
22
+ function readDecoratorMetadata(target) {
23
+ if (typeof target !== 'function') {
24
+ if (target && typeof target === 'object') {
25
+ // Plain config object — return as-is so the validate() can inspect
26
+ // top-level fields like `sqlite`, `redis`, `http` directly.
27
+ return target;
28
+ }
29
+ return undefined;
30
+ }
31
+ const reflect = globalThis.Reflect;
32
+ if (!reflect?.getMetadata)
33
+ return undefined;
34
+ const config = reflect.getMetadata(FRONTMCP_CONFIG_METADATA_KEY, target);
35
+ return (config ?? undefined);
36
+ }
37
+ /**
38
+ * Load the entry, extract its `@FrontMcp` decorator config, and return it.
39
+ * Falls back to esbuild for TS entries.
40
+ */
41
+ async function loadEntryDecoratorConfig(entry) {
42
+ const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];
43
+ process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';
44
+ try {
45
+ // Path 1: plain require() — works for compiled JS entries.
46
+ try {
47
+ const mod = require(entry);
48
+ const target = mod.default ?? mod;
49
+ const config = readDecoratorMetadata(target);
50
+ if (config)
51
+ return config;
52
+ }
53
+ catch {
54
+ // require failed (e.g., .ts entry without a hook); fall through to esbuild.
55
+ }
56
+ // Path 2: esbuild transpile + Module._compile, only if the entry is .ts/.tsx.
57
+ if (/\.tsx?$/i.test(entry)) {
58
+ try {
59
+ return await loadTsEntryViaEsbuild(entry);
60
+ }
61
+ catch {
62
+ // Anything throws (esbuild parse error, runtime error in the entry, etc.) —
63
+ // return undefined so the caller's validate() falls back to a no-op
64
+ // and the regular tsc compile produces the real error message.
65
+ }
66
+ }
67
+ }
68
+ finally {
69
+ if (prev === undefined)
70
+ delete process.env['FRONTMCP_SCHEMA_EXTRACT'];
71
+ else
72
+ process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;
73
+ }
74
+ return undefined;
75
+ }
76
+ async function loadTsEntryViaEsbuild(entryPath) {
77
+ const esbuild = require('esbuild');
78
+ const source = fs.readFileSync(entryPath, 'utf-8');
79
+ const transformed = esbuild.transformSync(source, {
80
+ loader: 'ts',
81
+ format: 'cjs',
82
+ target: 'es2022',
83
+ sourcefile: entryPath,
84
+ });
85
+ const Module = require('module');
86
+ const m = new Module(entryPath, module);
87
+ m.filename = entryPath;
88
+ // Resolve user `import { ... } from '@frontmcp/sdk'` against the user's
89
+ // node_modules, not the CLI's. Same trick as the config loader.
90
+ m.paths = Module._nodeModulePaths(path.dirname(entryPath));
91
+ m._compile(transformed.code, entryPath);
92
+ const exported = m.exports;
93
+ const target = exported.default ?? exported;
94
+ return readDecoratorMetadata(target);
95
+ }
96
+ //# sourceMappingURL=load-entry-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-entry-config.js","sourceRoot":"","sources":["../../../../src/commands/build/load-entry-config.ts"],"names":[],"mappings":";;AA0CA,4DA6BC;;AAvED;;;;;;;;;;;;;GAaG;AACH,+CAAyB;AACzB,mDAA6B;AAE7B,MAAM,4BAA4B,GAAG,mBAAmB,CAAC;AAMzD,SAAS,qBAAqB,CAAC,MAAe;IAC5C,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,mEAAmE;YACnE,4DAA4D;YAC5D,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAI,UAAwC,CAAC,OAAO,CAAC;IAClE,IAAI,CAAC,OAAO,EAAE,WAAW;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,4BAA4B,EAAE,MAAM,CAAC,CAAC;IACzE,OAAO,CAAC,MAAM,IAAI,SAAS,CAAwC,CAAC;AACtE,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,wBAAwB,CAAC,KAAa;IAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,GAAG,GAAG,CAAC;IAC7C,IAAI,CAAC;QACH,2DAA2D;QAC3D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAoD,CAAC;YAC9E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC;YAClC,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,4EAA4E;QAC9E,CAAC;QAED,8EAA8E;QAC9E,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,OAAO,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,4EAA4E;gBAC5E,oEAAoE;gBACpE,+DAA+D;YACjE,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;;YACjE,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,GAAG,IAAI,CAAC;IACrD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,SAAiB;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAA6B,CAAC;IAC/D,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE;QAChD,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,SAAS;KACtB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAA4B,CAAC;IAC5D,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC,CAAC,QAAQ,GAAG,SAAS,CAAC;IACvB,wEAAwE;IACxE,gEAAgE;IAChE,CAAC,CAAC,KAAK,GAAI,MAA+D,CAAC,gBAAgB,CACzF,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CACxB,CAAC;IAED,CAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAI,CAAS,CAAC,OAA0D,CAAC;IACvF,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC5C,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC","sourcesContent":["/**\n * Best-effort load of a user's `@FrontMcp()`-decorated entry to read its\n * decorator config at build time.\n *\n * Why it exists: `runAdapterBuild` and the adapter `validate` hook need\n * `@FrontMcp({ http, sqlite, redis })` *before* TypeScript compilation runs,\n * but the entry itself is usually `./src/main.ts` — `require()` can't load it\n * without a TS hook. Falling back to esbuild + `Module._compile` lets us\n * read the metadata regardless of whether the entry is `.ts` or `.js`.\n *\n * Returns `undefined` when the entry can't be loaded or doesn't carry a\n * decorator config (plain config object, missing decorator, etc.). Callers\n * should treat that as \"no metadata available\" and not as a build failure.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst FRONTMCP_CONFIG_METADATA_KEY = '__frontmcp:config';\n\ninterface ReflectLike {\n getMetadata?: (key: string, target: unknown) => unknown;\n}\n\nfunction readDecoratorMetadata(target: unknown): Record<string, unknown> | undefined {\n if (typeof target !== 'function') {\n if (target && typeof target === 'object') {\n // Plain config object — return as-is so the validate() can inspect\n // top-level fields like `sqlite`, `redis`, `http` directly.\n return target as Record<string, unknown>;\n }\n return undefined;\n }\n const reflect = (globalThis as { Reflect?: ReflectLike }).Reflect;\n if (!reflect?.getMetadata) return undefined;\n const config = reflect.getMetadata(FRONTMCP_CONFIG_METADATA_KEY, target);\n return (config ?? undefined) as Record<string, unknown> | undefined;\n}\n\n/**\n * Load the entry, extract its `@FrontMcp` decorator config, and return it.\n * Falls back to esbuild for TS entries.\n */\nexport async function loadEntryDecoratorConfig(entry: string): Promise<Record<string, unknown> | undefined> {\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n try {\n // Path 1: plain require() — works for compiled JS entries.\n try {\n const mod = require(entry) as { default?: unknown } & Record<string, unknown>;\n const target = mod.default ?? mod;\n const config = readDecoratorMetadata(target);\n if (config) return config;\n } catch {\n // require failed (e.g., .ts entry without a hook); fall through to esbuild.\n }\n\n // Path 2: esbuild transpile + Module._compile, only if the entry is .ts/.tsx.\n if (/\\.tsx?$/i.test(entry)) {\n try {\n return await loadTsEntryViaEsbuild(entry);\n } catch {\n // Anything throws (esbuild parse error, runtime error in the entry, etc.) —\n // return undefined so the caller's validate() falls back to a no-op\n // and the regular tsc compile produces the real error message.\n }\n }\n } finally {\n if (prev === undefined) delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n else process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n return undefined;\n}\n\nasync function loadTsEntryViaEsbuild(entryPath: string): Promise<Record<string, unknown> | undefined> {\n const esbuild = require('esbuild') as typeof import('esbuild');\n const source = fs.readFileSync(entryPath, 'utf-8');\n const transformed = esbuild.transformSync(source, {\n loader: 'ts',\n format: 'cjs',\n target: 'es2022',\n sourcefile: entryPath,\n });\n\n const Module = require('module') as typeof import('module');\n const m = new Module(entryPath, module);\n m.filename = entryPath;\n // Resolve user `import { ... } from '@frontmcp/sdk'` against the user's\n // node_modules, not the CLI's. Same trick as the config loader.\n m.paths = (Module as unknown as { _nodeModulePaths(p: string): string[] })._nodeModulePaths(\n path.dirname(entryPath),\n );\n \n (m as any)._compile(transformed.code, entryPath);\n \n const exported = (m as any).exports as { default?: unknown } & Record<string, unknown>;\n const target = exported.default ?? exported;\n return readDecoratorMetadata(target);\n}\n"]}
@@ -45,6 +45,13 @@ export interface McpbManifest {
45
45
  description: string;
46
46
  }>;
47
47
  tools_generated?: boolean;
48
+ resources?: Array<{
49
+ name?: string;
50
+ uri: string;
51
+ description?: string;
52
+ mimeType?: string;
53
+ }>;
54
+ resources_generated?: boolean;
48
55
  prompts?: Array<{
49
56
  name: string;
50
57
  description?: string;
@@ -103,6 +110,13 @@ export declare const mcpbManifestSchema: import("@frontmcp/lazy-zod").ZodObject<
103
110
  description: import("@frontmcp/lazy-zod").ZodString;
104
111
  }, import("zod/v4/core").$strict>>>;
105
112
  tools_generated: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
113
+ resources: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodObject<{
114
+ name: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
115
+ uri: import("@frontmcp/lazy-zod").ZodString;
116
+ description: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
117
+ mimeType: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
118
+ }, import("zod/v4/core").$strict>>>;
119
+ resources_generated: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
106
120
  prompts: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodObject<{
107
121
  name: import("@frontmcp/lazy-zod").ZodString;
108
122
  description: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodString>;
@@ -93,6 +93,17 @@ exports.mcpbManifestSchema = lazy_zod_1.z
93
93
  .array(lazy_zod_1.z.object({ name: lazy_zod_1.z.string(), description: lazy_zod_1.z.string() }).strict())
94
94
  .optional(),
95
95
  tools_generated: lazy_zod_1.z.boolean().optional(),
96
+ resources: lazy_zod_1.z
97
+ .array(lazy_zod_1.z
98
+ .object({
99
+ name: lazy_zod_1.z.string().optional(),
100
+ uri: lazy_zod_1.z.string(),
101
+ description: lazy_zod_1.z.string().optional(),
102
+ mimeType: lazy_zod_1.z.string().optional(),
103
+ })
104
+ .strict())
105
+ .optional(),
106
+ resources_generated: lazy_zod_1.z.boolean().optional(),
96
107
  prompts: lazy_zod_1.z
97
108
  .array(lazy_zod_1.z
98
109
  .object({ name: lazy_zod_1.z.string(), description: lazy_zod_1.z.string().optional() })
@@ -196,6 +207,15 @@ function generateMcpbManifest(input) {
196
207
  const tools = schema.tools
197
208
  .filter((t) => !schema_extractor_1.SYSTEM_TOOL_NAMES.has(t.name))
198
209
  .map((t) => ({ name: t.name, description: t.description || '' }));
210
+ // #376 — emit resources alongside tools/prompts. The server runtime
211
+ // already registers @Resource entries, but the manifest writer was
212
+ // dropping them, so MCPB-aware clients had no way to discover them.
213
+ const resources = schema.resources.map((r) => ({
214
+ ...(r.name ? { name: r.name } : {}),
215
+ uri: r.uri,
216
+ ...(r.description ? { description: r.description } : {}),
217
+ ...(r.mimeType ? { mimeType: r.mimeType } : {}),
218
+ }));
199
219
  const prompts = schema.prompts.map((p) => ({
200
220
  name: p.name,
201
221
  ...(p.description ? { description: p.description } : {}),
@@ -236,6 +256,15 @@ function generateMcpbManifest(input) {
236
256
  },
237
257
  tools,
238
258
  tools_generated: false,
259
+ ...(resources.length > 0
260
+ ? {
261
+ resources,
262
+ // FrontMCP resources resolve dynamically (their bodies come from
263
+ // execute()/read()), so consumers should still query the server at
264
+ // runtime — but the static manifest now exposes name/uri/mimeType.
265
+ resources_generated: true,
266
+ }
267
+ : {}),
239
268
  prompts,
240
269
  // FrontMCP prompts resolve dynamically via execute() — MCPB's static `text`
241
270
  // template cannot represent JS logic. Set generated:true so consumers query
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../../src/commands/build/mcpb/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCPB manifest generator + Zod schema for the emitted manifest.\n *\n * Source priority when resolving fields:\n * deployment.* (frontmcp.config) > package.json > hard defaults\n *\n * See https://github.com/modelcontextprotocol/mcpb/blob/main/MANIFEST.md\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { z } from '@frontmcp/lazy-zod';\nimport { type ExtractedSchema, SYSTEM_TOOL_NAMES } from '../exec/cli-runtime/schema-extractor';\nimport type {\n McpbAuthor,\n McpbCompatibility,\n McpbDeployment,\n McpbRepository,\n McpbUserConfigEntry,\n} from '../../../config/frontmcp-config.types';\nimport {\n DEFAULT_NODE_COMPAT,\n DEFAULT_PLATFORMS,\n MCPB_MANIFEST_VERSION,\n} from './constants';\n\n// ============================================\n// Emitted manifest shape (MCPB v0.3 subset we produce)\n// ============================================\n\nexport interface McpbMcpConfig {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n platform_overrides?: Record<string, McpbMcpConfig>;\n}\n\nexport interface McpbManifest {\n manifest_version: string;\n name: string;\n version: string;\n description: string;\n display_name?: string;\n long_description?: string;\n author: McpbAuthor;\n license?: string;\n homepage?: string;\n repository?: { type: string; url: string };\n documentation?: string;\n support?: string;\n icon?: string;\n keywords?: string[];\n privacy_policies?: string[];\n compatibility?: McpbCompatibility;\n server: {\n type: 'node' | 'python' | 'binary' | 'uv';\n entry_point: string;\n mcp_config: McpbMcpConfig;\n };\n tools?: Array<{ name: string; description: string }>;\n tools_generated?: boolean;\n prompts?: Array<{ name: string; description?: string }>;\n prompts_generated?: boolean;\n user_config?: Record<string, McpbUserConfigEntry>;\n _meta?: Record<string, unknown>;\n}\n\n// ============================================\n// Zod schema (used by validate.ts)\n// ============================================\n\nconst authorSchema = z\n .object({\n name: z.string(),\n email: z.string().optional(),\n url: z.string().optional(),\n })\n .strict();\n\nconst userConfigEntrySchema = z\n .object({\n type: z.enum(['string', 'number', 'boolean', 'directory', 'file']),\n title: z.string(),\n description: z.string().optional(),\n required: z.boolean().optional(),\n default: z.union([z.string(), z.number(), z.boolean()]).optional(),\n multiple: z.boolean().optional(),\n sensitive: z.boolean().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .strict();\n\nconst mcpConfigSchema: z.ZodType<McpbMcpConfig> = z.lazy(() =>\n z\n .object({\n command: z.string(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n platform_overrides: z.record(z.string(), mcpConfigSchema).optional(),\n })\n .strict(),\n);\n\nexport const mcpbManifestSchema = z\n .object({\n manifest_version: z.string(),\n name: z.string().min(1),\n version: z.string().min(1),\n description: z.string(),\n display_name: z.string().optional(),\n long_description: z.string().optional(),\n author: authorSchema,\n license: z.string().optional(),\n homepage: z.string().optional(),\n repository: z\n .object({ type: z.string(), url: z.string() })\n .strict()\n .optional(),\n documentation: z.string().optional(),\n support: z.string().optional(),\n icon: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n privacy_policies: z.array(z.string()).optional(),\n compatibility: z\n .object({\n claude_desktop: z.string().optional(),\n platforms: z.array(z.enum(['darwin', 'win32', 'linux'])).optional(),\n runtimes: z\n .object({ node: z.string().optional(), python: z.string().optional() })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n server: z\n .object({\n type: z.enum(['node', 'python', 'binary', 'uv']),\n entry_point: z.string(),\n mcp_config: mcpConfigSchema,\n })\n .strict(),\n tools: z\n .array(z.object({ name: z.string(), description: z.string() }).strict())\n .optional(),\n tools_generated: z.boolean().optional(),\n prompts: z\n .array(\n z\n .object({ name: z.string(), description: z.string().optional() })\n .strict(),\n )\n .optional(),\n prompts_generated: z.boolean().optional(),\n user_config: z.record(z.string(), userConfigEntrySchema).optional(),\n _meta: z.record(z.string(), z.unknown()).optional(),\n })\n .strict();\n\n// ============================================\n// Sources + helpers\n// ============================================\n\nexport interface PackageJsonMeta {\n name?: string;\n version?: string;\n description?: string;\n author?: string | McpbAuthor;\n license?: string;\n homepage?: string;\n repository?: string | { type?: string; url?: string };\n keywords?: string[];\n icon?: string;\n}\n\n/** Read and parse package.json from the given directory. Returns {} if absent. */\nexport function loadPackageJsonMeta(cwd: string): PackageJsonMeta {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) return {};\n try {\n return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as PackageJsonMeta;\n } catch {\n return {};\n }\n}\n\n/**\n * Parse npm-style \"Name <email> (url)\" into an McpbAuthor.\n * Falls back to `{ name: <raw> }` when parsing fails.\n */\nexport function parseAuthor(author: unknown): McpbAuthor {\n if (!author) return { name: 'unknown' };\n if (typeof author === 'object' && 'name' in author) {\n const a = author as McpbAuthor;\n return {\n name: a.name,\n ...(a.email ? { email: a.email } : {}),\n ...(a.url ? { url: a.url } : {}),\n };\n }\n if (typeof author !== 'string') {\n return { name: String(author) };\n }\n const match = author.match(/^([^<(]+?)(?:\\s*<([^>]+)>)?(?:\\s*\\(([^)]+)\\))?$/);\n if (!match || !match[1]) return { name: author };\n const [, name, email, url] = match;\n return {\n name: name.trim(),\n ...(email ? { email: email.trim() } : {}),\n ...(url ? { url: url.trim() } : {}),\n };\n}\n\n/** Normalize repository field (string → {type, url}). */\nexport function normalizeRepository(\n repo: McpbRepository | PackageJsonMeta['repository'] | undefined,\n): { type: string; url: string } | undefined {\n if (!repo) return undefined;\n if (typeof repo === 'string') {\n return { type: 'git', url: repo };\n }\n if (typeof repo === 'object' && repo.url) {\n return { type: repo.type || 'git', url: repo.url };\n }\n return undefined;\n}\n\n/** Resolve first-existing icon path. */\nexport function resolveIconPath(cwd: string, deploymentIcon?: string, pkgIcon?: string): string | undefined {\n const candidates = [deploymentIcon, pkgIcon, 'icon.png', 'assets/icon.png'].filter(\n (x): x is string => !!x,\n );\n for (const rel of candidates) {\n if (fs.existsSync(path.resolve(cwd, rel))) {\n return rel;\n }\n }\n return undefined;\n}\n\n// ============================================\n// Generator input + output\n// ============================================\n\nexport interface GenerateMcpbManifestInput {\n /** Resolved server name. */\n name: string;\n /** Resolved server version. */\n version: string;\n /** Optional resolved node version range (e.g., \">=22.0.0\"). */\n nodeVersion?: string;\n /** Project root. Used for icon resolution and package.json fallback. */\n cwd: string;\n /** Deployment config from frontmcp.config (may be undefined). */\n deployment?: McpbDeployment;\n /** Schema extracted from the compiled server bundle. */\n schema: ExtractedSchema;\n /** env → user_config reference map built by user-config.ts. */\n userConfigEnv: Record<string, string>;\n /** user_config entries built by user-config.ts. */\n userConfig: Record<string, McpbUserConfigEntry>;\n /** platform_overrides from binary.ts (may be empty). */\n platformOverrides?: Record<string, McpbMcpConfig>;\n /** Whether an icon was copied into the staged archive root. */\n hasIcon?: boolean;\n /** Tool name of the CLI version emitting this manifest (for _meta). */\n cliVersion?: string;\n}\n\n/** Produce the final MCPB manifest object. */\nexport function generateMcpbManifest(input: GenerateMcpbManifestInput): McpbManifest {\n const {\n name,\n version,\n cwd,\n deployment,\n schema,\n userConfig,\n userConfigEnv,\n platformOverrides,\n hasIcon,\n cliVersion,\n } = input;\n\n const pkg = loadPackageJsonMeta(cwd);\n\n const description = deployment?.longDescription?.split('\\n')[0]?.trim()\n || pkg.description\n || '';\n\n const author = deployment?.author || parseAuthor(pkg.author);\n const license = deployment?.license ?? pkg.license;\n const homepage = deployment?.homepage ?? pkg.homepage;\n const repository = normalizeRepository(deployment?.repository ?? pkg.repository);\n const keywords = deployment?.keywords ?? pkg.keywords;\n const icon = hasIcon ? 'icon.png' : undefined;\n\n const nodeVersion = deployment?.compatibility?.runtimes?.node\n ?? input.nodeVersion\n ?? DEFAULT_NODE_COMPAT;\n\n const compatibility: McpbCompatibility = {\n ...(deployment?.compatibility?.claude_desktop\n ? { claude_desktop: deployment.compatibility.claude_desktop }\n : {}),\n platforms: deployment?.compatibility?.platforms ?? [...DEFAULT_PLATFORMS],\n runtimes: {\n ...(deployment?.compatibility?.runtimes?.python\n ? { python: deployment.compatibility.runtimes.python }\n : {}),\n node: nodeVersion,\n },\n };\n\n const tools = schema.tools\n .filter((t) => !SYSTEM_TOOL_NAMES.has(t.name))\n .map((t) => ({ name: t.name, description: t.description || '' }));\n\n const prompts = schema.prompts.map((p) => ({\n name: p.name,\n ...(p.description ? { description: p.description } : {}),\n }));\n\n const mcpConfig: McpbMcpConfig = {\n command: 'node',\n args: ['${__dirname}/server/index.js'],\n ...(Object.keys(userConfigEnv).length > 0 ? { env: userConfigEnv } : {}),\n ...(platformOverrides && Object.keys(platformOverrides).length > 0\n ? { platform_overrides: platformOverrides }\n : {}),\n };\n\n const manifest: McpbManifest = {\n manifest_version: MCPB_MANIFEST_VERSION,\n name,\n version,\n description,\n ...(deployment?.displayName ? { display_name: deployment.displayName } : {}),\n ...(deployment?.longDescription\n ? { long_description: deployment.longDescription }\n : {}),\n author,\n ...(license ? { license } : {}),\n ...(homepage ? { homepage } : {}),\n ...(repository ? { repository } : {}),\n ...(deployment?.documentation ? { documentation: deployment.documentation } : {}),\n ...(deployment?.support ? { support: deployment.support } : {}),\n ...(icon ? { icon } : {}),\n ...(keywords && keywords.length > 0 ? { keywords } : {}),\n ...(deployment?.privacyPolicies && deployment.privacyPolicies.length > 0\n ? { privacy_policies: deployment.privacyPolicies }\n : {}),\n compatibility,\n server: {\n type: 'node',\n entry_point: 'server/index.js',\n mcp_config: mcpConfig,\n },\n tools,\n tools_generated: false,\n prompts,\n // FrontMCP prompts resolve dynamically via execute() — MCPB's static `text`\n // template cannot represent JS logic. Set generated:true so consumers query\n // the server at runtime while still getting name/description hints here.\n prompts_generated: true,\n ...(Object.keys(userConfig).length > 0 ? { user_config: userConfig } : {}),\n _meta: {\n 'dev.frontmcp.generator': cliVersion ? `frontmcp@${cliVersion}` : 'frontmcp',\n 'dev.frontmcp.capabilities': schema.capabilities,\n },\n };\n\n return manifest;\n}\n"]}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../../src/commands/build/mcpb/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCPB manifest generator + Zod schema for the emitted manifest.\n *\n * Source priority when resolving fields:\n * deployment.* (frontmcp.config) > package.json > hard defaults\n *\n * See https://github.com/modelcontextprotocol/mcpb/blob/main/MANIFEST.md\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { z } from '@frontmcp/lazy-zod';\nimport { type ExtractedSchema, SYSTEM_TOOL_NAMES } from '../exec/cli-runtime/schema-extractor';\nimport type {\n McpbAuthor,\n McpbCompatibility,\n McpbDeployment,\n McpbRepository,\n McpbUserConfigEntry,\n} from '../../../config/frontmcp-config.types';\nimport {\n DEFAULT_NODE_COMPAT,\n DEFAULT_PLATFORMS,\n MCPB_MANIFEST_VERSION,\n} from './constants';\n\n// ============================================\n// Emitted manifest shape (MCPB v0.3 subset we produce)\n// ============================================\n\nexport interface McpbMcpConfig {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n platform_overrides?: Record<string, McpbMcpConfig>;\n}\n\nexport interface McpbManifest {\n manifest_version: string;\n name: string;\n version: string;\n description: string;\n display_name?: string;\n long_description?: string;\n author: McpbAuthor;\n license?: string;\n homepage?: string;\n repository?: { type: string; url: string };\n documentation?: string;\n support?: string;\n icon?: string;\n keywords?: string[];\n privacy_policies?: string[];\n compatibility?: McpbCompatibility;\n server: {\n type: 'node' | 'python' | 'binary' | 'uv';\n entry_point: string;\n mcp_config: McpbMcpConfig;\n };\n tools?: Array<{ name: string; description: string }>;\n tools_generated?: boolean;\n resources?: Array<{ name?: string; uri: string; description?: string; mimeType?: string }>;\n resources_generated?: boolean;\n prompts?: Array<{ name: string; description?: string }>;\n prompts_generated?: boolean;\n user_config?: Record<string, McpbUserConfigEntry>;\n _meta?: Record<string, unknown>;\n}\n\n// ============================================\n// Zod schema (used by validate.ts)\n// ============================================\n\nconst authorSchema = z\n .object({\n name: z.string(),\n email: z.string().optional(),\n url: z.string().optional(),\n })\n .strict();\n\nconst userConfigEntrySchema = z\n .object({\n type: z.enum(['string', 'number', 'boolean', 'directory', 'file']),\n title: z.string(),\n description: z.string().optional(),\n required: z.boolean().optional(),\n default: z.union([z.string(), z.number(), z.boolean()]).optional(),\n multiple: z.boolean().optional(),\n sensitive: z.boolean().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .strict();\n\nconst mcpConfigSchema: z.ZodType<McpbMcpConfig> = z.lazy(() =>\n z\n .object({\n command: z.string(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n platform_overrides: z.record(z.string(), mcpConfigSchema).optional(),\n })\n .strict(),\n);\n\nexport const mcpbManifestSchema = z\n .object({\n manifest_version: z.string(),\n name: z.string().min(1),\n version: z.string().min(1),\n description: z.string(),\n display_name: z.string().optional(),\n long_description: z.string().optional(),\n author: authorSchema,\n license: z.string().optional(),\n homepage: z.string().optional(),\n repository: z\n .object({ type: z.string(), url: z.string() })\n .strict()\n .optional(),\n documentation: z.string().optional(),\n support: z.string().optional(),\n icon: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n privacy_policies: z.array(z.string()).optional(),\n compatibility: z\n .object({\n claude_desktop: z.string().optional(),\n platforms: z.array(z.enum(['darwin', 'win32', 'linux'])).optional(),\n runtimes: z\n .object({ node: z.string().optional(), python: z.string().optional() })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n server: z\n .object({\n type: z.enum(['node', 'python', 'binary', 'uv']),\n entry_point: z.string(),\n mcp_config: mcpConfigSchema,\n })\n .strict(),\n tools: z\n .array(z.object({ name: z.string(), description: z.string() }).strict())\n .optional(),\n tools_generated: z.boolean().optional(),\n resources: z\n .array(\n z\n .object({\n name: z.string().optional(),\n uri: z.string(),\n description: z.string().optional(),\n mimeType: z.string().optional(),\n })\n .strict(),\n )\n .optional(),\n resources_generated: z.boolean().optional(),\n prompts: z\n .array(\n z\n .object({ name: z.string(), description: z.string().optional() })\n .strict(),\n )\n .optional(),\n prompts_generated: z.boolean().optional(),\n user_config: z.record(z.string(), userConfigEntrySchema).optional(),\n _meta: z.record(z.string(), z.unknown()).optional(),\n })\n .strict();\n\n// ============================================\n// Sources + helpers\n// ============================================\n\nexport interface PackageJsonMeta {\n name?: string;\n version?: string;\n description?: string;\n author?: string | McpbAuthor;\n license?: string;\n homepage?: string;\n repository?: string | { type?: string; url?: string };\n keywords?: string[];\n icon?: string;\n}\n\n/** Read and parse package.json from the given directory. Returns {} if absent. */\nexport function loadPackageJsonMeta(cwd: string): PackageJsonMeta {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) return {};\n try {\n return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as PackageJsonMeta;\n } catch {\n return {};\n }\n}\n\n/**\n * Parse npm-style \"Name <email> (url)\" into an McpbAuthor.\n * Falls back to `{ name: <raw> }` when parsing fails.\n */\nexport function parseAuthor(author: unknown): McpbAuthor {\n if (!author) return { name: 'unknown' };\n if (typeof author === 'object' && 'name' in author) {\n const a = author as McpbAuthor;\n return {\n name: a.name,\n ...(a.email ? { email: a.email } : {}),\n ...(a.url ? { url: a.url } : {}),\n };\n }\n if (typeof author !== 'string') {\n return { name: String(author) };\n }\n const match = author.match(/^([^<(]+?)(?:\\s*<([^>]+)>)?(?:\\s*\\(([^)]+)\\))?$/);\n if (!match || !match[1]) return { name: author };\n const [, name, email, url] = match;\n return {\n name: name.trim(),\n ...(email ? { email: email.trim() } : {}),\n ...(url ? { url: url.trim() } : {}),\n };\n}\n\n/** Normalize repository field (string → {type, url}). */\nexport function normalizeRepository(\n repo: McpbRepository | PackageJsonMeta['repository'] | undefined,\n): { type: string; url: string } | undefined {\n if (!repo) return undefined;\n if (typeof repo === 'string') {\n return { type: 'git', url: repo };\n }\n if (typeof repo === 'object' && repo.url) {\n return { type: repo.type || 'git', url: repo.url };\n }\n return undefined;\n}\n\n/** Resolve first-existing icon path. */\nexport function resolveIconPath(cwd: string, deploymentIcon?: string, pkgIcon?: string): string | undefined {\n const candidates = [deploymentIcon, pkgIcon, 'icon.png', 'assets/icon.png'].filter(\n (x): x is string => !!x,\n );\n for (const rel of candidates) {\n if (fs.existsSync(path.resolve(cwd, rel))) {\n return rel;\n }\n }\n return undefined;\n}\n\n// ============================================\n// Generator input + output\n// ============================================\n\nexport interface GenerateMcpbManifestInput {\n /** Resolved server name. */\n name: string;\n /** Resolved server version. */\n version: string;\n /** Optional resolved node version range (e.g., \">=22.0.0\"). */\n nodeVersion?: string;\n /** Project root. Used for icon resolution and package.json fallback. */\n cwd: string;\n /** Deployment config from frontmcp.config (may be undefined). */\n deployment?: McpbDeployment;\n /** Schema extracted from the compiled server bundle. */\n schema: ExtractedSchema;\n /** env → user_config reference map built by user-config.ts. */\n userConfigEnv: Record<string, string>;\n /** user_config entries built by user-config.ts. */\n userConfig: Record<string, McpbUserConfigEntry>;\n /** platform_overrides from binary.ts (may be empty). */\n platformOverrides?: Record<string, McpbMcpConfig>;\n /** Whether an icon was copied into the staged archive root. */\n hasIcon?: boolean;\n /** Tool name of the CLI version emitting this manifest (for _meta). */\n cliVersion?: string;\n}\n\n/** Produce the final MCPB manifest object. */\nexport function generateMcpbManifest(input: GenerateMcpbManifestInput): McpbManifest {\n const {\n name,\n version,\n cwd,\n deployment,\n schema,\n userConfig,\n userConfigEnv,\n platformOverrides,\n hasIcon,\n cliVersion,\n } = input;\n\n const pkg = loadPackageJsonMeta(cwd);\n\n const description = deployment?.longDescription?.split('\\n')[0]?.trim()\n || pkg.description\n || '';\n\n const author = deployment?.author || parseAuthor(pkg.author);\n const license = deployment?.license ?? pkg.license;\n const homepage = deployment?.homepage ?? pkg.homepage;\n const repository = normalizeRepository(deployment?.repository ?? pkg.repository);\n const keywords = deployment?.keywords ?? pkg.keywords;\n const icon = hasIcon ? 'icon.png' : undefined;\n\n const nodeVersion = deployment?.compatibility?.runtimes?.node\n ?? input.nodeVersion\n ?? DEFAULT_NODE_COMPAT;\n\n const compatibility: McpbCompatibility = {\n ...(deployment?.compatibility?.claude_desktop\n ? { claude_desktop: deployment.compatibility.claude_desktop }\n : {}),\n platforms: deployment?.compatibility?.platforms ?? [...DEFAULT_PLATFORMS],\n runtimes: {\n ...(deployment?.compatibility?.runtimes?.python\n ? { python: deployment.compatibility.runtimes.python }\n : {}),\n node: nodeVersion,\n },\n };\n\n const tools = schema.tools\n .filter((t) => !SYSTEM_TOOL_NAMES.has(t.name))\n .map((t) => ({ name: t.name, description: t.description || '' }));\n\n // #376 — emit resources alongside tools/prompts. The server runtime\n // already registers @Resource entries, but the manifest writer was\n // dropping them, so MCPB-aware clients had no way to discover them.\n const resources = schema.resources.map((r) => ({\n ...(r.name ? { name: r.name } : {}),\n uri: r.uri,\n ...(r.description ? { description: r.description } : {}),\n ...(r.mimeType ? { mimeType: r.mimeType } : {}),\n }));\n\n const prompts = schema.prompts.map((p) => ({\n name: p.name,\n ...(p.description ? { description: p.description } : {}),\n }));\n\n const mcpConfig: McpbMcpConfig = {\n command: 'node',\n args: ['${__dirname}/server/index.js'],\n ...(Object.keys(userConfigEnv).length > 0 ? { env: userConfigEnv } : {}),\n ...(platformOverrides && Object.keys(platformOverrides).length > 0\n ? { platform_overrides: platformOverrides }\n : {}),\n };\n\n const manifest: McpbManifest = {\n manifest_version: MCPB_MANIFEST_VERSION,\n name,\n version,\n description,\n ...(deployment?.displayName ? { display_name: deployment.displayName } : {}),\n ...(deployment?.longDescription\n ? { long_description: deployment.longDescription }\n : {}),\n author,\n ...(license ? { license } : {}),\n ...(homepage ? { homepage } : {}),\n ...(repository ? { repository } : {}),\n ...(deployment?.documentation ? { documentation: deployment.documentation } : {}),\n ...(deployment?.support ? { support: deployment.support } : {}),\n ...(icon ? { icon } : {}),\n ...(keywords && keywords.length > 0 ? { keywords } : {}),\n ...(deployment?.privacyPolicies && deployment.privacyPolicies.length > 0\n ? { privacy_policies: deployment.privacyPolicies }\n : {}),\n compatibility,\n server: {\n type: 'node',\n entry_point: 'server/index.js',\n mcp_config: mcpConfig,\n },\n tools,\n tools_generated: false,\n ...(resources.length > 0\n ? {\n resources,\n // FrontMCP resources resolve dynamically (their bodies come from\n // execute()/read()), so consumers should still query the server at\n // runtime — but the static manifest now exposes name/uri/mimeType.\n resources_generated: true,\n }\n : {}),\n prompts,\n // FrontMCP prompts resolve dynamically via execute() — MCPB's static `text`\n // template cannot represent JS logic. Set generated:true so consumers query\n // the server at runtime while still getting name/description hints here.\n prompts_generated: true,\n ...(Object.keys(userConfig).length > 0 ? { user_config: userConfig } : {}),\n _meta: {\n 'dev.frontmcp.generator': cliVersion ? `frontmcp@${cliVersion}` : 'frontmcp',\n 'dev.frontmcp.capabilities': schema.capabilities,\n },\n };\n\n return manifest;\n}\n"]}
@@ -44,5 +44,24 @@ export type AdapterTemplate = {
44
44
  * @param bundleOutput - Name of the bundled file (e.g., 'handler.cjs')
45
45
  */
46
46
  postBundle?: (outDir: string, cwd: string, bundleOutput: string) => Promise<void>;
47
+ /**
48
+ * Pre-build validation hook. Runs after schema/decorator extraction but
49
+ * before TypeScript compilation. Allows the adapter to fail loudly when
50
+ * the user's config references runtime features that won't work on the
51
+ * target platform (e.g., sqlite on Cloudflare Workers).
52
+ *
53
+ * @param decoratorConfig - Best-effort `__frontmcp:config` metadata
54
+ * extracted from the entry's @FrontMcp() decorator. May be undefined
55
+ * when the entry exports a plain config object.
56
+ * @throws to abort the build with a user-facing message.
57
+ */
58
+ validate?: (decoratorConfig: Record<string, unknown> | undefined) => void;
59
+ /**
60
+ * Whether `getConfig()` output should overwrite an existing config file
61
+ * (e.g., wrangler.toml) on every build. When false, an existing file is
62
+ * left untouched but its contents are diffed against the build output and
63
+ * the build fails on mismatch (#374). Default: false (preserve existing).
64
+ */
65
+ alwaysWriteConfig?: boolean;
47
66
  };
48
67
  export type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare' | 'distributed';