@frontmcp/ui 1.9.6 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +145 -246
  2. package/esm/package.json +11 -14
  3. package/package.json +11 -14
package/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # @frontmcp/ui
2
2
 
3
- MUI-based React components, content renderers, and MCP bridge for building interactive MCP widgets.
3
+ `@frontmcp/ui` is the React side of FrontMCP widgets: the UI a tool shows inside an AI host such as ChatGPT or Claude. You write a widget as a React component in a file of its own and point the tool's `ui.template` at it; FrontMCP bundles the file, wraps it in `McpBridgeProvider` and mounts it with the call's data. This package gives that component hooks that read the call and call tools through the host, eleven components built on MUI (Material UI), a theme, and renderers for content such as charts, PDFs, maps and diagrams. A widget written as an HTML template doesn't need it ([Tools with a UI](https://frontmcp.dev/learn/tools-with-a-ui)). To make a React app that an agent can drive, use [`@frontmcp/react`](https://www.npmjs.com/package/@frontmcp/react) instead.
4
4
 
5
- [![NPM](https://img.shields.io/npm/v/@frontmcp/ui.svg)](https://www.npmjs.com/package/@frontmcp/ui)
6
- [![License](https://img.shields.io/npm/l/@frontmcp/ui.svg)](../../LICENSE)
5
+ [![npm](https://img.shields.io/npm/v/@frontmcp/ui.svg)](https://www.npmjs.com/package/@frontmcp/ui) [![License](https://img.shields.io/npm/l/@frontmcp/ui.svg)](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
7
6
 
8
7
  ## Install
9
8
 
@@ -11,292 +10,192 @@ MUI-based React components, content renderers, and MCP bridge for building inter
11
10
  npm install @frontmcp/ui react react-dom @mui/material @emotion/react @emotion/styled
12
11
  ```
13
12
 
14
- > **Note:** Renderer libraries (recharts, react-pdf, mermaid, katex, etc.) are **not** installed locally. They are automatically lazy-loaded from [esm.sh](https://esm.sh) in the browser at runtime.
15
-
16
- ## Quick Start
13
+ - For widget files, install it in the server's project, even when a widget doesn't import it: the code FrontMCP adds to mount a widget file imports `McpBridgeProvider` from `@frontmcp/ui/react`.
14
+ - The required peers are `react` and `react-dom` 18 or 19, `@mui/material` 7, 8 or 9, and `@emotion/react` and `@emotion/styled` 11. npm 7 and later install them with the package, along with `@types/react` and `@types/react-dom`.
15
+ - FrontMCP bundles widget files with `esbuild`, an optional peer of `@frontmcp/uipack` that nothing installs for you. A project from `frontmcp create` has it; anywhere else, run `npm install esbuild` and keep it a dependency, not a dev dependency, since it's loaded when the tool is called.
16
+ - The server needs Node.js 24 or later.
17
+
18
+ ### Optional peers for the renderers
19
+
20
+ The renderers' libraries are optional peers, not installed with the package:
21
+
22
+ | Renderer | Entry point | Optional peers |
23
+ | ---------------------- | ----------------------------------------------------- | ---------------------------------------------------------------- |
24
+ | Charts | `@frontmcp/ui/renderer/charts` | `recharts` |
25
+ | PDF | `@frontmcp/ui/renderer/pdf` | `react-pdf`, `pdfjs-dist` |
26
+ | Flow diagrams | `@frontmcp/ui/renderer/flow` | `@xyflow/react` |
27
+ | Maps | `@frontmcp/ui/renderer/maps` | `react-leaflet`, `leaflet` |
28
+ | Mermaid diagrams | `@frontmcp/ui/renderer/mermaid` | `mermaid` |
29
+ | Math | `@frontmcp/ui/renderer/math` | `katex` |
30
+ | Video and audio | `@frontmcp/ui/renderer/media` | `react-player` |
31
+ | Markdown and MDX | `@frontmcp/ui/renderer/mdx` | `react-markdown`, `remark-gfm`, `rehype-highlight`, `rehype-raw` |
32
+ | HTML | `@frontmcp/ui/renderer/html` | `dompurify`; without it the HTML is shown escaped |
33
+ | CSV, images, React JSX | `/renderer/csv`, `/renderer/image`, `/renderer/react` | None. JSX is compiled with Babel, loaded from esm.sh. |
34
+
35
+ Each renderer loads its library at runtime, the first time it renders: by package name where the page can resolve it (an installed peer under Node or a dev server, or an import map), and otherwise from [esm.sh](https://esm.sh) in the browser. The stylesheets for maps, math and flow diagrams, and the PDF.js worker, always come from esm.sh.
36
+
37
+ ## Usage
38
+
39
+ Point the tool's `ui.template` at the widget file with an absolute path:
40
+
41
+ ```ts
42
+ import { join } from 'node:path';
43
+
44
+ import { Tool, ToolContext, z } from '@frontmcp/sdk';
45
+
46
+ @Tool({
47
+ name: 'get_weather',
48
+ description: 'Current weather for a city',
49
+ inputSchema: { city: z.string() },
50
+ outputSchema: { city: z.string(), tempC: z.number(), conditions: z.string() },
51
+ ui: { template: { file: join(__dirname, 'weather.widget.tsx') } },
52
+ })
53
+ export class GetWeather extends ToolContext {
54
+ async execute({ city }: { city: string }) {
55
+ return { city, tempC: 21, conditions: 'Sunny' };
56
+ }
57
+ }
58
+ ```
17
59
 
18
- ### Theme + Components
60
+ Export the component as the default from `weather.widget.tsx`:
19
61
 
20
62
  ```tsx
21
- import { Badge, Button, Card } from '@frontmcp/ui/components';
63
+ import { Badge, Card } from '@frontmcp/ui/components';
22
64
  import { FrontMcpThemeProvider } from '@frontmcp/ui/theme';
23
65
 
24
- function App() {
66
+ type Weather = { city: string; tempC: number; conditions: string };
67
+
68
+ export default function WeatherWidget({ output }: { output: Weather | null }) {
69
+ if (!output?.city) return <p>Loading…</p>;
25
70
  return (
26
71
  <FrontMcpThemeProvider>
27
- <Card title="Weather" elevation={2}>
28
- <Badge label="Live" variant="success" />
29
- <p>Temperature: 18C</p>
30
- <Button variant="primary" onClick={() => {}}>
31
- Refresh
32
- </Button>
72
+ <Card title={output.city} subtitle="Current weather">
73
+ <p>{output.tempC}°C</p>
74
+ <Badge label={output.conditions} variant="success" />
33
75
  </Card>
34
76
  </FrontMcpThemeProvider>
35
77
  );
36
78
  }
37
79
  ```
38
80
 
39
- ### MCP Bridge Hooks
81
+ The widget gets three props: `output`, `input` and `loading`. Check for a field you need, like `output?.city`, rather than for `output`: a failed result's error text is truthy too. In an ES module server, use `join(import.meta.dirname, 'weather.widget.tsx')`. Name widget files `*.widget.tsx` and leave them out of the server's own `tsc` build; `frontmcp build` copies them into its output. [Building a widget file](https://frontmcp.dev/reference/ui/components#building-a-widget-file) covers relative paths, `resourceMode` and what each host gets.
82
+
83
+ ### Calling a tool from a widget
40
84
 
41
85
  ```tsx
42
- import { McpBridgeProvider, useCallTool, useToolOutput } from '@frontmcp/ui/react';
86
+ import { useCallTool } from '@frontmcp/ui/react';
87
+
88
+ type Weather = { city: string; tempC: number };
43
89
 
44
- function WeatherWidget() {
45
- const output = useToolOutput<{ temp: number }>();
46
- const [callWeather, { loading }] = useCallTool<{ city: string }>('get_weather');
90
+ export default function Refreshable({ output }: { output: Weather | null }) {
91
+ const [refresh, { data, loading, error }] = useCallTool<{ city: string }, Weather>('get_weather');
47
92
 
93
+ const weather = data?.structuredContent ?? (output?.city ? output : null);
48
94
  return (
49
95
  <div>
50
- <p>{output?.temp}C</p>
51
- <button onClick={() => callWeather({ city: 'London' })} disabled={loading}>
52
- Refresh
96
+ <p>{weather ? `${weather.city}: ${weather.tempC}°C` : 'No data yet'}</p>
97
+ <button disabled={loading} onClick={() => refresh({ city: 'Bergen' })}>
98
+ Bergen
53
99
  </button>
100
+ {error && <p role="alert">{error.message}</p>}
54
101
  </div>
55
102
  );
56
103
  }
57
-
58
- // Wrap with provider
59
- <McpBridgeProvider>
60
- <WeatherWidget />
61
- </McpBridgeProvider>;
62
104
  ```
63
105
 
64
- ### Content Rendering
106
+ `useCallTool()` returns an array, not an object. `call(args)` asks the host to call the tool and resolves with the host's whole result, so the tool's output is in `data.structuredContent`. A tool that fails usually comes back as a result with `isError: true` in `data`; `error` is for calls that couldn't be made, like `Tool calls not supported on this platform (ext-apps)` from a host that never answered the handshake.
107
+
108
+ ### Entry points
109
+
110
+ | Import | What's in it |
111
+ | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
112
+ | `@frontmcp/ui/react` | `McpBridgeProvider` and the hooks. `@frontmcp/ui` exports them too, with the bridge. |
113
+ | `@frontmcp/ui/components` | The eleven components. Each also has its own entry point, like `@frontmcp/ui/components/Card`. |
114
+ | `@frontmcp/ui/theme` | `FrontMcpThemeProvider`, `createFrontMcpTheme`, `defaultTheme`, `darkTheme`, `useFrontMcpTheme`. |
115
+ | `@frontmcp/ui/renderer` | `ContentView`, `renderContent`, `detectContentType`, `registerAllRenderers`, `registerRenderer`, and every renderer. Each renderer also has its own entry point; `/renderer/common` has the shared helpers. |
116
+ | `@frontmcp/ui/bridge` | The bridge as a class, `FrontMcpBridge` and `createBridge()`, with its adapters and `generateBridgeIIFE()`, for pages FrontMCP doesn't build. |
117
+ | `@frontmcp/ui/runtime` | `detectContentType`, and `transpileJsx()` with the Babel loader it uses. |
118
+ | `@frontmcp/ui/auth`, `@frontmcp/ui/auth/vanilla` | Components and helpers for a [custom login UI](https://frontmcp.dev/reference/auth/login-ui); the `vanilla` entry has no React. |
119
+
120
+ ### Hooks
121
+
122
+ A widget file is already inside `McpBridgeProvider`. Outside a provider the hooks return empty values, and `call()` fails with `Bridge not initialized`.
123
+
124
+ | Hook | Returns |
125
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
126
+ | `useToolOutput<T>()` | The call's output: the output embedded in the page, then each result the host sends. |
127
+ | `useToolInput<T>()` | The call's arguments. |
128
+ | `useStructuredContent<T>()` | The `structuredContent` of the result the host sent; empty until one arrives. |
129
+ | `useCallTool<In, Out>(name, options?)` | `[call, { data, loading, error, called }, reset]`. Options: `onSuccess`, `onError`, `resetOnToolChange` (default `true`). |
130
+ | `useToolCalls({ key: toolName, ... })` | `{ key: { call, data, loading, error, reset }, ... }`: `useCallTool` for several tools. |
131
+ | `useSendMessage()` | `[send(text), { loading, error, sent }]`: adds a message to the conversation, where the host allows it. |
132
+ | `useOpenLink()` | `open(url)`, through the host. |
133
+ | `useTheme()`, `useDisplayMode()` | `"light"` or `"dark"`, and `"inline"`, `"fullscreen"` or `"pip"`, as the host reports them. |
134
+ | `useHostContext()` | What the host said about itself: `{ theme, displayMode, locale, userAgent, safeArea, viewport?, ... }`. |
135
+ | `useCapability(name)` | Whether the host can do `name`, like `"canCallTools"`. `false` until the host answers the handshake. |
136
+ | `useMcpBridge()`, `useMcpBridgeContext()` | The bridge, and `{ bridge, loading, error, ready, adapterId, capabilities, revision }`. |
137
+
138
+ ### Components
139
+
140
+ `@frontmcp/ui/components` are React components built on MUI. Put them inside `FrontMcpThemeProvider`.
141
+
142
+ | Component | Main props | Renders |
143
+ | ----------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------- |
144
+ | `Button` | `variant` (`primary`, `secondary`, `danger`, `ghost`), `size`, `loading`, `disabled`, `onClick` | An MUI `Button`; `loading` adds a spinner. |
145
+ | `Card` | `title`, `subtitle`, `headerActions`, `footer`, `elevation`, `clickable`, `slotProps` | An MUI `Card`. |
146
+ | `Alert` | `severity`, `title`, `dismissible`, `onDismiss`, `icon` | An MUI `Alert`. |
147
+ | `Badge` | `label` (required), `variant`, `size`, `dot`, `removable`, `onRemove` | An MUI `Chip`. |
148
+ | `Avatar` | `src`, `alt`, `size`, `variant` | An MUI `Avatar`. |
149
+ | `Modal` | `open` and `onClose` (required), `title`, `actions`, `maxWidth`, `slotProps` | An MUI `Dialog`. |
150
+ | `Table` | `columns` (`{ key, label, align? }[]`), `rows`, `size`, `stickyHeader`, `maxHeight` | An MUI `Table`. |
151
+ | `TextField` | `label`, `value`, `onChange(value)`, `type`, `multiline`, `error`, `helperText` | An MUI `TextField`; `onChange` gets the value. |
152
+ | `Select` | `options` (`{ value, label }[]`), `label`, `value`, `onChange(value)` | An MUI select field. |
153
+ | `List` | `items` (`{ id, primary, secondary?, icon?, onClick?, divider? }[]`), `dense` | An MUI `List`. |
154
+ | `Loader` | `variant` (`spinner`, `bar`, `skeleton`, `overlay`), `label`, `determinate`, `value` | A spinner, progress bar, skeleton or backdrop. |
155
+
156
+ `LoaderProvider` replaces every `Loader` under it with your own render function. The theme comes from `createFrontMcpTheme({ palette?, typography?, shape?, mode? })`, or the ready-made `defaultTheme` and `darkTheme`, passed to `FrontMcpThemeProvider` as `theme`.
157
+
158
+ ### Renderers
65
159
 
66
160
  ```tsx
67
- import { detectContentType, renderContent } from '@frontmcp/ui/renderer';
161
+ import { ContentView, registerAllRenderers } from '@frontmcp/ui/renderer';
68
162
 
69
- // Auto-detects content type and renders with the appropriate renderer
70
- const type = detectContentType(data); // 'chart' | 'csv' | 'pdf' | 'mermaid' | ...
71
- const element = renderContent(data, { type });
72
- ```
73
-
74
- ## Features
75
-
76
- - **11 MUI components** — Button, Card, Alert, Badge, Avatar, Modal, Table, TextField, Select, List, Loader
77
- - **13 content renderers** — Charts, CSV, Flow, HTML, Image, Maps, Math, MDX, Media (video + audio), Mermaid, PDF, React JSX
78
- - **MCP bridge hooks** — `useCallTool`, `useToolInput`, `useToolOutput`, `useSendMessage`, `useOpenLink`, and more
79
- - **5 platform adapters** — OpenAI, ExtApps, Claude, Gemini, Generic (auto-detected)
80
- - **MUI theme system** — `FrontMcpThemeProvider` with light/dark presets and custom theme support
81
- - **Runtime utilities** — Content type auto-detection, Babel JSX transpilation
82
- - **Tree-shakeable** — Import individual components and renderers to minimize bundle size
83
-
84
- ## Entry Points
85
-
86
- | Path | Exports |
87
- | ------------------------------ | --------------------------------------------------------------------------- |
88
- | `@frontmcp/ui` | Bridge core, hooks (main barrel) |
89
- | `@frontmcp/ui/theme` | `FrontMcpThemeProvider`, `createFrontMcpTheme`, `defaultTheme`, `darkTheme` |
90
- | `@frontmcp/ui/components` | All 11 React components |
91
- | `@frontmcp/ui/components/*` | Individual components (`/Button`, `/Card`, `/Alert`, etc.) |
92
- | `@frontmcp/ui/renderer` | `renderContent`, `detectContentType`, `registerAllRenderers` |
93
- | `@frontmcp/ui/renderer/*` | Individual renderers (`/charts`, `/csv`, `/pdf`, `/mdx`, etc.) |
94
- | `@frontmcp/ui/renderer/common` | Shared renderer utilities (`useLazyModule`, `useRendererTheme`) |
95
- | `@frontmcp/ui/react` | `McpBridgeProvider`, all hooks |
96
- | `@frontmcp/ui/bridge` | `FrontMcpBridge`, `createBridge`, `AdapterRegistry`, adapters |
97
- | `@frontmcp/ui/runtime` | `detectContentType`, `transpileJsx`, Babel loader |
98
-
99
- ## Components
100
-
101
- | Component | Key Props | Description |
102
- | ----------- | ----------------------------------- | --------------------------------------- |
103
- | `Button` | `variant`, `onClick`, `disabled` | MUI button with FrontMCP variants |
104
- | `Card` | `title`, `elevation`, `slots` | Content card with customizable slots |
105
- | `Alert` | `severity`, `title`, `onClose` | Status alert banner |
106
- | `Badge` | `label`, `variant` | Status/label badge |
107
- | `Avatar` | `src`, `alt`, `size` | User avatar |
108
- | `Modal` | `open`, `onClose`, `title`, `slots` | Dialog modal |
109
- | `Table` | `columns`, `rows`, `sortable` | Data table |
110
- | `TextField` | `label`, `value`, `onChange` | Text input field |
111
- | `Select` | `options`, `value`, `onChange` | Dropdown select |
112
- | `List` | `items`, `onItemClick` | List with item definitions |
113
- | `Loader` | `variant`, context provider | Loading indicator with `LoaderProvider` |
114
-
115
- ## Renderers
116
-
117
- | Renderer | Type | Priority |
118
- | --------- | ----------------- | -------- |
119
- | PDF | `pdf` | 90 |
120
- | Charts | `chart` | 80 |
121
- | Flow | `flow` | 70 |
122
- | Maps | `map` | 60 |
123
- | Mermaid | `mermaid` | 50 |
124
- | Math | `math` | 40 |
125
- | Image | `image` | 30 |
126
- | Media | `video` / `audio` | 20 |
127
- | CSV | `csv` | 10 |
128
- | React JSX | `jsx` | 10 |
129
- | MDX | `mdx` | 5 |
130
- | HTML | `html` | 0 |
131
-
132
- All renderer libraries are lazy-loaded from [esm.sh](https://esm.sh) in the browser at runtime — no local installation is needed. Priority determines which renderer wins when multiple match.
133
-
134
- ## Bridge Adapters
135
-
136
- | Adapter | Detects Via | Priority | Key Capabilities |
137
- | ------- | ------------------------------------- | -------- | ----------------------------------------------- |
138
- | OpenAI | `window.openai.callTool` | 100 | `callTool`, `sendMessage`, `requestDisplayMode` |
139
- | ExtApps | `window.__mcpPlatform === 'ext-apps'` | 80 | Negotiated via `postMessage` handshake |
140
- | Claude | `window.claude`, hostname `claude.ai` | 60 | `openLink` |
141
- | Gemini | `window.__mcpPlatform === 'gemini'` | 40 | `openLink`, `networkAccess` |
142
- | Generic | Always matches (fallback) | 0 | `openLink`, `networkAccess` |
143
-
144
- ## Contributing
163
+ registerAllRenderers();
145
164
 
146
- ### Architecture
147
-
148
- ```mermaid
149
- graph TD
150
- subgraph "@frontmcp/ui"
151
- IDX["index.ts<br/><i>Main barrel exports</i>"]
152
-
153
- subgraph bridge["bridge/"]
154
- B_CORE["core/<br/>AdapterRegistry, bridge factory"]
155
- B_ADAPT["adapters/<br/>OpenAI, ExtApps, Claude,<br/>Gemini, Generic + base"]
156
- B_RT["runtime/<br/>Script generation<br/><i>re-exports from uipack</i>"]
157
- B_TYPES["types/<br/>TypeScript interfaces"]
158
- end
159
-
160
- subgraph theme["theme/"]
161
- TH_CREATE["create-theme.ts"]
162
- TH_PROV["FrontMcpThemeProvider.tsx"]
163
- TH_HOOK["use-theme.ts"]
164
- end
165
-
166
- subgraph components["components/ (11)"]
167
- C_BTN["Button"] --- C_CARD["Card"] --- C_ALERT["Alert"]
168
- C_BADGE["Badge"] --- C_AVATAR["Avatar"] --- C_MODAL["Modal"]
169
- C_TABLE["Table"] --- C_TF["TextField"] --- C_SEL["Select"]
170
- C_LIST["List"] --- C_LOADER["Loader<br/><i>+ LoaderProvider</i>"]
171
- end
172
-
173
- subgraph renderer["renderer/ (13)"]
174
- R_CHARTS["charts<br/><i>recharts</i>"]
175
- R_CSV["csv"]
176
- R_FLOW["flow<br/><i>@xyflow/react</i>"]
177
- R_HTML["html<br/><i>dompurify</i>"]
178
- R_IMG["image"]
179
- R_MAPS["maps<br/><i>leaflet</i>"]
180
- R_MATH["math<br/><i>katex</i>"]
181
- R_MDX["mdx<br/><i>react-markdown</i>"]
182
- R_MEDIA["media<br/><i>react-player</i>"]
183
- R_MERM["mermaid"]
184
- R_PDF["pdf<br/><i>react-pdf</i>"]
185
- R_REACT["react/jsx"]
186
- R_COMMON["common/<br/>useLazyModule,<br/>useRendererTheme"]
187
- end
188
-
189
- subgraph react["react/hooks/"]
190
- H_CTX["context.ts<br/>McpBridgeProvider,<br/>useMcpBridge, useTheme"]
191
- H_TOOLS["tools.ts<br/>useCallTool, useToolInput,<br/>useToolOutput"]
192
- end
193
-
194
- subgraph runtime["runtime/"]
195
- RT_DETECT["content-detector.ts<br/>detectContentType()"]
196
- RT_BABEL["babel-runtime.ts<br/>transpileJsx(), loadBabel()"]
197
- end
198
- end
199
-
200
- IDX --> bridge
201
- IDX --> react
202
- bridge --> B_CORE
203
- bridge --> B_ADAPT
204
- bridge --> B_RT
205
- react --> H_CTX
206
- react --> H_TOOLS
207
- H_CTX --> bridge
165
+ export function Report({ content }: { content: string }) {
166
+ return <ContentView content={content} />;
167
+ }
208
168
  ```
209
169
 
210
- ### How @frontmcp/ui works with @frontmcp/uipack
211
-
212
- These two packages split concerns between server-side (uipack) and browser-side (ui):
213
-
214
- | Concern | @frontmcp/ui (browser) | @frontmcp/uipack (server) |
215
- | ----------------- | --------------------------- | ------------------------------- |
216
- | React dependency | Yes (peer dep) | No |
217
- | Bridge | Runtime class + React hooks | IIFE generator (vanilla JS) |
218
- | Components | MUI React components | -- |
219
- | Shell building | -- | HTML shell, CSP, data injection |
220
- | Rendering | 13 content renderers | -- |
221
- | Import resolution | -- | esm.sh resolver, import maps |
222
- | Theme | MUI ThemeProvider | -- |
223
-
224
- **End-to-end data flow:**
225
-
226
- ```mermaid
227
- sequenceDiagram
228
- participant Dev as Developer
229
- participant SDK as @frontmcp/sdk
230
- participant UP as @frontmcp/uipack
231
- participant Browser
232
- participant UI as @frontmcp/ui
233
-
234
- Dev->>SDK: @Tool({ ui: { npm: '...' } })
235
- SDK->>UP: renderComponent(config, shellConfig)
236
- UP->>UP: resolve deps (esm.sh)
237
- UP->>UP: build shell (CSP + data injection)
238
- UP->>UP: inject bridge IIFE
239
- UP-->>SDK: ShellResult { html, hash, size }
240
-
241
- Browser->>SDK: readResource("ui://widget/tool.html")
242
- SDK-->>Browser: cached HTML
243
-
244
- Browser->>Browser: Load @frontmcp/ui via esm.sh
245
- Browser->>UI: Mount React components
246
- UI->>UI: Bridge hooks read window.__mcp* globals
247
- UI->>Browser: Interactive widget rendered
248
- ```
170
+ `ContentView` detects the kind of content in a string, such as CSV, a Mermaid diagram, a chart's JSON or an image URL, and renders it with the matching renderer, wrapped in `Suspense`. `renderContent(content, options?)` does the same without the `Suspense` boundary, and `detectContentType(content)` only names the kind. When more than one renderer matches, the higher priority wins, in this order: pdf, chart, flow, map, mermaid, math, image, video, audio, csv, jsx, mdx, html. `registerRenderer()` adds your own `{ type, priority?, canHandle(content), render(content, options?) }`.
249
171
 
250
- 1. **Server-side** (`@frontmcp/uipack`): Resolves component dependencies, builds HTML shell with CSP and bridge IIFE, injects tool data as `window.__mcp*` globals
251
- 2. **Browser-side** (`@frontmcp/ui`): Loaded via esm.sh in the browser, provides React components, hooks to read injected data, and the bridge runtime for host communication
172
+ ## Docs
252
173
 
253
- ### Renderer System
174
+ - [Widget components](https://frontmcp.dev/reference/ui/components): building a widget file, the props, hooks, components, theme and renderers
175
+ - [Building Widgets with React](https://frontmcp.dev/learn/building-widgets-with-react): the step-by-step guide
176
+ - [Tools with a UI](https://frontmcp.dev/learn/tools-with-a-ui): templates or React, and how a widget is served
177
+ - [The `ui` option](https://frontmcp.dev/reference/ui): every option of a tool's widget, serving modes, Content Security Policy
178
+ - [Hosts and platforms](https://frontmcp.dev/reference/ui/hosts): what each host gets, and the bridge the hooks talk to
254
179
 
255
- Each renderer implements the `ContentRenderer` interface:
180
+ ## Related packages
256
181
 
257
- ```typescript
258
- interface ContentRenderer {
259
- type: string; // e.g. 'chart', 'csv', 'pdf'
260
- priority: number; // Higher wins when multiple match
261
- canHandle(content: string): boolean;
262
- render(content: string, options?: RenderOptions): ReactElement;
263
- }
264
- ```
182
+ - [`@frontmcp/sdk`](https://www.npmjs.com/package/@frontmcp/sdk): the server framework; a tool's `ui` option comes from it.
183
+ - [`@frontmcp/uipack`](https://www.npmjs.com/package/@frontmcp/uipack): the React-free core that builds every widget page on the server, with the `html` template helpers.
184
+ - [`@frontmcp/testing`](https://www.npmjs.com/package/@frontmcp/testing): test helpers, with matchers for a tool's rendered widget.
185
+ - [`@frontmcp/react`](https://www.npmjs.com/package/@frontmcp/react): hooks and components for running a FrontMCP server inside a React app.
186
+
187
+ ## Contributing
265
188
 
266
- Renderers are registered via `registerAllRenderers()` and auto-detected at runtime. Each renderer lazy-loads its peer dependencies using `useLazyModule()` — if a peer dep is missing, the renderer gracefully skips.
189
+ Source: [`libs/ui`](https://github.com/agentfront/frontmcp/tree/main/libs/ui). Issues and pull requests are welcome; see [CONTRIBUTING.md](https://github.com/agentfront/frontmcp/blob/main/CONTRIBUTING.md).
267
190
 
268
- ### Development
191
+ The source has one folder per entry point under `src/`: `react/` (provider and hooks), `components/` (one folder per component), `theme/`, `renderer/` (one folder per renderer, plus `common/`), `bridge/` (adapters, the registry and the bridge class), `runtime/` (content detection and the Babel loader) and `auth/`. The bridge script the server puts in each page comes from `@frontmcp/uipack`. In the monorepo:
269
192
 
270
193
  ```bash
271
- # Build
272
194
  nx build ui
273
-
274
- # Test
275
195
  nx test ui
276
-
277
- # Lint
278
196
  nx lint ui
279
197
  ```
280
198
 
281
- ### Testing
282
-
283
- - 95%+ coverage required across statements, branches, functions, lines
284
- - Test all component variants and edge cases
285
- - Test renderer content detection logic
286
- - Test bridge adapter detection and capability checks
287
-
288
- ## Requirements
289
-
290
- - Node.js >= 22.0.0
291
- - React ^18.0.0 || ^19.0.0
292
- - MUI >= 7.0.0
293
-
294
- ## Related Packages
295
-
296
- - [`@frontmcp/uipack`](../uipack) — React-free server-side shell builder, import resolver, component loader
297
- - [`@frontmcp/sdk`](../sdk) — Core FrontMCP SDK
298
- - [`@frontmcp/testing`](../testing) — UI test assertions
299
-
300
199
  ## License
301
200
 
302
- Apache-2.0 — see [LICENSE](../../LICENSE).
201
+ Apache-2.0. See [LICENSE](https://github.com/agentfront/frontmcp/blob/main/LICENSE).
package/esm/package.json CHANGED
@@ -1,30 +1,27 @@
1
1
  {
2
2
  "name": "@frontmcp/ui",
3
- "version": "1.9.6",
4
- "description": "FrontMCP UI - MUI-based React components, renderers, and MCP bridge for MCP applications",
3
+ "version": "1.9.7",
4
+ "description": "React components, bridge hooks and content renderers (built on MUI) for FrontMCP tool widgets in ChatGPT, Claude and other MCP Apps hosts",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://frontmcp.dev/reference/ui",
7
7
  "license": "Apache-2.0",
8
8
  "keywords": [
9
+ "frontmcp",
9
10
  "mcp",
11
+ "model-context-protocol",
12
+ "mcp-apps",
13
+ "widgets",
10
14
  "ui",
11
- "agentfront",
12
- "frontmcp",
13
15
  "react",
14
16
  "mui",
15
- "material-ui",
16
- "hooks",
17
17
  "components",
18
18
  "renderers",
19
- "charts",
20
- "mermaid",
21
- "markdown",
22
- "pdf",
23
- "maps"
19
+ "agentfront"
24
20
  ],
25
21
  "repository": {
26
22
  "type": "git",
27
- "url": "git+https://github.com/agentfront/frontmcp.git"
23
+ "url": "git+https://github.com/agentfront/frontmcp.git",
24
+ "directory": "libs/ui"
28
25
  },
29
26
  "bugs": {
30
27
  "url": "https://github.com/agentfront/frontmcp/issues"
@@ -438,8 +435,8 @@
438
435
  "optional": true
439
436
  }
440
437
  },
441
- "optionalDependencies": {
442
- "@frontmcp/uipack": "1.9.6"
438
+ "dependencies": {
439
+ "@frontmcp/uipack": "1.9.7"
443
440
  },
444
441
  "devDependencies": {
445
442
  "@types/dompurify": "^3.0.0",
package/package.json CHANGED
@@ -1,30 +1,27 @@
1
1
  {
2
2
  "name": "@frontmcp/ui",
3
- "version": "1.9.6",
4
- "description": "FrontMCP UI - MUI-based React components, renderers, and MCP bridge for MCP applications",
3
+ "version": "1.9.7",
4
+ "description": "React components, bridge hooks and content renderers (built on MUI) for FrontMCP tool widgets in ChatGPT, Claude and other MCP Apps hosts",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://frontmcp.dev/reference/ui",
7
7
  "license": "Apache-2.0",
8
8
  "keywords": [
9
+ "frontmcp",
9
10
  "mcp",
11
+ "model-context-protocol",
12
+ "mcp-apps",
13
+ "widgets",
10
14
  "ui",
11
- "agentfront",
12
- "frontmcp",
13
15
  "react",
14
16
  "mui",
15
- "material-ui",
16
- "hooks",
17
17
  "components",
18
18
  "renderers",
19
- "charts",
20
- "mermaid",
21
- "markdown",
22
- "pdf",
23
- "maps"
19
+ "agentfront"
24
20
  ],
25
21
  "repository": {
26
22
  "type": "git",
27
- "url": "git+https://github.com/agentfront/frontmcp.git"
23
+ "url": "git+https://github.com/agentfront/frontmcp.git",
24
+ "directory": "libs/ui"
28
25
  },
29
26
  "bugs": {
30
27
  "url": "https://github.com/agentfront/frontmcp/issues"
@@ -438,8 +435,8 @@
438
435
  "optional": true
439
436
  }
440
437
  },
441
- "optionalDependencies": {
442
- "@frontmcp/uipack": "1.9.6"
438
+ "dependencies": {
439
+ "@frontmcp/uipack": "1.9.7"
443
440
  },
444
441
  "devDependencies": {
445
442
  "@types/dompurify": "^3.0.0",