From 312f60758c5aa0412e7371de1002c9e31c0c0f90 Mon Sep 17 00:00:00 2001 From: Fredrik Jensen Date: Sat, 6 Dec 2025 11:16:49 +0100 Subject: [PATCH] update readme --- README.md | 152 +++++++++++++++++++++++++++++++++++++++++++---------- nanobot.db | Bin 13512704 -> 13512704 bytes 2 files changed, 123 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 24f3313..9029747 100644 --- a/README.md +++ b/README.md @@ -2,51 +2,142 @@ Build interactive React UIs for MCP tools. +## Why? + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) enables AI assistants to interact with external tools and data. The [mcp-ui](https://github.com/anthropics/mcp-ui) project extends this with rich, interactive UI components that can be rendered directly in the AI chat. + +**mcp-ui-kit** simplifies building these UI components by: + +- **Bundling on-demand** — Write React components, they're bundled automatically when the tool is called +- **Zero config** — No webpack/vite setup needed, 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 + +Built on top of [@mcp-ui/server](https://www.npmjs.com/package/@mcp-ui/server). + ## Installation ```bash -npm install mcp-ui-kit +npm install mcp-ui-kit @modelcontextprotocol/sdk ``` ## Server Usage -Create UI components that bundle on-demand: +Create UI components that bundle on-demand within your MCP server: ```typescript -import { createUI } from 'mcp-ui-kit/server'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { createUI } from 'mcp-ui-kit/server'; // 👈 mcp-ui-kit -const dashboardUI = createUI('my-dashboard', import.meta.resolve('./MyComponent.tsx')); +// Create the MCP server +const server = new McpServer({ + name: 'my-server', + version: '1.0.0' +}); -server.registerTool('dashboard', { - description: 'Interactive dashboard', - _meta: dashboardUI.meta -}, async () => ({ - content: [ - { type: 'text', text: 'Dashboard loaded' }, - await dashboardUI.component({ props: { title: 'Hello' } }) - ] -})); +// Create a UI component // 👈 mcp-ui-kit +const dashboardUI = createUI( // 👈 mcp-ui-kit + 'my-dashboard', // 👈 mcp-ui-kit + import.meta.resolve('./MyComponent.tsx') // 👈 mcp-ui-kit +); // 👈 mcp-ui-kit + +// Register a tool with the UI +server.registerTool( + 'dashboard', + { + description: 'Interactive dashboard', + _meta: dashboardUI.meta // 👈 mcp-ui-kit + }, + async () => ({ + content: [ + { type: 'text', text: 'Dashboard loaded' }, + await dashboardUI.component({ // 👈 mcp-ui-kit + props: { title: 'Hello' }, // 👈 mcp-ui-kit + frameSize: ['700px', '500px'] // 👈 mcp-ui-kit + }) // 👈 mcp-ui-kit + ] + }) +); + +// Start the server +const transport = new StdioServerTransport(); +await server.connect(transport); +``` + +### With Input Schema + +Tools can accept parameters via `inputSchema` and pass them to your UI component: + +```typescript +import { z } from 'zod'; +import { createUI } from 'mcp-ui-kit/server'; // 👈 mcp-ui-kit + +const stockUI = createUI('stocks', import.meta.resolve('./StockDashboard.tsx')); // 👈 mcp-ui-kit + +server.registerTool( + 'stock_portfolio', + { + description: 'View stock portfolio with charts', + _meta: stockUI.meta, // 👈 mcp-ui-kit + 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({ // 👈 mcp-ui-kit + props: { symbols, timeframe }, // 👈 mcp-ui-kit (params → props) + }) + ] + }) +); ``` ## Client Usage -Helper functions for your React components: +Helper functions for your React components (the ones you pass to `createUI`): -```typescript -import { sendPrompt, callTool, useProps } from 'mcp-ui-kit/ui'; +```tsx +// StockDashboard.tsx +import { useState } from 'react'; +import { sendPrompt, callTool, useProps } from 'mcp-ui-kit/ui'; // 👈 mcp-ui-kit -function MyComponent() { - const { title } = useProps({ title: 'Default' }); +function StockDashboard() { + const [analysis, setAnalysis] = useState(null); + // Get props passed from server (matches inputSchema params) // 👈 mcp-ui-kit + const { symbols, timeframe } = useProps({ // 👈 mcp-ui-kit + symbols: ['AAPL'], // 👈 mcp-ui-kit + timeframe: '1M' // 👈 mcp-ui-kit + }); // 👈 mcp-ui-kit + + const handleAnalyze = (symbol: string) => { + // Send a message to the AI chat // 👈 mcp-ui-kit + sendPrompt(`Analyze ${symbol} stock performance over ${timeframe}`); // 👈 mcp-ui-kit + }; + + const handleRefresh = async (symbol: string) => { + // Call another MCP tool // 👈 mcp-ui-kit + callTool('get_stock_price', { symbol }); // 👈 mcp-ui-kit + }; + return (
-

{title}

- - +

Stock Portfolio ({timeframe})

+ {symbols.map(symbol => ( +
+ {symbol} + + +
+ ))}
); } @@ -57,15 +148,18 @@ function MyComponent() { ### Server (`mcp-ui-kit/server`) **`createUI(name, entryUrl)`** - Creates a UI component -- `name`: Component identifier +- `name`: Component identifier (used in the `ui://` URI) - `entryUrl`: Path to the component entry file -- Returns: `{ component(opts?) }` where opts: `{ props?, frameSize? }` +- Returns: `{ meta, component(opts?) }` + - `meta`: Object to spread into tool's `_meta` for UI resource linking + - `component(opts?)`: Async function returning the UI resource + - `opts.props`: Data to pass to your React component + - `opts.frameSize`: `[width, height]` e.g. `['700px', '500px']` The `entryUrl` parameter accepts both formats: ```typescript // ESM (recommended) - using import.meta.resolve() -// Requires "type": "module" in package.json createUI('dashboard', import.meta.resolve('./MyComponent.tsx')); // CommonJS - using require.resolve() or absolute paths @@ -75,7 +169,7 @@ createUI('dashboard', path.join(__dirname, './MyComponent.tsx')); ### UI (`mcp-ui-kit/ui`) -- **`useProps(defaults)`** - Get props passed from the server +- **`useProps(defaults)`** - Get props passed from the server via `component({ props })` - **`sendPrompt(message)`** - Send a message to the AI chat - **`callTool(name, params)`** - Invoke an MCP tool diff --git a/nanobot.db b/nanobot.db index a8bc454d3e1019036552d04752e8312f2ebd55aa..8b1f3732ac614d4698f43adda42bb1edb09d3cdd 100644 GIT binary patch delta 1082 zcmY+>$#WHD6b0~lNeBTkF($+aC}^T_>iG5ex{VnW=Kun~k1t zbc@l)?9{D)4%E4w%2$!?RDM6$)Ki_$te875@97$88Jikx6~~GNVxd?hjuXd=#bSv# zL7XVsM7uaioGdyjQ-i1M`?<|7Ibj<3j#Uhj(DKs6v!ZhB%T-d!^`k8F^S!(4yYuB~ zV`rtWdt|06?_by4U)la!(;o$>Q=v|SIvwf^C=03+>P#pFWkWeoE|dr5Lj_Pps1PcG zI%`^WO|NS613L>GJG7jlUA8?mNfi2_G$sEu*q|=Z*`RJv52zQ^2Ra9IE@&y}Jka@| z3qTiwE&^Q)x&(A7=rYjdpk<)tpnlL5pesRFfvyH!0~!Eb3%U+;J!lZL0(1jt2y`Q8 zB}ju}Py#A}%AgdKfmVTT0^JO{1#~NDHRv|b?VvkA!=O7sBcQuLcZ2Q$-3z)8bU)|; z(1W0dKo5f+0X+(O3^WQ_19}|v#5AqlvAW$Udr43%rj{M6$nu=bv*I*yt*oqM5Ic2( zEd6)O%96FseN}nyuQj##UmFXH$A%k>fEW|&#FOGF@w9kGjEiT*b7H;NAf6W+#S7v^ zF(GoXNxURp7O#j`#cN`7Ww>Dr?{Bw5FLcwAXSphtkELE}C5d0Q{GjN$B|me^!GAxW znQ5xaR}K|sSC$=W+FDbgL-pg;dEdnN0`L|vfwzKR2fqP+6Z{r<8~APTJK%T0?}6V3 zPl7)HZwG$}{s{arcn5e2{0Vp`cp6**?*e}c{tUbuya)U__zUot;2H2<@K@lk!QX(t X1@8mz2Y(0t9{dCN0Qkpk{8Rtm_^iv< delta 1092 zcmZwF*;kci6bA5h01?d~1BukqlxTeIJ>PWR69tsbgV98Vk?(iT_Z=Ioh~^oM_rV5A zv-oUu+h7w>Nph^LXwu4BSr@J@_hqea`VVT?wHLq3z4o*AeujB>{jk`yDA%!h@ycAs zdh2Y?*&1hSovm|rmb3NF&UUuJ**VV6b@ni4=Q(@0vyIL+IeUb&^PN4?*=A>3oL%5- ztFuS#vTc9$HFzruQ5c!9`p1FhuG(y7_1t+`SLaac=v053I9eOXPsFYTU}#<3q7rHg5(f;0(KF$_v73Cnq}k`~g`c-gN5HDPt> zNJ~#m*8N*uefIa3+>>duUVEyRoIWI{AC^pE>B0pihQA1^QHI1-%0LG-wT-hxVX- z=m0u|j-U(B2D%7+`n2wxUfUiAv9I&RRQW+rP=Rj}WlDaiJQI`)UJ~aMUH(5r*2_(E z#`iSN&;A)&&|2-@+Bq?+J;|r0ka$L=UMW(64pLQ$bgXn@j9)ZfsT>_@I@{mZW~vjD zBe~qNSQ{_!l%Mw!6%;Ea6~~dMbQH=d!n_}qivO+8gE~QHfX)PUfx1CGptC?{gI0ph z0i6pv4|G200?>t^i$E8HE&*K%S_Qfc)C;;CbOq>2&{d$TL4BZWK-Yq<1NDPegRTb+ zfNlV-0a;KCDuK$N1eAg*ptYbIK{tVJ2HgT$2f7t>8|Ze>Am|Rz5a>?OU7))`_kivN z-3PiK^Z@8V&_keyL63kQ1r3ANgEoL3o2HSS>)MTP@+R~`Wh#DA1(i~)N4NBq=LtPFRu3gykSRFi;a^Gc(N%*_wge?CPq6 z&D-m$bg*%(HtQK5TL|6;Ch&IfYv9+xZ-Czf?*P9AejEG__+9XO;0f^i;7RZY;19tc zfp>zZz#oHmfv3S$@NV!Y;7`Gyf%kwv2Y&(n5