pizzaz-mcp 1.0.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,3 @@
1
+ {
2
+ "html.autoClosingTags": false
3
+ }
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Pizzaz MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that serves pizza UI widgets for ChatGPT's Apps SDK. Deployed on Render to bypass proxy restrictions, using OpenAI's exact component architecture, styling system, and design tokens.
4
+
5
+ ## Live URL
6
+
7
+ ```
8
+ https://pizzaz-mcp.onrender.com/mcp
9
+ ```
10
+
11
+ ## What It Does
12
+
13
+ - Exposes 5 pizza widgets via MCP tools: map, carousel, list, albums, shop
14
+ - Uses `@openai/apps-sdk-ui` components (Button, Image) exactly as OpenAI does
15
+ - Tailwind CSS v4 + OpenAI design tokens for theming
16
+ - Each widget is a self-contained HTML file served from `/assets/`
17
+ - Auto-deploys to Render on every push to `main`
18
+
19
+ ## Project Structure
20
+
21
+ ```
22
+ pizzaz-mcp/
23
+ ├── src/ # React widget source (OpenAI SDK architecture)
24
+ │ ├── index.css # Tailwind + @openai/apps-sdk-ui/css
25
+ │ ├── types.ts # OpenAI globals / widget types
26
+ │ ├── use-openai-global.ts # Hook: reads window.openai globals
27
+ │ ├── use-widget-props.ts # Hook: reads toolOutput props
28
+ │ ├── use-widget-state.ts # Hook: reads/writes widget state
29
+ │ ├── use-display-mode.ts # Hook: pip | inline | fullscreen
30
+ │ ├── use-max-height.ts # Hook: max height constraint
31
+ │ ├── media-queries.ts # Shared media query helpers
32
+ │ ├── pizzaz/ # Pizza map widget + markers data
33
+ │ ├── pizzaz-carousel/ # Pizza carousel widget
34
+ │ ├── pizzaz-list/ # Pizza list widget
35
+ │ ├── pizzaz-albums/ # Pizza albums widget
36
+ │ └── pizzaz-shop/ # Pizza shop / cart widget
37
+ ├── server/
38
+ │ └── server.ts # MCP server (SSE transport)
39
+ ├── assets/ # Build output (committed to git)
40
+ │ ├── pizzaz.html
41
+ │ ├── pizzaz-carousel.html
42
+ │ ├── pizzaz-list.html
43
+ │ ├── pizzaz-albums.html
44
+ │ ├── pizzaz-shop.html
45
+ │ └── *.js / *.css # Hashed bundles
46
+ ├── build-all.mts # Vite multi-entry build script
47
+ ├── vite.config.mts # Vite dev server config
48
+ ├── tailwind.config.ts # Tailwind content paths
49
+ ├── tsconfig.json # TypeScript project references
50
+ ├── package.json # Dependencies + scripts
51
+ └── render.yaml # Render deployment config
52
+ ```
53
+
54
+ ## Architecture
55
+
56
+ The app follows OpenAI's exact widget architecture from `openai-apps-sdk-examples`:
57
+
58
+ - Components use `@openai/apps-sdk-ui/components/Button` and `Image` directly
59
+ - CSS uses Tailwind v4 with `@import "@openai/apps-sdk-ui/css"` for design tokens
60
+ - Hooks (`useWidgetProps`, `useWidgetState`, `useOpenAiGlobal`) match the SDK pattern
61
+ - Each widget entry is `src/<name>/index.{tsx,jsx}` — Vite builds each to `assets/<name>.js` + `assets/<name>.css`
62
+ - The build script generates `assets/<name>.html` with absolute URLs pointing to the Render deployment
63
+ - The MCP server reads those HTML files at startup and serves them via `text/html+skybridge` MIME type
64
+
65
+ ## Available MCP Tools
66
+
67
+ | Tool | Widget | Description |
68
+ |------|--------|-------------|
69
+ | `pizza-map` | pizzaz | Interactive pizza map of SF |
70
+ | `pizza-carousel` | pizzaz-carousel | Scrollable pizza place cards |
71
+ | `pizza-list` | pizzaz-list | Ranked list of pizzerias |
72
+ | `pizza-albums` | pizzaz-albums | Album-style pizza gallery |
73
+ | `pizza-shop` | pizzaz-shop | Full cart + checkout experience |
74
+
75
+ ## Quick Start
76
+
77
+ ### Local Development
78
+
79
+ ```bash
80
+ npm install
81
+ npm run dev
82
+ # Open http://localhost:4444
83
+ ```
84
+
85
+ ### Build
86
+
87
+ ```bash
88
+ npm run build
89
+ # Outputs to assets/
90
+ ```
91
+
92
+ ### Deploy to Render
93
+
94
+ 1. Push to GitHub
95
+ 2. On [Render Dashboard](https://dashboard.render.com/): New → Web Service → connect repo
96
+ 3. Render auto-detects `render.yaml` and deploys
97
+ 4. Add MCP connector in ChatGPT: `https://pizzaz-mcp.onrender.com/mcp`
98
+
99
+ ```bash
100
+ git add .
101
+ git commit -m "Deploy"
102
+ git push origin main
103
+ ```
104
+
105
+ ## Using in ChatGPT
106
+
107
+ 1. Go to https://chatgpt.com
108
+ 2. Settings → Beta Features → enable Developer Mode
109
+ 3. Add MCP connector: `https://pizzaz-mcp.onrender.com/mcp`
110
+ 4. Try: "Show me the pizza carousel" or "Open the Pizzaz shop"
111
+
112
+ ## Theming
113
+
114
+ Theming is handled by `@openai/apps-sdk-ui`'s CSS custom properties. The `src/index.css` imports the full token set:
115
+
116
+ ```css
117
+ @import "tailwindcss";
118
+ @import "@openai/apps-sdk-ui/css";
119
+ ```
120
+
121
+ To customize colors, override the CSS variables in `src/index.css`:
122
+
123
+ ```css
124
+ :root {
125
+ --color-background-primary-solid: #ff6b35;
126
+ }
127
+ ```
128
+
129
+ ## Notes
130
+
131
+ - Render free tier spins down after 15 min of inactivity — first request takes ~30s
132
+ - The MCP endpoint is `GET /mcp` (SSE) and `POST /mcp/messages` (messages)
133
+ - Build output is committed to git so Render only needs `npm install && npm start`
134
+
135
+ ## References
136
+
137
+ - [OpenAI Apps SDK Examples](https://github.com/openai/openai-apps-sdk-examples)
138
+ - [MCP Protocol](https://modelcontextprotocol.io/)
139
+ - [Render Docs](https://render.com/docs)
package/build-all.mts ADDED
@@ -0,0 +1,188 @@
1
+ import { build, type InlineConfig, type Plugin } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import fg from "fast-glob";
4
+ import path from "path";
5
+ import fs from "fs";
6
+ import crypto from "crypto";
7
+ import pkg from "./package.json" with { type: "json" };
8
+ import tailwindcss from "@tailwindcss/vite";
9
+
10
+ const entries = fg.sync("src/**/index.{tsx,jsx}");
11
+ const outDir = "assets";
12
+
13
+ const PER_ENTRY_CSS_GLOB = "**/*.{css,pcss,scss,sass}";
14
+ const PER_ENTRY_CSS_IGNORE = "**/*.module.*".split(",").map((s) => s.trim());
15
+ const GLOBAL_CSS_LIST = [path.resolve("src/index.css")];
16
+
17
+ const targets: string[] = [
18
+ "pizzaz",
19
+ "pizzaz-carousel",
20
+ "pizzaz-list",
21
+ "pizzaz-albums",
22
+ "pizzaz-shop",
23
+ ];
24
+ const builtNames: string[] = [];
25
+
26
+ function wrapEntryPlugin(
27
+ virtualId: string,
28
+ entryFile: string,
29
+ cssPaths: string[]
30
+ ): Plugin {
31
+ return {
32
+ name: `virtual-entry-wrapper:${entryFile}`,
33
+ resolveId(id) {
34
+ if (id === virtualId) return id;
35
+ },
36
+ load(id) {
37
+ if (id !== virtualId) {
38
+ return null;
39
+ }
40
+
41
+ const cssImports = cssPaths
42
+ .map((css) => `import ${JSON.stringify(css)};`)
43
+ .join("\n");
44
+
45
+ return `
46
+ ${cssImports}
47
+ export * from ${JSON.stringify(entryFile)};
48
+
49
+ import * as __entry from ${JSON.stringify(entryFile)};
50
+ export default (__entry.default ?? __entry.App);
51
+
52
+ import ${JSON.stringify(entryFile)};
53
+ `;
54
+ },
55
+ };
56
+ }
57
+
58
+ fs.rmSync(outDir, { recursive: true, force: true });
59
+
60
+ for (const file of entries) {
61
+ const name = path.basename(path.dirname(file));
62
+ if (targets.length && !targets.includes(name)) {
63
+ continue;
64
+ }
65
+
66
+ const entryAbs = path.resolve(file);
67
+ const entryDir = path.dirname(entryAbs);
68
+
69
+ // Collect CSS for this entry using the glob(s) rooted at its directory
70
+ const perEntryCss = fg.sync(PER_ENTRY_CSS_GLOB, {
71
+ cwd: entryDir,
72
+ absolute: true,
73
+ dot: false,
74
+ ignore: PER_ENTRY_CSS_IGNORE,
75
+ });
76
+
77
+ // Global CSS (Tailwind, etc.), only include those that exist
78
+ const globalCss = GLOBAL_CSS_LIST.filter((p) => fs.existsSync(p));
79
+
80
+ // Final CSS list (global first for predictable cascade)
81
+ const cssToInclude = [...globalCss, ...perEntryCss].filter((p) =>
82
+ fs.existsSync(p)
83
+ );
84
+
85
+ const virtualId = `\0virtual-entry:${entryAbs}`;
86
+
87
+ const createConfig = (): InlineConfig => ({
88
+ plugins: [
89
+ wrapEntryPlugin(virtualId, entryAbs, cssToInclude),
90
+ tailwindcss(),
91
+ react(),
92
+ {
93
+ name: "remove-manual-chunks",
94
+ outputOptions(options) {
95
+ if ("manualChunks" in options) {
96
+ delete (options as any).manualChunks;
97
+ }
98
+ return options;
99
+ },
100
+ },
101
+ ],
102
+ esbuild: {
103
+ jsx: "automatic",
104
+ jsxImportSource: "react",
105
+ target: "es2022",
106
+ },
107
+ build: {
108
+ target: "es2022",
109
+ outDir,
110
+ emptyOutDir: false,
111
+ chunkSizeWarningLimit: 2000,
112
+ minify: "esbuild",
113
+ cssCodeSplit: false,
114
+ rollupOptions: {
115
+ input: virtualId,
116
+ output: {
117
+ format: "es",
118
+ entryFileNames: `${name}.js`,
119
+ inlineDynamicImports: true,
120
+ assetFileNames: (info) =>
121
+ (info.name || "").endsWith(".css")
122
+ ? `${name}.css`
123
+ : `[name]-[hash][extname]`,
124
+ },
125
+ preserveEntrySignatures: "allow-extension",
126
+ treeshake: true,
127
+ },
128
+ },
129
+ });
130
+
131
+ console.group(`Building ${name} (react)`);
132
+ await build(createConfig());
133
+ console.groupEnd();
134
+ builtNames.push(name);
135
+ console.log(`Built ${name}`);
136
+ }
137
+
138
+ const outputs = fs
139
+ .readdirSync("assets")
140
+ .filter((f) => f.endsWith(".js") || f.endsWith(".css"))
141
+ .map((f) => path.join("assets", f))
142
+ .filter((p) => fs.existsSync(p));
143
+
144
+ const h = crypto
145
+ .createHash("sha256")
146
+ .update(pkg.version, "utf8")
147
+ .digest("hex")
148
+ .slice(0, 4);
149
+
150
+ console.group("Hashing outputs");
151
+ for (const out of outputs) {
152
+ const dir = path.dirname(out);
153
+ const ext = path.extname(out);
154
+ const base = path.basename(out, ext);
155
+ const newName = path.join(dir, `${base}-${h}${ext}`);
156
+
157
+ fs.renameSync(out, newName);
158
+ console.log(`${out} -> ${newName}`);
159
+ }
160
+ console.groupEnd();
161
+
162
+ console.log("new hash: ", h);
163
+
164
+ const defaultBaseUrl = "https://pizzaz-mcp.onrender.com";
165
+ const baseUrlCandidate = process.env.BASE_URL?.trim() ?? "";
166
+ const baseUrlRaw = baseUrlCandidate.length > 0 ? baseUrlCandidate : defaultBaseUrl;
167
+ const normalizedBaseUrl = baseUrlRaw.replace(/\/+$/, "") || defaultBaseUrl;
168
+ console.log(`Using BASE_URL ${normalizedBaseUrl} for generated HTML`);
169
+
170
+ for (const name of builtNames) {
171
+ const dir = outDir;
172
+ const hashedHtmlPath = path.join(dir, `${name}-${h}.html`);
173
+ const liveHtmlPath = path.join(dir, `${name}.html`);
174
+ const html = `<!doctype html>
175
+ <html>
176
+ <head>
177
+ <script type="module" src="${normalizedBaseUrl}/${name}-${h}.js"></script>
178
+ <link rel="stylesheet" href="${normalizedBaseUrl}/${name}-${h}.css">
179
+ </head>
180
+ <body>
181
+ <div id="${name}-root"></div>
182
+ </body>
183
+ </html>
184
+ `;
185
+ fs.writeFileSync(hashedHtmlPath, html, { encoding: "utf8" });
186
+ fs.writeFileSync(liveHtmlPath, html, { encoding: "utf8" });
187
+ console.log(`${liveHtmlPath}`);
188
+ }
@@ -0,0 +1,226 @@
1
+ # Pizza Carousel MCP Deployment Guide
2
+
3
+ ## Overview
4
+ Your pizza carousel with dual styling is ready to deploy! The MCP server is already configured to serve both carousel variants that users can switch between using commands in ChatGPT.
5
+
6
+ ## What's Already Configured
7
+
8
+ ### MCP Server Tools
9
+ The server (`src/server.ts`) already has two tools configured:
10
+
11
+ 1. **`pizza-carousel-default`** - Shows carousel with OpenAI default styling
12
+ - Green color scheme (#10a37f)
13
+ - 12px button border radius
14
+ - System fonts
15
+
16
+ 2. **`pizza-carousel-custom`** - Shows carousel with custom branding
17
+ - Cyan color scheme (#00caff)
18
+ - 20px button border radius
19
+ - Custom fonts
20
+
21
+ ### File Structure
22
+ ```
23
+ assets/
24
+ ├── tokens/
25
+ │ ├── default-tokens.css ✅ Ready
26
+ │ └── custom-tokens.css ✅ Ready
27
+ ├── pizzaz-carousel-default.html ✅ Ready
28
+ └── pizzaz-carousel-custom.html ✅ Ready
29
+ ```
30
+
31
+ ### Static Asset Serving
32
+ The server is configured to serve static files from `/assets/` path, which means:
33
+ - Token CSS files are accessible at `/assets/tokens/default-tokens.css`
34
+ - Token CSS files are accessible at `/assets/tokens/custom-tokens.css`
35
+ - HTML files correctly reference tokens with relative path: `href="tokens/default-tokens.css"`
36
+
37
+ ## Deployment Steps
38
+
39
+ ### 1. Start the MCP Server Locally
40
+
41
+ ```bash
42
+ npm start
43
+ ```
44
+
45
+ Expected output:
46
+ ```
47
+ Pizzaz MCP server listening on http://localhost:8000
48
+ SSE stream: GET http://localhost:8000/mcp
49
+ Message post endpoint: POST http://localhost:8000/mcp/messages?sessionId=...
50
+ ```
51
+
52
+ ### 2. Test the Server Locally
53
+
54
+ Open a new terminal and test the endpoints:
55
+
56
+ ```bash
57
+ # Test that token files are accessible
58
+ curl http://localhost:8000/assets/tokens/default-tokens.css
59
+ curl http://localhost:8000/assets/tokens/custom-tokens.css
60
+
61
+ # Test that HTML files load
62
+ curl http://localhost:8000/assets/pizzaz-carousel-default.html
63
+ curl http://localhost:8000/assets/pizzaz-carousel-custom.html
64
+ ```
65
+
66
+ All should return the file contents without 404 errors.
67
+
68
+ ### 3. Configure ChatGPT to Use Your MCP Server
69
+
70
+ You need to add your MCP server to ChatGPT's configuration:
71
+
72
+ 1. Open ChatGPT settings
73
+ 2. Navigate to MCP servers configuration
74
+ 3. Add your server with the URL: `http://localhost:8000/mcp`
75
+
76
+ Or if deploying to a cloud service (like Render.com):
77
+ - Use the public URL provided by your hosting service
78
+ - Example: `https://your-app-name.onrender.com/mcp`
79
+
80
+ ### 4. Deploy to Render.com (Optional)
81
+
82
+ Your `render.yaml` is already configured. To deploy:
83
+
84
+ 1. **Push to GitHub:**
85
+ ```bash
86
+ git add .
87
+ git commit -m "Add pizza carousel with dual styling"
88
+ git push origin main
89
+ ```
90
+
91
+ 2. **Connect to Render:**
92
+ - Go to https://render.com
93
+ - Create new Web Service
94
+ - Connect your GitHub repository
95
+ - Render will automatically detect `render.yaml` and deploy
96
+
97
+ 3. **Get your public URL:**
98
+ - Render will provide a URL like: `https://pizzaz-mcp.onrender.com`
99
+ - Use this URL in ChatGPT: `https://pizzaz-mcp.onrender.com/mcp`
100
+
101
+ ## How Users Will Interact in ChatGPT
102
+
103
+ Once deployed, users can interact with your pizza carousel in ChatGPT:
104
+
105
+ ### Example User Commands:
106
+
107
+ **Show default styled carousel:**
108
+ ```
109
+ Show me the pizza carousel with default styling
110
+ ```
111
+ or
112
+ ```
113
+ Use the pizza-carousel-default tool
114
+ ```
115
+
116
+ **Show custom styled carousel:**
117
+ ```
118
+ Show me the pizza carousel with custom branding
119
+ ```
120
+ or
121
+ ```
122
+ Use the pizza-carousel-custom tool
123
+ ```
124
+
125
+ **Switch between styles:**
126
+ ```
127
+ Now show it with custom styling instead
128
+ ```
129
+
130
+ ### What Happens:
131
+ 1. User types a command in ChatGPT
132
+ 2. ChatGPT calls your MCP server tool (`pizza-carousel-default` or `pizza-carousel-custom`)
133
+ 3. Server returns the HTML widget with the appropriate token file imported
134
+ 4. ChatGPT renders the carousel in the conversation
135
+ 5. User sees the styled carousel and can interact with it (hover, scroll)
136
+
137
+ ## Verification Checklist
138
+
139
+ Before deploying, verify:
140
+
141
+ - [x] Both HTML files exist in `assets/` directory
142
+ - [x] Both token CSS files exist in `assets/tokens/` directory
143
+ - [x] HTML files correctly reference token files with relative paths
144
+ - [x] MCP server has both tools configured
145
+ - [x] Server serves static assets from `/assets/` path
146
+ - [ ] Server starts without errors (`npm start`)
147
+ - [ ] Token files are accessible via HTTP
148
+ - [ ] HTML files are accessible via HTTP
149
+ - [ ] ChatGPT can connect to your MCP server
150
+ - [ ] Both carousel tools work in ChatGPT
151
+
152
+ ## Testing in ChatGPT
153
+
154
+ After deployment, test both tools:
155
+
156
+ 1. **Test default carousel:**
157
+ - Ask: "Show me the pizza carousel with default styling"
158
+ - Verify: Green buttons, 12px border radius
159
+ - Check: All 5 pizzas display correctly
160
+ - Test: Hover effects and scrolling work
161
+
162
+ 2. **Test custom carousel:**
163
+ - Ask: "Show me the pizza carousel with custom branding"
164
+ - Verify: Cyan buttons, 20px border radius
165
+ - Check: All 5 pizzas display correctly
166
+ - Test: Hover effects and scrolling work
167
+
168
+ 3. **Test switching:**
169
+ - Ask: "Now show the default version"
170
+ - Verify: Carousel switches to default styling
171
+ - Ask: "Now show the custom version"
172
+ - Verify: Carousel switches to custom styling
173
+
174
+ ## Troubleshooting
175
+
176
+ ### Token files not loading (404 errors)
177
+ **Problem:** HTML shows unstyled content
178
+ **Solution:**
179
+ - Verify token files exist in `assets/tokens/`
180
+ - Check server logs for 404 errors
181
+ - Ensure server is serving static assets from `/assets/` path
182
+
183
+ ### Styles not applying
184
+ **Problem:** Carousel appears but with wrong colors
185
+ **Solution:**
186
+ - Open browser DevTools in ChatGPT
187
+ - Check Network tab for CSS file loading
188
+ - Verify CSS variables are defined in token files
189
+ - Check that HTML uses `var(--variable-name)` syntax
190
+
191
+ ### Server won't start
192
+ **Problem:** `npm start` fails
193
+ **Solution:**
194
+ - Check port 8000 is not already in use
195
+ - Verify all dependencies are installed: `npm install`
196
+ - Check for TypeScript errors in `src/server.ts`
197
+
198
+ ### ChatGPT can't connect
199
+ **Problem:** MCP server not accessible from ChatGPT
200
+ **Solution:**
201
+ - Verify server is running and accessible
202
+ - Check firewall settings
203
+ - If using localhost, ensure ChatGPT can access local servers
204
+ - If deployed, verify public URL is correct
205
+
206
+ ## Next Steps
207
+
208
+ 1. ✅ **Verification Complete** - All automated checks passed
209
+ 2. ⏳ **Local Testing** - Start server and test locally
210
+ 3. ⏳ **Deploy** - Push to GitHub and deploy to Render
211
+ 4. ⏳ **ChatGPT Integration** - Configure ChatGPT to use your MCP server
212
+ 5. ⏳ **User Testing** - Test both carousel variants in ChatGPT
213
+
214
+ ## Summary
215
+
216
+ Your pizza carousel implementation is complete and ready for deployment! The MCP server is configured to serve both styling variants, and users will be able to switch between them using natural language commands in ChatGPT.
217
+
218
+ **Key Features:**
219
+ - ✅ Two distinct styling themes (default and custom)
220
+ - ✅ Design token system for easy theming
221
+ - ✅ All 5 pizzas with complete data
222
+ - ✅ Interactive hover effects and smooth scrolling
223
+ - ✅ MCP server tools configured and ready
224
+ - ✅ Static asset serving for token CSS files
225
+
226
+ **Ready to deploy!** 🚀
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "pizzaz-mcp",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Pizzaz MCP server — themed pizza UI widgets for ChatGPT Apps SDK, deployed on Render",
6
+ "scripts": {
7
+ "dev": "vite --config vite.config.mts",
8
+ "build": "tsx ./build-all.mts",
9
+ "serve": "serve -s ./assets -p 4444 --cors",
10
+ "start": "tsx server/server.ts",
11
+ "tsc": "tsc -b"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.0.0",
15
+ "@openai/apps-sdk-ui": "^0.2.1",
16
+ "@types/react": "^19.1.12",
17
+ "@types/react-dom": "^19.1.9",
18
+ "clsx": "^2.1.1",
19
+ "embla-carousel": "^8.0.0",
20
+ "embla-carousel-react": "^8.0.0",
21
+ "framer-motion": "^12.23.12",
22
+ "lucide-react": "^0.536.0",
23
+ "react": "^19.1.1",
24
+ "react-dom": "^19.1.1",
25
+ "react-router-dom": "^7.8.2",
26
+ "zod": "^3.23.8"
27
+ },
28
+ "devDependencies": {
29
+ "@tailwindcss/vite": "^4.1.11",
30
+ "@types/node": "^24.3.0",
31
+ "@vitejs/plugin-react": "^4.5.2",
32
+ "autoprefixer": "10.4.21",
33
+ "fast-glob": "^3.3.3",
34
+ "postcss": "8.5.6",
35
+ "serve": "^14.2.4",
36
+ "tailwindcss": "4.1.11",
37
+ "tsx": "^4.20.4",
38
+ "typescript": "^5.9.2",
39
+ "vite": "^7.1.1"
40
+ }
41
+ }
package/render.yaml ADDED
@@ -0,0 +1,12 @@
1
+ services:
2
+ - type: web
3
+ name: pizzaz-mcp
4
+ runtime: node
5
+ plan: free
6
+ buildCommand: npm install && BASE_URL=https://pizzaz-mcp.onrender.com npm run build
7
+ startCommand: npm start
8
+ envVars:
9
+ - key: NODE_ENV
10
+ value: production
11
+ - key: PORT
12
+ value: 10000