Nx generators

@frontmcp/nx has 21 generators. init prepares a workspace you already have. The structural ones make projects: a workspace, an app in apps/, a library in libs/, a deployment shell in servers/. The component ones write one class into an existing project, like a tool or a job, with TODOs to fill in. The UI ones write React components and HTML shells under ui/. Each was run with its defaults and with the options that change its output, against @frontmcp/sdk 1.9.2: the projects were built, type-checked and tested with their own targets, the component classes were type-checked and listed in an app that was started, and a server shell's Docker image was built and run. Everything passes in a frontmcp create --nx workspace and in a create-nx-workspace --preset=ts one. The server generator was run again with 1.9.3, and its shells built and ran. Changed in 1.9: apps and libraries come with a test and a typecheck target, all four library types type-check, server writes deployment files that match the build and a dev target, and ui-shell installs. Changed in 1.9.2: lib names its alias after the workspace's npm scope, a shell's Dockerfile uses the workspace's package manager, and UI packages build in a --preset=ts workspace. Changed in 1.9.3: server runs your install after it writes the shell, and its Docker, compose and Lambda files match those of frontmcp create.

nx g @frontmcp/nx:<generator> <name> [--project <project>] [options]

nx g @frontmcp/nx:app tickets
nx g @frontmcp/nx:tool search_tickets --project tickets
nx g @frontmcp/nx:server gateway --apps tickets,billing --deploymentTarget node

Reference

Options every generator takes

OptionDescription
nameThe first argument. Nx asks for it when it's missing.
--project <project>Component generators: the Nx project to write into. Required: without it, Required property 'project' is missing; an unknown one, Project "…" not found in the workspace.
--directory <dir>Component generators: a subfolder of the kind's folder, like src/tools/<dir>/. Structural generators: the project's folder, at any depth.
--skipFormatDon't run Prettier on the files written.
--dry-runNx's own: list the files without writing them.
--no-interactiveNx's own: don't ask for missing options; fail instead.

Names are turned into a file name and a class name: search_tickets writes search-tickets.tool.ts with class SearchTicketsTool. The name clients see is the name exactly as you typed it, so type it the way you want it listed: search_tickets, not search-tickets.

No generator registers what it writes. A new tool isn't listed until you add it to its app's @App({ tools }), and the same goes for resources, prompts, skills, jobs, workflows and the rest.

Structural generators

init

nx g @frontmcp/nx:init

Prepares an existing workspace. nx add @frontmcp/nx runs it for you. It adds the FrontMCP and Jest packages to package.json and installs them, keeping any version you already have, and adds cacheable targetDefaults for the executors to nx.json. Nx plugin lists what it writes. Running it again changes nothing, and it keeps the targetDefaults entries it finds.

OptionTypeDefaultDescription
--skipPackageJsonbooleanfalseDon't add dependencies to package.json, and install nothing. nx.json is still updated.
--skipFormatbooleanfalse

workspace

nx g @frontmcp/nx:workspace help-desk-platform --skipInstall

Writes a new workspace into <name>/, below the current folder. It's what frontmcp create --nx runs; Nx plugin lists what it writes.

OptionTypeDefaultDescription
namestringThe folder and the root package.json name.
--packageManagernpm, yarn, pnpm, bunnpmThe workspace's package manager, written to nx.json as cli.packageManager.
--skipInstallbooleanfalseDon't install the dependencies.
--skipGitbooleanfalseDon't run git init.
--createSampleAppbooleantrueAdd apps/demo with a hello tool. false leaves apps/, libs/ and servers/ empty.

app

nx g @frontmcp/nx:app tickets
CREATE apps/tickets/jest.config.cjs
CREATE apps/tickets/package.json
CREATE apps/tickets/project.json
CREATE apps/tickets/src/tickets.app.ts
CREATE apps/tickets/src/main.ts
CREATE apps/tickets/src/tools/hello.tool.spec.ts
CREATE apps/tickets/src/tools/hello.tool.ts
CREATE apps/tickets/tsconfig.json
CREATE apps/tickets/tsconfig.lib.json
CREATE apps/tickets/tsconfig.spec.json

src/main.ts is a @FrontMcp server named after the app (Tickets), src/tickets.app.ts an @App with id tickets and a hello tool, hello.tool.spec.ts a unit test of that tool, package.json holds the name and version 0.0.1 the CLI names the bundle with, jest.config.cjs is what nx test runs, and project.json has the targets build, dev, serve, test, typecheck and inspector (Executors), with cache: true on build, test and typecheck. typecheck runs tsc --noEmit on tsconfig.lib.json and tsconfig.spec.json.

apps/tickets/src/tools/hello.tool.spec.ts
import HelloTool from './hello.tool';

describe('HelloTool', () => {
  it('greets the caller by name', async () => {
    // execute() holds the tool's logic; build the tool without the request context the server injects.
    const tool: HelloTool = Object.create(HelloTool.prototype);

    await expect(tool.execute({ name: 'Ada' })).resolves.toEqual({ message: 'Hello, Ada!' });
  });
});
OptionTypeDefaultDescription
namestringThe project's name, the app's id, and the folder name.
--directorystringapps/<name>The folder, at any depth: apps/team/billing gets extends: "../../../tsconfig.base.json" and a $schema with three ../.
--tagsstringscope:appsComma-separated tags for project.json. They replace scope:apps.
--workspaceRootstringThe folder of the Nx workspace when it isn't the tree root.
--skipFormatbooleanfalse

It doesn't add dependencies: init does that once for the workspace. Its tsconfig.json overrides what a TypeScript-solution tsconfig.base.json sets, so the app builds in a create-nx-workspace --preset=ts workspace too (Nx plugin).

lib

nx g @frontmcp/nx:lib ticket-model --importPath @help-desk/ticket-model
CREATE libs/ticket-model/jest.config.cjs
CREATE libs/ticket-model/project.json
CREATE libs/ticket-model/tsconfig.json
CREATE libs/ticket-model/tsconfig.lib.json
CREATE libs/ticket-model/tsconfig.spec.json
CREATE libs/ticket-model/src/ticket-model.spec.ts
CREATE libs/ticket-model/src/ticket-model.ts
CREATE libs/ticket-model/src/index.ts
UPDATE tsconfig.base.json

It adds the alias to tsconfig.base.json, written from the workspace root ("@help-desk/ticket-model": ["./libs/ticket-model/src/index.ts"]), and writes a project.json with two targets: test (@frontmcp/nx:test) and typecheck, the same as an app's. A library has no build: an app that imports it compiles it into its own bundle (Monorepo patterns). Before 1.9 the test target was @nx/jest:jest, which nx add never installed, and the library got its build and typecheck from Nx's TypeScript plugin.

OptionTypeDefaultDescription
namestring
--directorystringlibs/<name>
--libTypegeneric, plugin, adapter, tool-registergenericWhat src/ holds, each with a spec beside it: <name>.ts with a class; <name>.plugin.ts; <name>.adapter.ts; or <name>.tools.ts with an example tool and a <Name>Tools array to spread into an app's tools.
--publishablebooleanfalseAdds the tag scope:publishable, a package.json named after --importPath (version 0.0.1, main ./dist/index.js), and a build target that runs tsc -p tsconfig.lib.json into the library's dist/.
--importPathstring<scope>/<name>, else <name>The alias to add, like @help-desk/schemas. The default takes the npm scope of the workspace's root package.json: @org/util in a workspace named @org/source. A workspace from frontmcp create --nx is named without a scope (help-desk-platform), so there the default is the bare name, ticket-model. It refuses an alias tsconfig.base.json already maps (The import path "ticket-model" is already mapped in tsconfig.base.json to ./libs/ticket-model/src/index.ts. Pass --importPath with another name.) and one that names a package the workspace depends on (The import path "@frontmcp/sdk" is a package this workspace depends on; the library would shadow it.). Before 1.9.2 the default was @frontmcp/<name>, under FrontMCP's own scope.
--tagsstringscope:libsThey replace scope:libs; scope:publishable is added after them.
--skipFormatbooleanfalse

All four types type-check and pass their tests against @frontmcp/sdk 1.9.2. plugin and adapter write the same classes as the component generators plugin and adapter; in 1.8.7 they wrote older copies that imported names the SDK doesn't export (TS2614 on PluginRegistrationContext and AdapterFetchResult).

server

nx g @frontmcp/nx:server gateway --apps demo,tickets
CREATE servers/gateway/package.json
CREATE servers/gateway/project.json
CREATE servers/gateway/src/main.ts
CREATE servers/gateway/tsconfig.json
CREATE servers/gateway/tsconfig.lib.json
CREATE servers/gateway/Dockerfile.dockerignore
CREATE servers/gateway/Dockerfile
CREATE servers/gateway/docker-compose.yml
CREATE servers/gateway/skills/create-tool/SKILL.md
…

A deployment shell: a @FrontMcp server that lists the apps (Gateway Server), the files its target needs, and a project.json named server-<name>, tagged scope:servers, with the targets build (with target: "<target>"), dev, typecheck, and deploy, which depends on build. main.ts imports each app from ../../../apps/<app>/src/<app>.app.

OptionTypeDefaultDescription
namestringThe folder; the project is server-<name>.
--appsstringRequired. Comma-separated app names to import and list in apps.
--deploymentTargetnode, vercel, lambda, cloudflarenodenode writes Dockerfile, Dockerfile.dockerignore and docker-compose.yml; vercel writes vercel.json; lambda writes template.yaml and adds @codegenie/serverless-express ^5.0.0 to the root package.json, which the generator's install puts in node_modules (since 1.9.3; before, you ran it); cloudflare writes wrangler.toml.
--redisdocker, existing, nonenonenode only. docker adds a redis:7-alpine service to docker-compose.yml, published on 127.0.0.1:6379 only (since 1.9.3; before, on every interface), with REDIS_HOST=redis for the app.
--skillsrecommended, minimal, full, nonerecommendedCopies catalog skills into servers/<name>/skills/: 9 skills and 348 files by default, 4 and 193 for minimal, 13 and 376 for full.
--directorystringservers/<name>
--tagsstringscope:servers
--skipFormatbooleanfalse

nx build server-gateway builds each app first (dependsOn: ["^build"]), then the shell: one bundle, servers/gateway/dist/node/server-gateway.bundle.js, with the apps and any library they import compiled in. Before 1.8.7 the shell's imports didn't resolve, its build target passed an --adapter flag the CLI rejected, and the build stopped with TS6059.

The files written for the platform name what the build writes:

--deploymentTargetFileWhat it does
nodeDockerfile, Dockerfile.dockerignoreBuilt from the workspace root (docker build -f servers/gateway/Dockerfile .): copies the workspace, installs with the workspace's package manager (npm ci --ignore-scripts in an npm workspace; pnpm, Yarn and bun get their own commands), runs nx build server-gateway, prunes the dev dependencies, and starts node dist/node/server-gateway.bundle.js as an unprivileged appuser, with FRONTMCP_BIND_ADDRESS=all, so a published port reaches it. Its HEALTHCHECK asks /health with Node's fetch, as in the Dockerfile of frontmcp create (since 1.9.3; before, it had none). Give the container MCP_SESSION_SECRET. Docker reads Dockerfile.dockerignore for this Dockerfile, which keeps node_modules and dist out of the context.
nodedocker-compose.ymlBuilds that image with the workspace root as context, publishes port 3000 (PORT), and sets NODE_ENV to production unless you set it. It requires MCP_SESSION_SECRET, from your shell or a .env file beside it, and without one stops with required variable MCP_SESSION_SECRET is missing a value: set MCP_SESSION_SECRET (openssl rand -hex 32). Its comment says to generate the secret once and keep it, since a new value ends every open session. (In 1.9.2 it passed none.)
vercelvercel.jsoninstallCommand and buildCommand run from the workspace root: cd ../../ && npm install and cd ../../ && npx nx build server-gateway in an npm workspace.
lambdatemplate.yamlCodeUri: dist/lambda/ and Handler: handler.handler, the handler.cjs the build writes, with NODE_ENV: production and MCP_SESSION_SECRET from a required NoEcho parameter, McpSessionSecret, as in the template of frontmcp create (AWS Lambda). Its comment says the function needs no layer, since the build puts @codegenie/serverless-express in handler.cjs. (In 1.9.2 it set neither variable, and its comment asked for a layer.)
cloudflarewrangler.tomlmain = "dist/cloudflare/index.js", compatibility_date = "2024-11-11" (since 1.9.2) and compatibility_flags = ["nodejs_compat"], and NODE_ENV = "production" in [vars].

The Dockerfile copies the workspace before it installs, so the lock file has to list every project's package.json, the new shell's included. Since 1.9.3 the generator runs your package manager's install after it writes the shell (in an npm workspace it printed added 1 package), so the lock file lists it. The image, built straight after nx g, started as appuser, answered /health and MCP at / on the published port, docker inspect reported it healthy, and docker stop ended it at once with exit code 0. In 1.9.2 the generator didn't install, and the image stopped at npm ci with Missing: server-gateway@0.0.1 from lock file until you did. Before 1.9.2 the Dockerfile assumed npm whatever the workspace used. In 1.8.7 it ran a dist/main.js nothing writes, vercel.json and template.yaml pointed at files the build doesn't write either, .dockerignore sat where Docker doesn't read it, and the shell had no dev target.

Component generators

Each writes one file (two for plugin --withContextExtension) into <project root>/src/<folder>/, or <project root>/skills/ for skill-dir. All thirteen outputs pass the app's nx typecheck, and an app that lists eleven of them in @App starts, with jobs: { enabled: true } on its server for the job and the workflow, and OPENAI_API_KEY set for the agent. skill-dir writes a folder, and no @App option takes a flow (flow). They write no test.

GeneratorWritesOptions besides the common ones
toolsrc/tools/<name>.tool.ts
resourcesrc/resources/<name>.resource.ts--template
promptsrc/prompts/<name>.prompt.ts--arguments
skillsrc/skills/<name>.skill.ts--tools
skill-dirskills/<name>/SKILL.md--description, --tags, --withReferences
jobsrc/jobs/<name>.job.ts
workflowsrc/workflows/<name>.workflow.ts
agentsrc/agents/<name>.agent.ts--model, --tools
providersrc/providers/<name>.provider.ts--scope
pluginsrc/plugins/<name>.plugin.ts--withContextExtension
adaptersrc/adapters/<name>.adapter.ts
auth-providersrc/auth/<name>.auth-provider.ts--type
flowsrc/flows/<name>.flow.ts

The last six wrote classes that didn't type-check before 1.8.7 (agent, provider, plugin, adapter, auth-provider, flow). Registering a class is still your job, and some need more than the list: an agent needs its key, a workflow needs your jobs.

tool

apps/tickets/src/tools/search-tickets.tool.ts
import { Tool, ToolContext, ToolInputOf, ToolOutputOf, z } from '@frontmcp/sdk';

const inputSchema = {
  value: z.string().describe('TODO: replace with actual input'),
};

const outputSchema = {
  result: z.string().describe('TODO: replace with actual output'),
};

export type SearchTicketsInput = ToolInputOf<{ inputSchema: typeof inputSchema }>;
export type SearchTicketsOutput = ToolOutputOf<{ outputSchema: typeof outputSchema }>;

@Tool({
  name: 'search_tickets',
  description: 'TODO: describe what this tool does',
  inputSchema,
  outputSchema,
})
export default class SearchTicketsTool extends ToolContext {
  async execute(input: SearchTicketsInput): Promise<SearchTicketsOutput> {
    // TODO: implement
    return { result: input.value };
  }
}

(Trimmed: the file also has a comment about hoisting the schemas.) It's a default export. @Tool has every option; Usage registers one.

resource

Without --template: a @Resource at <name>://data, returning {} as JSON. With --template: a @ResourceTemplate at <name>://{id} that returns { id }. Both are default exports.

prompt

A @Prompt returning one user message with TODO text. --arguments ticketId,tone declares each argument, all with required: true. The file imports its return type from @modelcontextprotocol/sdk/types.js, a package @frontmcp/sdk depends on. It resolves when your package manager hoists dependencies, as npm does; otherwise install it, or drop the annotation.

skill

A @Skill with TODO instructions and an empty class body. --tools search_tickets,close_ticket fills tools; use the tools' listed names.

skill-dir

apps/tickets/skills/escalation/SKILL.md
---
name: escalation
description: How to escalate a ticket
tags: [support, escalation]
---

# escalation

Add your skill instructions here.

(Trimmed: it ends with a Steps list of three placeholders.) --withReferences adds a references/ folder with only a .gitkeep in it, --directory another folder under the project. The server doesn't find the folder on its own: load it with skillDir() and list the result in an app's skills. skillDir() reads SKILL.md and the references/ folder too: each file you put there is listed in a table under the skill's instructions and served at skill://escalation/references/<file>. The .gitkeep isn't listed.

job

A @Job with a value input and a result output, returning Processed: <value>. Registered in an app with jobs on, it adds execute_job and get_job_status to the tools.

workflow

A @Workflow with trigger: 'manual' and two steps, step1 and step2, that run the jobs TODO-first-job and TODO-second-job, the second reading the first's result. The server starts with it registered, and adds execute_workflow and get_workflow_status to the tools; point the steps at your own jobs before running it.

agent

@Agent({
  name: 'desk-agent',
  description: 'TODO: describe what this agent does',
  llm: { provider: 'openai', model: 'gpt-4', apiKey: { env: 'OPENAI_API_KEY' } },
  inputSchema: { task: z.string().describe('TODO: describe the task the agent should perform') },
  systemInstructions: 'TODO: define system instructions for this agent',
})

--model (default gpt-4) fills llm.model, and --tools search_tickets imports that tool's class and adds tools: [SearchTicketsTool]. The execute() calls super.execute(input), which runs the model's loop. A server that lists the agent needs OPENAI_API_KEY: without it, startup fails with Failed to create LLM adapter for agent desk-agent and Environment variable OPENAI_API_KEY is not set. See @Agent.

provider

--scope is singleton (the default, written as ProviderScope.GLOBAL), request or context (both ProviderScope.CONTEXT). The class is @Provider({ name: '<Name>Provider', scope }), with a comment on registering it in providers and resolving it with this.get(<Name>Provider). See @Provider.

plugin

A DynamicPlugin subclass with options and a static dynamicProviders() that returns an empty list. --withContextExtension adds <name>.context-extension.ts, which declares this.<name> on every context and exports install<Name>ContextExtension(), which defines it on ExecutionContextBase.prototype. The generated plugin doesn't call it: call it once at startup. See @Plugin.

adapter

A DynamicAdapter subclass whose constructor takes { name } & Options and whose fetch() returns empty tools, resources and prompts. List it in an app as adapters: [CrmAdapter.init({ name: 'crm' })]. See @Adapter.

auth-provider

--type is bearer (the default), oauth, api-key, basic or custom, and decides the headers() method: bearer reads <NAME>_TOKEN, api-key <NAME>_API_KEY into X-API-Key, basic <NAME>_USERNAME and <NAME>_PASSWORD, from the environment (desk-bearer reads DESK_BEARER_TOKEN); oauth and custom are TODOs. List it in an app's authProviders. For how tools get credentials, see Progressive auth.

flow

A FlowBase subclass with pre, execute, post and finalize stages. It declares its name on ExtendFlows and its plan as an object of stages, the shape Flows and stages describes, so it type-checks as written. No @App option takes it (flows there is TS2353): register it at run time with scope.registryFlows(), as Flows and stages shows.

UI generators

They take no --project: they write to ui/ at the workspace root, and add a path alias to tsconfig.base.json. The first one you run for a folder creates its Nx project (ui-components, ui-pages or ui-shells, with project.json, tsconfig.json, jest.config.cjs and a barrel src/index.ts), adds what the generated files import to package.json, and installs it.

GeneratorNameWritesAlias added to tsconfig.base.json
ui-componentPascalCase, like TicketCardui/components/src/TicketCard/TicketCard.tsx, TicketCard.spec.tsx, index.ts@frontmcp/ui-components/TicketCard
ui-pagePascalCase, like AgentDashboardui/pages/src/AgentDashboard/AgentDashboard.tsx, .spec.tsx, index.ts@frontmcp/ui-pages/AgentDashboard
ui-shellkebab-case, like help-desk-shellui/shells/src/help-desk-shell/help-desk-shell.shell.ts, .shell.spec.ts, index.ts@frontmcp/ui-shells/help-desk-shell

Each also takes --description (default empty) and --skipFormat.

  • ui-component and ui-page write React components built with @mui/material (<Box>, <Typography>), and add react, react-dom, @mui/material, @emotion/react, @emotion/styled, their types, @testing-library/react and @testing-library/dom, and jest-environment-jsdom.
  • ui-shell writes build<Name>Shell({ toolName, input?, output? }), which wraps a <div> in buildShell() from @frontmcp/uipack and returns { html, hash, size }. It adds @frontmcp/uipack and esbuild ^0.27.3 (and @nx/esbuild at the workspace's Nx version when the workspace has none: 23.2.0 in a --preset=ts workspace on Nx 23.2.0).
  • All three install, their packages type-check with tsc -p ui/<folder>/tsconfig.json --noEmit, and the three tests each one writes pass. In 1.8.7 ui-shell added esbuild ^0.25.0, below what @frontmcp/uipack accepts, and the install stopped with ERESOLVE.
  • The packages get build-cjs and build-esm targets (@nx/esbuild:esbuild) and a build that runs both: nx build ui-components bundles to dist/ui/components/. build-cjs type-checks; build-esm doesn't, so a TypeScript-solution tsconfig.base.json doesn't stop it. A test target comes from jest.config.cjs when nx.json has @nx/jest/plugin, as in a frontmcp create --nx workspace; otherwise the generator writes one into project.json and installs Jest and @swc/jest.

In a create-nx-workspace --preset=ts workspace, all three build and pass their tests too: nx run-many -t build test -p ui-components, ui-pages and ui-shells succeeded there, each with a test target of its own. Before 1.9.2, build-esm stopped there with error TS5069: Option 'declarationMap' cannot be specified without specifying option 'declaration' or option 'composite'., and the packages had no test target.

Caveats

  • Every generated project.json points $schema at ../…/node_modules/…, and every tsconfig.json extends ../…/tsconfig.base.json, with as many ../ as the project is deep.
  • The generators never edit a project's existing files, only add new ones. The ones that add dependencies, aliases or targets edit the workspace's package.json, tsconfig.base.json or nx.json: init, lib, server --deploymentTarget lambda and the UI generators.
  • The aliases lib and the UI generators add are written with a leading ./ (./libs/<name>/src/index.ts), which works whether or not tsconfig.base.json has a baseUrl. Aliases written by 1.8.7 have no ./, and TypeScript refuses them (TS5090) in a workspace without a baseUrl, like the one create-nx-workspace --preset=ts makes.

Usage

Adding a tool to an app

nx g @frontmcp/nx:tool search_tickets --project tickets

The generator writes the file; you import it into the app and list it. Here's the result, with the generated file unchanged:

Open
import { App } from "@frontmcp/sdk";
import SearchTicketsTool from "./search-tickets.tool";

@App({
  id: "tickets",
  name: "Tickets",
  tools: [SearchTicketsTool], // ✅ the generator doesn't add it
})
export class TicketsApp {}

Starting FrontMCP in your browser…

FrontMCP starts when this example comes into view.

Replace the schemas and execute() next, and the TODO description, which is what the model reads.

Adding a resource template

nx g @frontmcp/nx:resource ticket --project tickets --template
Open
import { App } from "@frontmcp/sdk";
import TicketResource from "./ticket.resource";

@App({ id: "tickets", name: "Tickets", resources: [TicketResource] })
export class TicketsApp {}

Starting FrontMCP in your browser…

FrontMCP starts when this example comes into view.

Scaffolding a server

nx g @frontmcp/nx:server gateway --apps demo,tickets --skills none

The shell is ready to build, with the imports pointing at the apps' src/ folders:

servers/gateway/src/main.ts
import 'reflect-metadata';
import { FrontMcp } from '@frontmcp/sdk';
import { DemoApp } from '../../../apps/demo/src/demo.app';
import { TicketsApp } from '../../../apps/tickets/src/tickets.app';

@FrontMcp({
  info: { name: 'Gateway Server', version: '0.1.0' },
  apps: [DemoApp, TicketsApp],
})
export default class Server {}
nx build server-gateway
 NX   Running target build for project server-gateway and 2 tasks it depends on:
Running: frontmcp build --entry …/apps/demo/src/main.ts --out-dir …/apps/demo/dist (in …/apps/demo)
Running: frontmcp build --entry …/apps/tickets/src/main.ts --out-dir …/apps/tickets/dist (in …/apps/tickets)
Running: frontmcp build --target node --entry …/servers/gateway/src/main.ts --out-dir …/servers/gateway/dist (in …/servers/gateway)
[build:exec] bundle created: dist/node/server-gateway.bundle.js (26.8 KB)

 NX   Successfully ran target build for project server-gateway and 2 tasks it depends on

nx dev server-gateway serves both apps while you work, and nx typecheck server-gateway checks the shell with the apps it imports. To ship it as a container, build the image from the workspace root; the generator's install has already added the shell to the lock file:

docker build -f servers/gateway/Dockerfile -t help-desk-gateway .
docker run -p 3000:3000 -e MCP_SESSION_SECRET=... help-desk-gateway

Monorepo patterns covers composing apps.

Previewing what a generator writes

nx g @frontmcp/nx:app billing --directory apps/team/billing --tags scope:billing,type:app --dry-run
 NX  Generating @frontmcp/nx:app

CREATE apps/team/billing/jest.config.cjs
CREATE apps/team/billing/package.json
CREATE apps/team/billing/project.json
CREATE apps/team/billing/src/billing.app.ts
…
NOTE: The "dryRun" flag means no changes were made.

Troubleshooting

The workspace is probably out of sync … Missing root "tsconfig.json"

nx.json still lists @nx/js/typescript, as a workspace made with 1.8.7 does, and it gives a library made by lib an inferred build that needs a root tsconfig.json. See Upgrading a 1.8.7 workspace.

The new tool (or resource, prompt, job, …) isn't listed

Generators don't register classes. Import it into the app file and add it to tools, resources, prompts, jobs and so on in @App.

Required property 'project' is missing

Component generators need --project <name>: the Nx project name, like tickets or server-gateway, as nx show projects lists them.

has no exported member 'PluginRegistrationContext' or 'AdapterFetchResult'

A library made by the 1.8.7 nx g @frontmcp/nx:lib … --libType plugin or --libType adapter. Replace the class with what the 1.9.2 generator writes, the same shape as the component generators' (plugin, adapter).

Cannot find configuration for task server-gateway:dev

A server shell made before 1.9, when the server generator wrote only build and deploy. Add the target a 1.9.3 shell has: "dev": { "executor": "@frontmcp/nx:dev", "options": { "entry": "{projectRoot}/src/main.ts" } }.

Failed to create LLM adapter for agent … Environment variable OPENAI_API_KEY is not set

A server lists an agent from the agent generator, which reads its key from OPENAI_API_KEY. Set the variable, or change llm in the file.

error TS5069: Option 'declarationMap' cannot be specified … from a UI package's build

A UI package generated before 1.9.2 in a create-nx-workspace --preset=ts workspace. Add "composite": false, "declarationMap": false, "emitDeclarationOnly": false to the compilerOptions of ui/<folder>/tsconfig.json, or compare its project.json with one the 1.9.2 generator writes (UI generators).

The import path "…" is already mapped in tsconfig.base.json

lib refuses an alias another library has. Pass --importPath with another name. The same goes for … is a package this workspace depends on, an alias like @frontmcp/sdk that would shadow a dependency.

error TS5090: Non-relative paths are not allowed when 'baseUrl' is not set

An alias written by the 1.8.7 lib or UI generators, in a tsconfig.base.json that has no baseUrl. Write the target as ./libs/<name>/src/index.ts, as 1.9.2 does.