@mcpjam/inspector 0.8.2 → 0.9.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.
Files changed (180) hide show
  1. package/README.md +24 -6
  2. package/bin/start.js +78 -66
  3. package/dist/client/assets/index-C4nE74q5.css +1 -0
  4. package/dist/client/assets/index-CcfG-2LO.js +357 -0
  5. package/dist/client/assets/index-CcfG-2LO.js.map +1 -0
  6. package/dist/client/catalyst.png +0 -0
  7. package/dist/client/index.html +14 -0
  8. package/dist/main/main.cjs +1330 -0
  9. package/dist/preload/preload.js +26 -0
  10. package/dist/renderer/assets/index-CYiU4_x2.css +1 -0
  11. package/dist/renderer/assets/index-woGCpEdp.js +356 -0
  12. package/dist/renderer/catalyst.png +0 -0
  13. package/dist/renderer/demo_1.png +0 -0
  14. package/dist/renderer/demo_2.png +0 -0
  15. package/dist/renderer/demo_3.png +0 -0
  16. package/dist/renderer/file.svg +1 -0
  17. package/dist/renderer/globe.svg +1 -0
  18. package/dist/renderer/index.html +14 -0
  19. package/dist/renderer/mcp.svg +1 -0
  20. package/dist/renderer/mcp_jam.svg +12 -0
  21. package/dist/renderer/mcp_jam_dark.png +0 -0
  22. package/dist/renderer/mcp_jam_light.png +0 -0
  23. package/dist/renderer/next.svg +1 -0
  24. package/dist/renderer/vercel.svg +1 -0
  25. package/dist/renderer/window.svg +1 -0
  26. package/dist/server/index.js +1101 -0
  27. package/dist/server/index.js.map +1 -0
  28. package/package.json +32 -17
  29. package/.next/BUILD_ID +0 -1
  30. package/.next/app-build-manifest.json +0 -89
  31. package/.next/app-path-routes-manifest.json +0 -13
  32. package/.next/build-manifest.json +0 -33
  33. package/.next/cache/.previewinfo +0 -1
  34. package/.next/cache/.rscinfo +0 -1
  35. package/.next/cache/.tsbuildinfo +0 -1
  36. package/.next/cache/eslint/.cache_1pr0xfn +0 -1
  37. package/.next/cache/webpack/client-production/0.pack +0 -0
  38. package/.next/cache/webpack/client-production/index.pack +0 -0
  39. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  40. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  41. package/.next/cache/webpack/server-production/0.pack +0 -0
  42. package/.next/cache/webpack/server-production/index.pack +0 -0
  43. package/.next/diagnostics/build-diagnostics.json +0 -6
  44. package/.next/diagnostics/framework.json +0 -1
  45. package/.next/export-marker.json +0 -6
  46. package/.next/images-manifest.json +0 -57
  47. package/.next/next-minimal-server.js.nft.json +0 -1
  48. package/.next/next-server.js.nft.json +0 -1
  49. package/.next/package.json +0 -1
  50. package/.next/prerender-manifest.json +0 -41
  51. package/.next/react-loadable-manifest.json +0 -1
  52. package/.next/required-server-files.json +0 -318
  53. package/.next/routes-manifest.json +0 -65
  54. package/.next/server/app/_not-found/page.js +0 -2
  55. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  56. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  57. package/.next/server/app/api/mcp/chat/route.js +0 -45
  58. package/.next/server/app/api/mcp/chat/route.js.nft.json +0 -1
  59. package/.next/server/app/api/mcp/chat/route_client-reference-manifest.js +0 -1
  60. package/.next/server/app/api/mcp/connect/route.js +0 -1
  61. package/.next/server/app/api/mcp/connect/route.js.nft.json +0 -1
  62. package/.next/server/app/api/mcp/connect/route_client-reference-manifest.js +0 -1
  63. package/.next/server/app/api/mcp/prompts/get/route.js +0 -1
  64. package/.next/server/app/api/mcp/prompts/get/route.js.nft.json +0 -1
  65. package/.next/server/app/api/mcp/prompts/get/route_client-reference-manifest.js +0 -1
  66. package/.next/server/app/api/mcp/prompts/list/route.js +0 -1
  67. package/.next/server/app/api/mcp/prompts/list/route.js.nft.json +0 -1
  68. package/.next/server/app/api/mcp/prompts/list/route_client-reference-manifest.js +0 -1
  69. package/.next/server/app/api/mcp/resources/list/route.js +0 -1
  70. package/.next/server/app/api/mcp/resources/list/route.js.nft.json +0 -1
  71. package/.next/server/app/api/mcp/resources/list/route_client-reference-manifest.js +0 -1
  72. package/.next/server/app/api/mcp/resources/read/route.js +0 -1
  73. package/.next/server/app/api/mcp/resources/read/route.js.nft.json +0 -1
  74. package/.next/server/app/api/mcp/resources/read/route_client-reference-manifest.js +0 -1
  75. package/.next/server/app/api/mcp/tools/route.js +0 -21
  76. package/.next/server/app/api/mcp/tools/route.js.nft.json +0 -1
  77. package/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +0 -1
  78. package/.next/server/app/favicon.ico/route.js +0 -1
  79. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  80. package/.next/server/app/favicon.ico.body +0 -0
  81. package/.next/server/app/favicon.ico.meta +0 -1
  82. package/.next/server/app/oauth/callback/page.js +0 -2
  83. package/.next/server/app/oauth/callback/page.js.nft.json +0 -1
  84. package/.next/server/app/oauth/callback/page_client-reference-manifest.js +0 -1
  85. package/.next/server/app/page.js +0 -16
  86. package/.next/server/app/page.js.nft.json +0 -1
  87. package/.next/server/app/page_client-reference-manifest.js +0 -1
  88. package/.next/server/app-paths-manifest.json +0 -13
  89. package/.next/server/chunks/175.js +0 -8
  90. package/.next/server/chunks/260.js +0 -82
  91. package/.next/server/chunks/546.js +0 -1
  92. package/.next/server/chunks/548.js +0 -6
  93. package/.next/server/chunks/55.js +0 -1
  94. package/.next/server/chunks/985.js +0 -22
  95. package/.next/server/functions-config-manifest.json +0 -4
  96. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  97. package/.next/server/middleware-build-manifest.js +0 -1
  98. package/.next/server/middleware-manifest.json +0 -6
  99. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  100. package/.next/server/next-font-manifest.js +0 -1
  101. package/.next/server/next-font-manifest.json +0 -1
  102. package/.next/server/pages/500.html +0 -1
  103. package/.next/server/pages/_app.js +0 -1
  104. package/.next/server/pages/_app.js.nft.json +0 -1
  105. package/.next/server/pages/_document.js +0 -1
  106. package/.next/server/pages/_document.js.nft.json +0 -1
  107. package/.next/server/pages/_error.js +0 -19
  108. package/.next/server/pages/_error.js.nft.json +0 -1
  109. package/.next/server/pages-manifest.json +0 -5
  110. package/.next/server/server-reference-manifest.js +0 -1
  111. package/.next/server/server-reference-manifest.json +0 -1
  112. package/.next/server/webpack-runtime.js +0 -1
  113. package/.next/static/SigynMKeJ0KJs0Nc9i0E-/_buildManifest.js +0 -1
  114. package/.next/static/SigynMKeJ0KJs0Nc9i0E-/_ssgManifest.js +0 -1
  115. package/.next/static/chunks/214-cc4c35d88f2695ed.js +0 -1
  116. package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +0 -1
  117. package/.next/static/chunks/574-9365237f47ed3a68.js +0 -1
  118. package/.next/static/chunks/866-04c19dda4c52f2bf.js +0 -1
  119. package/.next/static/chunks/964-eda38e26c0391a47.js +0 -1
  120. package/.next/static/chunks/app/_not-found/page-8601c49989b0be94.js +0 -1
  121. package/.next/static/chunks/app/api/mcp/chat/route-0341498a8bf5f2da.js +0 -1
  122. package/.next/static/chunks/app/api/mcp/connect/route-0341498a8bf5f2da.js +0 -1
  123. package/.next/static/chunks/app/api/mcp/prompts/get/route-0341498a8bf5f2da.js +0 -1
  124. package/.next/static/chunks/app/api/mcp/prompts/list/route-0341498a8bf5f2da.js +0 -1
  125. package/.next/static/chunks/app/api/mcp/resources/list/route-0341498a8bf5f2da.js +0 -1
  126. package/.next/static/chunks/app/api/mcp/resources/read/route-0341498a8bf5f2da.js +0 -1
  127. package/.next/static/chunks/app/api/mcp/tools/route-0341498a8bf5f2da.js +0 -1
  128. package/.next/static/chunks/app/layout-9e8115d4bf656fa0.js +0 -1
  129. package/.next/static/chunks/app/oauth/callback/page-cf6cb1ac31175f40.js +0 -1
  130. package/.next/static/chunks/app/page-b0e3ed6257734413.js +0 -1
  131. package/.next/static/chunks/framework-7c95b8e5103c9e90.js +0 -1
  132. package/.next/static/chunks/main-app-4e9e28316818cdde.js +0 -1
  133. package/.next/static/chunks/main-bbdafee21a7bd1d6.js +0 -1
  134. package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +0 -1
  135. package/.next/static/chunks/pages/_error-03529f2c21436739.js +0 -1
  136. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  137. package/.next/static/chunks/webpack-cdfccaf38062dd25.js +0 -1
  138. package/.next/static/css/1e852d83e9c1d0c6.css +0 -1
  139. package/.next/static/css/f30152c0704fba31.css +0 -1
  140. package/.next/static/css/fe751fdbe975e9ca.css +0 -1
  141. package/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  142. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  143. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  144. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  145. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  146. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  147. package/.next/trace +0 -35
  148. package/.next/types/app/api/mcp/chat/route.ts +0 -347
  149. package/.next/types/app/api/mcp/connect/route.ts +0 -347
  150. package/.next/types/app/api/mcp/prompts/get/route.ts +0 -347
  151. package/.next/types/app/api/mcp/prompts/list/route.ts +0 -347
  152. package/.next/types/app/api/mcp/resources/list/route.ts +0 -347
  153. package/.next/types/app/api/mcp/resources/read/route.ts +0 -347
  154. package/.next/types/app/api/mcp/tools/route.ts +0 -347
  155. package/.next/types/app/layout.ts +0 -84
  156. package/.next/types/app/oauth/callback/page.ts +0 -84
  157. package/.next/types/app/page.ts +0 -84
  158. package/.next/types/cache-life.d.ts +0 -141
  159. package/.next/types/package.json +0 -1
  160. package/next.config.ts +0 -7
  161. /package/{public → dist/client}/claude_logo.png +0 -0
  162. /package/{public → dist/client}/demo_1.png +0 -0
  163. /package/{public → dist/client}/demo_2.png +0 -0
  164. /package/{public → dist/client}/demo_3.png +0 -0
  165. /package/{public → dist/client}/file.svg +0 -0
  166. /package/{public → dist/client}/globe.svg +0 -0
  167. /package/{public → dist/client}/mcp.svg +0 -0
  168. /package/{public → dist/client}/mcp_jam.svg +0 -0
  169. /package/{public → dist/client}/mcp_jam_dark.png +0 -0
  170. /package/{public → dist/client}/mcp_jam_light.png +0 -0
  171. /package/{public → dist/client}/next.svg +0 -0
  172. /package/{public → dist/client}/ollama_dark.png +0 -0
  173. /package/{public → dist/client}/ollama_logo.svg +0 -0
  174. /package/{public → dist/client}/openai_logo.png +0 -0
  175. /package/{public → dist/client}/vercel.svg +0 -0
  176. /package/{public → dist/client}/window.svg +0 -0
  177. /package/{.next/static/media/claude_logo.d33b25b0.png → dist/renderer/claude_logo.png} +0 -0
  178. /package/{.next/static/media/ollama_dark.9af45ac0.png → dist/renderer/ollama_dark.png} +0 -0
  179. /package/{.next/static/media/ollama_logo.9f08a95b.svg → dist/renderer/ollama_logo.svg} +0 -0
  180. /package/{.next/static/media/openai_logo.3f83154a.png → dist/renderer/openai_logo.png} +0 -0
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="./public/mcp_jam_dark.png">
5
- <source media="(prefers-color-scheme: light)" srcset="./public/mcp_jam_light.png">
6
- <img width="250" alt="MCPJam Inspector V1 logo" src="./public/mcp_jam_light.png">
4
+ <source media="(prefers-color-scheme: dark)" srcset="./client/public/mcp_jam_dark.png">
5
+ <source media="(prefers-color-scheme: light)" srcset="./client/public/mcp_jam_light.png">
6
+ <img width="250" alt="MCPJam Inspector V1 logo" src="./client/public/mcp_jam_light.png">
7
7
  </picture>
8
8
 
9
9
  <br/>
@@ -14,6 +14,10 @@
14
14
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
15
15
  [![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2.svg?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/JEnDtz8X6z)
16
16
 
17
+ <a href="https://www.opencoreventures.com/" target="_blank">
18
+ <img width="150" alt="Catalyst Logo" src="./client/public/catalyst.png" style="margin: -10px">
19
+ </a>
20
+
17
21
  </div>
18
22
 
19
23
  A developer tool for testing, debugging Model Context Protocol (MCP) servers. Test whether or not you built your MCP server correctly. The project is open source and fully compliant to the MCP spec.
@@ -30,19 +34,19 @@ A developer tool for testing, debugging Model Context Protocol (MCP) servers. Te
30
34
 
31
35
  ## 📸 Screenshots
32
36
 
33
- <img alt="MCPJam Inspector Demo" src="./public/demo_1.png">
37
+ <img alt="MCPJam Inspector Demo" src="./client/public/demo_1.png">
34
38
 
35
39
  <details>
36
40
  <summary><strong>LLM Playground</strong></summary>
37
41
 
38
- <img alt="LLM Chat Demo" src="./public/demo_2.png">
42
+ <img alt="LLM Chat Demo" src="./client/public/demo_2.png">
39
43
 
40
44
  </details>
41
45
 
42
46
  <details>
43
47
  <summary><strong>Connect to any server</strong></summary>
44
48
 
45
- <img alt="MCPJam Connection Demo" src="./public/demo_3.png">
49
+ <img alt="MCPJam Connection Demo" src="./client/public/demo_3.png">
46
50
 
47
51
  </details>
48
52
 
@@ -64,6 +68,20 @@ npx @mcpjam/inspector@latest --port 4000
64
68
  npx @mcpjam/inspector@latest --ollama llama3.2
65
69
  ```
66
70
 
71
+ ## 🐳 Docker
72
+
73
+ Run MCPJam Inspector using Docker:
74
+
75
+ ```bash
76
+ # Run the latest version from Docker Hub
77
+ docker run -p 3001:3001 mcpjam/mcp-inspector:latest
78
+
79
+ # Or run in the background
80
+ docker run -d -p 3001:3001 --name mcp-inspector mcpjam/mcp-inspector:latest
81
+ ```
82
+
83
+ The application will be available at `http://localhost:3001`.
84
+
67
85
  ## Requirements
68
86
 
69
87
  [![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg?style=for-the-badge&logo=node.js)](https://nodejs.org/)
package/bin/start.js CHANGED
@@ -5,6 +5,7 @@ import { spawn } from "child_process";
5
5
  import { fileURLToPath } from "url";
6
6
  import { createServer } from "net";
7
7
  import { execSync } from "child_process";
8
+ import { existsSync } from "fs";
8
9
 
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
11
 
@@ -61,7 +62,7 @@ function logError(message) {
61
62
  function logStep(step, message) {
62
63
  log(
63
64
  `\n${colors.cyan}${colors.bright}[${step}]${colors.reset} ${message}`,
64
- colors.white,
65
+ colors.white
65
66
  );
66
67
  }
67
68
 
@@ -87,7 +88,7 @@ function logBox(content, title = null) {
87
88
  title +
88
89
  " ".repeat(width - title.length - titlePadding) +
89
90
  "│",
90
- colors.cyan,
91
+ colors.cyan
91
92
  );
92
93
  log("├" + "─".repeat(width) + "┤", colors.cyan);
93
94
  }
@@ -121,14 +122,21 @@ function isPortAvailable(port) {
121
122
  });
122
123
  }
123
124
 
124
- async function findAvailablePort(startPort = 3000, maxPort = 3100) {
125
+ async function findAvailablePort(startPort = 3000, maxPort = 65535) {
126
+ logProgress(`Scanning for available ports starting from ${startPort}...`);
127
+
125
128
  for (let port = startPort; port <= maxPort; port++) {
126
129
  if (await isPortAvailable(port)) {
127
130
  return port;
128
131
  }
132
+
133
+ // Show progress every 10 ports to avoid spam
134
+ if (port % 10 === 0) {
135
+ logProgress(`Checked port ${port}, continuing search...`);
136
+ }
129
137
  }
130
138
  throw new Error(
131
- `No available ports found between ${startPort} and ${maxPort}`,
139
+ `No available ports found between ${startPort} and ${maxPort}`
132
140
  );
133
141
  }
134
142
 
@@ -157,41 +165,17 @@ function spawnPromise(command, args, options) {
157
165
  });
158
166
  }
159
167
 
160
- async function showWelcomeMessage() {
161
- console.clear();
162
- log(MCP_BANNER, colors.cyan);
163
-
164
- logDivider();
165
-
166
- const welcomeText = `Welcome to the MCP Inspector!
167
- This tool helps you explore and interact with Model Context Protocol servers.
168
- Get ready to discover the power of MCP integration.`;
169
-
170
- logBox(welcomeText, "🎯 Getting Started");
171
-
172
- logDivider();
173
- }
174
-
175
- async function showServerInfo(port) {
176
- const serverInfo = `Server URL: http://localhost:${port}
177
- Environment: Production
178
- Framework: Next.js
179
- Status: Starting up...`;
180
-
181
- logBox(serverInfo, "🌐 Server Configuration");
182
- }
183
-
184
168
  async function showSuccessMessage(port) {
185
169
  logDivider();
186
170
 
187
171
  const successText = `🎉 MCP Inspector is now running successfully!
188
172
 
189
- 📱 Access your application at: ${colors.bright}${colors.green}http://localhost:${port}${colors.reset}
190
- 🔧 Server is ready to handle MCP connections
173
+ 📱 Access your application at: http://localhost:${port}
174
+ 🔧 Unified server ready to handle MCP connections
191
175
  📊 Monitor your MCP tools and resources
192
176
  💬 Start chatting with your MCP-enabled AI
193
177
 
194
- ${colors.dim}Press Ctrl+C to stop the server${colors.reset}`;
178
+ Press Ctrl+C to stop the server`;
195
179
 
196
180
  logBox(successText, "🚀 Ready to Go!");
197
181
 
@@ -273,7 +257,7 @@ async function openTerminalWithMultipleCommands(commands, title) {
273
257
  await spawnPromise(
274
258
  terminalCmd[0],
275
259
  [...terminalCmd.slice(1), "bash", "-c", fullCommand],
276
- { echoOutput: false },
260
+ { echoOutput: false }
277
261
  );
278
262
  }
279
263
  }
@@ -287,11 +271,11 @@ async function setupOllamaInSingleTerminal(model) {
287
271
 
288
272
  await openTerminalWithMultipleCommands(
289
273
  commands,
290
- `Ollama: Pull ${model} & Serve`,
274
+ `Ollama: Pull ${model} & Serve`
291
275
  );
292
276
  logSuccess("Ollama pull and serve started in same terminal");
293
277
  logProgress(
294
- "Waiting for model download to complete and server to start...",
278
+ "Waiting for model download to complete and server to start..."
295
279
  );
296
280
 
297
281
  // Wait a bit for the model pull to start
@@ -320,7 +304,7 @@ async function setupOllamaInSingleTerminal(model) {
320
304
  await delay(10000); // Wait 10 seconds between checks
321
305
  if (i % 3 === 0) {
322
306
  logProgress(
323
- `Still waiting for model ${model} to be ready and server to start...`,
307
+ `Still waiting for model ${model} to be ready and server to start...`
324
308
  );
325
309
  }
326
310
  }
@@ -329,7 +313,7 @@ async function setupOllamaInSingleTerminal(model) {
329
313
  logSuccess(`Model ${model} is ready and Ollama server is running`);
330
314
  } else {
331
315
  logWarning(
332
- `Setup may still be in progress. Please check the terminal window.`,
316
+ `Setup may still be in progress. Please check the terminal window.`
333
317
  );
334
318
  }
335
319
  } catch (error) {
@@ -339,7 +323,10 @@ async function setupOllamaInSingleTerminal(model) {
339
323
  }
340
324
 
341
325
  async function main() {
342
- await showWelcomeMessage();
326
+ // Show MCP banner at startup
327
+ console.clear();
328
+ log(MCP_BANNER, colors.cyan);
329
+ logDivider();
343
330
 
344
331
  // Parse command line arguments
345
332
  const args = process.argv.slice(2);
@@ -363,7 +350,6 @@ async function main() {
363
350
  if (parsingFlags && arg === "--port" && i + 1 < args.length) {
364
351
  const port = args[++i];
365
352
  envVars.PORT = port;
366
- envVars.NEXT_PUBLIC_BASE_URL = `http://localhost:${port}`;
367
353
  envVars.BASE_URL = `http://localhost:${port}`;
368
354
  continue;
369
355
  }
@@ -390,7 +376,7 @@ async function main() {
390
376
  if (!isOllamaInstalled) {
391
377
  logError("Ollama is not installed. Please install Ollama first:");
392
378
  logInfo(
393
- "Visit https://ollama.ai/download to download and install Ollama",
379
+ "Visit https://ollama.ai/download to download and install Ollama"
394
380
  );
395
381
  process.exit(1);
396
382
  }
@@ -415,35 +401,51 @@ async function main() {
415
401
  // Apply parsed environment variables to process.env first
416
402
  Object.assign(process.env, envVars);
417
403
 
418
- // Get requested port and find available port
404
+ // Port discovery and configuration
419
405
  const requestedPort = parseInt(process.env.PORT ?? "3000", 10);
420
406
  let PORT;
421
407
 
422
408
  try {
423
- logStep("0", "Checking port availability...");
424
-
425
- if (await isPortAvailable(requestedPort)) {
426
- PORT = requestedPort.toString();
427
- logSuccess(`Port ${requestedPort} is available`);
409
+ // Check if user explicitly set a port via --port flag
410
+ const hasExplicitPort = envVars.PORT !== undefined;
411
+
412
+ if (hasExplicitPort) {
413
+ logInfo(`Using explicitly requested port: ${requestedPort}`);
414
+ if (await isPortAvailable(requestedPort)) {
415
+ PORT = requestedPort.toString();
416
+ logSuccess(`Port ${requestedPort} is available and ready`);
417
+ } else {
418
+ logError(`Explicitly requested port ${requestedPort} is not available`);
419
+ logInfo(
420
+ "Use a different port with --port <number> or let the system find one automatically"
421
+ );
422
+ throw new Error(`Port ${requestedPort} is already in use`);
423
+ }
428
424
  } else {
429
- logWarning(`Port ${requestedPort} is in use, finding alternative...`);
430
- const availablePort = await findAvailablePort(requestedPort + 1);
431
- PORT = availablePort.toString();
432
- logSuccess(`Using available port ${availablePort}`);
433
-
434
- // Update environment variables with the new port
435
- envVars.PORT = PORT;
436
- envVars.NEXT_PUBLIC_BASE_URL = `http://localhost:${PORT}`;
437
- envVars.BASE_URL = `http://localhost:${PORT}`;
438
- Object.assign(process.env, envVars);
425
+ // Dynamic port discovery
426
+ logInfo("No specific port requested, using dynamic port discovery");
427
+ if (await isPortAvailable(requestedPort)) {
428
+ PORT = requestedPort.toString();
429
+ logSuccess(`Default port ${requestedPort} is available`);
430
+ } else {
431
+ logWarning(
432
+ `Default port ${requestedPort} is in use, searching for next available port...`
433
+ );
434
+ const availablePort = await findAvailablePort(requestedPort + 1);
435
+ PORT = availablePort.toString();
436
+ logSuccess(`Found available port: ${availablePort}`);
437
+ }
439
438
  }
439
+
440
+ // Update environment variables with the final port
441
+ envVars.PORT = PORT;
442
+ envVars.BASE_URL = `http://localhost:${PORT}`;
443
+ Object.assign(process.env, envVars);
440
444
  } catch (error) {
441
- logError(`Failed to find available port: ${error.message}`);
445
+ logError(`Port configuration failed: ${error.message}`);
442
446
  throw error;
443
447
  }
444
448
 
445
- await showServerInfo(PORT);
446
-
447
449
  const abort = new AbortController();
448
450
 
449
451
  let cancelled = false;
@@ -459,19 +461,29 @@ async function main() {
459
461
  });
460
462
 
461
463
  try {
462
- logStep("1", "Initializing Next.js production server");
463
- await delay(1000);
464
+ const distServerPath = resolve(projectRoot, "dist", "server", "index.js");
464
465
 
465
- logStep("2", "Building application for production");
466
- logProgress("This may take a few moments...");
467
- await delay(500);
466
+ // Check if production build exists
467
+ if (!existsSync(distServerPath)) {
468
+ logProgress("Building client and server for production...");
468
469
 
469
- logStep("3", "Starting server on port " + PORT);
470
+ await spawnPromise("npm", ["run", "build"], {
471
+ env: process.env,
472
+ cwd: projectRoot,
473
+ signal: abort.signal,
474
+ echoOutput: false,
475
+ });
476
+
477
+ logSuccess("Build completed successfully");
478
+ await delay(500);
479
+ } else {
480
+ await delay(500);
481
+ }
470
482
 
471
- await spawnPromise("npm", ["run", "start"], {
483
+ await spawnPromise("node", [distServerPath], {
472
484
  env: {
473
485
  ...process.env,
474
- ...envVars,
486
+ NODE_ENV: "production",
475
487
  PORT: PORT,
476
488
  },
477
489
  cwd: projectRoot,