@mcpjam/inspector 0.8.2 → 0.9.1

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 +82 -69
  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
  }
@@ -109,26 +110,34 @@ function isPortAvailable(port) {
109
110
  const server = createServer();
110
111
 
111
112
  server.listen(port, () => {
112
- server.once("close", () => {
113
+ // Port is available, close the server and resolve true
114
+ server.close(() => {
113
115
  resolve(true);
114
116
  });
115
- server.close();
116
117
  });
117
118
 
118
119
  server.on("error", () => {
120
+ // Port is not available
119
121
  resolve(false);
120
122
  });
121
123
  });
122
124
  }
123
125
 
124
- async function findAvailablePort(startPort = 3000, maxPort = 3100) {
126
+ async function findAvailablePort(startPort = 3000, maxPort = 65535) {
127
+ logProgress(`Scanning for available ports starting from ${startPort}...`);
128
+
125
129
  for (let port = startPort; port <= maxPort; port++) {
126
130
  if (await isPortAvailable(port)) {
127
131
  return port;
128
132
  }
133
+
134
+ // Show progress every 10 ports to avoid spam
135
+ if (port % 10 === 0) {
136
+ logProgress(`Checked port ${port}, continuing search...`);
137
+ }
129
138
  }
130
139
  throw new Error(
131
- `No available ports found between ${startPort} and ${maxPort}`,
140
+ `No available ports found between ${startPort} and ${maxPort}`
132
141
  );
133
142
  }
134
143
 
@@ -157,41 +166,17 @@ function spawnPromise(command, args, options) {
157
166
  });
158
167
  }
159
168
 
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
169
  async function showSuccessMessage(port) {
185
170
  logDivider();
186
171
 
187
172
  const successText = `🎉 MCP Inspector is now running successfully!
188
173
 
189
- 📱 Access your application at: ${colors.bright}${colors.green}http://localhost:${port}${colors.reset}
190
- 🔧 Server is ready to handle MCP connections
174
+ 📱 Access your application at: http://localhost:${port}
175
+ 🔧 Unified server ready to handle MCP connections
191
176
  📊 Monitor your MCP tools and resources
192
177
  💬 Start chatting with your MCP-enabled AI
193
178
 
194
- ${colors.dim}Press Ctrl+C to stop the server${colors.reset}`;
179
+ Press Ctrl+C to stop the server`;
195
180
 
196
181
  logBox(successText, "🚀 Ready to Go!");
197
182
 
@@ -273,7 +258,7 @@ async function openTerminalWithMultipleCommands(commands, title) {
273
258
  await spawnPromise(
274
259
  terminalCmd[0],
275
260
  [...terminalCmd.slice(1), "bash", "-c", fullCommand],
276
- { echoOutput: false },
261
+ { echoOutput: false }
277
262
  );
278
263
  }
279
264
  }
@@ -287,11 +272,11 @@ async function setupOllamaInSingleTerminal(model) {
287
272
 
288
273
  await openTerminalWithMultipleCommands(
289
274
  commands,
290
- `Ollama: Pull ${model} & Serve`,
275
+ `Ollama: Pull ${model} & Serve`
291
276
  );
292
277
  logSuccess("Ollama pull and serve started in same terminal");
293
278
  logProgress(
294
- "Waiting for model download to complete and server to start...",
279
+ "Waiting for model download to complete and server to start..."
295
280
  );
296
281
 
297
282
  // Wait a bit for the model pull to start
@@ -320,7 +305,7 @@ async function setupOllamaInSingleTerminal(model) {
320
305
  await delay(10000); // Wait 10 seconds between checks
321
306
  if (i % 3 === 0) {
322
307
  logProgress(
323
- `Still waiting for model ${model} to be ready and server to start...`,
308
+ `Still waiting for model ${model} to be ready and server to start...`
324
309
  );
325
310
  }
326
311
  }
@@ -329,7 +314,7 @@ async function setupOllamaInSingleTerminal(model) {
329
314
  logSuccess(`Model ${model} is ready and Ollama server is running`);
330
315
  } else {
331
316
  logWarning(
332
- `Setup may still be in progress. Please check the terminal window.`,
317
+ `Setup may still be in progress. Please check the terminal window.`
333
318
  );
334
319
  }
335
320
  } catch (error) {
@@ -339,7 +324,10 @@ async function setupOllamaInSingleTerminal(model) {
339
324
  }
340
325
 
341
326
  async function main() {
342
- await showWelcomeMessage();
327
+ // Show MCP banner at startup
328
+ console.clear();
329
+ log(MCP_BANNER, colors.cyan);
330
+ logDivider();
343
331
 
344
332
  // Parse command line arguments
345
333
  const args = process.argv.slice(2);
@@ -363,7 +351,6 @@ async function main() {
363
351
  if (parsingFlags && arg === "--port" && i + 1 < args.length) {
364
352
  const port = args[++i];
365
353
  envVars.PORT = port;
366
- envVars.NEXT_PUBLIC_BASE_URL = `http://localhost:${port}`;
367
354
  envVars.BASE_URL = `http://localhost:${port}`;
368
355
  continue;
369
356
  }
@@ -390,7 +377,7 @@ async function main() {
390
377
  if (!isOllamaInstalled) {
391
378
  logError("Ollama is not installed. Please install Ollama first:");
392
379
  logInfo(
393
- "Visit https://ollama.ai/download to download and install Ollama",
380
+ "Visit https://ollama.ai/download to download and install Ollama"
394
381
  );
395
382
  process.exit(1);
396
383
  }
@@ -415,35 +402,51 @@ async function main() {
415
402
  // Apply parsed environment variables to process.env first
416
403
  Object.assign(process.env, envVars);
417
404
 
418
- // Get requested port and find available port
419
- const requestedPort = parseInt(process.env.PORT ?? "3000", 10);
405
+ // Port discovery and configuration
406
+ const requestedPort = parseInt(process.env.PORT ?? "6274", 10);
420
407
  let PORT;
421
408
 
422
409
  try {
423
- logStep("0", "Checking port availability...");
424
-
425
- if (await isPortAvailable(requestedPort)) {
426
- PORT = requestedPort.toString();
427
- logSuccess(`Port ${requestedPort} is available`);
410
+ // Check if user explicitly set a port via --port flag
411
+ const hasExplicitPort = envVars.PORT !== undefined;
412
+
413
+ if (hasExplicitPort) {
414
+ logInfo(`Using explicitly requested port: ${requestedPort}`);
415
+ if (await isPortAvailable(requestedPort)) {
416
+ PORT = requestedPort.toString();
417
+ logSuccess(`Port ${requestedPort} is available and ready`);
418
+ } else {
419
+ logError(`Explicitly requested port ${requestedPort} is not available`);
420
+ logInfo(
421
+ "Use a different port with --port <number> or let the system find one automatically"
422
+ );
423
+ throw new Error(`Port ${requestedPort} is already in use`);
424
+ }
428
425
  } 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);
426
+ // Dynamic port discovery
427
+ logInfo("No specific port requested, using dynamic port discovery");
428
+ if (await isPortAvailable(requestedPort)) {
429
+ PORT = requestedPort.toString();
430
+ logSuccess(`Default port ${requestedPort} is available`);
431
+ } else {
432
+ logWarning(
433
+ `Default port ${requestedPort} is in use, searching for next available port...`
434
+ );
435
+ const availablePort = await findAvailablePort(requestedPort + 1);
436
+ PORT = availablePort.toString();
437
+ logSuccess(`Found available port: ${availablePort}`);
438
+ }
439
439
  }
440
+
441
+ // Update environment variables with the final port
442
+ envVars.PORT = PORT;
443
+ envVars.BASE_URL = `http://localhost:${PORT}`;
444
+ Object.assign(process.env, envVars);
440
445
  } catch (error) {
441
- logError(`Failed to find available port: ${error.message}`);
446
+ logError(`Port configuration failed: ${error.message}`);
442
447
  throw error;
443
448
  }
444
449
 
445
- await showServerInfo(PORT);
446
-
447
450
  const abort = new AbortController();
448
451
 
449
452
  let cancelled = false;
@@ -459,19 +462,29 @@ async function main() {
459
462
  });
460
463
 
461
464
  try {
462
- logStep("1", "Initializing Next.js production server");
463
- await delay(1000);
465
+ const distServerPath = resolve(projectRoot, "dist", "server", "index.js");
464
466
 
465
- logStep("2", "Building application for production");
466
- logProgress("This may take a few moments...");
467
- await delay(500);
467
+ // Check if production build exists
468
+ if (!existsSync(distServerPath)) {
469
+ logProgress("Building client and server for production...");
468
470
 
469
- logStep("3", "Starting server on port " + PORT);
471
+ await spawnPromise("npm", ["run", "build"], {
472
+ env: process.env,
473
+ cwd: projectRoot,
474
+ signal: abort.signal,
475
+ echoOutput: false,
476
+ });
477
+
478
+ logSuccess("Build completed successfully");
479
+ await delay(500);
480
+ } else {
481
+ await delay(500);
482
+ }
470
483
 
471
- await spawnPromise("npm", ["run", "start"], {
484
+ await spawnPromise("node", [distServerPath], {
472
485
  env: {
473
486
  ...process.env,
474
- ...envVars,
487
+ NODE_ENV: "production",
475
488
  PORT: PORT,
476
489
  },
477
490
  cwd: projectRoot,