Skip to main content

Why Use FrontMCP UI?

When your MCP tools return data, AI platforms typically display it as raw text or JSON. FrontMCP UI lets you create visual widgets that make your tool outputs more useful and engaging.

Plain Text Response

{
  "location": "San Francisco",
  "temperature": 18,
  "conditions": "Foggy",
  "humidity": 75
}

With FrontMCP UI

A beautifully styled weather card with icons, formatted temperatures, and visual humidity indicators.

Key Features

Multi-Framework Support

Build widgets with HTML strings, React components, or MDX (Markdown + JSX). The renderer auto-detects your template type.

Platform-Aware

Works across OpenAI, Claude, and Gemini with automatic platform detection and fallbacks for different capabilities.

Built-in Components

Pre-built components for buttons, cards, badges, alerts, forms, tables, and more with consistent styling.

Theming System

Customize colors, typography, and spacing. Includes dark mode support and CSS variable integration.

Zod Validation

All component inputs are validated with Zod schemas. Invalid options render helpful error boxes instead of crashing.

HTMX Integration

Add dynamic interactions without JavaScript using HTMX attributes for server-driven updates.

OpenAI Resource Widgets

Render App SDK-ready cards, lists, and previews for documents, tasks, and media without rebuilding layouts.

Platform Support

FrontMCP UI adapts to different AI platform capabilities:
PlatformNetworkWidget DisplayResponse Format
OpenAIFullInline, Fullscreen, PiP_meta['ui/html']
ext-appsFullInline, Fullscreen, PiP_meta['ui/html']
CursorFullInline_meta['ui/html']
ClaudeBlockedArtifactsDual-payload
GeminiLimitedBasicJSON only
The library automatically detects the platform and selects the appropriate delivery format.
With servingMode: 'auto' (default), unsupported platforms automatically receive JSON-only responses—no broken widgets.

Architecture

┌─────────────────────────────────────────────────────────┐
│                    Your MCP Tool                         │
│  @Tool({ ui: { template: ..., servingMode: 'auto' } })  │
└───────────────────────┬─────────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│               FrontMCP UI Renderer                       │
│  Auto-detects: HTML | React | MDX                       │
│  + Platform Detection + Response Format Selection       │
└───────────────────────┬─────────────────────────────────┘

          ┌─────────────┼─────────────┐
          ▼             ▼             ▼
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │  OpenAI  │  │  Claude  │  │  Gemini  │
    │  Widget  │  │ Artifact │  │ JSON-only│
    │ _meta UI │  │Dual-Pay  │  │          │
    └──────────┘  └──────────┘  └──────────┘

Tool UI metadata at a glance

Every @Tool exposes a ui block that describes how the widget should render across hosts. Configure these settings once and the renderer keeps your UX consistent everywhere.
SettingControlsUse it when
displayModeWhether the widget renders inline, fullscreen, or picture-in-pictureHighlight dashboards inline or promote immersive flows into fullscreen/PiP.
servingModeHow HTML is delivered (auto, inline, static, hybrid, URL-based). Default: 'auto'Let FrontMCP select the best format per platform, or force a specific mode.
htmlResponsePrefixText shown before HTML in Claude’s dual-payload formatCustomize the artifact description for better Claude UX.
widgetAccessibleAllows widgets to call tools through the MCP BridgeCreate follow-up actions (retry, escalate, schedule) directly from the UI.
cspAllowed domains for scripts, images, fonts, and network callsRestrict remote dependencies per platform and satisfy Claude’s strict CSP.
hydrateShips the React runtime for client-side interactivityPower forms, tabs, and charts that need local state or event handlers.
Pair metadata with a concise widgetDescription so clients can preview the UI before rendering it, just like Mintlify recommends leading with the most important context.
Limit csp.resourceDomains and csp.connectDomains to only the hosts your widget truly needs—broad allowlists can leak data or be blocked entirely by conservative clients.

OpenAI App SDK resource widgets

Need a richer layout than a single card? @frontmcp/ui ships resourceWidget, resourceList, resourceItem, codePreview, and imagePreview helpers so your responses match OpenAI Apps SDK expectations without manual HTML.
import { resourceWidget } from '@frontmcp/ui';

const widget = resourceWidget({
  type: 'document',
  title: ctx.output.title,
  description: ctx.output.summary,
  status: ctx.output.archived
    ? { label: 'Archived', variant: 'secondary' }
    : { label: 'Active', variant: 'success' },
  meta: {
    author: ctx.output.author,
    updatedAt: ctx.output.updatedAt,
    tags: ctx.output.tags,
  },
  actions: [
    {
      label: 'Open in CRM',
      icon: '↗️',
      href: `https://crm.example.com/notes/${ctx.output.id}`,
      variant: 'primary',
    },
    {
      label: 'Sync',
      icon: '♻️',
      htmx: {
        post: `/api/notes/${ctx.output.id}/sync`,
        target: '#note-sync',
        swap: 'innerHTML',
      },
    },
  ],
});
Combine resourceList with showLoadMore and HTMX actions to paginate large datasets without writing any client-side JavaScript.
Escape every user-provided field via escapeHtml or ctx.helpers.escapeHtml before injecting it into widget helpers—App SDK platforms will reject or sanitize unsafe markup.
See /docs/ui/components/resource-widgets for the full API surface plus preview helpers.

Quick Example

Add a visual widget to your tool in just a few lines:
src/tools/weather.tool.ts
import { Tool, ToolContext } from '@frontmcp/sdk';
import { card, badge } from '@frontmcp/ui';
import { z } from 'zod';

@Tool({
  name: 'get_weather',
  description: 'Get current weather for a location',
  inputSchema: {
    location: z.string().describe('City name'),
  },
  ui: {
    template: (ctx) => card(`
      <div class="text-4xl mb-4">🌤️</div>
      <div class="text-2xl font-bold">${ctx.output.temperature}°C</div>
      <div class="text-gray-600">${ctx.output.conditions}</div>
    `, {
      title: ctx.output.location,
      subtitle: 'Current Weather',
    }),
  },
})
export class GetWeatherTool extends ToolContext {
  async execute(input: { location: string }) {
    // Fetch weather data...
    return {
      location: input.location,
      temperature: 18,
      conditions: 'Partly Cloudy',
    };
  }
}

Installation

npm install @frontmcp/ui
@frontmcp/ui has a peer dependency on react for React/MDX templates. If you only use HTML templates, React is optional.

Next Steps