Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ _Made with ❤️ for TypeScript developers_
[![NPM - @frontmcp/sdk](https://img.shields.io/npm/v/@frontmcp/sdk.svg?v=2)](https://www.npmjs.com/package/@frontmcp/sdk)
[![Node](https://img.shields.io/badge/node-%3E%3D22-339933?logo=node.js&logoColor=white)](https://nodejs.org)
[![License](https://img.shields.io/github/license/agentfront/frontmcp.svg?v=1)](https://github.com/agentfront/frontmcp/blob/main/LICENSE)
[![Snyk](https://snyk.io/test/github/agentfront/frontmcp/badge.svg)](https://snyk.io/test/github/agentfront/frontmcp)

</div>

Expand Down
30 changes: 17 additions & 13 deletions docs/draft/docs/guides/ui-library.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
title: Platform-Aware UI Library
slug: guides/ui-library
icon: layout-grid
description: Ship HTML-first consent, status, and orchestration screens with the @frontmcp/ui package.
description: Ship HTML-first consent, status, and orchestration screens with @frontmcp/ui and @frontmcp/uipack.
---

@frontmcp/ui is a platform-aware HTML component kit designed for LLM surfaces. It renders complete markup (no React runtime) and ships a Tailwind v4 theme system, HTMX hooks, page templates, and OpenAI App widgets so you can style consent screens, troubleshooting pages, or CodeCall dashboards inside any MCP server.
The FrontMCP UI packages provide a platform-aware HTML component system designed for LLM surfaces. They render complete markup (no React runtime required) and ship a Tailwind v4 theme system, HTMX hooks, page templates, and OpenAI App widgets so you can style consent screens, troubleshooting pages, or CodeCall dashboards inside any MCP server.

<Note>
All exports live in `@frontmcp/ui`—bring them into your server, Remix app, or plain Node script without extra bundling.
**Package responsibilities:**
- `@frontmcp/ui` provides HTML components (`/components`), layouts (`/layouts`), widgets (`/widgets`), and React components (`/react`)
- `@frontmcp/uipack` provides theming (`/theme`), page templates (`/pages`), and build utilities
</Note>

## Why @frontmcp/ui
## Why use the UI packages

- **Pure HTML output** – Components return strings so you can stream them directly through MCP resources, auth flows, or ngrok tunnels.
- **Platform detection** – Helpers detect OpenAI, Claude, Gemini, and local browsers so you can inline scripts when networks are blocked.
Expand All @@ -23,15 +25,15 @@ All exports live in `@frontmcp/ui`—bring them into your server, Remix app, or

<CodeGroup>
```bash npm
npm install @frontmcp/ui
npm install @frontmcp/ui @frontmcp/uipack
```

```bash pnpm
pnpm add @frontmcp/ui
pnpm add @frontmcp/ui @frontmcp/uipack
```

```bash yarn
yarn add @frontmcp/ui
yarn add @frontmcp/ui @frontmcp/uipack
```

</CodeGroup>
Expand All @@ -41,7 +43,9 @@ yarn add @frontmcp/ui
Wrap content with `baseLayout`, then mix components such as `card` and `button`. Everything resolves to HTML strings you can hand to an MCP resource or auth callback.

```ts
import { baseLayout, card, button, DEFAULT_THEME } from '@frontmcp/ui';
import { baseLayout } from '@frontmcp/ui/layouts';
import { card, button } from '@frontmcp/ui/components';
import { DEFAULT_THEME } from '@frontmcp/uipack/theme';

const content = card(
`
Expand Down Expand Up @@ -69,7 +73,7 @@ Send `html` as the body of an MCP resource, OAuth consent page, or even an email
Every interactive component accepts an `htmx` block. Attributes are sanitized to prevent unsafe protocols or arbitrary data-\* fields, so you can expose progressive enhancement safely.

```ts
import { form, input, button } from '@frontmcp/ui';
import { form, input, button } from '@frontmcp/ui/components';

const loginForm = form(
`
Expand Down Expand Up @@ -102,7 +106,7 @@ import {
getPlatform,
canUseCdn,
needsInlineScripts,
} from '@frontmcp/ui';
} from '@frontmcp/uipack/theme';

const theme = createTheme({
name: 'crm-dark',
Expand Down Expand Up @@ -142,7 +146,7 @@ Claude Artifacts and other restricted environments block outbound requests. Call
Need a consent or error page fast? Use the prebuilt templates so every auth flow looks consistent.

```ts
import { consentPage, consentSuccessPage, errorPage } from '@frontmcp/ui';
import { consentPage, consentSuccessPage, errorPage } from '@frontmcp/uipack/pages';

const grantScreen = consentPage({
client: { name: 'CodeCall CRM', id: 'crm' },
Expand All @@ -169,7 +173,7 @@ const outageScreen = errorPage({
Components validate input with Zod. When something is wrong, the library renders a compact error box that lists the component name and the failed property—perfect for catching mistakes before you ship.

```ts
import { button } from '@frontmcp/ui';
import { button } from '@frontmcp/ui/components';

const ok = button('Continue', { variant: 'primary' });

Expand All @@ -183,4 +187,4 @@ Validation errors render in development and production. Keep an eye on your prev

## Widgets and next steps

The `widgets` module exposes OpenAI App SDK components (resource pickers, action lists) plus status badges, progress indicators, and table helpers. Pair this guide with the [CodeCall CRM demo](/docs/guides/codecall-crm-demo) to see the library inside a full orchestration workflow, or expose the HTML through an MCP resource for your own consent flows.
The `@frontmcp/ui/widgets` module exposes OpenAI App SDK components (resource pickers, action lists) plus status badges, progress indicators, and table helpers. Pair this guide with the [CodeCall CRM demo](/docs/guides/codecall-crm-demo) to see the library inside a full orchestration workflow, or expose the HTML through an MCP resource for your own consent flows.
4 changes: 2 additions & 2 deletions docs/draft/docs/servers/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ ui: {
Combine Tool UI with the `@frontmcp/ui` component library:

```ts
import { card, badge, button, descriptionList } from '@frontmcp/ui';
import { card, badge, button, descriptionList } from '@frontmcp/ui/components';

@Tool({
name: 'get_order',
Expand Down Expand Up @@ -817,7 +817,7 @@ test('renders weather UI correctly', async ({ mcp }) => {
Complete configuration options and examples
</Card>
<Card title="UI Components" icon="puzzle-piece" href="/docs/ui/components/overview">
Pre-built components from @frontmcp/ui
Pre-built components from @frontmcp/uipack
</Card>
<Card title="Templates" icon="code" href="/docs/ui/templates/overview">
HTML, React, and MDX template patterns
Expand Down
12 changes: 7 additions & 5 deletions docs/draft/docs/ui/advanced/custom-renderers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const mdxTemplate = `# {output.title}\n<Card>{output.body}</Card>`;
### 1. Define the Renderer Interface

```typescript title="src/renderers/my-renderer.ts"
import { TemplateContext } from '@frontmcp/ui';
import type { TemplateContext } from '@frontmcp/uipack/types';

export interface CustomRendererOptions {
// Renderer-specific options
Expand All @@ -81,7 +81,7 @@ export interface CustomRenderer {
### 2. Implement the Renderer

```typescript title="src/renderers/my-renderer.ts"
import { escapeHtml } from '@frontmcp/ui';
import { escapeHtml } from '@frontmcp/ui/components';

export const myCustomRenderer: CustomRenderer = {
canHandle(template: unknown): boolean {
Expand Down Expand Up @@ -121,7 +121,7 @@ interface MyCustomTemplate {
### 3. Register the Renderer

```typescript title="src/renderers/index.ts"
import { htmlRenderer, reactRenderer, mdxRenderer } from '@frontmcp/ui';
import { htmlRenderer, reactRenderer, mdxRenderer } from '@frontmcp/uipack/renderers';
import { myCustomRenderer } from './my-renderer';

// Order matters - first matching renderer wins
Expand Down Expand Up @@ -152,7 +152,8 @@ A renderer for YAML-based templates:

```typescript title="src/renderers/yaml-renderer.ts"
import yaml from 'js-yaml';
import { escapeHtml, TemplateContext } from '@frontmcp/ui';
import { escapeHtml } from '@frontmcp/ui/components';
import type { TemplateContext } from '@frontmcp/uipack/types';

interface YamlTemplate {
title?: string;
Expand Down Expand Up @@ -262,7 +263,8 @@ This example uses `new Function()` for expression evaluation, which is equivalen

```typescript title="src/renderers/markdown-renderer.ts"
import { marked } from 'marked';
import { escapeHtml, TemplateContext } from '@frontmcp/ui';
import { escapeHtml } from '@frontmcp/ui/components';
import type { TemplateContext } from '@frontmcp/uipack/types';

export const markdownRenderer = {
canHandle(template: unknown): boolean {
Expand Down
6 changes: 3 additions & 3 deletions docs/draft/docs/ui/advanced/hydration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ With hydration, you can use full React features:

```tsx title="src/widgets/counter.tsx"
import { useState } from 'react';
import { TemplateContext } from '@frontmcp/ui';
import type { TemplateContext } from '@frontmcp/uipack/types';

interface CounterOutput {
initialCount: number;
Expand Down Expand Up @@ -173,7 +173,7 @@ export function ToolInvoker() {

```tsx title="src/widgets/hooks/use-mcp-bridge.ts"
import { useEffect, useState, useCallback } from 'react';
import type { MCPBridge, HostContext } from '@frontmcp/ui';
import type { MCPBridge, HostContext } from '@frontmcp/uipack/runtime';

export function useMcpBridge() {
const [bridge, setBridge] = useState<MCPBridge | null>(null);
Expand Down Expand Up @@ -279,7 +279,7 @@ ui: {
For simpler interactions, consider HTMX instead:

```typescript
import { button } from '@frontmcp/ui';
import { button } from '@frontmcp/ui/components';

// No hydration needed
const html = button('Load More', {
Expand Down
4 changes: 2 additions & 2 deletions docs/draft/docs/ui/advanced/mcp-bridge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async function loadMoreItems() {
### Button with Tool Call

```typescript
import { button } from '@frontmcp/ui';
import { button } from '@frontmcp/ui/components';

const html = `
${button('Load More', { id: 'load-more-btn' })}
Expand Down Expand Up @@ -191,7 +191,7 @@ const unsubscribe = window.mcpBridge.onToolResult((result) => {

```typescript
import { Tool, ToolContext } from '@frontmcp/sdk';
import { card, button, input, badge } from '@frontmcp/ui';
import { card, button, input, badge } from '@frontmcp/ui/components';

@Tool({
name: 'todo_list',
Expand Down
23 changes: 12 additions & 11 deletions docs/draft/docs/ui/advanced/platforms.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ description: FrontMCP UI adapts to different AI platform capabilities. Each plat
FrontMCP automatically detects the platform:

```typescript
import { getPlatform, OPENAI_PLATFORM, CLAUDE_PLATFORM } from '@frontmcp/ui';
import { getPlatform, OPENAI_PLATFORM, CLAUDE_PLATFORM } from '@frontmcp/uipack/theme';

// Auto-detect from user agent or context
const platform = getPlatform();
Expand Down Expand Up @@ -54,7 +54,7 @@ OpenAI's Apps SDK provides full widget capabilities:
### Configuration

```typescript
import { OPENAI_PLATFORM } from '@frontmcp/ui';
import { OPENAI_PLATFORM } from '@frontmcp/uipack/theme';

// Platform preset
console.log(OPENAI_PLATFORM);
Expand Down Expand Up @@ -166,7 +166,7 @@ ui: {
### Configuration

```typescript
import { CLAUDE_PLATFORM } from '@frontmcp/ui';
import { CLAUDE_PLATFORM } from '@frontmcp/uipack/theme';

// Platform preset
console.log(CLAUDE_PLATFORM);
Expand All @@ -189,7 +189,8 @@ console.log(CLAUDE_PLATFORM);
For environments without any network access, inline all scripts:

```typescript
import { baseLayout, fetchAndCacheScriptsFromTheme } from '@frontmcp/ui';
import { baseLayout } from '@frontmcp/ui/layouts';
import { fetchAndCacheScriptsFromTheme } from '@frontmcp/uipack/theme';

// Pre-fetch and cache scripts at startup
await fetchAndCacheScriptsFromTheme(theme);
Expand All @@ -210,7 +211,7 @@ Gemini has limited widget support:
### Configuration

```typescript
import { GEMINI_PLATFORM } from '@frontmcp/ui';
import { GEMINI_PLATFORM } from '@frontmcp/uipack/theme';

// Platform preset
console.log(GEMINI_PLATFORM);
Expand Down Expand Up @@ -241,7 +242,7 @@ For Gemini, keep widgets simple:
Create templates that adapt to the platform:

```typescript
import { getPlatform, canUseCdn, needsInlineScripts } from '@frontmcp/ui';
import { getPlatform, canUseCdn, needsInlineScripts } from '@frontmcp/uipack/theme';

@Tool({
name: 'my_tool',
Expand All @@ -268,7 +269,7 @@ import { getPlatform, canUseCdn, needsInlineScripts } from '@frontmcp/ui';
Handle missing capabilities gracefully:

```typescript
import { supportsFullInteractivity, getFallbackMode } from '@frontmcp/ui';
import { supportsFullInteractivity, getFallbackMode } from '@frontmcp/uipack/theme';

const platform = getPlatform();

Expand All @@ -287,7 +288,7 @@ if (supportsFullInteractivity(platform)) {
Define custom platform configurations:

```typescript
import { createPlatform } from '@frontmcp/ui';
import { createPlatform } from '@frontmcp/uipack/theme';

const myPlatform = createPlatform({
id: 'my-platform',
Expand Down Expand Up @@ -329,11 +330,11 @@ Test your widgets on each platform:

```typescript
// Mock platform for testing
import { getPlatform } from '@frontmcp/ui';
import { getPlatform } from '@frontmcp/uipack/theme';

// Override for testing
jest.mock('@frontmcp/ui', () => ({
...jest.requireActual('@frontmcp/ui'),
jest.mock('@frontmcp/uipack/theme', () => ({
...jest.requireActual('@frontmcp/uipack/theme'),
getPlatform: jest.fn().mockReturnValue({
id: 'claude',
network: 'blocked',
Expand Down
Loading
Loading