ng-mcp-ui/web
The Angular host bridge. provideMcpUi, bootstrapWidget, the full inject* signal API, and the declarables.
You must call each inject* function from an Angular injection context. Each one resolves the
host adaptor from the MCP_ADAPTOR DI token that provideMcpUi() supplies. Therefore your widget
code is the same for Claude, for ChatGPT and for other MCP-Apps hosts.
Each row links to the full page for that symbol. For the behavior of each function on each host, see host support.
Setup
| Symbol | Signature | Purpose |
|---|---|---|
provideMcpUi |
(): EnvironmentProviders |
Zoneless change detection, the two host-derived tokens, and the mcp-app modal service. |
bootstrapWidget |
(component, providers?): Promise<ApplicationRef> |
Boots a standalone widget into the host shell's #root, with provideMcpUi() applied first. |
MCP_ADAPTOR / MCP_SERVER_URL |
InjectionToken<…> |
The host bridge and server-origin tokens. Provide MCP_ADAPTOR yourself to use a custom or mock adaptor. |
Reading host and tool state
All signal-returning.
| Symbol | Signature | Purpose |
|---|---|---|
injectToolInfo |
<…>(): Signal<ToolState<…>> |
The rendering tool's input, output and metadata as an idle/pending/success state signal. |
injectLayout |
(): Signal<LayoutState> |
Host theme, safe-area insets and max height. |
injectUser |
(): Signal<UserState> |
Locale and device capabilities. |
injectViewState |
<T>(default?): InjectViewStateResult<T> |
{ value, set } over the host's persisted, bidirectionally synced view state. |
injectViewStore |
<…>(initial?, default?, options?): InjectViewStore<…> |
Store-style view state: state signal plus set/update/patch/select/flush, with debounced host writes and a conflict guard. |
injectDisplayMode |
(): InjectDisplayModeResult |
{ displayMode, setDisplayMode }. Reads the mode, and requests inline, fullscreen or pip. |
injectHostContext |
(): HostContextSignals |
Low-level: a readonly signal per raw host-context key. |
Calling the server and driving the host
All callable.
| Symbol | Signature | Purpose |
|---|---|---|
injectCallTool |
<Args, Resp>(name: string): InjectCallToolResult<…> |
{ callTool, callToolAsync, status, data, error } to invoke a server tool and track its lifecycle. |
injectSendFollowUpMessage |
(): SendFollowUpMessageFn |
Send a follow-up prompt into the conversation. |
injectOpenExternal |
(): OpenExternalFn |
Ask the host to open an external URL. |
injectRequestModal |
(): InjectRequestModalResult |
{ isOpen, params, open } for a host modal. |
injectRequestSize |
(): RequestSizeFn |
Request a new iframe size. |
injectRequestClose |
(): RequestCloseFn |
Ask the host to close the view. |
injectDownload |
(): { download: DownloadFn } |
Trigger a host-mediated download. |
injectFiles |
(): InjectFilesResult |
{ upload, getDownloadUrl, selectFiles } for host-managed files. |
injectSetOpenInAppUrl |
(): SetOpenInAppUrlFn |
Set the "open in app" deep link. |
injectRegisterViewTool |
(config, handler): RegisterViewToolHandle |
Register a view-scoped tool with the host. |
injectAppHelpers |
<AppType = never>(). Call it as injectAppHelpers<typeof server>() |
Typed sugar: tool-name-narrowed injectCallTool and injectToolInfo, inferred from the server's registry. |
Declarables
| Symbol | Use | Purpose |
|---|---|---|
DataLlmDirective |
[dataLlm]="content" |
Surfaces in-view content to the model, persisted on the host's viewState, with no extra tool call. |
McpAssetPipe |
path | mcpAsset |
Rewrites a relative asset path to an absolute URL on the MCP server origin, fixing the cross-origin asset hazard inside the host iframe. |
Advanced surface
Also exported for callers who need them:
- Modal:
MCP_MODAL,provideMcpModal,createMcpModal,MCP_MODAL_ENABLED,McpModal. - Host context:
createHostContextSignals(the non-DI form ofinjectHostContext),HostContextSignals. - View context:
VIEW_CONTEXT_KEY,injectViewContext,filterViewContext. - data-llm internals:
getLLMDescriptionString,DataLlmContent,DataLlmNode. - Bridge core:
getAdaptor(the non-DI adaptor accessor),Adaptor,HostContextand the per-host adaptor types. Prefer theMCP_ADAPTORtoken overgetAdaptor(). - Version:
NG_MCP_UI_VERSION.
Example widget
import { ChangeDetectionStrategy, Component, computed } from "@angular/core";
import { DataLlmDirective, injectCallTool, injectToolInfo, injectViewState } from "ng-mcp-ui/web";
@Component({
selector: "poll-widget",
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [DataLlmDirective],
template: `
@let p = poll();
@if (p) {
<h1>{{ p.question }}</h1>
@for (o of p.options; track o) {
<button (click)="vote(p.pollId, o)" [class.voted]="myVote() === o">{{ o }}</button>
}
<p [dataLlm]="voteSummary()"></p>
}
`,
})
export default class PollWidget {
private readonly tool = injectToolInfo<{ output: PollSnapshot }>();
private readonly castVote = injectCallTool<VoteArgs, VoteResult>("cast_vote");
private readonly viewState = injectViewState<{ myVote: string | null }>({ myVote: null });
protected readonly poll = computed(() => {
const s = this.tool();
return s.isSuccess ? s.output : null;
});
protected readonly myVote = computed(() => this.viewState.value()?.myVote ?? null);
protected readonly voteSummary = computed(() =>
this.myVote() ? `User voted: ${this.myVote()}` : "User has not voted yet.",
);
protected vote(pollId: string, option: string): void {
this.castVote.callTool(
{ pollId, option },
{ onSuccess: () => this.viewState.set({ myVote: option }) },
);
}
}
The shell boots it with bootstrapWidget(PollWidget).