Skip to main content
Generative UI allows models to create custom UI on the fly through a JSON Schema.

What you’ll build

You will create an MCP App that allows for dynamically generated content, powered by JSON-Render. The app:
  • gives the model a catalog of allowed components and actions
  • accepts a type-safe JSON-Render specification as structured input
  • streams partial specifications into the mounted view
  • renders the completed specification from structuredContent
The complete implementation is available in the generative-ui example. For details about catalogs, specifications, and renderers, use the official JSON-Render documentation.

How the integration works

A JSON-Render catalog defines the components and actions the model can use. The catalog also produces the Zod schema and prompt guidance for the MCP tool. The model writes a JSON-Render spec into the tool’s structured arguments. While the tool call is pending, MCP Apps hosts send partial arguments to the view through ui/notifications/tool-input-partial. After validation, the tool returns the final specification in structuredContent. The tool argument must be a structured object. Do not ask the model to stream a stringified JSON document. Structured input lets the MCP host parse and deliver usable partial values without repairing incomplete JSON strings in the view.

Install JSON-Render

Install the JSON-Render core, React renderer, and shadcn component catalog:
This guide uses the prebuilt shadcn catalog. You can define a smaller custom catalog when your app needs tighter control over the generated interface.

Define the component catalog

Create views/generative-ui/catalog.ts and export one catalog for both the server and the React view:
The catalog is the model’s UI vocabulary and the runtime validation boundary. Only catalog components can appear in a valid specification. For a production app, expose only the components the model needs. You can also add your own typed component definitions and actions. See Catalogs in the JSON-Render documentation.

Create the generative UI tool

Generate the tool’s input schema from the catalog instead of maintaining a separate schema:
The tool description tells the model to write spec.root and the root element first. This ordering gives the view enough information to mount before the rest of the element map arrives.
catalog.prompt() includes output instructions for JSONL patch streaming. The catalogGuidance() helper removes those instructions because this MCP tool accepts one structured spec object. It preserves the catalog’s component, action, state, and event guidance.
Return the final specification in structuredContent. The view uses it after the tool completes and on hosts that provide a result without partial tool-input notifications. Keep content concise because it is the result the model reads.

Register the React components

Create views/generative-ui/view.tsx. Define a registry that maps each catalog component to its React implementation:
The catalog describes what the model may generate. The registry supplies the components that render those names. When you add a custom catalog component, add its React implementation to the registry too.

Stream the generated interface

useToolContext() updates toolInput as the model generates the structured spec argument. Read that live input first, then use the validated toolOutput after the call completes:
The toRenderableSpec() helper checks whether the partial input contains a root element and removes elements that have not received their props yet. Copy the helper from the complete example rather than treating a pending value as a complete Spec. loading tells JSON-Render that referenced children may still be on the way. The view can render the usable part of the interface without warning about missing children. When the host does not send partial input, the loading state remains visible until structuredContent arrives. ThemeProvider applies the host’s color scheme and CSS variables.

Add state and interactions

JSON-Render specifications can include initial state, event bindings, and actions. Describe those capabilities in the tool instructions so the model generates working interactions instead of static controls. For example, an addable task list needs:
  • an array in spec.state
  • a repeated element bound to that array
  • an input bound to the new-task value
  • a button event bound to an action such as pushState
Only promise an interaction in visible text when the specification contains the matching state and event binding. See the JSON-Render React API for providers, actions, visibility, validation, and state.

Prepare the integration for production

  • Keep the catalog limited to components and actions your app supports.
  • Validate the completed specification with catalog.zodSchema().
  • Return the final specification through structuredContent.
  • Include a concise text result for the model and hosts without views.
  • Configure Content Security Policy for external images, APIs, scripts, or embeds.

Next steps