@frontmcp/ui library to build professional-looking tool outputs.
Prerequisites:
- A working FrontMCP tool (see Your First Tool)
- Basic understanding of HTML/CSS
What You’ll Build
A weather tool that displays temperature, conditions, and other data in a styled card with badges and description lists.Step 1: Install the UI Package
Required for
.tsx/.jsx FileSource widgets. When you point ui.template at a .tsx/.jsx file (the recommended pattern for non-trivial widgets), FrontMCP injects an auto-generated React mount that imports McpBridgeProvider from @frontmcp/ui/react. Server-side bundling fails without this package installed. Match the version to @frontmcp/sdk. react and react-dom stay external and load from the CDN at runtime, so only @frontmcp/ui needs to be present on disk.resourceMode is host-detected. Leave it unset and FrontMCP auto-switches to 'inline' when the connecting client is Claude (#456), so the widget actually renders in Claude’s sandboxed iframe — React is bundled into the widget itself (#454). Other hosts keep 'cdn' for a smaller payload. Set resourceMode explicitly to override the detection. Auto-detection only applies to per-call rendering modes (inline / hybrid / lean); servingMode: 'static' widgets are pre-compiled at startup with no client context, so set resourceMode: 'inline' explicitly when targeting Claude in static mode.ui.csp is now emitted on the widget resource (#455). FrontMCP attaches ui.csp to the resources/read content item’s _meta.ui.csp (and _meta['ui/csp']) so MCP Apps hosts — particularly Claude — actually honor it. Previously the CSP only appeared on the tool listing, where Claude ignored it.Use the
*.widget.tsx naming convention. The scaffolded tsconfig.json excludes **/*.widget.tsx / **/*.widget.jsx from the server typecheck (frontmcp init also adds the excludes to existing tsconfigs). Widget sources are bundled separately by uipack/esbuild at render time, so the server tsconfig doesn’t need jsx: 'react-jsx' or @types/react for them. Add a sibling tsconfig.widget.json if you want IDE typecheck for widget files.Step 2: Create a Tool with UI Template
Theui property in the @Tool decorator lets you define a template function that renders HTML:
UI Configuration Options
Human-readable description of what the widget displays. Shown to users in UI-capable hosts.
Preferred display mode (hint to the host — may be ignored):
inline- Rendered inline in the conversation (default)fullscreen- Request fullscreen displaypip- Picture-in-picture
How the HTML is delivered to the client (default:
auto):auto- Auto-select per host (OpenAI / Claude / unknown)inline- Embedded in tool response_meta['ui/html'](works everywhere)static- Pre-compiled at startup; client fetchesui://widget/{toolName}.htmlviaresources/readhybrid- Shell pre-compiled; component code + data delivered per call in_meta['ui/component']direct-url- Served from an HTTP path on the MCP server (directPath)custom-url- Served from an external URL (customWidgetUrl, supports a{token}placeholder)
A function that receives the execution context and returns an HTML string.
Available UI Components
Card
Wrap content in a styled container:Badge
Display status or category labels:Description List
Show key-value pairs:Button
Create styled buttons:Form and Input
Build forms with validation:Template Context
The template function receives a context object with:Available Helpers
| Helper | Description |
|---|---|
escapeHtml(str) | Escape HTML entities to prevent XSS (handles null/undefined) |
formatDate(date, format?) | Format a date (accepts Date or ISO string) |
formatCurrency(amount, ccy?) | ISO-4217 currency formatting (defaults to 'USD') |
uniqueId(prefix?) | Generate a unique ID for DOM elements |
jsonEmbed(data) | Safely embed JSON in an inline <script> (escapes </script>) |
Practical Example: Expense Summary
Platform Considerations
Different MCP hosts have different capabilities and network policies:- OpenAI Apps SDK — any CDN reachable; widget URI surfaces under
_meta['openai/outputTemplate'] - Claude (MCP-UI) — only
cdnjs.cloudflare.comis reachable; preferresourceMode: 'inline'so the widget shell is self-contained, and pin anyexternalsto cdnjs URLs viadependencies - MCP Inspector — useful for local development; honors
servingMode: 'static' - Gemini / unknown hosts —
uiis ignored; the tool returns JSON only
servingMode: 'auto' selects the right mode per host. See the Tool UI reference for the full surface: serving modes, CSP, the window.FrontMcpBridge runtime, file-based .tsx widgets, and platform-specific troubleshooting.
Next Steps
React SDK Components
Pre-built React components
Tool Reference
Full @Tool decorator options
CodeCall CRM Demo
See UI in a full application
Create Prompts
Build prompts alongside tools