create-mcp-use-app 0.3.5 → 0.4.0

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.
@@ -0,0 +1,398 @@
1
+ # Pizzaz MCP Server - OpenAI Apps SDK Reference
2
+
3
+ An MCP server demonstrating OpenAI Apps SDK integration using `mcp-use`. This implementation showcases the **pizzaz reference widgets** that demonstrate Apps SDK best practices.
4
+
5
+ ## 🌟 Features
6
+
7
+ - **šŸ• Pizzaz Widgets**: 5 reference widgets from OpenAI's Apps SDK examples
8
+ - **šŸš€ Apps SDK Integration**: Full OpenAI Apps SDK metadata support
9
+ - **šŸ“¦ Automatic Registration**: Tools and resources created automatically
10
+ - **šŸ”’ Content Security Policy**: Proper CSP configuration for external resources
11
+ - **⚔ Tool Invocation Status**: Real-time status messages during tool execution
12
+ - **šŸŽØ External Resources**: Load scripts and styles from OpenAI's CDN
13
+ - **šŸ› ļø TypeScript Support**: Complete type safety and IntelliSense
14
+
15
+ ## šŸ“‹ What's Included
16
+
17
+ ### Pizzaz Widgets
18
+
19
+ This server includes all 5 pizzaz reference widgets:
20
+
21
+ 1. **pizza-map** - Interactive map widget
22
+ 2. **pizza-carousel** - Carousel browsing widget
23
+ 3. **pizza-albums** - Album gallery widget
24
+ 4. **pizza-list** - List view widget
25
+ 5. **pizza-video** - Video player widget
26
+
27
+ Each widget demonstrates:
28
+
29
+ - External resource loading from OpenAI CDN
30
+ - Apps SDK metadata configuration
31
+ - Structured content injection
32
+ - Tool invocation status messages
33
+
34
+ ## šŸš€ Getting Started
35
+
36
+ ### Development
37
+
38
+ ```bash
39
+ # Install dependencies
40
+ npm install
41
+
42
+ # Start development server with hot reloading
43
+ npm run dev
44
+ ```
45
+
46
+ This will start:
47
+
48
+ - MCP server on port 8000
49
+ - Inspector UI at `/inspector`
50
+ - SSE endpoint at `/mcp`
51
+
52
+ ### Production
53
+
54
+ ```bash
55
+ # Build the server
56
+ npm run build
57
+
58
+ # Run the built server
59
+ npm start
60
+ ```
61
+
62
+ ## šŸ“– Usage
63
+
64
+ ### Via MCP Client
65
+
66
+ ```typescript
67
+ import { createMCPClient } from 'mcp-use/client'
68
+
69
+ const client = createMCPClient({
70
+ serverUrl: 'http://localhost:8000/mcp',
71
+ })
72
+
73
+ // Call a pizzaz widget tool
74
+ const result = await client.callTool('ui_pizza-map', {
75
+ pizzaTopping: 'pepperoni',
76
+ })
77
+
78
+ // Access widget template as resource
79
+ const resource = await client.readResource('ui://widget/pizza-map.html')
80
+
81
+ // List all available widgets
82
+ const widgetList = await client.callTool('list-widgets', {})
83
+
84
+ // Get info about a specific widget
85
+ const widgetInfo = await client.callTool('get-widget-info', {
86
+ widgetId: 'pizza-carousel',
87
+ })
88
+ ```
89
+
90
+ ### Via Inspector UI
91
+
92
+ 1. Start the server: `npm run dev`
93
+ 2. Open: `http://localhost:8000/inspector`
94
+ 3. Test tools and resources interactively
95
+
96
+ ### Direct HTTP Access
97
+
98
+ ```bash
99
+ # SSE connection
100
+ curl -N http://localhost:8000/mcp
101
+
102
+ # Post message (requires sessionId)
103
+ curl -X POST http://localhost:8000/mcp/messages?sessionId=<session-id> \
104
+ -H "Content-Type: application/json" \
105
+ -d '{"method": "tools/list"}'
106
+ ```
107
+
108
+ ## šŸŽÆ How It Works
109
+
110
+ ### Apps SDK Integration
111
+
112
+ The server uses `mcp-use`'s `AppsSdkUIResource` type to create Apps SDK compatible widgets:
113
+
114
+ ```typescript
115
+ import type { AppsSdkUIResource } from 'mcp-use/server'
116
+
117
+ const widget: AppsSdkUIResource = {
118
+ type: 'appsSdk',
119
+ name: 'pizza-map',
120
+ title: 'Show Pizza Map',
121
+ description: 'Interactive map widget',
122
+ htmlTemplate: `
123
+ <div id="pizzaz-root"></div>
124
+ <link rel="stylesheet" href="https://persistent.oaistatic.com/...">
125
+ <script type="module" src="https://persistent.oaistatic.com/..."></script>
126
+ `,
127
+ props: {
128
+ pizzaTopping: {
129
+ type: 'string',
130
+ description: 'Topping to mention',
131
+ required: true,
132
+ },
133
+ },
134
+ appsSdkMetadata: {
135
+ 'openai/widgetDescription': 'Interactive map widget',
136
+ 'openai/toolInvocation/invoking': 'Hand-tossing a map',
137
+ 'openai/toolInvocation/invoked': 'Served a fresh map',
138
+ 'openai/widgetAccessible': true,
139
+ 'openai/widgetCSP': {
140
+ connect_domains: [],
141
+ resource_domains: ['https://persistent.oaistatic.com'],
142
+ },
143
+ },
144
+ }
145
+
146
+ server.uiResource(widget)
147
+ ```
148
+
149
+ This automatically:
150
+
151
+ 1. Creates a tool (`ui_pizza-map`)
152
+ 2. Creates a resource (`ui://widget/pizza-map.html`)
153
+ 3. Sets MIME type to `text/html+skybridge`
154
+ 4. Injects Apps SDK metadata
155
+ 5. Handles structured content injection
156
+
157
+ ### Widget Registration
158
+
159
+ Widgets are defined in `src/widgets.ts` and registered in `src/server.ts`:
160
+
161
+ ```typescript
162
+ import { getPizzazUIResources } from './widgets.js'
163
+
164
+ const pizzazWidgets = getPizzazUIResources()
165
+
166
+ pizzazWidgets.forEach((widget) => {
167
+ server.uiResource(widget)
168
+ })
169
+ ```
170
+
171
+ ### Structured Content
172
+
173
+ When a tool is called, the `structuredContent` is automatically injected as `window.openai.toolOutput`:
174
+
175
+ ```typescript
176
+ // In your tool handler
177
+ return {
178
+ content: [
179
+ {
180
+ type: 'text',
181
+ text: 'Rendered a pizza map!',
182
+ },
183
+ ],
184
+ structuredContent: {
185
+ pizzaTopping: params.pizzaTopping,
186
+ },
187
+ }
188
+
189
+ // Widget can access it via:
190
+ // window.openai.toolOutput.pizzaTopping
191
+ ```
192
+
193
+ ## šŸ“š Apps SDK Metadata
194
+
195
+ Each widget includes rich Apps SDK metadata:
196
+
197
+ ### Widget Description
198
+
199
+ ```typescript
200
+ 'openai/widgetDescription': 'Interactive map widget for displaying pizza locations'
201
+ ```
202
+
203
+ ### Tool Invocation Status
204
+
205
+ ```typescript
206
+ 'openai/toolInvocation/invoking': 'Hand-tossing a map'
207
+ 'openai/toolInvocation/invoked': 'Served a fresh map'
208
+ ```
209
+
210
+ ### Content Security Policy
211
+
212
+ ```typescript
213
+ 'openai/widgetCSP': {
214
+ connect_domains: [], // Domains widget can connect to
215
+ resource_domains: ['https://persistent.oaistatic.com'] // CDN domains
216
+ }
217
+ ```
218
+
219
+ ### Accessibility
220
+
221
+ ```typescript
222
+ 'openai/widgetAccessible': true
223
+ ```
224
+
225
+ ### Output Template
226
+
227
+ ```typescript
228
+ 'openai/outputTemplate': 'ui://widget/pizza-map.html'
229
+ ```
230
+
231
+ ## šŸ”§ Configuration
232
+
233
+ ### Port Configuration
234
+
235
+ Set the port via environment variable:
236
+
237
+ ```bash
238
+ PORT=3000 npm start
239
+ ```
240
+
241
+ Default port is `8000`.
242
+
243
+ ### Widget Customization
244
+
245
+ To add or modify widgets, edit `src/widgets.ts`:
246
+
247
+ ```typescript
248
+ export const pizzazWidgets: PizzazWidgetDefinition[] = [
249
+ {
250
+ id: 'my-widget',
251
+ title: 'My Custom Widget',
252
+ description: 'A custom widget',
253
+ templateUri: 'ui://widget/my-widget.html',
254
+ invoking: 'Loading widget...',
255
+ invoked: 'Widget ready!',
256
+ html: `
257
+ <div id="widget-root"></div>
258
+ <script>
259
+ // Your widget code
260
+ </script>
261
+ `,
262
+ responseText: 'Widget rendered!',
263
+ },
264
+ // ... other widgets
265
+ ]
266
+ ```
267
+
268
+ ## šŸŽØ Widget Structure
269
+
270
+ Each pizzaz widget follows this structure:
271
+
272
+ ```html
273
+ <!-- Container div with specific ID -->
274
+ <div id="pizzaz-root"></div>
275
+
276
+ <!-- External stylesheet -->
277
+ <link rel="stylesheet" href="https://persistent.oaistatic.com/..." />
278
+
279
+ <!-- External script (type="module") -->
280
+ <script type="module" src="https://persistent.oaistatic.com/..."></script>
281
+ ```
282
+
283
+ The external scripts:
284
+
285
+ - Render interactive components into the container
286
+ - Access structured content via `window.openai.toolOutput`
287
+ - Handle user interactions
288
+ - Communicate with parent via postMessage
289
+
290
+ ## šŸ› ļø Available Tools
291
+
292
+ ### Widget Tools
293
+
294
+ Each pizzaz widget creates a tool:
295
+
296
+ - `ui_pizza-map` - Display interactive map
297
+ - `ui_pizza-carousel` - Browse in carousel format
298
+ - `ui_pizza-albums` - Show album gallery
299
+ - `ui_pizza-list` - Display list view
300
+ - `ui_pizza-video` - Play video content
301
+
302
+ All accept a `pizzaTopping` parameter:
303
+
304
+ ```typescript
305
+ await client.callTool('ui_pizza-map', {
306
+ pizzaTopping: 'pepperoni',
307
+ })
308
+ ```
309
+
310
+ ### Helper Tools
311
+
312
+ - `list-widgets` - Get list of all available widgets
313
+ - `get-widget-info` - Get detailed info about a specific widget
314
+
315
+ ## šŸ“¦ Available Resources
316
+
317
+ Each widget template is available as a resource:
318
+
319
+ - `ui://widget/pizza-map.html`
320
+ - `ui://widget/pizza-carousel.html`
321
+ - `ui://widget/pizza-albums.html`
322
+ - `ui://widget/pizza-list.html`
323
+ - `ui://widget/pizza-video.html`
324
+
325
+ Plus:
326
+
327
+ - `config://server` - Server configuration (JSON)
328
+
329
+ ## šŸ” Testing
330
+
331
+ ### Test with Inspector
332
+
333
+ 1. Start server: `npm run dev`
334
+ 2. Open inspector: `http://localhost:8000/inspector`
335
+ 3. Click on any widget tool
336
+ 4. Enter a pizza topping
337
+ 5. Execute and view the result
338
+
339
+ ### Test with MCP Client
340
+
341
+ ```typescript
342
+ import { createMCPClient } from 'mcp-use/client'
343
+
344
+ const client = createMCPClient({
345
+ serverUrl: 'http://localhost:8000/mcp',
346
+ })
347
+
348
+ // Test listing tools
349
+ const tools = await client.listTools()
350
+ console.log('Available tools:', tools)
351
+
352
+ // Test calling a widget
353
+ const result = await client.callTool('ui_pizza-map', {
354
+ pizzaTopping: 'mushroom',
355
+ })
356
+ console.log('Result:', result)
357
+
358
+ // Test reading resource
359
+ const resource = await client.readResource('ui://widget/pizza-map.html')
360
+ console.log('Resource:', resource)
361
+ ```
362
+
363
+ ## šŸ“‚ Project Structure
364
+
365
+ ```
366
+ apps_sdk/
367
+ ā”œā”€ā”€ index.ts # Entry point
368
+ ā”œā”€ā”€ package.json # Dependencies
369
+ ā”œā”€ā”€ tsconfig.json # TypeScript config
370
+ ā”œā”€ā”€ README.md # This file
371
+ └── src/
372
+ ā”œā”€ā”€ server.ts # Main server implementation
373
+ └── widgets.ts # Pizzaz widget definitions
374
+ ```
375
+
376
+ ## šŸ”— Related Documentation
377
+
378
+ - [MCP Documentation](https://modelcontextprotocol.io)
379
+ - [MCP-UI Apps SDK Guide](https://mcpui.dev/guide/apps-sdk)
380
+ - [mcp-use Documentation](https://github.com/pyroprompt/mcp-use)
381
+ - [OpenAI Apps SDK](https://platform.openai.com/docs/guides/apps)
382
+
383
+ ## šŸ¤ Contributing
384
+
385
+ This is a reference implementation demonstrating Apps SDK integration patterns. Feel free to:
386
+
387
+ - Add new widget types
388
+ - Customize existing widgets
389
+ - Extend Apps SDK metadata
390
+ - Add additional tools and resources
391
+
392
+ ## šŸ“ License
393
+
394
+ MIT
395
+
396
+ ---
397
+
398
+ Built with ā¤ļø using [mcp-use](https://github.com/pyroprompt/mcp-use) and [OpenAI Apps SDK](https://platform.openai.com/docs/guides/apps)
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP Apps SDK Server Entry Point
3
+ *
4
+ * This file serves as the main entry point for the MCP server with Apps SDK support.
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 demonstrates OpenAI Apps SDK integration using mcp-use's uiResource method.
9
+ */
10
+ export * from './src/server.js'
11
+
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "mcp-apps-sdk-server",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "MCP server with OpenAI Apps SDK integration using mcp-use",
6
+ "author": "",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "mcp",
10
+ "server",
11
+ "apps-sdk",
12
+ "openai",
13
+ "chatgpt",
14
+ "widgets",
15
+ "pizzaz",
16
+ "ai",
17
+ "tools"
18
+ ],
19
+ "main": "dist/index.js",
20
+ "scripts": {
21
+ "build": "mcp-use build",
22
+ "dev": "mcp-use dev",
23
+ "start": "mcp-use start"
24
+ },
25
+ "dependencies": {
26
+ "@mcp-ui/server": "^5.11.0",
27
+ "cors": "^2.8.5",
28
+ "express": "^4.18.0",
29
+ "mcp-use": "workspace:*",
30
+ "zod": "^3.23.0"
31
+ },
32
+ "devDependencies": {
33
+ "@mcp-use/cli": "workspace:*",
34
+ "@mcp-use/inspector": "workspace:*",
35
+ "@types/cors": "^2.8.0",
36
+ "@types/express": "^4.17.0",
37
+ "@types/node": "^20.0.0",
38
+ "tsx": "^4.0.0",
39
+ "typescript": "^5.0.0"
40
+ }
41
+ }
42
+
@@ -0,0 +1,239 @@
1
+ import { createMCPServer } from 'mcp-use/server'
2
+ import { getPizzazUIResources, getPizzazWidgetsSummary } from './widgets.js'
3
+
4
+ /**
5
+ * ════════════════════════════════════════════════════════════════════
6
+ * Pizzaz MCP Server - OpenAI Apps SDK Reference Implementation
7
+ * ════════════════════════════════════════════════════════════════════
8
+ *
9
+ * This server demonstrates OpenAI Apps SDK integration using mcp-use's
10
+ * uiResource method. It implements the pizzaz reference widgets that
11
+ * showcase best practices for Apps SDK widgets.
12
+ *
13
+ * Key Features:
14
+ * - Apps SDK metadata (CSP, tool invocation status, widget description)
15
+ * - External resource loading (OpenAI CDN assets)
16
+ * - Structured content injection via window.openai.toolOutput
17
+ * - text/html+skybridge MIME type for Apps SDK compatibility
18
+ * - Automatic tool and resource registration
19
+ */
20
+
21
+ // Create the MCP server
22
+ const server = createMCPServer('pizzaz-node', {
23
+ version: '0.1.0',
24
+ description: 'OpenAI Apps SDK reference implementation with pizzaz widgets',
25
+ })
26
+
27
+ const PORT = process.env.PORT || 8000
28
+
29
+ /**
30
+ * ════════════════════════════════════════════════════════════════════
31
+ * Register Pizzaz Widgets
32
+ * ════════════════════════════════════════════════════════════════════
33
+ *
34
+ * Using mcp-use's uiResource method with AppsSdkUIResource type:
35
+ *
36
+ * This automatically:
37
+ * 1. Creates tools (ui_pizza-map, ui_pizza-carousel, etc.)
38
+ * 2. Creates resources (ui://widget/pizza-map.html, etc.)
39
+ * 3. Sets MIME type to text/html+skybridge
40
+ * 4. Injects Apps SDK metadata
41
+ * 5. Handles structuredContent injection
42
+ */
43
+
44
+ const pizzazWidgets = getPizzazUIResources()
45
+
46
+ pizzazWidgets.forEach(widget => {
47
+ server.uiResource(widget)
48
+ })
49
+
50
+ /**
51
+ * ════════════════════════════════════════════════════════════════════
52
+ * Helper Tools
53
+ * ════════════════════════════════════════════════════════════════════
54
+ *
55
+ * Additional tools for discovering and testing widgets
56
+ */
57
+
58
+ server.tool({
59
+ name: 'list-widgets',
60
+ title: 'List Available Widgets',
61
+ description: 'Get a list of all available pizzaz widgets',
62
+ cb: async () => {
63
+ const widgets = getPizzazWidgetsSummary()
64
+
65
+ return {
66
+ content: [{
67
+ type: 'text',
68
+ text: `Available Pizzaz Widgets (${widgets.length} total):\n\n${widgets.map(w =>
69
+ `šŸ• ${w.title}\n` +
70
+ ` ID: ${w.id}\n` +
71
+ ` Tool: ${w.tool}\n` +
72
+ ` Resource: ${w.resource}\n` +
73
+ ` Description: ${w.description}\n`
74
+ ).join('\n')}\n` +
75
+ `\nUsage:\n` +
76
+ `await client.callTool('ui_${widgets[0].id}', { pizzaTopping: 'pepperoni' })\n` +
77
+ `await client.readResource('${widgets[0].resource}')`
78
+ }]
79
+ }
80
+ }
81
+ })
82
+
83
+ server.tool({
84
+ name: 'get-widget-info',
85
+ title: 'Get Widget Information',
86
+ description: 'Get detailed information about a specific widget',
87
+ inputs: [{
88
+ name: 'widgetId',
89
+ type: 'string',
90
+ description: 'Widget ID (e.g., pizza-map, pizza-carousel)',
91
+ required: true
92
+ }],
93
+ cb: async (params) => {
94
+ const widgets = getPizzazWidgetsSummary()
95
+ const widget = widgets.find(w => w.id === params.widgetId)
96
+
97
+ if (!widget) {
98
+ return {
99
+ content: [{
100
+ type: 'text',
101
+ text: `Widget '${params.widgetId}' not found. Available widgets: ${widgets.map(w => w.id).join(', ')}`
102
+ }]
103
+ }
104
+ }
105
+
106
+ return {
107
+ content: [{
108
+ type: 'text',
109
+ text: `Widget Information:\n\n` +
110
+ `ID: ${widget.id}\n` +
111
+ `Title: ${widget.title}\n` +
112
+ `Description: ${widget.description}\n` +
113
+ `Tool Name: ${widget.tool}\n` +
114
+ `Resource URI: ${widget.resource}\n` +
115
+ `Response Text: ${widget.responseText}\n\n` +
116
+ `Usage:\n` +
117
+ `await client.callTool('${widget.tool}', { pizzaTopping: 'mushroom' })\n` +
118
+ `await client.readResource('${widget.resource}')`
119
+ }]
120
+ }
121
+ }
122
+ })
123
+
124
+ /**
125
+ * ════════════════════════════════════════════════════════════════════
126
+ * Server Configuration Resource
127
+ * ════════════════════════════════════════════════════════════════════
128
+ */
129
+
130
+ server.resource({
131
+ name: 'server-config',
132
+ uri: 'config://server',
133
+ title: 'Server Configuration',
134
+ description: 'Current server configuration and capabilities',
135
+ mimeType: 'application/json',
136
+ readCallback: async () => ({
137
+ contents: [{
138
+ uri: 'config://server',
139
+ mimeType: 'application/json',
140
+ text: JSON.stringify({
141
+ name: 'pizzaz-node',
142
+ version: '0.1.0',
143
+ port: PORT,
144
+ widgets: {
145
+ total: pizzazWidgets.length,
146
+ ids: pizzazWidgets.map(w => w.name)
147
+ },
148
+ endpoints: {
149
+ mcp: `http://localhost:${PORT}/mcp`,
150
+ inspector: `http://localhost:${PORT}/inspector`,
151
+ },
152
+ features: {
153
+ appsSdk: true,
154
+ externalResources: true,
155
+ structuredContent: true,
156
+ pizzazWidgets: true
157
+ },
158
+ appsSdk: {
159
+ mimeType: 'text/html+skybridge',
160
+ metadata: [
161
+ 'openai/widgetDescription',
162
+ 'openai/widgetCSP',
163
+ 'openai/widgetAccessible',
164
+ 'openai/outputTemplate',
165
+ 'openai/toolInvocation/invoking',
166
+ 'openai/toolInvocation/invoked',
167
+ 'openai/resultCanProduceWidget'
168
+ ]
169
+ }
170
+ }, null, 2)
171
+ }]
172
+ })
173
+ })
174
+
175
+ /**
176
+ * ════════════════════════════════════════════════════════════════════
177
+ * Start the Server
178
+ * ════════════════════════════════════════════════════════════════════
179
+ */
180
+
181
+ server.listen(PORT)
182
+
183
+ // Display helpful startup message
184
+ console.log(`
185
+ ╔═══════════════════════════════════════════════════════════════╗
186
+ ā•‘ šŸ• Pizzaz MCP Server - Apps SDK Reference ā•‘
187
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
188
+
189
+ Server is running on port ${PORT}
190
+
191
+ šŸ“ Endpoints:
192
+ MCP Protocol: http://localhost:${PORT}/mcp
193
+ Inspector UI: http://localhost:${PORT}/inspector
194
+
195
+ šŸ• Available Pizzaz Widgets (${pizzazWidgets.length} total):
196
+
197
+ ${pizzazWidgets.map((w, i) => ` ${i + 1}. ${w.name}
198
+ Tool: ui_${w.name}
199
+ Resource: ${w.appsSdkMetadata?.['openai/outputTemplate'] || 'N/A'}
200
+ `).join('\n')}
201
+
202
+ šŸ“ Usage Examples:
203
+
204
+ // Call a widget tool with parameters
205
+ await client.callTool('ui_pizza-map', {
206
+ pizzaTopping: 'pepperoni'
207
+ })
208
+
209
+ // Access widget template as resource
210
+ await client.readResource('ui://widget/pizza-map.html')
211
+
212
+ // List all widgets
213
+ await client.callTool('list-widgets', {})
214
+
215
+ // Get widget info
216
+ await client.callTool('get-widget-info', {
217
+ widgetId: 'pizza-map'
218
+ })
219
+
220
+ šŸ”§ Apps SDK Features:
221
+ āœ“ text/html+skybridge MIME type
222
+ āœ“ External resource loading (OpenAI CDN)
223
+ āœ“ Structured content injection
224
+ āœ“ Tool invocation status messages
225
+ āœ“ Content Security Policy (CSP)
226
+ āœ“ Widget accessibility metadata
227
+
228
+ šŸ’” Tip: Open the Inspector UI to test all widgets interactively!
229
+ šŸŽØ These widgets demonstrate OpenAI's Apps SDK best practices.
230
+ `)
231
+
232
+ // Handle graceful shutdown
233
+ process.on('SIGINT', () => {
234
+ console.log('\n\nšŸ‘‹ Shutting down pizzaz server...')
235
+ process.exit(0)
236
+ })
237
+
238
+ export default server
239
+