@mcp-apps-kit/ui-react-builder
v0.5.0
Published
Build tool for React-based MCP application UIs
Maintainers
Readme
@mcp-apps-kit/ui-react-builder
Build tool for React-based MCP application UIs.
@mcp-apps-kit/ui-react-builder allows you to define UI resources using React components instead of pre-built HTML files. The framework handles bundling React, ReactDOM, and @mcp-apps-kit/ui-react into self-contained HTML that works with both MCP Apps and ChatGPT.
Table of Contents
Background
Building interactive UI widgets for MCP applications traditionally requires manually bundling React components into self-contained HTML files. This package automates that process, letting you define UIs with React components directly in your tool definitions.
Features
defineReactUI()helper for type-safe React component definitions- Vite plugin for automatic discovery and building of React UIs
- AST-based parsing using
@typescript-eslint/typescript-estreefor reliable detection - esbuild-powered bundling to self-contained HTML
- Auto-generated HTML paths from component names (kebab-case)
- Global CSS injection support
- Configurable logging for plugin output
- Full compatibility with
defineTool()from@mcp-apps-kit/core
Compatibility
- Node.js:
>= 18 - Peer dependencies:
@mcp-apps-kit/core^0.2.0@mcp-apps-kit/ui-react^0.2.0reactandreact-dom^18 || ^19vite^5 || ^6 || ^7(optional, for Vite plugin)
Install
npm install @mcp-apps-kit/ui-react-builderUsage
Quick start
Define your React component:
// src/ui/GreetingWidget.tsx
import { useToolResult, useHostContext } from "@mcp-apps-kit/ui-react";
export function GreetingWidget() {
const result = useToolResult();
const { theme } = useHostContext();
return (
<div data-theme={theme}>
<h1>{result?.greet?.message}</h1>
</div>
);
}Use defineReactUI in your tool definition:
// src/index.ts
import { createApp, defineTool } from "@mcp-apps-kit/core";
import { defineReactUI } from "@mcp-apps-kit/ui-react-builder";
import { GreetingWidget } from "./ui/GreetingWidget";
import { z } from "zod";
const app = createApp({
name: "my-app",
version: "1.0.0",
tools: {
greet: defineTool({
description: "Greet someone",
input: z.object({ name: z.string() }),
output: z.object({ message: z.string() }),
ui: defineReactUI({
component: GreetingWidget,
name: "Greeting Widget",
prefersBorder: true,
// Optional: Disable automatic size notifications (default: true)
// autoResize: false,
}),
handler: async ({ name }) => ({
message: `Hello, ${name}!`,
}),
}),
},
});Vite Plugin
The Vite plugin automatically discovers defineReactUI calls and builds them into self-contained HTML files.
Configuration
// vite.config.ts
import { defineConfig } from "vite";
import { mcpReactUI } from "@mcp-apps-kit/ui-react-builder/vite";
export default defineConfig({
plugins: [
mcpReactUI({
// Server entry point to scan for defineReactUI calls
serverEntry: "./src/index.ts",
// Output directory for built HTML files
outDir: "./src/ui/dist",
// Optional: Global CSS to include in all UIs
globalCss: "./src/ui/styles.css",
// Optional: Standalone mode takes over the Vite build and outputs only UI HTML.
// Use this if this Vite config exists solely to build MCP UI widgets.
// standalone: true,
}),
],
});How it works
- The plugin scans your
serverEntryfile fordefineReactUIcalls using AST parsing - It resolves component imports to their source files
- Each component is bundled with React, ReactDOM, and
@mcp-apps-kit/ui-react - Self-contained HTML files are written to
outDir
The plugin uses @typescript-eslint/typescript-estree for reliable AST-based detection of imports and defineReactUI calls. This is more robust than regex-based parsing and correctly handles:
- Nested
defineReactUIcalls (e.g., insidedefineTool) - Comments around definitions
- Various import styles (named, default, aliased)
- Complex code structures (conditionals, arrays, objects)
Supported patterns
The plugin discovers defineReactUI calls using static analysis. For reliable detection:
- Import components directly from their source files:
import { MyWidget } from "./ui/MyWidget"; // ✓ Works import { MyWidget } from "./ui"; // ✗ Barrel imports not supported - Use string literals for the
nameproperty:name: "My Widget"; // ✓ Works name: `My ${type}`; // ✗ Template literals not supported - Reference components by identifier:
component: MyWidget; // ✓ Works component: widgets.MyWidget; // ✗ Property access not supported
If you need patterns not supported by auto-discovery, use defineUI({ html: "..." }) with manual Vite bundling.
Build commands
{
"scripts": {
"dev": "concurrently \"pnpm dev:server\" \"pnpm dev:ui\"",
"dev:server": "tsx watch src/index.ts",
"dev:ui": "vite build --watch",
"build": "pnpm build:ui && tsc",
"build:ui": "vite build"
}
}API
Definition Helpers
| Export | Description |
| --------------- | ---------------------------------------------- |
| defineReactUI | Define a UI using a React component |
| isReactUIDef | Type guard to check if a value is a ReactUIDef |
defineReactUI Options
| Option | Type | Default | Description |
| --------------- | --------------- | ---------- | ------------------------------------------------------------------------------------------- |
| component | ComponentType | (required) | React component to render |
| name | string | (required) | Display name for the UI |
| description | string | - | Description of the UI widget |
| prefersBorder | boolean | - | Hint to the host whether a border should be drawn |
| autoResize | boolean | true | Enable automatic size change notifications. Only supported in MCP Apps; ignored in ChatGPT. |
| csp | CSPConfig | - | Content Security Policy configuration (ChatGPT only) |
Types
| Type | Description |
| -------------- | --------------------------------------- |
| ReactUIInput | Input type for defineReactUI() |
| ReactUIDef | Output type (extends UIDef from core) |
| BuildOptions | Options for the build process |
| BuildResult | Result of building React UIs |
| PluginLogger | Logger interface for Vite plugin |
Build Functions
| Export | Description |
| --------------- | -------------------------------- |
| buildReactUIs | Build multiple React UIs to HTML |
| buildReactUI | Build a single React UI to HTML |
Note: The programmatic build functions (
buildReactUIs,buildReactUI) serialize components using.toString(), which has limitations:
- No external imports (components cannot import other modules)
- No closures (components that capture external variables won't work)
- Simple components only (best for self-contained components)
For production use, prefer the Vite plugin which uses file paths for proper import resolution.
Transform Utilities
| Export | Description |
| -------------------------- | -------------------------------------- |
| transformToCoreDefs | Convert ReactUIDefs to standard UIDefs |
| transformSingleToCoreDef | Convert a single ReactUIDef to UIDef |
| extractReactUIs | Separate React UIs from standard UIs |
| buildAndTransform | Build and transform in one step |
HTML Utilities
| Export | Description |
| -------------------- | -------------------------------------- |
| generateHTML | Generate HTML document from bundled JS |
| generateEntryPoint | Generate React entry point code |
Vite Plugin
import { mcpReactUI } from "@mcp-apps-kit/ui-react-builder/vite";| Option | Type | Default | Description |
| ------------- | ----------------------- | -------------- | ------------------------------------------- |
| serverEntry | string | (required) | Server entry point to scan |
| outDir | string | "./dist/ui" | Output directory for HTML files |
| minify | boolean | true in prod | Minify output JavaScript |
| globalCss | string | - | Path to global CSS file |
| logger | PluginLogger \| false | console | Custom logger or false to disable logging |
| standalone | boolean | false | Take over Vite build (emit only UI HTML) |
Custom logging
// Disable all logging
mcpReactUI({
serverEntry: "./src/index.ts",
logger: false,
});
// Custom logger
mcpReactUI({
serverEntry: "./src/index.ts",
logger: {
info: (msg) => myLogger.info(msg),
warn: (msg) => myLogger.warn(msg),
error: (msg) => myLogger.error(msg),
},
});Examples
../../examples/minimal- Simple hello world with React UI
Contributing
See ../../CONTRIBUTING.md for development setup and guidelines. Issues and pull requests are welcome.
License
MIT
