@modelcontextprotocol/inspector-client 0.14.3 → 0.16.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.
package/bin/client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import open from "open";
3
4
  import { join, dirname } from "path";
4
5
  import { fileURLToPath } from "url";
5
6
  import handler from "serve-handler";
@@ -39,19 +40,23 @@ const server = http.createServer((request, response) => {
39
40
  return handler(request, response, handlerOptions);
40
41
  });
41
42
 
42
- const port = process.env.PORT || 6274;
43
+ const port = parseInt(process.env.CLIENT_PORT || "6274", 10);
44
+ const host = process.env.HOST || "localhost";
43
45
  server.on("listening", () => {
44
- console.log(
45
- `🔍 MCP Inspector is up and running at http://127.0.0.1:${port} 🚀`,
46
- );
46
+ const url = process.env.INSPECTOR_URL || `http://${host}:${port}`;
47
+ console.log(`\n🚀 MCP Inspector is up and running at:\n ${url}\n`);
48
+ if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
49
+ console.log(`🌐 Opening browser...`);
50
+ open(url);
51
+ }
47
52
  });
48
53
  server.on("error", (err) => {
49
54
  if (err.message.includes(`EADDRINUSE`)) {
50
55
  console.error(
51
- `❌ MCP Inspector PORT IS IN USE at http://127.0.0.1:${port} ❌ `,
56
+ `❌ MCP Inspector PORT IS IN USE at http://${host}:${port} ❌ `,
52
57
  );
53
58
  } else {
54
59
  throw err;
55
60
  }
56
61
  });
57
- server.listen(port);
62
+ server.listen(port, host);
package/bin/start.js CHANGED
@@ -2,15 +2,202 @@
2
2
 
3
3
  import open from "open";
4
4
  import { resolve, dirname } from "path";
5
- import { spawnPromise } from "spawn-rx";
5
+ import { spawnPromise, spawn } from "spawn-rx";
6
6
  import { fileURLToPath } from "url";
7
+ import { randomBytes } from "crypto";
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const DEFAULT_MCP_PROXY_LISTEN_PORT = "6277";
9
11
 
10
12
  function delay(ms) {
11
13
  return new Promise((resolve) => setTimeout(resolve, ms, true));
12
14
  }
13
15
 
16
+ function getClientUrl(port, authDisabled, sessionToken, serverPort) {
17
+ const host = process.env.HOST || "localhost";
18
+ const baseUrl = `http://${host}:${port}`;
19
+
20
+ const params = new URLSearchParams();
21
+ if (serverPort && serverPort !== DEFAULT_MCP_PROXY_LISTEN_PORT) {
22
+ params.set("MCP_PROXY_PORT", serverPort);
23
+ }
24
+ if (!authDisabled) {
25
+ params.set("MCP_PROXY_AUTH_TOKEN", sessionToken);
26
+ }
27
+ return params.size > 0 ? `${baseUrl}/?${params.toString()}` : baseUrl;
28
+ }
29
+
30
+ async function startDevServer(serverOptions) {
31
+ const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort } =
32
+ serverOptions;
33
+ const serverCommand = "npx";
34
+ const serverArgs = ["tsx", "watch", "--clear-screen=false", "src/index.ts"];
35
+ const isWindows = process.platform === "win32";
36
+
37
+ const spawnOptions = {
38
+ cwd: resolve(__dirname, "../..", "server"),
39
+ env: {
40
+ ...process.env,
41
+ SERVER_PORT,
42
+ CLIENT_PORT,
43
+ MCP_PROXY_TOKEN: sessionToken,
44
+ MCP_ENV_VARS: JSON.stringify(envVars),
45
+ },
46
+ signal: abort.signal,
47
+ echoOutput: true,
48
+ };
49
+
50
+ // For Windows, we need to use stdin: 'ignore' to simulate < NUL
51
+ if (isWindows) {
52
+ spawnOptions.stdin = "ignore";
53
+ }
54
+
55
+ const server = spawn(serverCommand, serverArgs, spawnOptions);
56
+
57
+ // Give server time to start
58
+ const serverOk = await Promise.race([
59
+ new Promise((resolve) => {
60
+ server.subscribe({
61
+ complete: () => resolve(false),
62
+ error: () => resolve(false),
63
+ next: () => {}, // We're using echoOutput
64
+ });
65
+ }),
66
+ delay(3000).then(() => true),
67
+ ]);
68
+
69
+ return { server, serverOk };
70
+ }
71
+
72
+ async function startProdServer(serverOptions) {
73
+ const {
74
+ SERVER_PORT,
75
+ CLIENT_PORT,
76
+ sessionToken,
77
+ envVars,
78
+ abort,
79
+ command,
80
+ mcpServerArgs,
81
+ } = serverOptions;
82
+ const inspectorServerPath = resolve(
83
+ __dirname,
84
+ "../..",
85
+ "server",
86
+ "build",
87
+ "index.js",
88
+ );
89
+
90
+ const server = spawnPromise(
91
+ "node",
92
+ [
93
+ inspectorServerPath,
94
+ ...(command ? [`--env`, command] : []),
95
+ ...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []),
96
+ ],
97
+ {
98
+ env: {
99
+ ...process.env,
100
+ SERVER_PORT,
101
+ CLIENT_PORT,
102
+ MCP_PROXY_TOKEN: sessionToken,
103
+ MCP_ENV_VARS: JSON.stringify(envVars),
104
+ },
105
+ signal: abort.signal,
106
+ echoOutput: true,
107
+ },
108
+ );
109
+
110
+ // Make sure server started before starting client
111
+ const serverOk = await Promise.race([server, delay(2 * 1000)]);
112
+
113
+ return { server, serverOk };
114
+ }
115
+
116
+ async function startDevClient(clientOptions) {
117
+ const {
118
+ CLIENT_PORT,
119
+ SERVER_PORT,
120
+ authDisabled,
121
+ sessionToken,
122
+ abort,
123
+ cancelled,
124
+ } = clientOptions;
125
+ const clientCommand = "npx";
126
+ const host = process.env.HOST || "localhost";
127
+ const clientArgs = ["vite", "--port", CLIENT_PORT, "--host", host];
128
+
129
+ const client = spawn(clientCommand, clientArgs, {
130
+ cwd: resolve(__dirname, ".."),
131
+ env: { ...process.env, CLIENT_PORT },
132
+ signal: abort.signal,
133
+ echoOutput: true,
134
+ });
135
+
136
+ const url = getClientUrl(
137
+ CLIENT_PORT,
138
+ authDisabled,
139
+ sessionToken,
140
+ SERVER_PORT,
141
+ );
142
+
143
+ // Give vite time to start before opening or logging the URL
144
+ setTimeout(() => {
145
+ console.log(`\n🚀 MCP Inspector is up and running at:\n ${url}\n`);
146
+ if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
147
+ console.log("🌐 Opening browser...");
148
+ open(url);
149
+ }
150
+ }, 3000);
151
+
152
+ await new Promise((resolve) => {
153
+ client.subscribe({
154
+ complete: resolve,
155
+ error: (err) => {
156
+ if (!cancelled || process.env.DEBUG) {
157
+ console.error("Client error:", err);
158
+ }
159
+ resolve(null);
160
+ },
161
+ next: () => {}, // We're using echoOutput
162
+ });
163
+ });
164
+ }
165
+
166
+ async function startProdClient(clientOptions) {
167
+ const {
168
+ CLIENT_PORT,
169
+ SERVER_PORT,
170
+ authDisabled,
171
+ sessionToken,
172
+ abort,
173
+ cancelled,
174
+ } = clientOptions;
175
+ const inspectorClientPath = resolve(
176
+ __dirname,
177
+ "../..",
178
+ "client",
179
+ "bin",
180
+ "client.js",
181
+ );
182
+
183
+ const url = getClientUrl(
184
+ CLIENT_PORT,
185
+ authDisabled,
186
+ sessionToken,
187
+ SERVER_PORT,
188
+ );
189
+
190
+ await spawnPromise("node", [inspectorClientPath], {
191
+ env: {
192
+ ...process.env,
193
+ CLIENT_PORT,
194
+ INSPECTOR_URL: url,
195
+ },
196
+ signal: abort.signal,
197
+ echoOutput: true,
198
+ });
199
+ }
200
+
14
201
  async function main() {
15
202
  // Parse command line arguments
16
203
  const args = process.argv.slice(2);
@@ -18,6 +205,7 @@ async function main() {
18
205
  const mcpServerArgs = [];
19
206
  let command = null;
20
207
  let parsingFlags = true;
208
+ let isDev = false;
21
209
 
22
210
  for (let i = 0; i < args.length; i++) {
23
211
  const arg = args[i];
@@ -27,6 +215,11 @@ async function main() {
27
215
  continue;
28
216
  }
29
217
 
218
+ if (parsingFlags && arg === "--dev") {
219
+ isDev = true;
220
+ continue;
221
+ }
222
+
30
223
  if (parsingFlags && arg === "-e" && i + 1 < args.length) {
31
224
  const envVar = args[++i];
32
225
  const equalsIndex = envVar.indexOf("=");
@@ -38,34 +231,25 @@ async function main() {
38
231
  } else {
39
232
  envVars[envVar] = "";
40
233
  }
41
- } else if (!command) {
234
+ } else if (!command && !isDev) {
42
235
  command = arg;
43
- } else {
236
+ } else if (!isDev) {
44
237
  mcpServerArgs.push(arg);
45
238
  }
46
239
  }
47
240
 
48
- const inspectorServerPath = resolve(
49
- __dirname,
50
- "../..",
51
- "server",
52
- "build",
53
- "index.js",
54
- );
241
+ const CLIENT_PORT = process.env.CLIENT_PORT ?? "6274";
242
+ const SERVER_PORT = process.env.SERVER_PORT ?? DEFAULT_MCP_PROXY_LISTEN_PORT;
55
243
 
56
- // Path to the client entry point
57
- const inspectorClientPath = resolve(
58
- __dirname,
59
- "../..",
60
- "client",
61
- "bin",
62
- "client.js",
244
+ console.log(
245
+ isDev
246
+ ? "Starting MCP inspector in development mode..."
247
+ : "Starting MCP inspector...",
63
248
  );
64
249
 
65
- const CLIENT_PORT = process.env.CLIENT_PORT ?? "6274";
66
- const SERVER_PORT = process.env.SERVER_PORT ?? "6277";
67
-
68
- console.log("Starting MCP inspector...");
250
+ // Generate session token for authentication
251
+ const sessionToken = randomBytes(32).toString("hex");
252
+ const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
69
253
 
70
254
  const abort = new AbortController();
71
255
 
@@ -74,42 +258,42 @@ async function main() {
74
258
  cancelled = true;
75
259
  abort.abort();
76
260
  });
261
+
77
262
  let server, serverOk;
263
+
78
264
  try {
79
- server = spawnPromise(
80
- "node",
81
- [
82
- inspectorServerPath,
83
- ...(command ? [`--env`, command] : []),
84
- ...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []),
85
- ],
86
- {
87
- env: {
88
- ...process.env,
89
- PORT: SERVER_PORT,
90
- MCP_ENV_VARS: JSON.stringify(envVars),
91
- },
92
- signal: abort.signal,
93
- echoOutput: true,
94
- },
95
- );
265
+ const serverOptions = {
266
+ SERVER_PORT,
267
+ CLIENT_PORT,
268
+ sessionToken,
269
+ envVars,
270
+ abort,
271
+ command,
272
+ mcpServerArgs,
273
+ };
96
274
 
97
- // Make sure server started before starting client
98
- serverOk = await Promise.race([server, delay(2 * 1000)]);
275
+ const result = isDev
276
+ ? await startDevServer(serverOptions)
277
+ : await startProdServer(serverOptions);
278
+
279
+ server = result.server;
280
+ serverOk = result.serverOk;
99
281
  } catch (error) {}
100
282
 
101
283
  if (serverOk) {
102
284
  try {
103
- // Only auto-open when auth is disabled
104
- const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
105
- if (process.env.MCP_AUTO_OPEN_ENABLED !== "false" && authDisabled) {
106
- open(`http://127.0.0.1:${CLIENT_PORT}`);
107
- }
108
- await spawnPromise("node", [inspectorClientPath], {
109
- env: { ...process.env, PORT: CLIENT_PORT },
110
- signal: abort.signal,
111
- echoOutput: true,
112
- });
285
+ const clientOptions = {
286
+ CLIENT_PORT,
287
+ SERVER_PORT,
288
+ authDisabled,
289
+ sessionToken,
290
+ abort,
291
+ cancelled,
292
+ };
293
+
294
+ await (isDev
295
+ ? startDevClient(clientOptions)
296
+ : startProdClient(clientOptions));
113
297
  } catch (e) {
114
298
  if (!cancelled || process.env.DEBUG) throw e;
115
299
  }
@@ -1,4 +1,4 @@
1
- import { u as useToast, r as reactExports, j as jsxRuntimeExports, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-DYfnI33L.js";
1
+ import { u as useToast, r as reactExports, j as jsxRuntimeExports, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-DI7JCTeu.js";
2
2
  import { p as parseOAuthCallbackParams, g as generateOAuthErrorDescription } from "./oauthUtils-DTcoXpSP.js";
3
3
  const OAuthCallback = ({ onConnect }) => {
4
4
  const { toast } = useToast();
@@ -1,4 +1,4 @@
1
- import { r as reactExports, S as SESSION_KEYS, j as jsxRuntimeExports } from "./index-DYfnI33L.js";
1
+ import { r as reactExports, S as SESSION_KEYS, j as jsxRuntimeExports } from "./index-DI7JCTeu.js";
2
2
  import { p as parseOAuthCallbackParams, g as generateOAuthErrorDescription } from "./oauthUtils-DTcoXpSP.js";
3
3
  const OAuthDebugCallback = ({ onConnect }) => {
4
4
  reactExports.useEffect(() => {
@@ -1,7 +1,3 @@
1
- #root {
2
- margin: 0 auto;
3
- }
4
-
5
1
  .logo {
6
2
  height: 6em;
7
3
  padding: 1.5em;