Skip to content

Schema-Driven UI

Use `@xmcp-dev/ui` to render LLM-generated JSON schemas into interactive apps with `createRenderJsonTool()`, `Rendered`, or `App`.

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

Overview

Schema-driven UI lets an LLM create apps on the fly. The model generates a JSON schema describing the layout, components, and tool bindings based on what the user asked, and @xmcp-dev/ui renders it into a working app at runtime. The user never writes UI code.

There are three entry points, each giving you more control:

  • createRenderJsonTool(): zero-config, handles everything
  • Rendered: custom tool wrapper with the built-in preview engine
  • App: full ownership of parsing, validation, and rendering

Start with createRenderJsonTool() and move down only when you need more control.

Info

If you are building a handwritten React MCP App instead of rendering LLM-generated schemas, see MCP Apps.

Quickstart

To scaffold a new project with the package, stylesheet, tools, and schema reference resource already wired up:

You can also run the interactive command and select MCP App with UI kit. The starter puts globals.css at the project root, matching the Tailwind MCP App template. xmcp automatically includes this stylesheet in tool UIs.

Import the package stylesheet once:

globals.css

Expose the render-json tool:

src/tools/render-json.tsx

Add the schema reference as a normal xmcp resource so the model can read the component contract named in the tool description:

src/resources/(skill)/xmcp-ui/schema-reference.ts

Both create-xmcp-app --ui-kit and npx @xmcp-dev/ui init create this resource for you.

The model supplies a schemaJson string and the package handles parsing, validation, progressive preview, theme fallback, and rendering.

Adding shadcn components

The create-xmcp-app --ui-kit starter is configured for the shadcn CLI. From its project directory, add the components you want:

You can use the generated components in handwritten React tools:

components.json points to src/components/ui, with native #components, #lib, and #hooks package imports. The starter enables TypeScript's bundler resolution and includes src/lib/utils.ts, which re-exports the kit's cn. The CLI installs dependencies required by each component.

The root stylesheet imports @xmcp-dev/ui/shadcn.css and tw-animate-css. The optional shadcn stylesheet maps Tailwind v4's semantic utilities, such as bg-primary and text-foreground, to the kit's HSL theme tokens. Keep this stylesheet when adding components; there is no need to run shadcn init again.

AppShell supplies its theme tokens and dark-mode class to nested components. For dialogs and other components that render portals under document.body, keep the document's dark class in sync with the AppShell theme as well. The stylesheet supplies light and dark defaults at the document level.

The existing-project xmcp-ui init command adds the UI kit files; configuring shadcn in an existing project follows the manual installation guide. Use the same optional stylesheet to keep the kit's theme tokens compatible.

Transport

transportMode defaults to "auto": tool calls use the MCP App host when the renderer is embedded in one and fall back to direct HTTP for standalone previews. Set "host" only when the app must require a host:

src/tools/render-json.tsx

Use "http" to require direct HTTP. The same option exists on Rendered and App.

Direct HTTP uses the MCP SDK to negotiate modern or legacy servers and correlate JSON and SSE responses. Connections are shared within an app and closed when it unmounts or switches to the host. Stateless servers remain stateless; when a server issues a session, the client reuses it and requests its deletion during cleanup. If a session expires, the failed tool call is not replayed: the next user action opens a new connection.

Constrain model-provided endpoints

An AppSchema can include mcpServerUrl and mcpHeaders. When the schema comes from a model, pin the endpoint in server-owned configuration:

src/tools/render-json.tsx

serverUrl ignores the schema's mcpServerUrl. allowedOrigins renders a visible error if the effective origin is not allowed. The renderer always rejects non-HTTP(S) endpoints, embedded credentials, and redirects. It drops invalid, transport-reserved, or unsafe header entries before calling fetch.

What the packaged path does for you

The packaged renderJson path renders through Rendered and handles common model-generated mistakes:

  • Progressive previewing while the model is still streaming
  • Partial JSON recovery instead of waiting for the full payload
  • Normalizes common safe sizing mistakes
  • Rejects unreadable inline themeTokens, falling back to the preset theme
  • Light mode and zinc preset by default

The server author controls defaults while the model only needs to supply schemaJson.

Using Rendered

Use this when you want your own tool definition but still want the built-in preview engine.

src/tools/render-dashboard.tsx

Using App directly

Use this when you want to own the full preview pipeline: JSON parsing, schema validation, loading states, error handling, and theme control.

src/tools/render-strict-app.tsx

References

On this page

One framework to rule them all