v0.2.7Live-host validation signed off on Claude & ChatGPT

Turn your Angular app into interactive MCP widgets — without leaving Angular

A schematic and a signals-native library that retrofits an existing Angular app with MCP views that render inside Claude, ChatGPT, and any MCP-Apps host. One ng add. No new runtime.

$ng add ng-mcp-ui --example=demo
View repo

This is 0.2.x, the line today's hosts connect to. Targeting the MCP 2026-07-28 revision? Read the 1.x beta docs →

terminal
$ng add ng-mcp-ui --example=demoretrofits SSR + /mcp
$ng generate ng-mcp-ui:view pollscaffold a widget
$ng generate ng-mcp-ui:tool cast_votetyped Zod tool
$npm run build:widgetscode-split chunks
$npm run dev:mcpng serve, /mcp live
$npm run tunnelexpose it to a real host

The library

Everything a widget needs, injected

One Adaptor interface abstracts the OpenAI Apps SDK and the open MCP-Apps postMessage spec. Your widget code is identical across hosts.

injectToolInfo()
Typed tool input, output and metadata, pushed by the host after boot.
injectCallTool()
Call a server tool from inside the view and track its lifecycle.
injectViewState()
Persisted per-view state that survives host re-renders.
injectLayout()
Theme, display mode, and safe-area insets from the host.
[dataLlm]
Directive surfacing in-view content the model may read as context.
mcpAsset
Pipe resolving widget asset URLs inside the sandboxed iframe.

Architecture

SSR gives you the server, not the render

The MCP JSON-RPC endpoint and widget asset routes mount into your existing Angular server.ts before the SSR catch-all. Views are client-bootstrapped widgets, code-split by the standard Angular builder, hydrated from host-pushed tool data.

/mcp and widget asset routes mount before Angular's SSR catch-all.
Widgets hydrate from host-pushed tool data, never from baked-in HTML.
Signals, not hooks — zoneless-friendly and Angular-native throughout.
src/widgets/poll/poll.widget.ts
import { Component, computed } from '@angular/core';import { injectCallTool, injectToolInfo } from 'ng-mcp-ui/web';@Component({  selector: 'poll-widget',  template: `    @let p = poll();    @if (p) {      <h1>{{ p.question }}</h1>      @for (o of p.options; track o) {        <button (click)="vote(o)">{{ o }}</button>      }    }  `,})export default class PollWidget {  // the tool that rendered this view, as a state signal  private readonly tool = injectToolInfo<{ output: Poll }>();  private readonly castVote = injectCallTool<VoteArgs, VoteResult>('cast_vote');  protected readonly poll = computed(() => {    const s = this.tool();    return s.isSuccess ? s.output : null;  });  protected vote(option: string) {    this.castVote.callTool({ pollId: this.poll()?.pollId, option });  }}

Retrofit your app in one command

Ships a working poll widget, an MCP endpoint, and a dev tunnel you can point a real host at.

$ng add ng-mcp-ui --example=demo
Read the docs →