create-mcp-use-app 0.4.8 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,82 +1,96 @@
1
- import React from 'react';
2
- import { z } from 'zod';
3
- import { useWidget } from 'mcp-use/react';
4
- import '../styles.css';
1
+ import React from "react";
2
+ import { z } from "zod";
3
+ import { useWidget } from "mcp-use/react";
4
+ import "../styles.css";
5
5
 
6
6
  /*
7
- * Apps SDK widget
8
- * Just export widgetMetadata with description and Zod schema, and mcp-use handles the rest!
9
- * See docs: https://docs.mcp-use.com/typescript/server/ui-widgets
10
- */
7
+ * Apps SDK widget
8
+ * Just export widgetMetadata with description and Zod schema, and mcp-use handles the rest!
9
+ * See docs: https://docs.mcp-use.com/typescript/server/ui-widgets
10
+ */
11
11
 
12
12
  const propSchema = z.object({
13
- city: z.string().describe('The city to display weather for'),
14
- weather: z.enum(['sunny', 'rain', 'snow', 'cloudy']).describe('The weather condition'),
15
- temperature: z.number().min(-20).max(50).describe('The temperature in Celsius'),
13
+ city: z.string().describe("The city to display weather for"),
14
+ weather: z
15
+ .enum(["sunny", "rain", "snow", "cloudy"])
16
+ .describe("The weather condition"),
17
+ temperature: z
18
+ .number()
19
+ .min(-20)
20
+ .max(50)
21
+ .describe("The temperature in Celsius"),
16
22
  });
17
23
 
18
24
  export const widgetMetadata = {
19
- description: 'Display weather for a city',
25
+ description: "Display weather for a city",
20
26
  inputs: propSchema,
21
- }
27
+ };
22
28
 
23
29
  type WeatherProps = z.infer<typeof propSchema>;
24
30
 
25
31
  const WeatherWidget: React.FC = () => {
26
32
  // Use the useWidget hook to get props from OpenAI Apps SDK
27
33
  const { props, theme } = useWidget<WeatherProps>();
28
-
29
- console.log(props)
34
+
35
+ console.log(props);
30
36
 
31
37
  const { city, weather, temperature } = props;
32
38
  const getWeatherIcon = (weatherType: string) => {
33
39
  switch (weatherType?.toLowerCase()) {
34
- case 'sunny':
35
- return '☀️';
36
- case 'rain':
37
- return '🌧️';
38
- case 'snow':
39
- return '❄️';
40
- case 'cloudy':
41
- return '☁️';
40
+ case "sunny":
41
+ return "☀️";
42
+ case "rain":
43
+ return "🌧️";
44
+ case "snow":
45
+ return "❄️";
46
+ case "cloudy":
47
+ return "☁️";
42
48
  default:
43
- return '🌤️';
49
+ return "🌤️";
44
50
  }
45
51
  };
46
52
 
47
53
  const getWeatherColor = (weatherType: string) => {
48
54
  switch (weatherType?.toLowerCase()) {
49
- case 'sunny':
50
- return 'from-yellow-400 to-orange-500';
51
- case 'rain':
52
- return 'from-blue-400 to-blue-600';
53
- case 'snow':
54
- return 'from-blue-100 to-blue-300';
55
- case 'cloudy':
56
- return 'from-gray-400 to-gray-600';
55
+ case "sunny":
56
+ return "from-yellow-400 to-orange-500";
57
+ case "rain":
58
+ return "from-blue-400 to-blue-600";
59
+ case "snow":
60
+ return "from-blue-100 to-blue-300";
61
+ case "cloudy":
62
+ return "from-gray-400 to-gray-600";
57
63
  default:
58
- return 'from-gray-300 to-gray-500';
64
+ return "from-gray-300 to-gray-500";
59
65
  }
60
66
  };
61
67
 
62
68
  // Theme-aware styling
63
- const bgColor = theme === 'dark' ? 'bg-gray-900' : 'bg-white';
64
- const textColor = theme === 'dark' ? 'text-gray-100' : 'text-gray-800';
65
- const subtextColor = theme === 'dark' ? 'text-gray-400' : 'text-gray-600';
69
+ const bgColor = theme === "dark" ? "bg-gray-900" : "bg-white";
70
+ const textColor = theme === "dark" ? "text-gray-100" : "text-gray-800";
71
+ const subtextColor = theme === "dark" ? "text-gray-400" : "text-gray-600";
66
72
 
67
73
  return (
68
- <div className={`max-w-sm mx-auto ${bgColor} rounded-xl shadow-lg overflow-hidden`}>
69
- <div className={`h-32 bg-gradient-to-br ${getWeatherColor(weather)} flex items-center justify-center`}>
74
+ <div
75
+ className={`max-w-sm mx-auto ${bgColor} rounded-xl shadow-lg overflow-hidden`}
76
+ >
77
+ <div
78
+ className={`h-32 bg-gradient-to-br ${getWeatherColor(weather)} flex items-center justify-center`}
79
+ >
70
80
  <div className="text-6xl">{getWeatherIcon(weather)}</div>
71
81
  </div>
72
-
82
+
73
83
  <div className="p-6">
74
84
  <div className="text-center">
75
85
  <h2 className={`text-2xl font-bold ${textColor} mb-2`}>{city}</h2>
76
86
  <div className="flex items-center justify-center space-x-4">
77
- <span className={`text-4xl font-light ${textColor}`}>{temperature}°</span>
87
+ <span className={`text-4xl font-light ${textColor}`}>
88
+ {temperature}°
89
+ </span>
78
90
  <div className="text-right">
79
- <p className={`text-lg font-medium ${subtextColor} capitalize`}>{weather}</p>
91
+ <p className={`text-lg font-medium ${subtextColor} capitalize`}>
92
+ {weather}
93
+ </p>
80
94
  <p className={`text-sm ${subtextColor}`}>Current</p>
81
95
  </div>
82
96
  </div>
@@ -3,10 +3,9 @@
3
3
  /* Custom styles */
4
4
  body {
5
5
  margin: 0;
6
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
7
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
8
- sans-serif;
6
+ font-family:
7
+ -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
8
+ "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
9
9
  -webkit-font-smoothing: antialiased;
10
10
  -moz-osx-font-smoothing: grayscale;
11
11
  }
12
-
@@ -22,4 +22,3 @@
22
22
  "include": ["index.ts", "src/**/*", "resources/**/*", "server.ts"],
23
23
  "exclude": ["node_modules", "dist"]
24
24
  }
25
-
@@ -1,17 +1,14 @@
1
- import { createMCPServer } from 'mcp-use/server'
2
- import type {
3
- RawHtmlUIResource,
4
- RemoteDomUIResource
5
- } from 'mcp-use/server'
1
+ import { createMCPServer } from "mcp-use/server";
2
+ import type { RawHtmlUIResource, RemoteDomUIResource } from "mcp-use/server";
6
3
 
7
4
  // Create an MCP server with MCP-UI UIResource support
8
- const server = createMCPServer('uiresource-mcp-server', {
9
- version: '1.0.0',
10
- description: 'MCP server demonstrating all UIResource types',
5
+ const server = createMCPServer("uiresource-mcp-server", {
6
+ version: "1.0.0",
7
+ description: "MCP server demonstrating all UIResource types",
11
8
  baseUrl: process.env.MCP_URL, // Full base URL (e.g., https://myserver.com)
12
- })
9
+ });
13
10
 
14
- const PORT = process.env.PORT || 3000
11
+ const PORT = process.env.PORT || 3000;
15
12
 
16
13
  /**
17
14
  * ════════════════════════════════════════════════════════════════════
@@ -27,7 +24,6 @@ const PORT = process.env.PORT || 3000
27
24
  * 3. Serves the widget from dist/resources/mcp-use/widgets/kanban-board/
28
25
  */
29
26
 
30
-
31
27
  /**
32
28
  * ════════════════════════════════════════════════════════════════════
33
29
  * Type 2: Raw HTML
@@ -40,10 +36,10 @@ const PORT = process.env.PORT || 3000
40
36
  * - Resource: ui://widget/welcome-card
41
37
  */
42
38
  server.uiResource({
43
- type: 'rawHtml',
44
- name: 'welcome-card',
45
- title: 'Welcome Message',
46
- description: 'A welcoming card with server information',
39
+ type: "rawHtml",
40
+ name: "welcome-card",
41
+ title: "Welcome Message",
42
+ description: "A welcoming card with server information",
47
43
  htmlContent: `
48
44
  <!DOCTYPE html>
49
45
  <html>
@@ -122,9 +118,9 @@ server.uiResource({
122
118
  </body>
123
119
  </html>
124
120
  `,
125
- encoding: 'text',
126
- size: ['600px', '400px']
127
- } satisfies RawHtmlUIResource)
121
+ encoding: "text",
122
+ size: ["600px", "400px"],
123
+ } satisfies RawHtmlUIResource);
128
124
 
129
125
  /**
130
126
  * ════════════════════════════════════════════════════════════════════
@@ -138,17 +134,17 @@ server.uiResource({
138
134
  * - Resource: ui://widget/quick-poll
139
135
  */
140
136
  server.uiResource({
141
- type: 'remoteDom',
142
- name: 'quick-poll',
143
- title: 'Quick Poll',
144
- description: 'Create instant polls with interactive voting',
137
+ type: "remoteDom",
138
+ name: "quick-poll",
139
+ title: "Quick Poll",
140
+ description: "Create instant polls with interactive voting",
145
141
  script: `
146
142
  // Remote DOM script for quick-poll widget
147
143
  // Note: Remote DOM only supports registered MCP-UI components like ui-stack, ui-text, ui-button
148
144
  // Standard HTML elements (div, h2, p, etc.) are NOT available
149
145
 
150
146
  // Get props (passed from tool parameters)
151
- const props = ${JSON.stringify({ question: 'What is your favorite framework?', options: ['React', 'Vue', 'Svelte', 'Angular'] })};
147
+ const props = ${JSON.stringify({ question: "What is your favorite framework?", options: ["React", "Vue", "Svelte", "Angular"] })};
152
148
 
153
149
  // Create main container stack (vertical layout)
154
150
  const container = document.createElement('ui-stack');
@@ -235,22 +231,22 @@ container.appendChild(resultsTitle);
235
231
  // Append to root
236
232
  root.appendChild(container);
237
233
  `,
238
- framework: 'react',
239
- encoding: 'text',
240
- size: ['500px', '450px'],
234
+ framework: "react",
235
+ encoding: "text",
236
+ size: ["500px", "450px"],
241
237
  props: {
242
238
  question: {
243
- type: 'string',
244
- description: 'The poll question',
245
- default: 'What is your favorite framework?'
239
+ type: "string",
240
+ description: "The poll question",
241
+ default: "What is your favorite framework?",
246
242
  },
247
243
  options: {
248
- type: 'array',
249
- description: 'Poll options',
250
- default: ['React', 'Vue', 'Svelte']
251
- }
252
- }
253
- } satisfies RemoteDomUIResource)
244
+ type: "array",
245
+ description: "Poll options",
246
+ default: ["React", "Vue", "Svelte"],
247
+ },
248
+ },
249
+ } satisfies RemoteDomUIResource);
254
250
 
255
251
  /**
256
252
  * ════════════════════════════════════════════════════════════════════
@@ -261,83 +257,95 @@ root.appendChild(container);
261
257
  */
262
258
 
263
259
  server.tool({
264
- name: 'get-widget-info',
265
- description: 'Get information about available UI widgets',
260
+ name: "get-widget-info",
261
+ description: "Get information about available UI widgets",
266
262
  cb: async () => {
267
263
  const widgets = [
268
264
  {
269
- name: 'kanban-board',
270
- type: 'externalUrl',
271
- tool: 'kanban-board',
272
- resource: 'ui://widget/kanban-board',
273
- url: `http://localhost:${PORT}/mcp-use/widgets/kanban-board`
265
+ name: "kanban-board",
266
+ type: "externalUrl",
267
+ tool: "kanban-board",
268
+ resource: "ui://widget/kanban-board",
269
+ url: `http://localhost:${PORT}/mcp-use/widgets/kanban-board`,
274
270
  },
275
271
  {
276
- name: 'welcome-card',
277
- type: 'rawHtml',
278
- tool: 'welcome-card',
279
- resource: 'ui://widget/welcome-card'
272
+ name: "welcome-card",
273
+ type: "rawHtml",
274
+ tool: "welcome-card",
275
+ resource: "ui://widget/welcome-card",
280
276
  },
281
277
  {
282
- name: 'quick-poll',
283
- type: 'remoteDom',
284
- tool: 'quick-poll',
285
- resource: 'ui://widget/quick-poll'
286
- }
287
- ]
278
+ name: "quick-poll",
279
+ type: "remoteDom",
280
+ tool: "quick-poll",
281
+ resource: "ui://widget/quick-poll",
282
+ },
283
+ ];
288
284
 
289
285
  return {
290
- content: [{
291
- type: 'text',
292
- text: `Available UI Widgets:\n\n${widgets.map(w =>
293
- `📦 ${w.name} (${w.type})\n` +
294
- ` Tool: ${w.tool}\n` +
295
- ` Resource: ${w.resource}\n` +
296
- (w.url ? ` Browser: ${w.url}\n` : '')
297
- ).join('\n')}\n` +
298
- `\nTypes Explained:\n` +
299
- `• externalUrl: Iframe widget from filesystem\n` +
300
- `• rawHtml: Direct HTML rendering\n` +
301
- `• remoteDom: React/Web Components scripting`
302
- }]
303
- }
304
- }
305
- })
286
+ content: [
287
+ {
288
+ type: "text",
289
+ text:
290
+ `Available UI Widgets:\n\n${widgets
291
+ .map(
292
+ (w) =>
293
+ `📦 ${w.name} (${w.type})\n` +
294
+ ` Tool: ${w.tool}\n` +
295
+ ` Resource: ${w.resource}\n` +
296
+ (w.url ? ` Browser: ${w.url}\n` : "")
297
+ )
298
+ .join("\n")}\n` +
299
+ `\nTypes Explained:\n` +
300
+ `• externalUrl: Iframe widget from filesystem\n` +
301
+ `• rawHtml: Direct HTML rendering\n` +
302
+ `• remoteDom: React/Web Components scripting`,
303
+ },
304
+ ],
305
+ };
306
+ },
307
+ });
306
308
 
307
309
  server.resource({
308
- name: 'server-config',
309
- uri: 'config://server',
310
- title: 'Server Configuration',
311
- description: 'Current server configuration and status',
312
- mimeType: 'application/json',
310
+ name: "server-config",
311
+ uri: "config://server",
312
+ title: "Server Configuration",
313
+ description: "Current server configuration and status",
314
+ mimeType: "application/json",
313
315
  readCallback: async () => ({
314
- contents: [{
315
- uri: 'config://server',
316
- mimeType: 'application/json',
317
- text: JSON.stringify({
318
- port: PORT,
319
- version: '1.0.0',
320
- widgets: {
321
- total: 3,
322
- types: {
323
- externalUrl: ['kanban-board'],
324
- rawHtml: ['welcome-card'],
325
- remoteDom: ['quick-poll']
316
+ contents: [
317
+ {
318
+ uri: "config://server",
319
+ mimeType: "application/json",
320
+ text: JSON.stringify(
321
+ {
322
+ port: PORT,
323
+ version: "1.0.0",
324
+ widgets: {
325
+ total: 3,
326
+ types: {
327
+ externalUrl: ["kanban-board"],
328
+ rawHtml: ["welcome-card"],
329
+ remoteDom: ["quick-poll"],
330
+ },
331
+ baseUrl: `http://localhost:${PORT}/mcp-use/widgets/`,
332
+ },
333
+ endpoints: {
334
+ mcp: `http://localhost:${PORT}/mcp`,
335
+ inspector: `http://localhost:${PORT}/inspector`,
336
+ widgets: `http://localhost:${PORT}/mcp-use/widgets/`,
337
+ },
326
338
  },
327
- baseUrl: `http://localhost:${PORT}/mcp-use/widgets/`
328
- },
329
- endpoints: {
330
- mcp: `http://localhost:${PORT}/mcp`,
331
- inspector: `http://localhost:${PORT}/inspector`,
332
- widgets: `http://localhost:${PORT}/mcp-use/widgets/`
333
- }
334
- }, null, 2)
335
- }]
336
- })
337
- })
339
+ null,
340
+ 2
341
+ ),
342
+ },
343
+ ],
344
+ }),
345
+ });
338
346
 
339
347
  // Start the server
340
- server.listen(PORT)
348
+ server.listen(PORT);
341
349
 
342
350
  // Display helpful startup message
343
351
  console.log(`
@@ -388,12 +396,12 @@ Server is running on port ${PORT}
388
396
  })
389
397
 
390
398
  💡 Tip: Open the Inspector UI to test all widget types interactively!
391
- `)
399
+ `);
392
400
 
393
401
  // Handle graceful shutdown
394
- process.on('SIGINT', () => {
395
- console.log('\n\nShutting down server...')
396
- process.exit(0)
397
- })
402
+ process.on("SIGINT", () => {
403
+ console.log("\n\nShutting down server...");
404
+ process.exit(0);
405
+ });
398
406
 
399
- export default server
407
+ export default server;
@@ -35,4 +35,4 @@
35
35
  "@types/react": "^19.2.2",
36
36
  "@types/react-dom": "^19.2.2"
37
37
  }
38
- }
38
+ }