Skip to main content

Import

import {
  resourceWidget,
  resourceList,
  resourceItem,
  codePreview,
  imagePreview,
  escapeHtml,
} from '@frontmcp/ui';

Resource types

TypeDescription
documentFiles, notes, briefs, or transcripts
imageScreenshots, marketing assets, generated art
codeSnippets, diffs, or diagnostics
dataMetrics, analytics exports, dashboards
fileGeneric binary downloads
linkDeep links to dashboards or SaaS tools
userPeople/org profiles
eventMeetings, incidents, launches
messageConversations or notifications
taskTickets, todos, approvals
customCatch-all with your own icon

resourceWidget()

resourceWidget renders an App SDK-ready card with iconography, metadata, tags, and action buttons. It returns a pure HTML string, so you can embed it directly inside a tool response or reuse it from a resource template.
const noteWidget = resourceWidget({
  type: 'document',
  title: ctx.output.title,
  description: ctx.output.summary,
  icon: '📝',
  url: `notes://${ctx.output.id}`,
  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',
      },
    },
  ],
});

Metadata & statuses

Use the meta object for timestamps, MIME types, sizes, authors, and tags. Dates accept ISO strings or Date objects and are formatted automatically. Add a status badge whenever you need to highlight SLA breaches, approvals, or lifecycle states.
const attachment = resourceWidget({
  type: 'file',
  title: 'Invoice.pdf',
  description: 'Quarterly renewal',
  status: { label: 'Needs approval', variant: 'warning' },
  meta: {
    size: 48211,
    mimeType: 'application/pdf',
    createdAt: ctx.output.createdAt,
    tags: ['finance', 'q4'],
  },
});

Actions

Actions render as buttons with optional HTMX attributes for server-driven interactivity. Use them for quick approvals, assignments, or follow-up tool calls.
const actions = [
  { label: 'Approve', variant: 'primary', icon: '', href: '/api/approve' },
  {
    label: 'Assign',
    icon: '👤',
    htmx: {
      post: `/api/tasks/${ctx.output.id}/assign`,
      target: '#task-status',
      swap: 'innerHTML',
      confirm: 'Assign this task?',
    },
  },
];
Always escape user-provided strings with escapeHtml or ctx.helpers.escapeHtml before passing them into title, description, meta, or custom markup.

resourceList()

resourceList composes multiple widgets into a list or grid layout and can render an optional “Load more” button that wires into HTMX for infinite scroll.
const incidentList = resourceList({
  title: 'Active incidents',
  layout: 'grid',
  columns: 2,
  resources: ctx.output.incidents.map((incident) =>
    resourceWidget({
      type: 'event',
      title: incident.title,
      description: incident.summary,
      status: {
        label: incident.severity.toUpperCase(),
        variant: incident.severity === 'critical' ? 'danger' : 'warning',
      },
      meta: {
        updatedAt: incident.updatedAt,
        tags: incident.services,
      },
    }),
  ),
  showLoadMore: ctx.output.hasMore,
  loadMoreUrl: '/api/incidents?page=2',
});
Pick layout: 'grid' with columns: 1 for tight clients like Claude Artifacts, or switch to 'list' when you want a vertically stacked newsfeed.

resourceItem()

resourceItem renders a compact row that fits sidebars, drawers, or inline search results.
const upcoming = resourceItem({
  type: 'task',
  title: ctx.output.task.title,
  description: ctx.output.task.dueDate,
  status: ctx.output.task.completed
    ? { label: 'Done', variant: 'success' }
    : { label: 'Pending', variant: 'secondary' },
});

Preview helpers

codePreview

Embed highlighted code, diffs, or JSON with optional filenames, copy buttons, and line numbers.
const preview = codePreview({
  code: ctx.output.diff,
  language: 'diff',
  filename: ctx.output.path,
  lineNumbers: true,
  showCopy: true,
  maxHeight: '320px',
});

imagePreview

Display screenshots or generated media with captions and optional click-through behavior.
const hero = imagePreview({
  src: ctx.output.url,
  alt: ctx.output.description,
  caption: 'Latest marketing mockup',
  maxHeight: '420px',
  clickable: true,
});

Best practices

  • Build data objects first, then pass only trusted fields into the widget helpers.
  • Combine resourceWidget with card, badge, or button helpers when you want additional layout control.
  • Use HTMX actions (get, post, target, swap) for mutations so your widget stays interactive without custom JavaScript.
  • Gate external network requests behind the tool’s csp configuration so hosts like Claude know which domains to allow.
  • Pair widgets with widgetDescription in the tool metadata so clients can explain the UI before rendering it.
Expose the same HTML via a static serving mode or resource template when you want to preload widgets outside of a tool response.