Skip to content

Use @usefractal/frac/server

This guide shows you how to add @usefractal/frac/server to an existing MCP server to enable view support with full TypeScript type inference.

You should already have:

Terminal window
pnpm add @usefractal/frac

Replace your McpServer import with frac’s enhanced version:

// Before
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// After
import { McpServer } from "@usefractal/frac/server";

Keep your existing dependency to @modelcontextprotocol/sdk in your project, frac does not replaces it, but depends on it.

Your existing tools, resources, and prompts will continue to work as before. With @usefractal/frac/server, registerTool provides an optional view field so you can bind a tool to a React view in a single call.

your-project/
├── src/
│ ├── server.ts # MCP server entry
│ └── views/
│ └── my-view.tsx # View component
├── vite.config.ts # Vite + frac plugin
├── tsconfig.json
└── package.json

By default views are located in src/views/. This is configurable via the viewsDir option in the provided Vite plugin.

A view’s file name must match view.component in registerTool. See registerTool.

Install the web dependencies and create vite.config.ts at the project root:

Terminal window
pnpm add react react-dom
pnpm add -D vite typescript @types/react @types/react-dom
vite.config.ts
import { defineConfig } from "vite";
import { frac } from "@usefractal/frac/vite";
export default defineConfig({
plugins: [frac()],
});

Make sure tsconfig.json includes the generated types:

{
"include": ["src", ".frac/**/*.d.ts"]
}

Create src/views/my-view.tsx and export default a React component:

export default function MyView() {
return (
<div>
<h2>Hello from frac!</h2>
<p>Your view is working!</p>
</div>
);
}

In your server code, register the view with registerTool and view:

import { McpServer } from "@usefractal/frac/server";
import { z } from "zod";
const server = new McpServer({ name: "my-app", version: "1.0" }, {})
.registerTool(
{
name: "my-view",
description: "My first view",
inputSchema: { message: z.string() },
view: { component: "my-view" }, // must match src/views/my-view.tsx
},
async ({ message }) => {
return { content: message || "Hello World" };
},
);

Update your server startup to serve the MCP endpoint. If you’re using Express:

import { McpServer } from "@usefractal/frac/server";
import { z } from "zod";
import express from "express";
const app = express();
const server = new McpServer({ name: "my-app", version: "1.0" }, {})
.registerTool(
{
name: "my-view",
inputSchema: { message: z.string() },
view: { component: "my-view" },
},
async ({ message }) => {
return { content: message };
},
);
// Serve the MCP endpoint
app.use("/mcp", server.handler());
// Start the server
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});

For full type inference and autocomplete, use method chaining and generateHelpers. See Type Safety for the complete setup.

Quick summary:

  1. Use method chaining when registering tools
  2. Export type AppType = typeof server
  3. Create a helpers.ts file with generateHelpers<AppType>()
  4. Import typed hooks from your helper file