frontmcp 1.0.3 → 1.1.0-beta.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 (100) hide show
  1. package/package.json +10 -4
  2. package/src/commands/build/adapters/cloudflare.d.ts +1 -1
  3. package/src/commands/build/adapters/cloudflare.js +1 -0
  4. package/src/commands/build/adapters/cloudflare.js.map +1 -1
  5. package/src/commands/build/adapters/distributed.d.ts +15 -0
  6. package/src/commands/build/adapters/distributed.js +30 -0
  7. package/src/commands/build/adapters/distributed.js.map +1 -0
  8. package/src/commands/build/adapters/index.d.ts +3 -2
  9. package/src/commands/build/adapters/index.js +4 -1
  10. package/src/commands/build/adapters/index.js.map +1 -1
  11. package/src/commands/build/adapters/lambda.d.ts +1 -1
  12. package/src/commands/build/adapters/lambda.js +1 -0
  13. package/src/commands/build/adapters/lambda.js.map +1 -1
  14. package/src/commands/build/adapters/vercel.d.ts +1 -1
  15. package/src/commands/build/adapters/vercel.js +1 -0
  16. package/src/commands/build/adapters/vercel.js.map +1 -1
  17. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +122 -34
  18. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  19. package/src/commands/build/exec/config.d.ts +2 -2
  20. package/src/commands/build/exec/config.js.map +1 -1
  21. package/src/commands/build/exec/esbuild-bundler.d.ts +1 -1
  22. package/src/commands/build/exec/esbuild-bundler.js +1 -1
  23. package/src/commands/build/exec/esbuild-bundler.js.map +1 -1
  24. package/src/commands/build/exec/index.d.ts +1 -1
  25. package/src/commands/build/exec/index.js +5 -38
  26. package/src/commands/build/exec/index.js.map +1 -1
  27. package/src/commands/build/exec/skill-assets.d.ts +27 -0
  28. package/src/commands/build/exec/skill-assets.js +60 -0
  29. package/src/commands/build/exec/skill-assets.js.map +1 -0
  30. package/src/commands/build/index.d.ts +1 -1
  31. package/src/commands/build/index.js +44 -5
  32. package/src/commands/build/index.js.map +1 -1
  33. package/src/commands/build/mcpb/binary.d.ts +37 -0
  34. package/src/commands/build/mcpb/binary.js +72 -0
  35. package/src/commands/build/mcpb/binary.js.map +1 -0
  36. package/src/commands/build/mcpb/constants.d.ts +21 -0
  37. package/src/commands/build/mcpb/constants.js +31 -0
  38. package/src/commands/build/mcpb/constants.js.map +1 -0
  39. package/src/commands/build/mcpb/index.d.ts +20 -0
  40. package/src/commands/build/mcpb/index.js +241 -0
  41. package/src/commands/build/mcpb/index.js.map +1 -0
  42. package/src/commands/build/mcpb/manifest.d.ts +183 -0
  43. package/src/commands/build/mcpb/manifest.js +252 -0
  44. package/src/commands/build/mcpb/manifest.js.map +1 -0
  45. package/src/commands/build/mcpb/stage.d.ts +50 -0
  46. package/src/commands/build/mcpb/stage.js +94 -0
  47. package/src/commands/build/mcpb/stage.js.map +1 -0
  48. package/src/commands/build/mcpb/user-config.d.ts +26 -0
  49. package/src/commands/build/mcpb/user-config.js +147 -0
  50. package/src/commands/build/mcpb/user-config.js.map +1 -0
  51. package/src/commands/build/mcpb/validate.d.ts +27 -0
  52. package/src/commands/build/mcpb/validate.js +218 -0
  53. package/src/commands/build/mcpb/validate.js.map +1 -0
  54. package/src/commands/build/mcpb/zip.d.ts +37 -0
  55. package/src/commands/build/mcpb/zip.js +85 -0
  56. package/src/commands/build/mcpb/zip.js.map +1 -0
  57. package/src/commands/build/register.d.ts +1 -1
  58. package/src/commands/build/register.js +7 -1
  59. package/src/commands/build/register.js.map +1 -1
  60. package/src/commands/build/sdk/index.d.ts +1 -1
  61. package/src/commands/build/sdk/index.js +1 -1
  62. package/src/commands/build/sdk/index.js.map +1 -1
  63. package/src/commands/build/types.d.ts +1 -1
  64. package/src/commands/build/types.js.map +1 -1
  65. package/src/commands/mcpb/register.d.ts +2 -0
  66. package/src/commands/mcpb/register.js +14 -0
  67. package/src/commands/mcpb/register.js.map +1 -0
  68. package/src/commands/mcpb/validate.d.ts +1 -0
  69. package/src/commands/mcpb/validate.js +28 -0
  70. package/src/commands/mcpb/validate.js.map +1 -0
  71. package/src/commands/scaffold/create.js +5 -7
  72. package/src/commands/scaffold/create.js.map +1 -1
  73. package/src/config/define-config.d.ts +26 -0
  74. package/src/config/define-config.js +31 -0
  75. package/src/config/define-config.js.map +1 -0
  76. package/src/config/frontmcp-config.loader.d.ts +32 -0
  77. package/src/config/frontmcp-config.loader.js +113 -0
  78. package/src/config/frontmcp-config.loader.js.map +1 -0
  79. package/src/config/frontmcp-config.schema.d.ts +1062 -0
  80. package/src/config/frontmcp-config.schema.js +313 -0
  81. package/src/config/frontmcp-config.schema.js.map +1 -0
  82. package/src/config/frontmcp-config.types.d.ts +287 -0
  83. package/src/config/frontmcp-config.types.js +14 -0
  84. package/src/config/frontmcp-config.types.js.map +1 -0
  85. package/src/config/index.d.ts +5 -0
  86. package/src/config/index.js +13 -0
  87. package/src/config/index.js.map +1 -0
  88. package/src/core/args.d.ts +7 -2
  89. package/src/core/args.js +12 -2
  90. package/src/core/args.js.map +1 -1
  91. package/src/core/bridge.d.ts +1 -1
  92. package/src/core/bridge.js +10 -0
  93. package/src/core/bridge.js.map +1 -1
  94. package/src/core/cli.js +3 -1
  95. package/src/core/cli.js.map +1 -1
  96. package/src/core/program.js +15 -13
  97. package/src/core/program.js.map +1 -1
  98. package/src/index.d.ts +2 -0
  99. package/src/index.js +7 -0
  100. package/src/index.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.0.3",
3
+ "version": "1.1.0-beta.1",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -30,17 +30,23 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^0.10.0",
33
- "@frontmcp/utils": "1.0.3",
33
+ "@frontmcp/lazy-zod": "1.1.0-beta.1",
34
+ "@frontmcp/utils": "1.1.0-beta.1",
35
+ "@frontmcp/skills": "1.1.0-beta.1",
34
36
  "commander": "^13.0.0",
35
37
  "tslib": "^2.3.0",
36
38
  "vectoriadb": "^2.2.0",
37
39
  "@rspack/core": "^1.7.6",
38
- "esbuild": "^0.27.3"
40
+ "esbuild": "^0.27.3",
41
+ "yauzl": "^3.2.0",
42
+ "yazl": "^3.3.1"
39
43
  },
40
44
  "devDependencies": {
41
45
  "typescript": "^5.5.3",
42
46
  "tsx": "^4.20.6",
43
- "@types/node": "^24.0.0"
47
+ "@types/node": "^24.0.0",
48
+ "@types/yauzl": "^2.10.3",
49
+ "@types/yazl": "^2.4.5"
44
50
  },
45
51
  "types": "./index.d.js",
46
52
  "type": "commonjs"
@@ -1,4 +1,4 @@
1
- import { AdapterTemplate } from '../types';
1
+ import type { AdapterTemplate } from '../types';
2
2
  /**
3
3
  * Cloudflare Workers adapter - edge deployment on Cloudflare.
4
4
  * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.
@@ -12,6 +12,7 @@ exports.cloudflareAdapter = {
12
12
  getEntryTemplate: (mainModulePath) => `// Auto-generated Cloudflare Workers entry point
13
13
  // Generated by: frontmcp build --target cloudflare
14
14
  process.env.FRONTMCP_SERVERLESS = '1';
15
+ process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
15
16
 
16
17
  require('${mainModulePath}');
17
18
  const { getServerlessHandlerAsync } = require('@frontmcp/sdk');
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;WAIrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --target cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;WAKrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --target cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
@@ -0,0 +1,15 @@
1
+ import type { AdapterTemplate } from '../types';
2
+ /**
3
+ * Distributed adapter - multi-pod Node.js deployment with HA.
4
+ * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.
5
+ *
6
+ * The setup file runs before any imports to ensure the deployment mode
7
+ * is detected before @FrontMcp decorators execute. This enables:
8
+ * - Session heartbeat and takeover
9
+ * - Cross-pod notification relay via Redis pub/sub
10
+ * - LB affinity via __frontmcp_node cookie
11
+ * - Machine ID from K8s HOSTNAME or os.hostname()
12
+ *
13
+ * Requires Redis configuration in @FrontMcp() for session persistence.
14
+ */
15
+ export declare const distributedAdapter: AdapterTemplate;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.distributedAdapter = void 0;
4
+ /**
5
+ * Distributed adapter - multi-pod Node.js deployment with HA.
6
+ * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.
7
+ *
8
+ * The setup file runs before any imports to ensure the deployment mode
9
+ * is detected before @FrontMcp decorators execute. This enables:
10
+ * - Session heartbeat and takeover
11
+ * - Cross-pod notification relay via Redis pub/sub
12
+ * - LB affinity via __frontmcp_node cookie
13
+ * - Machine ID from K8s HOSTNAME or os.hostname()
14
+ *
15
+ * Requires Redis configuration in @FrontMcp() for session persistence.
16
+ */
17
+ exports.distributedAdapter = {
18
+ moduleFormat: 'commonjs',
19
+ getSetupTemplate: () => `// Distributed HA setup - MUST be imported first
20
+ // Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run
21
+ // Enables heartbeat, session takeover, and notification relay
22
+ process.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';
23
+ `,
24
+ getEntryTemplate: (mainModulePath) => `// Auto-generated distributed entry point
25
+ // Generated by: frontmcp build --target distributed
26
+ require('./ha-setup.js');
27
+ require('${mainModulePath}');
28
+ `,
29
+ };
30
+ //# sourceMappingURL=distributed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distributed.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/distributed.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACU,QAAA,kBAAkB,GAAoB;IACjD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;WAGrC,cAAc;CACxB;CACA,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Distributed adapter - multi-pod Node.js deployment with HA.\n * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.\n *\n * The setup file runs before any imports to ensure the deployment mode\n * is detected before @FrontMcp decorators execute. This enables:\n * - Session heartbeat and takeover\n * - Cross-pod notification relay via Redis pub/sub\n * - LB affinity via __frontmcp_node cookie\n * - Machine ID from K8s HOSTNAME or os.hostname()\n *\n * Requires Redis configuration in @FrontMcp() for session persistence.\n */\nexport const distributedAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getSetupTemplate: () => `// Distributed HA setup - MUST be imported first\n// Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run\n// Enables heartbeat, session takeover, and notification relay\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated distributed entry point\n// Generated by: frontmcp build --target distributed\nrequire('./ha-setup.js');\nrequire('${mainModulePath}');\n`,\n};\n"]}
@@ -1,12 +1,13 @@
1
- import { AdapterTemplate, AdapterName } from '../types';
1
+ import type { AdapterTemplate, AdapterName } from '../types';
2
2
  import { nodeAdapter } from './node';
3
3
  import { vercelAdapter } from './vercel';
4
4
  import { lambdaAdapter } from './lambda';
5
5
  import { cloudflareAdapter } from './cloudflare';
6
+ import { distributedAdapter } from './distributed';
6
7
  /**
7
8
  * Registry of all available deployment adapters.
8
9
  * Each adapter configures how the FrontMCP server is compiled and packaged
9
10
  * for a specific deployment target.
10
11
  */
11
12
  export declare const ADAPTERS: Record<AdapterName, AdapterTemplate>;
12
- export { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter };
13
+ export { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter, distributedAdapter };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cloudflareAdapter = exports.lambdaAdapter = exports.vercelAdapter = exports.nodeAdapter = exports.ADAPTERS = void 0;
3
+ exports.distributedAdapter = exports.cloudflareAdapter = exports.lambdaAdapter = exports.vercelAdapter = exports.nodeAdapter = exports.ADAPTERS = void 0;
4
4
  const node_1 = require("./node");
5
5
  Object.defineProperty(exports, "nodeAdapter", { enumerable: true, get: function () { return node_1.nodeAdapter; } });
6
6
  const vercel_1 = require("./vercel");
@@ -9,6 +9,8 @@ const lambda_1 = require("./lambda");
9
9
  Object.defineProperty(exports, "lambdaAdapter", { enumerable: true, get: function () { return lambda_1.lambdaAdapter; } });
10
10
  const cloudflare_1 = require("./cloudflare");
11
11
  Object.defineProperty(exports, "cloudflareAdapter", { enumerable: true, get: function () { return cloudflare_1.cloudflareAdapter; } });
12
+ const distributed_1 = require("./distributed");
13
+ Object.defineProperty(exports, "distributedAdapter", { enumerable: true, get: function () { return distributed_1.distributedAdapter; } });
12
14
  /**
13
15
  * Registry of all available deployment adapters.
14
16
  * Each adapter configures how the FrontMCP server is compiled and packaged
@@ -19,5 +21,6 @@ exports.ADAPTERS = {
19
21
  vercel: vercel_1.vercelAdapter,
20
22
  lambda: lambda_1.lambdaAdapter,
21
23
  cloudflare: cloudflare_1.cloudflareAdapter,
24
+ distributed: distributed_1.distributedAdapter,
22
25
  };
23
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/index.ts"],"names":[],"mappings":";;;AACA,iCAAqC;AAiB5B,4FAjBA,kBAAW,OAiBA;AAhBpB,qCAAyC;AAgBnB,8FAhBb,sBAAa,OAgBa;AAfnC,qCAAyC;AAeJ,8FAf5B,sBAAa,OAe4B;AAdlD,6CAAiD;AAcG,kGAd3C,8BAAiB,OAc2C;AAZrE;;;;GAIG;AACU,QAAA,QAAQ,GAAyC;IAC5D,IAAI,EAAE,kBAAW;IACjB,MAAM,EAAE,sBAAa;IACrB,MAAM,EAAE,sBAAa;IACrB,UAAU,EAAE,8BAAiB;CAC9B,CAAC","sourcesContent":["import { AdapterTemplate, AdapterName } from '../types';\nimport { nodeAdapter } from './node';\nimport { vercelAdapter } from './vercel';\nimport { lambdaAdapter } from './lambda';\nimport { cloudflareAdapter } from './cloudflare';\n\n/**\n * Registry of all available deployment adapters.\n * Each adapter configures how the FrontMCP server is compiled and packaged\n * for a specific deployment target.\n */\nexport const ADAPTERS: Record<AdapterName, AdapterTemplate> = {\n node: nodeAdapter,\n vercel: vercelAdapter,\n lambda: lambdaAdapter,\n cloudflare: cloudflareAdapter,\n};\n\nexport { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/index.ts"],"names":[],"mappings":";;;AACA,iCAAqC;AAmB5B,4FAnBA,kBAAW,OAmBA;AAlBpB,qCAAyC;AAkBnB,8FAlBb,sBAAa,OAkBa;AAjBnC,qCAAyC;AAiBJ,8FAjB5B,sBAAa,OAiB4B;AAhBlD,6CAAiD;AAgBG,kGAhB3C,8BAAiB,OAgB2C;AAfrE,+CAAmD;AAeoB,mGAf9D,gCAAkB,OAe8D;AAbzF;;;;GAIG;AACU,QAAA,QAAQ,GAAyC;IAC5D,IAAI,EAAE,kBAAW;IACjB,MAAM,EAAE,sBAAa;IACrB,MAAM,EAAE,sBAAa;IACrB,UAAU,EAAE,8BAAiB;IAC7B,WAAW,EAAE,gCAAkB;CAChC,CAAC","sourcesContent":["import type { AdapterTemplate, AdapterName } from '../types';\nimport { nodeAdapter } from './node';\nimport { vercelAdapter } from './vercel';\nimport { lambdaAdapter } from './lambda';\nimport { cloudflareAdapter } from './cloudflare';\nimport { distributedAdapter } from './distributed';\n\n/**\n * Registry of all available deployment adapters.\n * Each adapter configures how the FrontMCP server is compiled and packaged\n * for a specific deployment target.\n */\nexport const ADAPTERS: Record<AdapterName, AdapterTemplate> = {\n node: nodeAdapter,\n vercel: vercelAdapter,\n lambda: lambdaAdapter,\n cloudflare: cloudflareAdapter,\n distributed: distributedAdapter,\n};\n\nexport { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter, distributedAdapter };\n"]}
@@ -1,4 +1,4 @@
1
- import { AdapterTemplate } from '../types';
1
+ import type { AdapterTemplate } from '../types';
2
2
  /**
3
3
  * AWS Lambda adapter - serverless deployment on AWS Lambda.
4
4
  * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
@@ -24,6 +24,7 @@ exports.lambdaAdapter = {
24
24
  // This sets FRONTMCP_SERVERLESS before any decorators run
25
25
  // Required because ESM hoists imports before other statements
26
26
  process.env.FRONTMCP_SERVERLESS = '1';
27
+ process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
27
28
  `,
28
29
  getEntryTemplate: (mainModulePath) => `// Auto-generated AWS Lambda entry point
29
30
  // Generated by: frontmcp build --target lambda
@@ -1 +1 @@
1
- {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --target lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
1
+ {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;;CAKzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --target lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { AdapterTemplate } from '../types';
1
+ import type { AdapterTemplate } from '../types';
2
2
  /**
3
3
  * Vercel adapter - serverless deployment on Vercel.
4
4
  * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
@@ -55,6 +55,7 @@ exports.vercelAdapter = {
55
55
  // This sets FRONTMCP_SERVERLESS before any decorators run
56
56
  // Required because ESM hoists imports before other statements
57
57
  process.env.FRONTMCP_SERVERLESS = '1';
58
+ process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
58
59
  `,
59
60
  getEntryTemplate: (mainModulePath) => `// Auto-generated Vercel entry point
60
61
  // Generated by: frontmcp build --target vercel
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs24.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport type { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs24.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
@@ -86,19 +86,52 @@ ${selfContained ? `
86
86
  // using the inlined (bundled) server code — no external requires needed.
87
87
  if (process.env.__FRONTMCP_DAEMON_MODE === '1') {
88
88
  require('reflect-metadata');
89
+ // Suppress @FrontMcp decorator auto-bootstrap — daemon handles bootstrap explicitly below.
90
+ process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
89
91
  var _dMod = require(${JSON.stringify('../' + serverBundleFilename)});
92
+ delete process.env.FRONTMCP_SCHEMA_EXTRACT;
90
93
  var _dSdk = require('@frontmcp/sdk');
91
94
  var _FMI = _dSdk.FrontMcpInstance || _dSdk.default.FrontMcpInstance;
92
95
  var _raw = _dMod.default || _dMod;
93
96
  var _cfg = (typeof _raw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
94
97
  ? (Reflect.getMetadata('__frontmcp:config', _raw) || _raw) : _raw;
95
- var _sp = process.env.FRONTMCP_DAEMON_SOCKET;
96
- _FMI.runUnixSocket(Object.assign({}, _cfg, { socketPath: _sp }))
97
- .then(function() { console.log('Daemon listening on ' + _sp); })
98
- .catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
98
+ var _dp = process.env.FRONTMCP_DAEMON_PORT;
99
+ if (_dp) {
100
+ var _port = parseInt(_dp, 10);
101
+ _cfg = Object.assign({}, _cfg, { http: Object.assign({}, _cfg.http || {}, { port: _port }) });
102
+ process.env.PORT = _dp;
103
+ _FMI.bootstrap(_cfg)
104
+ .then(function() { console.log('Daemon listening on port ' + _port); })
105
+ .catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
106
+ } else {
107
+ var _sp = process.env.FRONTMCP_DAEMON_SOCKET;
108
+ _FMI.runUnixSocket(Object.assign({}, _cfg, { socketPath: _sp }))
109
+ .then(function() { console.log('Daemon listening on ' + _sp); })
110
+ .catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
111
+ }
99
112
  return;
100
113
  }
101
114
  ` : ''}
115
+ // Stdio mode: when --stdio is passed, run as an MCP stdio server (stdin/stdout JSON-RPC).
116
+ // Detected early before commander to avoid overhead and ensure stdout stays clean for MCP protocol.
117
+ // Logs go to ~/.frontmcp/logs/{appName}-*.log — stdout is reserved for MCP JSON-RPC only.
118
+ if (process.argv.includes('--stdio')) {
119
+ // Set app name for file logging before any initialization
120
+ process.env.FRONTMCP_APP_NAME = process.env.FRONTMCP_APP_NAME || ${JSON.stringify(appName)};
121
+ require('reflect-metadata');
122
+ process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
123
+ var _sMod = require(${selfContained ? `${JSON.stringify('../' + serverBundleFilename)}` : `require('path').join(__dirname, ${JSON.stringify(serverBundleFilename)})`});
124
+ delete process.env.FRONTMCP_SCHEMA_EXTRACT;
125
+ var _sSdk = require('@frontmcp/sdk');
126
+ var _sFMI = _sSdk.FrontMcpInstance || _sSdk.default.FrontMcpInstance;
127
+ var _sRaw = _sMod.default || _sMod;
128
+ var _sCfg = (typeof _sRaw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
129
+ ? (Reflect.getMetadata('__frontmcp:config', _sRaw) || _sRaw) : _sRaw;
130
+ _sFMI.runStdio(_sCfg)
131
+ .catch(function(e) { console.error('Stdio server failed:', e); process.exit(1); });
132
+ return;
133
+ }
134
+
102
135
  var { Command, Option } = require('commander');
103
136
  var path = require('path');
104
137
  var fs = require('fs');
@@ -1059,11 +1092,31 @@ function generateServeCommand(serverBundleFilename, selfContained) {
1059
1092
  : `require(path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)}))`;
1060
1093
  return `program
1061
1094
  .command('serve')
1062
- .description('Start the HTTP/SSE server')
1095
+ .description('Start the MCP server')
1063
1096
  .option('-p, --port <port>', 'Port number', function(v) { return parseInt(v, 10); })
1097
+ .option('--stdio', 'Run as stdio transport (stdin/stdout JSON-RPC) for use in .mcp.json')
1064
1098
  .action(async function(opts) {
1099
+ if (opts.stdio) {
1100
+ // --stdio on serve is handled by the early argv check at the top of the entry;
1101
+ // this branch is a fallback in case commander parsed it first.
1102
+ _isLongRunning = true;
1103
+ process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
1104
+ var sMod = ${requireExpr};
1105
+ delete process.env.FRONTMCP_SCHEMA_EXTRACT;
1106
+ var sRaw = sMod.default || sMod;
1107
+ var sSdk = require('@frontmcp/sdk');
1108
+ var sFMI = sSdk.FrontMcpInstance || sSdk.default.FrontMcpInstance;
1109
+ var sCfg = (typeof sRaw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
1110
+ ? (Reflect.getMetadata('__frontmcp:config', sRaw) || sRaw) : sRaw;
1111
+ await sFMI.runStdio(sCfg);
1112
+ return;
1113
+ }
1065
1114
  _isLongRunning = true;
1115
+ // Suppress @FrontMcp decorator auto-bootstrap during require() — the CLI
1116
+ // serve command handles bootstrap explicitly below with port/config overrides.
1117
+ process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
1066
1118
  var mod = ${requireExpr};
1119
+ delete process.env.FRONTMCP_SCHEMA_EXTRACT;
1067
1120
  if (opts.port) process.env.PORT = String(opts.port);
1068
1121
  // If the bundle exports a start() function (@FrontMcp-decorated class auto-bootstraps), use it
1069
1122
  if (typeof mod.start === 'function') { await mod.start(); return; }
@@ -1277,7 +1330,8 @@ function generateDaemonCommands(appName, serverBundleFilename, selfContained) {
1277
1330
 
1278
1331
  daemonCmd
1279
1332
  .command('start')
1280
- .description('Start as a background daemon (Unix socket)')
1333
+ .description('Start as a background daemon')
1334
+ .option('-p, --port <port>', 'Listen on a TCP port instead of a Unix socket', function(v) { return parseInt(v, 10); })
1281
1335
  .option('--idle-timeout <ms>', 'Auto-stop after idle period (ms, 0 to disable)', function(v) { return parseInt(v, 10); }, 300000)
1282
1336
  .action(async function(opts) {
1283
1337
  var { spawn } = require('child_process');
@@ -1289,10 +1343,11 @@ daemonCmd
1289
1343
  fs.mkdirSync(logDir, { recursive: true });
1290
1344
  fs.mkdirSync(socketDir, { recursive: true });
1291
1345
 
1346
+ var usePort = !!opts.port;
1292
1347
  var socketPath = pathMod.join(socketDir, ${JSON.stringify(appName)} + '.sock');
1293
1348
 
1294
- // Clean up stale socket file
1295
- try { fs.unlinkSync(socketPath); } catch (_) { /* ok */ }
1349
+ // Clean up stale socket file (only relevant in socket mode)
1350
+ if (!usePort) { try { fs.unlinkSync(socketPath); } catch (_) { /* ok */ } }
1296
1351
 
1297
1352
  // Check if already running
1298
1353
  var pidPath = pathMod.join(pidDir, ${JSON.stringify(appName)} + '.pid');
@@ -1304,9 +1359,13 @@ daemonCmd
1304
1359
  } catch (_) { /* not running, proceed */ }
1305
1360
 
1306
1361
  var env = Object.assign({}, process.env, {
1307
- FRONTMCP_DAEMON_SOCKET: socketPath,
1308
1362
  FRONTMCP_DAEMON_IDLE_TIMEOUT: String(opts.idleTimeout)
1309
1363
  });
1364
+ if (usePort) {
1365
+ env.FRONTMCP_DAEMON_PORT = String(opts.port);
1366
+ } else {
1367
+ env.FRONTMCP_DAEMON_SOCKET = socketPath;
1368
+ }
1310
1369
 
1311
1370
  var logPath = pathMod.join(logDir, ${JSON.stringify(appName)} + '.log');
1312
1371
  var out = fs.openSync(logPath, 'a');
@@ -1318,20 +1377,33 @@ ${selfContained ? ` // SEA mode: spawn the binary itself in daemon mode — a
1318
1377
  detached: true,
1319
1378
  stdio: ['ignore', out, err],
1320
1379
  env: env
1321
- });` : ` // Start the daemon using runUnixSocket via a small wrapper script
1380
+ });` : ` // Start the daemon via a small wrapper script
1322
1381
  // Always use absolute path for the server bundle (SCRIPT_DIR resolves to __dirname at runtime)
1323
1382
  var serverBundlePath = pathMod.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)});
1324
1383
  var daemonScript = 'require("reflect-metadata");' +
1384
+ 'process.env.FRONTMCP_SCHEMA_EXTRACT="1";' +
1325
1385
  'var mod = require(' + JSON.stringify(serverBundlePath) + ');' +
1386
+ 'delete process.env.FRONTMCP_SCHEMA_EXTRACT;' +
1326
1387
  'var sdk = require("@frontmcp/sdk");' +
1327
1388
  'var FrontMcpInstance = sdk.FrontMcpInstance || sdk.default.FrontMcpInstance;' +
1328
1389
  'var raw = mod.default || mod;' +
1329
1390
  ${'// If the export is a @FrontMcp-decorated class, extract config via Reflect metadata'}
1330
1391
  'var config = (typeof raw === "function" && typeof Reflect !== "undefined" && Reflect.getMetadata) ' +
1331
- ' ? (Reflect.getMetadata("__frontmcp:config", raw) || raw) : raw;' +
1332
- 'FrontMcpInstance.runUnixSocket(Object.assign({}, config, { socketPath: ' + JSON.stringify(socketPath) + ' }))' +
1333
- '.then(function() { console.log("Daemon listening on " + ' + JSON.stringify(socketPath) + '); })' +
1334
- '.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
1392
+ ' ? (Reflect.getMetadata("__frontmcp:config", raw) || raw) : raw;';
1393
+
1394
+ if (usePort) {
1395
+ daemonScript +=
1396
+ 'config = Object.assign({}, config, { http: Object.assign({}, config.http || {}, { port: ' + opts.port + ' }) });' +
1397
+ 'process.env.PORT = ' + JSON.stringify(String(opts.port)) + ';' +
1398
+ 'FrontMcpInstance.bootstrap(config)' +
1399
+ '.then(function() { console.log("Daemon listening on port ' + opts.port + '"); })' +
1400
+ '.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
1401
+ } else {
1402
+ daemonScript +=
1403
+ 'FrontMcpInstance.runUnixSocket(Object.assign({}, config, { socketPath: ' + JSON.stringify(socketPath) + ' }))' +
1404
+ '.then(function() { console.log("Daemon listening on " + ' + JSON.stringify(socketPath) + '); })' +
1405
+ '.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
1406
+ }
1335
1407
 
1336
1408
  var child = spawn('node', ['-e', daemonScript], {
1337
1409
  detached: true,
@@ -1343,26 +1415,40 @@ ${selfContained ? ` // SEA mode: spawn the binary itself in daemon mode — a
1343
1415
  fs.closeSync(out);
1344
1416
  fs.closeSync(err);
1345
1417
 
1346
- fs.writeFileSync(pidPath, JSON.stringify({
1347
- pid: child.pid,
1348
- socketPath: socketPath,
1349
- startedAt: new Date().toISOString()
1350
- }));
1351
- child.unref();
1352
-
1353
- // Wait for socket file to appear (max 5s)
1354
- var waited = 0;
1355
- while (!fs.existsSync(socketPath) && waited < 5000) {
1356
- await new Promise(function(r) { setTimeout(r, 100); });
1357
- waited += 100;
1418
+ var pidData = { pid: child.pid, startedAt: new Date().toISOString() };
1419
+ if (usePort) {
1420
+ pidData.port = opts.port;
1421
+ } else {
1422
+ pidData.socketPath = socketPath;
1358
1423
  }
1424
+ fs.writeFileSync(pidPath, JSON.stringify(pidData));
1425
+ child.unref();
1359
1426
 
1360
- if (fs.existsSync(socketPath)) {
1361
- console.log('Daemon started (PID: ' + child.pid + '). Socket: ' + socketPath);
1362
- console.log('Logs: ' + logPath);
1427
+ if (usePort) {
1428
+ // For port mode, wait briefly then check if process is still alive
1429
+ await new Promise(function(r) { setTimeout(r, 1000); });
1430
+ try {
1431
+ process.kill(child.pid, 0);
1432
+ console.log('Daemon started (PID: ' + child.pid + '). Port: ' + opts.port);
1433
+ console.log('Logs: ' + logPath);
1434
+ } catch (_) {
1435
+ console.log('Daemon failed to start. Check logs: ' + logPath);
1436
+ }
1363
1437
  } else {
1364
- console.log('Daemon started (PID: ' + child.pid + ') but socket not yet available.');
1365
- console.log('Check logs: ' + logPath);
1438
+ // Wait for socket file to appear (max 5s)
1439
+ var waited = 0;
1440
+ while (!fs.existsSync(socketPath) && waited < 5000) {
1441
+ await new Promise(function(r) { setTimeout(r, 100); });
1442
+ waited += 100;
1443
+ }
1444
+
1445
+ if (fs.existsSync(socketPath)) {
1446
+ console.log('Daemon started (PID: ' + child.pid + '). Socket: ' + socketPath);
1447
+ console.log('Logs: ' + logPath);
1448
+ } else {
1449
+ console.log('Daemon started (PID: ' + child.pid + ') but socket not yet available.');
1450
+ console.log('Check logs: ' + logPath);
1451
+ }
1366
1452
  }
1367
1453
  });
1368
1454
 
@@ -1396,8 +1482,8 @@ daemonCmd
1396
1482
  var data = JSON.parse(fs.readFileSync(pidPath, 'utf8'));
1397
1483
  try {
1398
1484
  process.kill(data.pid, 0);
1399
- var socketStatus = data.socketPath && fs.existsSync(data.socketPath) ? ', socket: active' : '';
1400
- console.log('Running (PID: ' + data.pid + ', started: ' + data.startedAt + socketStatus + ')');
1485
+ var listenInfo = data.port ? ', port: ' + data.port : (data.socketPath && fs.existsSync(data.socketPath) ? ', socket: active' : '');
1486
+ console.log('Running (PID: ' + data.pid + ', started: ' + data.startedAt + listenInfo + ')');
1401
1487
  } catch (_) {
1402
1488
  console.log('Not running (stale PID file).');
1403
1489
  fs.unlinkSync(pidPath);
@@ -1431,7 +1517,9 @@ program.parseAsync(process.argv).then(async function() {
1431
1517
  // on unclosed handles (file loggers, in-memory transport, etc.).
1432
1518
  if (_isLongRunning) return;
1433
1519
  await closeClient();
1434
- process.exit(process.exitCode || 0);
1520
+ // Defer process.exit() by one event-loop tick so native addon destructors
1521
+ // (ONNX runtime, etc.) can release mutexes before V8 tears down.
1522
+ setImmediate(function() { process.exit(process.exitCode || 0); });
1435
1523
  }).catch(async function(err) {
1436
1524
  console.error('Fatal:', err.message || err);
1437
1525
  await closeClient();