Skip to Content
Consumer SdkV1Fractal Chat Quickstart

Fractal Chat Quickstart

This quickstart will show you how to set up a minimal chat app that can render interactive UI components returned by MCP tools. In just a few minutes, you’ll be able to run a backend + frontend locally and see UI components appear inline in chat messages.


Prerequisite

This quickstart assumes you already have an MCP server running at http://localhost:3000/mcp. If you don’t, first complete the MCP Quickstart.


Overview

  • Backend: Express + Vercel AI SDK, connected to an MCP server.
  • Frontend: React + Vite chat app with Fractal’s FractalUIResourceRenderer.
  • Goal: Type a message → model calls an MCP tool → tool returns a UIResource → UI is rendered inline.

Your project will look like this:

fractal-chat-quickstart/ backend/ server.ts # Chat backend ui/ src/Chat.tsx # Chat UI src/main.tsx index.html

Step 1. Backend

Create a backend folder:

mkdir -p fractal-chat-quickstart/backend && cd fractal-chat-quickstart/backend npm init -y npm i express cors ai @ai-sdk/openai @modelcontextprotocol/sdk

Add a server.ts:

import express from "express"; import cors from "cors"; import dotenv from "dotenv"; import { streamText, UIMessage, convertToCoreMessages, experimental_createMCPClient } from "ai"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { createOpenAI } from "@ai-sdk/openai"; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json({ limit: "50mb" })); const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY! }); const systemMessage = "You are a helpful assistant."; app.post("/api/chat", async (req, res) => { const { messages } = req.body as { messages: UIMessage[] }; const transport = new StreamableHTTPClientTransport(new URL("http://localhost:3000/mcp")); const mcpClient = await experimental_createMCPClient({ transport }); const mcpTools = await mcpClient.tools(); const result = streamText({ model: openai("gpt-4.1"), system: systemMessage, messages: convertToCoreMessages(messages), temperature: 0.0, tools: mcpTools, }); result.pipeDataStreamToResponse(res); }); app.listen(3001, () => console.log("Backend on http://localhost:3001"));

Run it:

OPENAI_API_KEY=YOUR_KEY node server.ts

Step 2. Frontend

In a new terminal:

cd ../.. mkdir -p fractal-chat-quickstart/ui && cd fractal-chat-quickstart/ui npm init -y npm i react react-dom @ai-sdk/react @fractal-mcp/client-ui-react @mcp-ui/client npm i -D vite typescript @vitejs/plugin-react

Add index.html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Fractal Chat Example</title> </head> <body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body> </html>

Add src/main.tsx:

import React from "react"; import ReactDOM from "react-dom/client"; import Chat from "./Chat"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(<Chat />);

Add src/Chat.tsx (with CSS classes for clean styling):

import { useChat } from "@ai-sdk/react"; import { FractalUIResourceRenderer, isUIResource } from "@fractal-mcp/client-ui-react"; import { UIResource } from "@fractal-mcp/protocol"; export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat({ api: "http://localhost:3001/api/chat", }); return ( <div className="chat-container"> <div className="chat-messages"> {messages.map((m, i) => ( <div key={i} className={`message message-${m.role}`}> <div className="role">{m.role === "user" ? "You" : "Assistant"}</div> <div className="content"> {m.parts.map((part, j) => { if (part.type === "text") return <span key={j}>{part.text}</span>; if (part.type === "tool-invocation") { const toolInvocation = (part as any).toolInvocation; if (toolInvocation?.result?.content?.length) { const content = toolInvocation.result.content[0]; if (isUIResource(content)) { return <FractalUIResourceRenderer key={j} resource={content.resource} autoResizeIframe />; } } } return null; })} </div> </div> ))} </div> <form onSubmit={handleSubmit} className="chat-input"> <input value={input} onChange={handleInputChange} placeholder="Type your message..." /> <button type="submit" disabled={!input.trim()}>Send</button> </form> </div> ); }

Run the UI:

npx vite --port 5174

Step 3. Run it all together

  1. Start your MCP server (from Producer Quickstart) at http://localhost:3000/mcp.

  2. Start backend:

    cd fractal-chat-quickstart/backend OPENAI_API_KEY=YOUR_KEY node server.ts
  3. Start frontend:

    cd fractal-chat-quickstart/ui npm run dev

Open http://localhost:5174 → Ask your MCP-backed tool something like “Create a button” → see an inline UI render. 🎉


Next Steps