@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.
- package/generators/server/files/lambda/template.yaml__tmpl__ +11 -1
- package/generators/server/files/node/Dockerfile__tmpl__ +2 -0
- package/generators/server/files/node/docker-compose.yml__tmpl__ +8 -1
- package/generators/server/server.js +5 -4
- package/generators/server/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|
-
#
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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":";;
|
|
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.
|
|
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.
|
|
36
|
+
"@frontmcp/skills": "1.9.4",
|
|
37
37
|
"@nx/devkit": "22.6.4",
|
|
38
38
|
"tslib": "^2.3.0"
|
|
39
39
|
},
|