Skip to main content
Build a small weather widget from a new React project. A tool returns the weather, Vite builds the widget into one HTML file, and your MCP host displays it. Use Node 24.20.0 or later in Node 24, npm, and matching Toolgrain 0.3.0 or later packages. The Vite build recipe is pinned so you can reproduce it.

1. Create a Vite project

Run the Vite CLI with its React and TypeScript template:
Add Toolgrain and the single-file build plugin:
Use matching Toolgrain 0.3.0 or later versions. The commands resolve npm’s latest tag and save exact versions in your application.

2. Define the tool

Create a server folder and add this file. The tool’s registration key, weather, is also the name React will use.
server/tools.ts
This is deterministic demo data. Toolgrain converts the returned object into structured content and assistant-facing text, so the tool also works in clients that do not display widgets.

3. Create the React bindings

Add one small module that connects your components to the tool types:
src/mcp.ts
The import type is important: it gives React the tool’s input/output types without bundling the server code. Create these bindings once, outside your components.

4. Replace the starter component

Replace src/App.tsx:
src/App.tsx
One provider and one hook are enough to display a result. The provider connects to the host, applies its theme, and reports widget size. useToolResult() receives the initiating tool result. The host creates the widget when the tool is called. Replace the starter’s full-page styles with a widget layout:
src/index.css

5. Configure one HTML build

Rename the generated index.html to weather.html and replace its contents:
weather.html
The generated src/main.tsx renders the App you replaced. Now replace vite.config.ts:
vite.config.ts
In package.json, set the build script and add start:mcp:
Merge those entries into the existing scripts object. In tsconfig.node.json, add server to include so the build also checks your server files:
--mode weather selects weather.html. The plugin inlines its JavaScript and CSS into dist/widgets/weather.html. publicDir: false skips the starter’s public assets; the HTML above also removes its favicon/logo references.

6. Serve the HTML as a resource

Create the MCP server. Keep the widget’s presentation and permission settings in the named widgetUi object:
server/index.ts
The tool’s resourceUri and the resource’s uri must match exactly. The resource reads your built file; a ui:// URI is an identifier, not a filesystem location. The HTML MIME type appears on both the resource definition and the returned content. To request permissions or allow network origins later, edit widgetUi using the permissions and metadata guide.

7. Build and connect a host

You should have dist/widgets/weather.html, with its script and styles inside it. The server runs with npm run start:mcp. It waits for MCP messages over stdio; it does not open a browser or print a startup message to stdout. Node 24 runs these TypeScript server files directly. In Claude Desktop, open Settings → Developer → Edit Config (host setup guide). Add the following entry to its mcpServers object, using your Node executable and the absolute server entry path:
Find your Node executable with node -p "process.execPath". Replace both paths, save the configuration, and restart Claude Desktop to load it. Ask Claude to call weather with { "city": "Dubai" }. An Apps-capable host should load the widget and supply that tool’s result. Opening weather.html directly, or visiting Vite’s dev server, does not create an MCP host connection. Test the connected widget inside a compatible host. See host testing and troubleshooting for the recorded environments, setup details, and resource-cache behavior.

Next steps