@frontmcp/nx 1.9.2 → 1.9.4

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.
@@ -2,18 +2,28 @@ AWSTemplateFormatVersion: '2010-09-09'
2
2
  Transform: AWS::Serverless-2016-10-31
3
3
  Description: '<%= className %> FrontMCP Server'
4
4
 
5
+ Parameters:
6
+ McpSessionSecret:
7
+ Type: String
8
+ NoEcho: true
9
+ Description: MCP_SESSION_SECRET for session-ID encryption, required in production. Generate it once (openssl rand -hex 32) and pass the same value on every deploy; a new value ends every open session
10
+
5
11
  Globals:
6
12
  Function:
7
13
  Timeout: 30
8
14
  Runtime: nodejs24.x
9
15
  MemorySize: 256
16
+ Environment:
17
+ Variables:
18
+ NODE_ENV: production
19
+ MCP_SESSION_SECRET: !Ref McpSessionSecret
10
20
 
11
21
  Resources:
12
22
  McpFunction:
13
23
  Type: AWS::Serverless::Function
14
24
  Properties:
15
25
  # `nx build <%= projectName %>` (target lambda) writes dist/lambda/handler.cjs, which exports `handler`.
16
- # The bundle requires @codegenie/serverless-express at runtime without bundling it: add a Lambda layer that provides it.
26
+ # It bundles @codegenie/serverless-express, so the function needs no layer.
17
27
  CodeUri: dist/lambda/
18
28
  Handler: handler.handler
19
29
  Events:
@@ -20,5 +20,7 @@ COPY --from=builder /app/node_modules ./node_modules
20
20
  COPY --from=builder /app/<%= projectRoot %>/dist ./dist
21
21
  USER appuser
22
22
  EXPOSE 3000
23
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
24
+ CMD node -e "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/health').then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))"
23
25
  # `nx build <%= projectName %>` (target node) writes dist/node/<%= projectName %>.bundle.js
24
26
  CMD ["node", "dist/node/<%= projectName %>.bundle.js"]
@@ -8,6 +8,8 @@ services:
8
8
  environment:
9
9
  - NODE_ENV=${NODE_ENV:-production}
10
10
  - PORT=${PORT:-3000}
11
+ # Required in production (session-ID encryption). Compose stops here when it is unset.
12
+ - MCP_SESSION_SECRET=${MCP_SESSION_SECRET:?set MCP_SESSION_SECRET (openssl rand -hex 32)}
11
13
  <% if (redis === 'docker') { %>
12
14
  - REDIS_HOST=redis
13
15
  - REDIS_PORT=6379
@@ -17,8 +19,10 @@ services:
17
19
 
18
20
  redis:
19
21
  image: redis:7-alpine
22
+ # Loopback only: the app reaches Redis over the compose network, and a
23
+ # Redis without a password must not listen on the host's public interfaces.
20
24
  ports:
21
- - '6379:6379'
25
+ - '127.0.0.1:6379:6379'
22
26
  volumes:
23
27
  - redis-data:/data
24
28
  command: redis-server --appendonly yes
@@ -32,6 +36,9 @@ volumes:
32
36
  redis-data:
33
37
  <% } %>
34
38
 
39
+ # Generate MCP_SESSION_SECRET once and keep it in a .env file next to this file (a new value ends every open session):
40
+ # echo "MCP_SESSION_SECRET=$(openssl rand -hex 32)" >> .env
41
+ # docker compose up --build
35
42
  # Selective rebuild:
36
43
  # docker compose up --build app # rebuild only the app
37
44
  # docker compose up --build # rebuild everything
@@ -34,13 +34,14 @@ async function serverGeneratorInternal(tree, schema) {
34
34
  scaffoldCatalogSkills(tree, options.projectRoot, options.deploymentTarget, bundle);
35
35
  }
36
36
  // The lambda build wraps the server with @codegenie/serverless-express and fails without it.
37
- const installTask = options.deploymentTarget === 'lambda'
38
- ? (0, add_dependencies_js_1.addFrontmcpDependencies)(tree, (0, versions_js_1.getLambdaDependencies)(), {}, { keepExistingVersions: true })
39
- : undefined;
37
+ if (options.deploymentTarget === 'lambda') {
38
+ (0, add_dependencies_js_1.addFrontmcpDependencies)(tree, (0, versions_js_1.getLambdaDependencies)(), {}, { keepExistingVersions: true });
39
+ }
40
40
  if (!options.skipFormat) {
41
41
  await (0, devkit_1.formatFiles)(tree);
42
42
  }
43
- return installTask;
43
+ // The shell's package.json joins the workspace: install, so the lock file the Dockerfile installs from lists it.
44
+ return () => (0, devkit_1.installPackagesTask)(tree, true);
44
45
  }
45
46
  function scaffoldCatalogSkills(tree, projectRoot, target, bundle) {
46
47
  // Load skills catalog via @frontmcp/skills package at runtime
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../src/generators/server/server.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { join } from 'path';\n\nimport { formatFiles, generateFiles, names as nxNames, type GeneratorCallback, type Tree } from '@nx/devkit';\n\nimport { addFrontmcpDependencies } from '../../utils/add-dependencies.js';\nimport { ensureYarnBerryPinned } from '../../utils/package-manager.js';\nimport { getLambdaDependencies } from '../../utils/versions.js';\nimport { normalizeOptions } from './lib/index.js';\nimport type { ServerGeneratorSchema } from './schema.js';\n\nexport async function serverGenerator(tree: Tree, schema: ServerGeneratorSchema): Promise<GeneratorCallback | void> {\n return serverGeneratorInternal(tree, schema);\n}\n\nasync function serverGeneratorInternal(tree: Tree, schema: ServerGeneratorSchema): Promise<GeneratorCallback | void> {\n const options = normalizeOptions(tree, schema);\n\n const templateVars = {\n ...options,\n names: nxNames,\n tmpl: '',\n dot: '.',\n };\n\n // Generate common files (main.ts, project.json, tsconfig)\n generateFiles(tree, join(__dirname, 'files', 'common'), options.projectRoot, templateVars);\n\n // Generate target-specific files\n const targetDir = join(__dirname, 'files', options.deploymentTarget);\n generateFiles(tree, targetDir, options.projectRoot, templateVars);\n if (options.deploymentTarget === 'node') {\n ensureYarnBerryPinned(tree);\n }\n\n // Copy skills from catalog\n const bundle = schema.skills ?? 'recommended';\n if (bundle !== 'none') {\n scaffoldCatalogSkills(tree, options.projectRoot, options.deploymentTarget, bundle);\n }\n\n // The lambda build wraps the server with @codegenie/serverless-express and fails without it.\n const installTask =\n options.deploymentTarget === 'lambda'\n ? addFrontmcpDependencies(tree, getLambdaDependencies(), {}, { keepExistingVersions: true })\n : undefined;\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n\n return installTask;\n}\n\nfunction scaffoldCatalogSkills(tree: Tree, projectRoot: string, target: string, bundle: string): void {\n // Load skills catalog via @frontmcp/skills package at runtime\n let skills: {\n loadManifest: () => {\n skills: Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n };\n resolveSkillPath: (entry: { path: string }) => string;\n getSkillsByTarget: (\n s: Array<{ targets: string[] }>,\n t: string,\n ) => Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n getSkillsByBundle: (\n s: Array<{ bundle?: string[] }>,\n b: string,\n ) => Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n };\n try {\n skills = require('@frontmcp/skills');\n } catch {\n return;\n }\n\n let manifest;\n try {\n manifest = skills.loadManifest();\n } catch {\n return;\n }\n\n const targetFiltered = skills.getSkillsByTarget(manifest.skills, target);\n const matchingSkills = skills.getSkillsByBundle(targetFiltered, bundle);\n\n for (const skill of matchingSkills) {\n const sourceDir = skills.resolveSkillPath(skill);\n const destDir = join(projectRoot, 'skills', skill.name);\n copyDirToTree(tree, sourceDir, destDir);\n }\n}\n\nfunction copyDirToTree(tree: Tree, sourceDir: string, destDir: string): void {\n if (!fs.existsSync(sourceDir)) return;\n const entries = fs.readdirSync(sourceDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(sourceDir, entry.name);\n const destPath = join(destDir, entry.name);\n if (entry.isDirectory()) {\n copyDirToTree(tree, srcPath, destPath);\n } else {\n const content = fs.readFileSync(srcPath);\n tree.write(destPath, content);\n }\n }\n}\n\nexport default serverGenerator;\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../src/generators/server/server.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { join } from 'path';\n\nimport {\n formatFiles,\n generateFiles,\n installPackagesTask,\n names as nxNames,\n type GeneratorCallback,\n type Tree,\n} from '@nx/devkit';\n\nimport { addFrontmcpDependencies } from '../../utils/add-dependencies.js';\nimport { ensureYarnBerryPinned } from '../../utils/package-manager.js';\nimport { getLambdaDependencies } from '../../utils/versions.js';\nimport { normalizeOptions } from './lib/index.js';\nimport type { ServerGeneratorSchema } from './schema.js';\n\nexport async function serverGenerator(tree: Tree, schema: ServerGeneratorSchema): Promise<GeneratorCallback | void> {\n return serverGeneratorInternal(tree, schema);\n}\n\nasync function serverGeneratorInternal(tree: Tree, schema: ServerGeneratorSchema): Promise<GeneratorCallback | void> {\n const options = normalizeOptions(tree, schema);\n\n const templateVars = {\n ...options,\n names: nxNames,\n tmpl: '',\n dot: '.',\n };\n\n // Generate common files (main.ts, project.json, tsconfig)\n generateFiles(tree, join(__dirname, 'files', 'common'), options.projectRoot, templateVars);\n\n // Generate target-specific files\n const targetDir = join(__dirname, 'files', options.deploymentTarget);\n generateFiles(tree, targetDir, options.projectRoot, templateVars);\n if (options.deploymentTarget === 'node') {\n ensureYarnBerryPinned(tree);\n }\n\n // Copy skills from catalog\n const bundle = schema.skills ?? 'recommended';\n if (bundle !== 'none') {\n scaffoldCatalogSkills(tree, options.projectRoot, options.deploymentTarget, bundle);\n }\n\n // The lambda build wraps the server with @codegenie/serverless-express and fails without it.\n if (options.deploymentTarget === 'lambda') {\n addFrontmcpDependencies(tree, getLambdaDependencies(), {}, { keepExistingVersions: true });\n }\n\n if (!options.skipFormat) {\n await formatFiles(tree);\n }\n\n // The shell's package.json joins the workspace: install, so the lock file the Dockerfile installs from lists it.\n return () => installPackagesTask(tree, true);\n}\n\nfunction scaffoldCatalogSkills(tree: Tree, projectRoot: string, target: string, bundle: string): void {\n // Load skills catalog via @frontmcp/skills package at runtime\n let skills: {\n loadManifest: () => {\n skills: Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n };\n resolveSkillPath: (entry: { path: string }) => string;\n getSkillsByTarget: (\n s: Array<{ targets: string[] }>,\n t: string,\n ) => Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n getSkillsByBundle: (\n s: Array<{ bundle?: string[] }>,\n b: string,\n ) => Array<{ name: string; path: string; targets: string[]; hasResources: boolean; bundle?: string[] }>;\n };\n try {\n skills = require('@frontmcp/skills');\n } catch {\n return;\n }\n\n let manifest;\n try {\n manifest = skills.loadManifest();\n } catch {\n return;\n }\n\n const targetFiltered = skills.getSkillsByTarget(manifest.skills, target);\n const matchingSkills = skills.getSkillsByBundle(targetFiltered, bundle);\n\n for (const skill of matchingSkills) {\n const sourceDir = skills.resolveSkillPath(skill);\n const destDir = join(projectRoot, 'skills', skill.name);\n copyDirToTree(tree, sourceDir, destDir);\n }\n}\n\nfunction copyDirToTree(tree: Tree, sourceDir: string, destDir: string): void {\n if (!fs.existsSync(sourceDir)) return;\n const entries = fs.readdirSync(sourceDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(sourceDir, entry.name);\n const destPath = join(destDir, entry.name);\n if (entry.isDirectory()) {\n copyDirToTree(tree, srcPath, destPath);\n } else {\n const content = fs.readFileSync(srcPath);\n tree.write(destPath, content);\n }\n }\n}\n\nexport default serverGenerator;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/nx",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "description": "Nx plugin for FrontMCP — generators and executors for building MCP servers",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -33,7 +33,7 @@
33
33
  "node": ">=24.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@frontmcp/skills": "1.9.2",
36
+ "@frontmcp/skills": "1.9.4",
37
37
  "@nx/devkit": "22.6.4",
38
38
  "tslib": "^2.3.0"
39
39
  },