@frontmcp/ui 1.9.5 → 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.
- package/README.md +145 -246
- package/esm/package.json +11 -14
- package/package.json +11 -14
package/README.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# @frontmcp/ui
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
[](../../LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/@frontmcp/ui) [](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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
60
|
+
Export the component as the default from `weather.widget.tsx`:
|
|
19
61
|
|
|
20
62
|
```tsx
|
|
21
|
-
import { Badge,
|
|
63
|
+
import { Badge, Card } from '@frontmcp/ui/components';
|
|
22
64
|
import { FrontMcpThemeProvider } from '@frontmcp/ui/theme';
|
|
23
65
|
|
|
24
|
-
|
|
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="
|
|
28
|
-
<
|
|
29
|
-
<
|
|
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
|
-
|
|
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 {
|
|
86
|
+
import { useCallTool } from '@frontmcp/ui/react';
|
|
87
|
+
|
|
88
|
+
type Weather = { city: string; tempC: number };
|
|
43
89
|
|
|
44
|
-
function
|
|
45
|
-
const
|
|
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>{
|
|
51
|
-
<button onClick={() =>
|
|
52
|
-
|
|
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
|
-
|
|
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 {
|
|
161
|
+
import { ContentView, registerAllRenderers } from '@frontmcp/ui/renderer';
|
|
68
162
|
|
|
69
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
+
## Related packages
|
|
256
181
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
442
|
-
"@frontmcp/uipack": "1.9.
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
442
|
-
"@frontmcp/uipack": "1.9.
|
|
438
|
+
"dependencies": {
|
|
439
|
+
"@frontmcp/uipack": "1.9.7"
|
|
443
440
|
},
|
|
444
441
|
"devDependencies": {
|
|
445
442
|
"@types/dompurify": "^3.0.0",
|