146 lines
4.2 KiB
Markdown
146 lines
4.2 KiB
Markdown
# MCP UI Kit
|
|
|
|
Build interactive React UIs for MCP tools.
|
|
|
|
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) enables AI assistants to interact with external tools. **mcp-ui-kit** simplifies building rich UI components that render directly in AI chat:
|
|
|
|
- **Bundling on-demand** — Write React components, bundled automatically when the tool is called
|
|
- **Zero config** — No webpack/vite setup, just `createUI()` and point to your `.tsx` file
|
|
- **Props from server** — Pass data from your MCP tool directly to React via `useProps()`
|
|
- **Two-way communication** — Components can `sendPrompt()` to the AI or `callTool()` to invoke other MCP tools
|
|
|
|
<a href="https://mcp-ui-kit-inspector.vercel.app/" target="_blank">MCP UI Inspector</a> — Built on [@mcp-ui/server](https://www.npmjs.com/package/@mcp-ui/server)
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install mcp-ui-kit @modelcontextprotocol/sdk
|
|
```
|
|
|
|
## Server Usage
|
|
|
|
```typescript
|
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
import { createUI } from 'mcp-ui-kit/server';
|
|
|
|
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
|
|
|
|
const dashboardUI = createUI('my-dashboard', import.meta.resolve('./Dashboard.tsx'));
|
|
|
|
server.registerTool(
|
|
'dashboard',
|
|
{ description: 'Interactive dashboard', _meta: dashboardUI.meta },
|
|
async () => ({
|
|
content: [
|
|
{ type: 'text', text: 'Dashboard loaded' },
|
|
await dashboardUI.component({ props: { title: 'Hello' }, frameSize: ['700px', '500px'] })
|
|
]
|
|
})
|
|
);
|
|
```
|
|
|
|
### With Input Schema
|
|
|
|
```typescript
|
|
import { z } from 'zod';
|
|
import { createUI } from 'mcp-ui-kit/server';
|
|
|
|
const stockUI = createUI('stocks', import.meta.resolve('./StockDashboard.tsx'));
|
|
|
|
server.registerTool(
|
|
'stock_portfolio',
|
|
{
|
|
description: 'View stock portfolio',
|
|
_meta: stockUI.meta,
|
|
inputSchema: {
|
|
symbols: z.array(z.string()).default(['AAPL', 'GOOGL']),
|
|
timeframe: z.enum(['1D', '1W', '1M', '1Y']).default('1M'),
|
|
},
|
|
},
|
|
async ({ symbols, timeframe }) => ({
|
|
content: [
|
|
{ type: 'text', text: `Showing ${symbols.join(', ')} for ${timeframe}` },
|
|
await stockUI.component({ props: { symbols, timeframe } })
|
|
]
|
|
})
|
|
);
|
|
```
|
|
|
|
## Client Usage
|
|
|
|
The file passed to `createUI()` must render a component to the `#root` element:
|
|
|
|
```tsx
|
|
// StockDashboard.tsx
|
|
import { createRoot } from 'react-dom/client';
|
|
import { sendPrompt, callTool, useProps } from 'mcp-ui-kit/ui';
|
|
|
|
function StockDashboard() {
|
|
const { symbols, timeframe } = useProps({ symbols: ['AAPL'], timeframe: '1M' });
|
|
|
|
return (
|
|
<div>
|
|
<h1>Stock Portfolio ({timeframe})</h1>
|
|
{symbols.map(symbol => (
|
|
<div key={symbol}>
|
|
<span>{symbol}</span>
|
|
<button onClick={() => sendPrompt(`Analyze ${symbol} over ${timeframe}`)}>
|
|
Analyze
|
|
</button>
|
|
<button onClick={() => callTool('get_stock_price', { symbol })}>
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Render to DOM
|
|
createRoot(document.getElementById('root')!).render(<StockDashboard />);
|
|
```
|
|
|
|
## API
|
|
|
|
### Server (`mcp-ui-kit/server`)
|
|
|
|
**`createUI(name, componentPath)`** — Creates a UI component
|
|
- `componentPath`: Path to a `.tsx` file that renders to `#root`
|
|
- Returns `{ meta, component(opts?) }`
|
|
- `opts.props`: Data passed to your React component via `useProps()`
|
|
- `opts.frameSize`: `[width, height]` e.g. `['700px', '500px']`
|
|
|
|
```typescript
|
|
createUI('dashboard', import.meta.resolve('./Dashboard.tsx')); // ESM
|
|
createUI('dashboard', require.resolve('./Dashboard.tsx')); // CommonJS
|
|
```
|
|
|
|
### UI (`mcp-ui-kit/ui`)
|
|
|
|
- **`useProps(defaults)`** — Get props passed from the server
|
|
- **`sendPrompt(message)`** — Send a message to the AI chat
|
|
- **`callTool(name, params)`** — Invoke an MCP tool
|
|
- **`resizeToContent(element?)`** — Request parent to resize iframe to fit content
|
|
- **`useResizeToContent()`** — Hook that auto-notifies parent when content size changes
|
|
|
|
```tsx
|
|
import { useResizeToContent } from 'mcp-ui-kit/ui';
|
|
|
|
function MyComponent() {
|
|
const containerRef = useResizeToContent();
|
|
|
|
return (
|
|
<div ref={containerRef}>
|
|
{/* Content that may change size */}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev:all # Start demo server + inspector
|
|
```
|