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
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-Renderspec 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:Define the component catalog
Createviews/generative-ui/catalog.ts and export one catalog for both the server and the React view:
Create the generative UI tool
Generate the tool’s input schema from the catalog instead of maintaining a separate schema:spec.root and the root element first. This ordering gives the view enough information to mount before the rest of the element map arrives.
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
Createviews/generative-ui/view.tsx. Define a registry that maps each catalog component to its React implementation:
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:
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
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
- Run the complete
generative-uiexample. - Learn how to build a view with the MCP Apps quickstart.
- Add tool calls and follow-up messages with MCP App interactivity.
- Read the JSON-Render quick start for custom catalogs and renderers.