MCP-UI Weather Dashboard Server
A clean example of an MCP server that provides an interactive weather dashboard UI using React and remoteDom.
Architecture
┌─────────────────────────┐
│ React Component (TSX) │
│ server/components/ │
└───────────┬─────────────┘
│
▼ esbuild bundles
┌─────────────────────────┐
│ Bundle (JS) │
│ dist/ │
└───────────┬─────────────┘
│
▼ loaded by server
┌─────────────────────────┐
│ MCP Server │
│ Creates remoteDom │
│ resource │
└───────────┬─────────────┘
│
▼ MCP protocol
┌─────────────────────────┐
│ Client (nanobot, etc.) │
│ Renders in sandbox │
│ with React provided │
└─────────────────────────┘
Setup
Install Dependencies
npm install
Build the UI Bundle
npm run build:ui
This bundles the React component using esbuild:
- Input:
server/components/index.tsx - Output:
dist/weather-dashboard.bundle.js
Start the Server
npm start
This will:
- Build the UI bundle
- Start the MCP server on
http://localhost:3000/mcp
Development with Auto-reload
npm run watch
Rebuilds UI and restarts server on file changes.
How It Works
1. React Component (server/components/WeatherDashboard.tsx)
- Written in modern React with hooks, JSX, TypeScript
- Includes interactive city selection, weather data, forecast
- Uses
postMessageto send data back to chat via MCP-UI protocol
2. Build Step (build-ui.mjs)
- Uses esbuild to bundle the React component
- Transpiles JSX → JavaScript
- Bundles imports (except React/ReactDOM which host provides)
- Output: Single IIFE bundle
3. Server (server/ui.ts)
- Reads the bundled JavaScript
- Creates a
remoteDomUI resource with MIME type:application/vnd.mcp-ui.remote-dom+javascript; framework=react - Server sends this to clients via MCP protocol
4. Client Rendering
- Client (nanobot, etc.) receives the bundle as text
- Provides React/ReactDOM in a sandboxed environment
- Executes the bundle, which renders the component
- Handles
postMessageevents from the UI
Available Scripts
npm run build:ui- Build the React UI bundlenpm start- Build UI + start servernpm run watch- Development mode with auto-reloadnpm run nanobot- Start nanobot client
Key Files
server/components/WeatherDashboard.tsx- Main React componentserver/components/index.tsx- Entry point for bundlebuild-ui.mjs- esbuild configurationserver/ui.ts- Creates MCP-UI resourceserver/index.ts- MCP server setupdist/weather-dashboard.bundle.js- Generated bundle
remoteDom vs rawHtml
remoteDom (current)
- ✅ Full React with hooks, state management
- ✅ Better component architecture
- ✅ Type safety with TypeScript
- ⚠️ Requires client support for remoteDom
- ⚠️ Requires build step
rawHtml (alternative)
- ✅ Works everywhere
- ✅ No build step needed
- ⚠️ Vanilla JS only
- ⚠️ Manual DOM manipulation
Testing
With Nanobot
npm run nanobot
Then in the nanobot chat:
Can you show me the weather dashboard?
Or directly call the tool:
weather_dashboard
Features
The UI allows:
- Switching between cities (Oslo, San Francisco, New York)
- Viewing current weather and 5-day forecast
- Sending weather data back to chat with "Send to Chat" button
Project Structure
mcp-ui/
├── server/
│ ├── components/
│ │ ├── WeatherDashboard.tsx # React component
│ │ └── index.tsx # Entry point
│ ├── index.ts # MCP server
│ └── ui.ts # UI resource definition
├── dist/
│ └── weather-dashboard.bundle.js # Generated bundle
├── build-ui.mjs # esbuild configuration
├── nanobot.yaml # Nanobot configuration
├── package.json
└── README.md
Description
Languages
TypeScript
73.8%
CSS
23.7%
JavaScript
2.2%
HTML
0.3%