Skip to Content
Provider SdkV1Fractal MCP Quickstart

Quickstart: Fractal MCP Server

Overview

This guide will show you how to set up an MCP server that returns UI components. You will learn how to:

  • set up a simple MCP server using express and typescript
  • return a simple UI component from one of your tools.
  • create UI’s with rich functionality in the chat.

Your project will look like this:

fractal-mcp-quickstart/ server/ package.json # Generated by `npm init` tsconfig.json # Shown below - copy it! src/ index.ts # The actual server code - copy it! ui/

Your first MCP Server

First lets set up the project.

mkdir -p fractal-mcp-quickstart && cd fractal-mcp-quickstart

And we’ll set up the server piece with npm in a server subdirectory:

mkdir -p server && cd server npm init

Next we install the necessary packages:

npm i --save-dev express zod npm i --save-dev @modelcontextprotocol/sdk npm i --save-dev @mcp-ui/server npm i --save-dev @fractal-mcp/mcp-express

What’s in these libraries?

  • We use express as our web framework.
  • @modelcontextprotocol/sdk is the official MCP library for typescript
  • @mcp-ui/server provides the UIResource interface that we will use to format the responses from our MCP tools.
  • zod is a schema and validation library used to define the input schemas for our MCP tools.
  • @fractal-mcp/mcp-express is sugar for connecting an MCP server through express.

Next, you’ll need to set up a tsconfig.json. You can copy ours:

{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Node", "declaration": true, "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "dist", ] }

Add these scripts to your package.json:

"scripts": { "build": "tsc", "start": "node dist/index.js" },

Now to define the server! We’ll create a new file in src/index.ts and import our dependencies

import express from "express"; import { z } from "zod"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { makeExpressRoutes, defaultCorsMiddleware } from "@fractal-mcp/mcp-express"; import { createUIResource } from "@mcp-ui/server";

And then we can set up a basic MCP server with one tool that says “hello”

const server = new McpServer({ name: 'fractal-mcp-quickstart', version: '1.0.0' }); server.registerTool('hello', { title: 'HelloBasic', description: 'Simple tool that says hello', inputSchema: { name: z.string().describe('Name to say hello to'), }, }, async ({ name }: { name: string }) => { return { content: [{ type: "text", text: `Hello ${name}!`}] } });

Finally we create an express app and connect it all together:

const app = express(); app.use(express.json({ limit: '50mb' })); app.use(defaultCorsMiddleware); // Optional - only required if testing with a web preview tool. makeExpressRoutes(app, server); // Fractal's sugar so you can avoid the repetitive stuff. console.log(`Server started on port ${port}`); app.listen(3000);

You can start your server by runnning npm run build && npm run start

Returning UI components

That was fun, but we’re really here to get your server returning UI components. To do this we use we use the UIResource interface from @mcp-ui/server. The only thing you need to do to return a UI from your tool is to return a UIResource from your tool. For a complete guide on how to use the UIResource and the createUIResource function, see https://MCPUI.dev/

It should look something like this:

server.registerTool('my-ui-tool', { title: 'MyUITool', description: 'Tool that returns a UIResource', inputSchema: { /* ... your input params ... */ } }, async () => { const uiResource = createUIResource({ /* ... ui resource params ... */ }); // Return the UIResource in place of, or in addition to your type="text" or other tool response return { content: [uiResource], }; })

The simple way

For simple UIs, we can place a raw HTML snippet in the call to createUIResource:

const uiResource = createUIResource({ uri: 'ui://hello', content: { type: 'rawHtml', htmlString: `<p>hello ${name}</p>` }, encoding: 'text', });

But doing so makes it painful to develop complex rich UIs. We solve this by treating our UI as a proper app.

The proper way

Since we will be managing the UI as a standalone react app, we will need to adjust our UI resource to point to it. Assume that the UI will run on port 5174.

We should adjust our tool to point to the address of the UI:

  • We use the externalUrl content type instead of rawHtml
  • Any data that the component needs to render is passed through uiMetadata['initial-render-data']
  • everything else is the same :)

Here is the updated tool code:

server.registerTool('hello-ui', { title: 'HelloUI', description: 'Tool that returns a UI showing a hello message', inputSchema: { name: z.string().describe('Name to say hello to'), }, const uiResource = createUIResource({ uri: 'ui://hello', content: { type: 'externalUrl', iframeUrl: `http://localhost:5174` }, encoding: 'text', uiMetadata: { 'initial-render-data': { name }, // name is defined in }, }); // Return the UIResource in place of, or in addition to your type="text" or other tool response return { content: [uiResource], }; })

We’ll move to setting up the UI side of things now: Set up a react (npm) project in fractal-mcp-quickstart/ui:

mkdir -p fractal-mcp-quickstart/ui npm init

We’re now going to set up a proper react application! Buckle in!

Install the following packages:

npm i --save react react-dom npm i --save @fractal-mcp/server-ui-react npm i --save-dev vite typescript @vitejs/plugin-react

We use @fractal-mcp/server-ui-react to avoid worrying about the details of the fractal messaging protocol.

In fractal-mcp-quickstart/ui/tsconfig.json:

{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "jsx": "react-jsx", "moduleResolution": "Node", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src"] }

Add a vite.config.ts in fractal-mcp-quickstart/ui/vite.config.ts:

import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], });

Place a simple index.html file at fractal-mcp-quickstart/ui/index.html:

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

And now for the real magic - We’ll define a component in fractal-mcp-quickstart/ui/src/Hello.tsx:

import { useFractal } from '@fractal-mcp/server-ui-react'; export default function Hello() { const { data } = useFractal(); const name = (data && (data.name as string)) || 'world'; return ( <div> <p>Hello, <span>{name}</span></p> </div> ); }

And we’ll bootstrap it together in fractal-mcp-quickstart/ui/src/main.tsx:

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

It’s also recommended to put something like this in your package.json:

"scripts": { "dev": "vite --port 5174", },

You can now start this by running npm run dev from fractal-mcp-quickstart/ui.