Import
import {
resourceWidget,
resourceList,
resourceItem,
codePreview,
imagePreview,
escapeHtml,
} from '@frontmcp/ui';
Resource types
| Type | Description |
|---|
document | Files, notes, briefs, or transcripts |
image | Screenshots, marketing assets, generated art |
code | Snippets, diffs, or diagnostics |
data | Metrics, analytics exports, dashboards |
file | Generic binary downloads |
link | Deep links to dashboards or SaaS tools |
user | People/org profiles |
event | Meetings, incidents, launches |
message | Conversations or notifications |
task | Tickets, todos, approvals |
custom | Catch-all with your own icon |
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',
},
},
],
});
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.