Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit ea5fb1f

Browse files
committed
feat: add new uiresource template
1 parent 9a55066 commit ea5fb1f

6 files changed

Lines changed: 972 additions & 0 deletions

File tree

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
# UIResource MCP Server
2+
3+
An MCP server with the new UIResource integration for simplified widget management and MCP-UI compatibility.
4+
5+
## Features
6+
7+
- **🚀 UIResource Method**: Single method to register both tools and resources
8+
- **🎨 React Widgets**: Interactive UI components built with React
9+
- **🔄 Automatic Registration**: Tools and resources created automatically
10+
- **📦 Props to Parameters**: Widget props automatically become tool parameters
11+
- **🌐 MCP-UI Compatible**: Full compatibility with MCP-UI clients
12+
- **🛠️ TypeScript Support**: Complete type safety and IntelliSense
13+
14+
## What's New: UIResource
15+
16+
The `uiResource` method is a powerful new addition that simplifies widget registration:
17+
18+
```typescript
19+
// Old way: Manual registration of tool and resource
20+
server.tool({ /* tool config */ })
21+
server.resource({ /* resource config */ })
22+
23+
// New way: Single method does both!
24+
server.uiResource({
25+
name: 'kanban-board',
26+
widget: 'kanban-board',
27+
title: 'Kanban Board',
28+
props: {
29+
initialTasks: { type: 'array', required: false },
30+
theme: { type: 'string', default: 'light' }
31+
}
32+
})
33+
```
34+
35+
This automatically creates:
36+
- **Tool**: `ui_kanban-board` - Accepts parameters and returns UIResource
37+
- **Resource**: `ui://widget/kanban-board` - Static access with defaults
38+
39+
## Getting Started
40+
41+
### Development
42+
43+
```bash
44+
# Install dependencies
45+
npm install
46+
47+
# Start development server with hot reloading
48+
npm run dev
49+
```
50+
51+
This will start:
52+
- MCP server on port 3000
53+
- Widget serving at `/mcp-use/widgets/*`
54+
- Inspector UI at `/inspector`
55+
56+
### Production
57+
58+
```bash
59+
# Build the server and widgets
60+
npm run build
61+
62+
# Run the built server
63+
npm start
64+
```
65+
66+
## Basic Usage
67+
68+
### Simple Widget Registration
69+
70+
```typescript
71+
import { createMCPServer } from 'mcp-use'
72+
73+
const server = createMCPServer('my-server', {
74+
version: '1.0.0',
75+
description: 'Server with UIResource widgets'
76+
})
77+
78+
// Register a widget - creates both tool and resource
79+
server.uiResource({
80+
name: 'my-widget',
81+
widget: 'my-widget',
82+
title: 'My Widget',
83+
description: 'An interactive widget'
84+
})
85+
86+
server.listen(3000)
87+
```
88+
89+
### Widget with Props
90+
91+
```typescript
92+
server.uiResource({
93+
name: 'data-chart',
94+
widget: 'chart',
95+
title: 'Data Chart',
96+
description: 'Interactive data visualization',
97+
props: {
98+
data: {
99+
type: 'array',
100+
description: 'Data points to display',
101+
required: true
102+
},
103+
chartType: {
104+
type: 'string',
105+
description: 'Type of chart (line/bar/pie)',
106+
default: 'line'
107+
},
108+
theme: {
109+
type: 'string',
110+
description: 'Visual theme',
111+
default: 'light'
112+
}
113+
},
114+
size: ['800px', '400px'], // Preferred iframe size
115+
annotations: {
116+
audience: ['user', 'assistant'],
117+
priority: 0.8
118+
}
119+
})
120+
```
121+
122+
## Widget Development
123+
124+
### 1. Create Your Widget Component
125+
126+
```typescript
127+
// resources/my-widget.tsx
128+
import React, { useState, useEffect } from 'react'
129+
import { createRoot } from 'react-dom/client'
130+
131+
interface MyWidgetProps {
132+
initialData?: any
133+
theme?: 'light' | 'dark'
134+
}
135+
136+
const MyWidget: React.FC<MyWidgetProps> = ({
137+
initialData = [],
138+
theme = 'light'
139+
}) => {
140+
const [data, setData] = useState(initialData)
141+
142+
// Load props from URL query parameters
143+
useEffect(() => {
144+
const params = new URLSearchParams(window.location.search)
145+
146+
const dataParam = params.get('initialData')
147+
if (dataParam) {
148+
try {
149+
setData(JSON.parse(dataParam))
150+
} catch (e) {
151+
console.error('Error parsing data:', e)
152+
}
153+
}
154+
155+
const themeParam = params.get('theme')
156+
if (themeParam) {
157+
// Apply theme
158+
}
159+
}, [])
160+
161+
return (
162+
<div className={`widget theme-${theme}`}>
163+
{/* Your widget UI */}
164+
</div>
165+
)
166+
}
167+
168+
// Mount the widget
169+
const container = document.getElementById('widget-root')
170+
if (container) {
171+
createRoot(container).render(<MyWidget />)
172+
}
173+
```
174+
175+
### 2. Register with UIResource
176+
177+
```typescript
178+
// src/server.ts
179+
server.uiResource({
180+
name: 'my-widget',
181+
widget: 'my-widget',
182+
title: 'My Custom Widget',
183+
description: 'A custom interactive widget',
184+
props: {
185+
initialData: {
186+
type: 'array',
187+
description: 'Initial data for the widget',
188+
required: false
189+
},
190+
theme: {
191+
type: 'string',
192+
description: 'Widget theme',
193+
default: 'light'
194+
}
195+
},
196+
size: ['600px', '400px']
197+
})
198+
```
199+
200+
## How It Works
201+
202+
### Tool Registration
203+
When you call `uiResource`, it automatically creates a tool:
204+
- Name: `ui_[widget-name]`
205+
- Accepts all props as parameters
206+
- Returns both text description and UIResource object
207+
208+
### Resource Registration
209+
Also creates a resource:
210+
- URI: `ui://widget/[widget-name]`
211+
- Returns UIResource with default prop values
212+
- Discoverable by MCP clients
213+
214+
### Parameter Passing
215+
Tool parameters are automatically:
216+
1. Converted to URL query parameters
217+
2. Complex objects are JSON-stringified
218+
3. Passed to widget via iframe URL
219+
220+
## Advanced Examples
221+
222+
### Multiple Widgets
223+
224+
```typescript
225+
const widgets = [
226+
{
227+
name: 'todo-list',
228+
widget: 'todo-list',
229+
title: 'Todo List',
230+
props: {
231+
items: { type: 'array', default: [] }
232+
}
233+
},
234+
{
235+
name: 'calendar',
236+
widget: 'calendar',
237+
title: 'Calendar',
238+
props: {
239+
date: { type: 'string', required: false }
240+
}
241+
}
242+
]
243+
244+
// Register all widgets
245+
widgets.forEach(widget => server.uiResource(widget))
246+
```
247+
248+
### Mixed Registration
249+
250+
```typescript
251+
// UIResource for widgets
252+
server.uiResource({
253+
name: 'dashboard',
254+
widget: 'dashboard',
255+
title: 'Analytics Dashboard'
256+
})
257+
258+
// Traditional tool for actions
259+
server.tool({
260+
name: 'calculate',
261+
description: 'Perform calculations',
262+
fn: async (params) => { /* ... */ }
263+
})
264+
265+
// Traditional resource for data
266+
server.resource({
267+
name: 'config',
268+
uri: 'config://app',
269+
mimeType: 'application/json',
270+
fn: async () => { /* ... */ }
271+
})
272+
```
273+
274+
## API Reference
275+
276+
### `server.uiResource(definition)`
277+
278+
#### Parameters
279+
280+
- `definition: UIResourceDefinition`
281+
- `name: string` - Resource identifier
282+
- `widget: string` - Widget directory name
283+
- `title?: string` - Human-readable title
284+
- `description?: string` - Widget description
285+
- `props?: WidgetProps` - Widget properties configuration
286+
- `size?: [string, string]` - Preferred iframe size
287+
- `annotations?: ResourceAnnotations` - Discovery hints
288+
289+
#### WidgetProps
290+
291+
Each prop can have:
292+
- `type: 'string' | 'number' | 'boolean' | 'object' | 'array'`
293+
- `required?: boolean` - Whether the prop is required
294+
- `default?: any` - Default value if not provided
295+
- `description?: string` - Prop description
296+
297+
## Testing Your Widgets
298+
299+
### Via Inspector UI
300+
1. Start the server: `npm run dev`
301+
2. Open: `http://localhost:3000/inspector`
302+
3. Test tools and resources
303+
304+
### Direct Browser Access
305+
Visit: `http://localhost:3000/mcp-use/widgets/[widget-name]`
306+
307+
### Via MCP Client
308+
```typescript
309+
// Call as tool
310+
const result = await client.callTool('ui_kanban-board', {
311+
initialTasks: [...],
312+
theme: 'dark'
313+
})
314+
315+
// Access as resource
316+
const resource = await client.readResource('ui://widget/kanban-board')
317+
```
318+
319+
## Benefits of UIResource
320+
321+
**Simplified API** - One method instead of two
322+
**Automatic Wiring** - Props become tool inputs automatically
323+
**Type Safety** - Full TypeScript support
324+
**MCP-UI Compatible** - Works with all MCP-UI clients
325+
**DRY Principle** - No duplicate UIResource creation
326+
**Discoverable** - Both tools and resources are listed
327+
328+
## Troubleshooting
329+
330+
### Widget Not Loading
331+
- Ensure widget exists in `dist/resources/mcp-use/widgets/`
332+
- Check server console for errors
333+
- Verify widget is registered with `uiResource()`
334+
335+
### Props Not Passed
336+
- Check URL parameters in browser DevTools
337+
- Ensure prop names match exactly
338+
- Complex objects must be JSON-stringified
339+
340+
### Type Errors
341+
- Import types: `import type { UIResourceDefinition } from 'mcp-use'`
342+
- Ensure mcp-use is updated to latest version
343+
344+
## Migration from Old Pattern
345+
346+
If you have existing code using separate tool/resource:
347+
348+
```typescript
349+
// Old pattern
350+
server.tool({ name: 'show-widget', /* ... */ })
351+
server.resource({ uri: 'ui://widget', /* ... */ })
352+
353+
// New pattern - replace both with:
354+
server.uiResource({
355+
name: 'widget',
356+
widget: 'widget',
357+
// ... consolidated configuration
358+
})
359+
```
360+
361+
## Future Enhancements
362+
363+
Coming soon:
364+
- Automatic widget discovery from filesystem
365+
- Widget manifests (widget.json)
366+
- Prop extraction from TypeScript interfaces
367+
- Build-time optimization
368+
369+
## Learn More
370+
371+
- [MCP Documentation](https://modelcontextprotocol.io)
372+
- [MCP-UI Documentation](https://github.com/idosal/mcp-ui)
373+
- [mcp-use Documentation](https://github.com/pyroprompt/mcp-use)
374+
- [React Documentation](https://react.dev/)
375+
376+
Happy widget building! 🚀
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* MCP Server Entry Point
3+
*
4+
* This file serves as the main entry point for the MCP server application.
5+
* It re-exports all functionality from the server implementation, allowing
6+
* the CLI and other tools to locate and start the server.
7+
*
8+
* The server is automatically started when this module is imported, making
9+
* it suitable for both direct execution and programmatic usage.
10+
*/
11+
export * from './src/server.js'
12+

0 commit comments

Comments
 (0)