Nx executors
The executors of @frontmcp/nx let Nx run the frontmcp CLI: nx build tickets runs frontmcp build with the project's entry and output folder, and Nx adds caching, dependsOn ordering and nx affected on top. Each one turns its options into CLI flags and runs the workspace's own frontmcp from the project's folder, so the CLI reads the project's package.json, tsconfig.json, .env and frontmcp.config.*. deploy is the exception: it runs a platform's own CLI in the project's folder. Before 1.8.7 they ran npx frontmcp at the workspace root, and most of their surprises came from that.
{
"targets": {
"build": {
"executor": "@frontmcp/nx:build",
"cache": true,
"outputs": ["{projectRoot}/dist"],
"options": { "entry": "{projectRoot}/src/main.ts", "outputPath": "{projectRoot}/dist" }
},
"dev": { "executor": "@frontmcp/nx:dev", "options": { "entry": "{projectRoot}/src/main.ts" } }
}
}Reference
How an executor runs
| Executor | Runs | From | Kind |
|---|---|---|---|
build | frontmcp build [--target] --entry <path> --out-dir <path> | Project folder | Runs to the end |
build-exec | frontmcp build --target node --entry <path> --out-dir <path> | Project folder | Runs to the end |
dev | frontmcp dev --entry <path> [--port] | Project folder | Long-running |
serve | frontmcp start <project> [--entry] [--port] [--max-restarts] | Project folder | Long-running |
test | frontmcp test [--runInBand] [--watch] [--coverage] [--verbose] [--timeout] | Project folder | Runs to the end |
inspector | frontmcp inspector, with CLIENT_PORT set from port | Project folder | Long-running |
deploy | The target platform's CLI | Project folder | Runs to the end |
Each prints the command first, with the folder it runs in (Running: frontmcp build --entry /Users/you/help-desk-platform/apps/tickets/src/main.ts --out-dir /Users/you/help-desk-platform/apps/tickets/dist (in /Users/you/help-desk-platform/apps/tickets)), runs it with the terminal's output and FORCE_COLOR=1, and succeeds when it exits with 0. Paths in options are made absolute from the workspace root first. A long-running one ends when its command does, on Ctrl+C or when the server exits. Options can be set in project.json or on the command line (nx dev tickets --port=4100), and {projectRoot} in them is replaced by the project's folder.
The CLI is the one in the workspace's node_modules/frontmcp. If it isn't there, the target stops with The "frontmcp" CLI is not installed in …; the executors never fall back to npx, which would download the newest version (Nx plugin).
Because the CLI runs in the project's folder, a bundle is named after the project, with its own version: tickets.bundle.js and 0.0.1 from apps/tickets/package.json. A frontmcp.config.ts in the app's folder is read: its name and version name the bundle (billing-desk.bundle.js), and transport.http sets the port and path nx dev uses. Configuration files lists the fields.
build, build-exec and test are cacheable. The app generator writes cache: true on its build and test targets (and on typecheck, which runs tsc, not an executor), and init (run by nx add @frontmcp/nx) adds targetDefaults that cache all three by executor: build and build-exec also dependsOn ^build, so Nx builds the projects an app imports first. test keeps Nx's default inputs, every file of the project, so editing a test runs the tests again. dev, serve, inspector and deploy aren't cached. A cache hit replays the recorded terminal output, including its absolute paths, and restores the target's outputs.
build
| Option | CLI flag | Description |
|---|---|---|
entry | --entry | The server's entry file, relative to the workspace root. |
outputPath | --out-dir | The base output folder. The build writes to <outputPath>/<target>/, so apps/tickets/dist/node/ by default. |
target | --target | node (the default), vercel, lambda, cloudflare, distributed, cli, sdk, browser or mcpb. |
adapter | --target | The old spelling of target, kept as an alias: node, vercel, lambda or cloudflare. Use target. |
Every target builds from a project made by the app generator: node, sdk, browser, vercel, cloudflare, distributed, mcpb and cli. lambda stops with missing required peer dependency @codegenie/serverless-express until that package is in the workspace; nx g @frontmcp/nx:server … --deploymentTarget lambda adds it to package.json. Files a target writes next to the project, vercel.json and wrangler.toml, go in the project's folder, with paths relative to it. The CLI page says what each target writes.
nx build tickets --target=vercel doesn't work: Nx reads --target itself and stops with Cannot find configuration for task tickets:build,vercel. Set target in project.json, in a configuration, or use the nx run form, which passes it on: Building for another target.
build-exec
| Option | CLI flag | Description |
|---|---|---|
entry | --entry | |
outputPath | --out-dir | Writes to <outputPath>/node/. |
The same as build with --target node spelled out. No generator adds it to a project: add a target with "executor": "@frontmcp/nx:build-exec" to use it. It's cached like build.
dev
| Option | CLI flag | Description |
|---|---|---|
entry | --entry | |
port | --port | The port. Default: frontmcp dev's, which is transport.http.port from the app's frontmcp.config, then PORT, then 3000. |
$ npx nx dev tickets --port=4110
> nx run tickets:dev --port=4110
Running: frontmcp dev --entry /Users/you/help-desk-platform/apps/tickets/src/main.ts --port 4110 (in /Users/you/help-desk-platform/apps/tickets)
[dev] using entry: src/main.ts
[dev] listening on port: 4110
…
MCP HTTP (Express) on 127.0.0.1:4110
[10:01:12 AM] Found 0 errors. Watching for file changes.
The type checker frontmcp dev starts runs in the project's folder too, with the app's tsconfig.json, so it checks the app.
serve
| Option | CLI flag | Default | Description |
|---|---|---|---|
entry | --entry | ||
port | --port | ||
maxRestarts | --max-restarts | 5 | How many times the supervisor restarts a server that exits. |
Runs the process manager's frontmcp start, with the Nx project's name as the process name, so frontmcp list, logs tickets and stop tickets work from any terminal. It stays in the foreground, like start.
$ npx nx serve tickets --port=4105
Running: frontmcp start tickets --entry /Users/you/help-desk-platform/apps/tickets/src/main.ts --port 4105 --max-restarts 5 (in /Users/you/help-desk-platform/apps/tickets)
[pm] starting "tickets"...
[pm] entry: src/main.ts
Started successfully:
Name: tickets
Status: running
Port: 4105
test
| Option | CLI flag | Default |
|---|---|---|
runInBand | --runInBand | false |
watch | --watch | false |
coverage | --coverage | false |
verbose | --verbose | false |
timeout | --timeout <ms> |
Runs frontmcp test in the project's folder, where it finds the jest.config.cjs the generator wrote and runs it. That configuration compiles with @swc/jest, loads @frontmcp/testing/setup for the matchers, and maps the aliases in tsconfig.base.json's paths to their folders, so a test can import a library through its alias. Every *.spec.ts and *.test.ts in the project is a test.
A new app comes with one, src/tools/hello.tool.spec.ts, and a library with one too, so nx test and nx run-many -t test pass as generated:
> nx run demo:test
Running: frontmcp test (in /Users/you/help-desk-platform/apps/demo)
[test] running tests in .
[test] using user Jest config: jest.config.cjs
hint: press Ctrl+C to stop
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
…
NX Successfully ran target test for project demo
In 1.8.7 a new app had no test, and nx test failed with No tests found, exiting with code 1. A library's test target is @frontmcp/nx:test too since 1.9; it was @nx/jest:jest.
inspector
| Option | CLI flag | Description |
|---|---|---|
port | CLIENT_PORT | The port of the Inspector's page. The CLI has no port flag, so the executor sets this variable, which the Inspector reads. |
Runs frontmcp inspector, which starts the Inspector at http://localhost:6274, or at port when you set it: nx inspector tickets --port=4400 opens it at http://127.0.0.1:4400. The Running: line shows frontmcp inspector without a flag. An app without a frontmcp.config gives the Inspector no server address: enter http://localhost:3000/ (or the dev port) yourself. With a config in the app's folder that has transport.http, the Inspector is pointed at it.
deploy
| Option | Type | Description |
|---|---|---|
target | node, vercel, lambda, cloudflare | Required. Which command to run. |
target | Command, run in the project's folder |
|---|---|
node | docker compose up --build -d |
vercel | npx vercel deploy --prebuilt --prod: it uploads the .vercel/output/ that build wrote, instead of building again on Vercel (since 1.9.2; before, npx vercel --prod) |
lambda | sam build && sam deploy |
cloudflare | npx wrangler deploy |
It runs nothing else: those tools, and your login to the platform, have to be set up. For node, the shell's docker-compose.yml requires MCP_SESSION_SECRET since 1.9.3, from your shell or a .env file in the shell's folder; without it the target fails with required variable MCP_SESSION_SECRET is missing a value. The server generator's deploy target has dependsOn: ["build"], so it builds first, and since 1.9 the files it wrote for the platform name what the build writes (Generators).
$ npx nx run server-gw-lambda:deploy --exclude-task-dependencies
Deploying server-gw-lambda to lambda...
Running: sam build && sam deploy
/bin/sh: sam: command not found
Caveats
dev,serveandinspectorrun until you stop them.outputsin the generatedbuildtarget is{projectRoot}/dist, which holdsdist/node/and every other target you build, so the cache restores them.
Usage
Building an app
npx nx build tickets> nx run tickets:build
Running: frontmcp build --entry /Users/you/help-desk-platform/apps/tickets/src/main.ts --out-dir /Users/you/help-desk-platform/apps/tickets/dist (in /Users/you/help-desk-platform/apps/tickets)
[build] cleaned dist/node
[build:exec] Building executable bundle...
[build:exec] name: tickets
[build:exec] version: 0.0.1
[build:exec] entry: src/main.ts
…
[build:exec] bundle created: dist/node/tickets.bundle.js (24.3 KB)
NX Successfully ran target build for project tickets
Run it again without changes, and Nx replays it from the cache:
> nx run tickets:build [local cache]
NX Successfully ran target build for project tickets
Nx read the output from the cache instead of running the command for 1 out of 1 tasks.
Run the result as any node build: PORT=8080 apps/tickets/dist/node/tickets, from the workspace, where the packages are. An app can import code from outside its own folder, a library through its alias or a shell's apps by relative path, and the build bundles it (Monorepo patterns).
Building for another target
target picks the target, but not on the command line through the nx build shorthand. Three ways that work:
{
"targets": {
"build": {
"executor": "@frontmcp/nx:build",
"cache": true,
"outputs": ["{projectRoot}/dist"],
"options": { "entry": "{projectRoot}/src/main.ts", "outputPath": "{projectRoot}/dist" },
"configurations": { "vercel": { "target": "vercel" }, "cloudflare": { "target": "cloudflare" } }
},
"build-distributed": {
"executor": "@frontmcp/nx:build",
"cache": true,
"outputs": ["{projectRoot}/dist/distributed"],
"options": { "entry": "{projectRoot}/src/main.ts", "outputPath": "{projectRoot}/dist", "target": "distributed" }
}
}
}npx nx build tickets -c vercel # a configuration
npx nx run tickets:build-distributed # a target of its own, cached like build
npx nx run tickets:build --target=cloudflare # the nx run form passes --target onEach builds into apps/tickets/dist/<target>/, and the cache keeps them apart. nx build tickets --target=vercel is read by Nx and fails.
Running an app while you work
npx nx dev tickets
npx nx dev demo --port=4101 # a second app, beside itEach app is its own server, on its own port. Both answer at the root of their port, http://localhost:3000/ and http://localhost:4101/, unless the app has a frontmcp.config.ts that sets transport.http.path.
Running an app's tests
The generated hello.tool.spec.ts tests the tool's execute() alone. Add an end-to-end test beside it, and nx test runs both. A test in the app starts the app's server from the app's folder, since that's where the CLI runs:
import { expect, test } from "@frontmcp/testing";
test.use({ server: "./src/main.ts", port: 3110 });
test("hello greets by name", async ({ mcp }) => {
const result = await mcp.tools.call("hello", { name: "Ada" });
expect(result).toBeSuccessful();
expect(result.json()).toEqual({ message: "Hello, Ada!" });
});$ npx nx test tickets
> nx run tickets:test
Running: frontmcp test (in /Users/you/help-desk-platform/apps/tickets)
[test] running tests in .
[test] using user Jest config: jest.config.cjs
hint: press Ctrl+C to stop
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
…
NX Successfully ran target test for project tickets
A second run with nothing changed is replayed from the cache; after an edit to a test, they run again. Give each app's tests their own port, so nx run-many -t test doesn't start two servers on one. A plain unit spec, with no server, runs the same way, and can import a library through its alias:
import { formatTicketId } from "@help-desk/ticket-model";
test("formats ticket ids through the alias", () => {
expect(formatTicketId(7)).toBe("T-0007");
});Keeping an app running
npx nx serve tickets --port=4105 # stays in the foreground
npx frontmcp list # from another terminal
npx frontmcp stop ticketsmaxRestarts in project.json sets how often the supervisor restarts it. The process manager keeps its files in ~/.frontmcp/, shared by every project on the machine, so give apps in different workspaces different names.
Troubleshooting
The "frontmcp" CLI is not installed in …
The workspace has no node_modules/frontmcp. Run npx nx g @frontmcp/nx:init, or npm install --save-dev frontmcp.
Cannot find configuration for task tickets:build,vercel
nx build tickets --target=vercel: Nx took --target for its own. Use nx run tickets:build --target=vercel, a configuration, or a target of its own (Building for another target).
No tests found, exiting with code 1 from nx test
The project has no *.spec.ts or *.test.ts: an app made before 1.9, which came without one, or one whose generated spec you deleted. Add a test (Running an app's tests).
Could not resolve "…/dist/node/main.js"
tsc emitted no JavaScript: the app's tsconfig.json inherits emitDeclarationOnly from a TypeScript-solution tsconfig.base.json. An app generated with 1.9.2 sets it to false; for one made before, see Nx plugin.
sam: command not found (or wrangler, vercel, docker)
deploy runs the platform's tool and doesn't install it.
[--target lambda] missing required peer dependency @codegenie/serverless-express
nx build with target: "lambda" needs that package in the workspace: npm install @codegenie/serverless-express.