HomeSchematicsng add

ng add

The retrofit schematic. It adds SSR, mounts the MCP server, adds the widget build target, and scaffolds a demo.

ng add ng-mcp-ui@next installs the package and runs the retrofit in one step. The @next tag is needed while 1.x is beta. If the package is already installed, run the schematic directly.

ng generate ng-mcp-ui:ng-add

The schematic is also named init. It is idempotent: a second run on a retrofitted app corrects the wiring, and it does not duplicate it.

It first reads @angular/core from your package.json file. A major version outside 20 to 22 stops the run with a message that names the found version.

Dependencies

The schematic adds one dependency, ng-mcp-ui, and it schedules one install task. It delegates to the ng-add schematic of @angular/ssr when the project has no SSR.

Everything else is a peer dependency of ng-mcp-ui, and npm installs it with the package: @modelcontextprotocol/server, @modelcontextprotocol/express and @modelcontextprotocol/node at ^2.0.0, @modelcontextprotocol/ext-apps ^1.7.0 and @modelcontextprotocol/sdk >=1.29.0 for the widget-side bridge, zod ^4.2.0, and express ^5. Install them by hand if your package manager does not install peers. The package needs Node 22 or later.

Options

Option Type Default Notes
--project string current project The target project.
--ssr boolean true Makes sure that Angular SSR is set up. Adds it if it is absent.
--bundling lazy, targets, esbuild lazy Reserved. Only the default lazy path is implemented. The other values do nothing today.
--tunnelProvider cloudflare, localtunnel, untun cloudflare Reserved. It does nothing today. The scaffolded tunnel script prints guidance instead of a provider CLI.
--example demo, minimal, none demo The example app to scaffold.
--skipInstall boolean false Skips the dependency install.
--migrateBuildScript boolean true On a legacy install, deletes the scaffolded tools/build-widgets.mjs file and repoints build:widgets. Set false to keep your own copy.

What it writes

Path Purpose
src/mcp/server.ts createMcpServer() with the state option and a sample echo tool with its paired view
src/mcp/views.manifest.ts Resolves the widget build output for the view shell
src/widgets/registry.ts Maps each view name to a lazy import(). Each entry becomes its own chunk
src/widgets/main.ts The widget entry. It reads viewName from the shell
src/widgets/index.html The shell document for the widget browser build
src/widgets/echo/echo.widget.ts The sample widget
tsconfig.widgets.json The TypeScript project for the widget build

The scaffolded createMcpServer() carries the sealed-state option:

const server = new McpServer(
  { name: "my-app", version: "0.0.0" },
  {
    viewManifest: resolveViewManifest(),
    state: { key: process.env["NG_MCP_STATE_KEY"] },
  },
);

That option gives each tool handler a ctx.state. In development an absent NG_MCP_STATE_KEY falls back to an ephemeral per-process key, and the server logs a warning. In production a missing key throws. Delete the option if no tool of yours uses ctx.state or requestState. See sealed state.

The schematic also changes src/server.ts. It mounts /mcp and /assets/widgets before the SSR catch-all route of Angular. The order matters, because a catch-all route that runs first answers /mcp with your application shell.

It then adds a build-widgets target on the ng-mcp-ui:build-widgets builder.

npm scripts

The schematic adds three scripts. It never overwrites a script that already has the same name.

Script Body What it does
build:widgets ng run <project>:build-widgets Bundles the widgets, proves that each registered view emitted a chunk, and derives views.manifest.json
dev:mcp ng serve Serves the SSR app. /mcp and /assets/widgets are then reachable
tunnel an echo guidance line Documents the manual step. Replace the body with your command, for example cloudflared tunnel --url http://localhost:4200

The example generator

--example=demo is the default. It runs the example schematic, which scaffolds the Quick Poll demo: src/mcp/tools/poll.ts with three tools (create_poll, cast_vote and tally_votes), and the src/widgets/poll/ view with its stylesheet. The schematic wires them into the new createMcpServer() function, into the widget registry, and into src/widgets/views.d.ts, which declares the view names.

The demo keeps no poll in server memory. create_poll seals the whole poll into a token, and it returns that token under _meta["ng-mcp-ui/state"]. The widget keeps the token in view state, and it passes the token back as the state argument of cast_vote and tally_votes. Each of those tools opens the token, updates the poll, and seals it again. Therefore the demo runs on serverless hosting and on several instances. See sealed state.

--example=minimal and --example=none leave the echo-only baseline unchanged.

You can run the generator later on its own.

ng generate ng-mcp-ui:example              # variant=demo
ng generate ng-mcp-ui:example --variant=minimal
Option Type Default Notes
--variant demo, minimal, none demo The example app to scaffold.
--project string current project The target project.

Migrating a legacy install

Before the builder existed, ng add copied its validation into each app as tools/build-widgets.mjs.

Run ng generate ng-mcp-ui:ng-add again to migrate such an app. The schematic rewrites the target onto the builder, deletes the scaffolded script by its header marker, and repoints the build:widgets npm script.

Pass --migrate-build-script=false to keep your own copy of the script. The schematic still rewrites the target. The old script tolerates that, because it runs the target and then checks the same output.