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 nodeReference
Options every generator takes
| Option | Description |
|---|---|
name | The 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. |
--skipFormat | Don't run Prettier on the files written. |
--dry-run | Nx's own: list the files without writing them. |
--no-interactive | Nx'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:initPrepares 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.
| Option | Type | Default | Description |
|---|---|---|---|
--skipPackageJson | boolean | false | Don't add dependencies to package.json, and install nothing. nx.json is still updated. |
--skipFormat | boolean | false |
workspace
nx g @frontmcp/nx:workspace help-desk-platform --skipInstallWrites a new workspace into <name>/, below the current folder. It's what frontmcp create --nx runs; Nx plugin lists what it writes.
| Option | Type | Default | Description |
|---|---|---|---|
name | string | The folder and the root package.json name. | |
--packageManager | npm, yarn, pnpm, bun | npm | The workspace's package manager, written to nx.json as cli.packageManager. |
--skipInstall | boolean | false | Don't install the dependencies. |
--skipGit | boolean | false | Don't run git init. |
--createSampleApp | boolean | true | Add apps/demo with a hello tool. false leaves apps/, libs/ and servers/ empty. |
app
nx g @frontmcp/nx:app ticketsCREATE 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.
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!' });
});
});| Option | Type | Default | Description |
|---|---|---|---|
name | string | The project's name, the app's id, and the folder name. | |
--directory | string | apps/<name> | The folder, at any depth: apps/team/billing gets extends: "../../../tsconfig.base.json" and a $schema with three ../. |
--tags | string | scope:apps | Comma-separated tags for project.json. They replace scope:apps. |
--workspaceRoot | string | The folder of the Nx workspace when it isn't the tree root. | |
--skipFormat | boolean | false |
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-modelCREATE 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.
| Option | Type | Default | Description |
|---|---|---|---|
name | string | ||
--directory | string | libs/<name> | |
--libType | generic, plugin, adapter, tool-register | generic | What 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. |
--publishable | boolean | false | Adds 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/. |
--importPath | string | <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. |
--tags | string | scope:libs | They replace scope:libs; scope:publishable is added after them. |
--skipFormat | boolean | false |
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,ticketsCREATE 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.
| Option | Type | Default | Description |
|---|---|---|---|
name | string | The folder; the project is server-<name>. | |
--apps | string | Required. Comma-separated app names to import and list in apps. | |
--deploymentTarget | node, vercel, lambda, cloudflare | node | node 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. |
--redis | docker, existing, none | none | node 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. |
--skills | recommended, minimal, full, none | recommended | Copies catalog skills into servers/<name>/skills/: 9 skills and 348 files by default, 4 and 193 for minimal, 13 and 376 for full. |
--directory | string | servers/<name> | |
--tags | string | scope:servers | |
--skipFormat | boolean | false |
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:
--deploymentTarget | File | What it does |
|---|---|---|
node | Dockerfile, Dockerfile.dockerignore | Built 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. |
node | docker-compose.yml | Builds 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.) |
vercel | vercel.json | installCommand and buildCommand run from the workspace root: cd ../../ && npm install and cd ../../ && npx nx build server-gateway in an npm workspace. |
lambda | template.yaml | CodeUri: 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.) |
cloudflare | wrangler.toml | main = "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.
| Generator | Writes | Options besides the common ones |
|---|---|---|
tool | src/tools/<name>.tool.ts | |
resource | src/resources/<name>.resource.ts | --template |
prompt | src/prompts/<name>.prompt.ts | --arguments |
skill | src/skills/<name>.skill.ts | --tools |
skill-dir | skills/<name>/SKILL.md | --description, --tags, --withReferences |
job | src/jobs/<name>.job.ts | |
workflow | src/workflows/<name>.workflow.ts | |
agent | src/agents/<name>.agent.ts | --model, --tools |
provider | src/providers/<name>.provider.ts | --scope |
plugin | src/plugins/<name>.plugin.ts | --withContextExtension |
adapter | src/adapters/<name>.adapter.ts | |
auth-provider | src/auth/<name>.auth-provider.ts | --type |
flow | src/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
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
---
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.
| Generator | Name | Writes | Alias added to tsconfig.base.json |
|---|---|---|---|
ui-component | PascalCase, like TicketCard | ui/components/src/TicketCard/TicketCard.tsx, TicketCard.spec.tsx, index.ts | @frontmcp/ui-components/TicketCard |
ui-page | PascalCase, like AgentDashboard | ui/pages/src/AgentDashboard/AgentDashboard.tsx, .spec.tsx, index.ts | @frontmcp/ui-pages/AgentDashboard |
ui-shell | kebab-case, like help-desk-shell | ui/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-componentandui-pagewrite React components built with@mui/material(<Box>,<Typography>), and addreact,react-dom,@mui/material,@emotion/react,@emotion/styled, their types,@testing-library/reactand@testing-library/dom, andjest-environment-jsdom.ui-shellwritesbuild<Name>Shell({ toolName, input?, output? }), which wraps a<div>inbuildShell()from@frontmcp/uipackand returns{ html, hash, size }. It adds@frontmcp/uipackandesbuild^0.27.3(and@nx/esbuildat the workspace's Nx version when the workspace has none: 23.2.0 in a--preset=tsworkspace 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.7ui-shelladdedesbuild^0.25.0, below what@frontmcp/uipackaccepts, and the install stopped withERESOLVE. - The packages get
build-cjsandbuild-esmtargets (@nx/esbuild:esbuild) and abuildthat runs both:nx build ui-componentsbundles todist/ui/components/.build-cjstype-checks;build-esmdoesn't, so a TypeScript-solutiontsconfig.base.jsondoesn't stop it. Atesttarget comes fromjest.config.cjswhennx.jsonhas@nx/jest/plugin, as in afrontmcp create --nxworkspace; otherwise the generator writes one intoproject.jsonand 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.jsonpoints$schemaat../…/node_modules/…, and everytsconfig.jsonextends../…/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.jsonornx.json:init,lib,server --deploymentTarget lambdaand the UI generators. - The aliases
liband the UI generators add are written with a leading./(./libs/<name>/src/index.ts), which works whether or nottsconfig.base.jsonhas abaseUrl. Aliases written by 1.8.7 have no./, and TypeScript refuses them (TS5090) in a workspace without abaseUrl, like the onecreate-nx-workspace --preset=tsmakes.
Usage
Adding a tool to an app
nx g @frontmcp/nx:tool search_tickets --project ticketsThe generator writes the file; you import it into the app and list it. Here's the result, with the generated file unchanged:
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 --templateimport { 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 noneThe shell is ready to build, with the imports pointing at the apps' src/ folders:
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-gatewayMonorepo 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.