@modelcontextprotocol/inspector-client 0.14.3 → 0.15.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/start.js CHANGED
@@ -2,8 +2,9 @@
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));
9
10
 
@@ -11,6 +12,157 @@ function delay(ms) {
11
12
  return new Promise((resolve) => setTimeout(resolve, ms, true));
12
13
  }
13
14
 
15
+ async function startDevServer(serverOptions) {
16
+ const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort } =
17
+ serverOptions;
18
+ const serverCommand = "npx";
19
+ const serverArgs = ["tsx", "watch", "--clear-screen=false", "src/index.ts"];
20
+ const isWindows = process.platform === "win32";
21
+
22
+ const spawnOptions = {
23
+ cwd: resolve(__dirname, "../..", "server"),
24
+ env: {
25
+ ...process.env,
26
+ PORT: SERVER_PORT,
27
+ CLIENT_PORT: CLIENT_PORT,
28
+ MCP_PROXY_TOKEN: sessionToken,
29
+ MCP_ENV_VARS: JSON.stringify(envVars),
30
+ },
31
+ signal: abort.signal,
32
+ echoOutput: true,
33
+ };
34
+
35
+ // For Windows, we need to use stdin: 'ignore' to simulate < NUL
36
+ if (isWindows) {
37
+ spawnOptions.stdin = "ignore";
38
+ }
39
+
40
+ const server = spawn(serverCommand, serverArgs, spawnOptions);
41
+
42
+ // Give server time to start
43
+ const serverOk = await Promise.race([
44
+ new Promise((resolve) => {
45
+ server.subscribe({
46
+ complete: () => resolve(false),
47
+ error: () => resolve(false),
48
+ next: () => {}, // We're using echoOutput
49
+ });
50
+ }),
51
+ delay(3000).then(() => true),
52
+ ]);
53
+
54
+ return { server, serverOk };
55
+ }
56
+
57
+ async function startProdServer(serverOptions) {
58
+ const {
59
+ SERVER_PORT,
60
+ CLIENT_PORT,
61
+ sessionToken,
62
+ envVars,
63
+ abort,
64
+ command,
65
+ mcpServerArgs,
66
+ } = serverOptions;
67
+ const inspectorServerPath = resolve(
68
+ __dirname,
69
+ "../..",
70
+ "server",
71
+ "build",
72
+ "index.js",
73
+ );
74
+
75
+ const server = spawnPromise(
76
+ "node",
77
+ [
78
+ inspectorServerPath,
79
+ ...(command ? [`--env`, command] : []),
80
+ ...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []),
81
+ ],
82
+ {
83
+ env: {
84
+ ...process.env,
85
+ PORT: SERVER_PORT,
86
+ CLIENT_PORT: CLIENT_PORT,
87
+ MCP_PROXY_TOKEN: sessionToken,
88
+ MCP_ENV_VARS: JSON.stringify(envVars),
89
+ },
90
+ signal: abort.signal,
91
+ echoOutput: true,
92
+ },
93
+ );
94
+
95
+ // Make sure server started before starting client
96
+ const serverOk = await Promise.race([server, delay(2 * 1000)]);
97
+
98
+ return { server, serverOk };
99
+ }
100
+
101
+ async function startDevClient(clientOptions) {
102
+ const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } =
103
+ clientOptions;
104
+ const clientCommand = "npx";
105
+ const clientArgs = ["vite", "--port", CLIENT_PORT];
106
+
107
+ const client = spawn(clientCommand, clientArgs, {
108
+ cwd: resolve(__dirname, ".."),
109
+ env: { ...process.env, PORT: CLIENT_PORT },
110
+ signal: abort.signal,
111
+ echoOutput: true,
112
+ });
113
+
114
+ // Auto-open browser after vite starts
115
+ if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
116
+ const url = authDisabled
117
+ ? `http://127.0.0.1:${CLIENT_PORT}`
118
+ : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
119
+
120
+ // Give vite time to start before opening browser
121
+ setTimeout(() => {
122
+ open(url);
123
+ console.log(`\n🔗 Opening browser at: ${url}\n`);
124
+ }, 3000);
125
+ }
126
+
127
+ await new Promise((resolve) => {
128
+ client.subscribe({
129
+ complete: resolve,
130
+ error: (err) => {
131
+ if (!cancelled || process.env.DEBUG) {
132
+ console.error("Client error:", err);
133
+ }
134
+ resolve(null);
135
+ },
136
+ next: () => {}, // We're using echoOutput
137
+ });
138
+ });
139
+ }
140
+
141
+ async function startProdClient(clientOptions) {
142
+ const { CLIENT_PORT, authDisabled, sessionToken, abort } = clientOptions;
143
+ const inspectorClientPath = resolve(
144
+ __dirname,
145
+ "../..",
146
+ "client",
147
+ "bin",
148
+ "client.js",
149
+ );
150
+
151
+ // Auto-open browser with token
152
+ if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
153
+ const url = authDisabled
154
+ ? `http://127.0.0.1:${CLIENT_PORT}`
155
+ : `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
156
+ open(url);
157
+ }
158
+
159
+ await spawnPromise("node", [inspectorClientPath], {
160
+ env: { ...process.env, PORT: CLIENT_PORT },
161
+ signal: abort.signal,
162
+ echoOutput: true,
163
+ });
164
+ }
165
+
14
166
  async function main() {
15
167
  // Parse command line arguments
16
168
  const args = process.argv.slice(2);
@@ -18,6 +170,7 @@ async function main() {
18
170
  const mcpServerArgs = [];
19
171
  let command = null;
20
172
  let parsingFlags = true;
173
+ let isDev = false;
21
174
 
22
175
  for (let i = 0; i < args.length; i++) {
23
176
  const arg = args[i];
@@ -27,6 +180,11 @@ async function main() {
27
180
  continue;
28
181
  }
29
182
 
183
+ if (parsingFlags && arg === "--dev") {
184
+ isDev = true;
185
+ continue;
186
+ }
187
+
30
188
  if (parsingFlags && arg === "-e" && i + 1 < args.length) {
31
189
  const envVar = args[++i];
32
190
  const equalsIndex = envVar.indexOf("=");
@@ -38,34 +196,25 @@ async function main() {
38
196
  } else {
39
197
  envVars[envVar] = "";
40
198
  }
41
- } else if (!command) {
199
+ } else if (!command && !isDev) {
42
200
  command = arg;
43
- } else {
201
+ } else if (!isDev) {
44
202
  mcpServerArgs.push(arg);
45
203
  }
46
204
  }
47
205
 
48
- const inspectorServerPath = resolve(
49
- __dirname,
50
- "../..",
51
- "server",
52
- "build",
53
- "index.js",
54
- );
55
-
56
- // Path to the client entry point
57
- const inspectorClientPath = resolve(
58
- __dirname,
59
- "../..",
60
- "client",
61
- "bin",
62
- "client.js",
63
- );
64
-
65
206
  const CLIENT_PORT = process.env.CLIENT_PORT ?? "6274";
66
207
  const SERVER_PORT = process.env.SERVER_PORT ?? "6277";
67
208
 
68
- console.log("Starting MCP inspector...");
209
+ console.log(
210
+ isDev
211
+ ? "Starting MCP inspector in development mode..."
212
+ : "Starting MCP inspector...",
213
+ );
214
+
215
+ // Generate session token for authentication
216
+ const sessionToken = randomBytes(32).toString("hex");
217
+ const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
69
218
 
70
219
  const abort = new AbortController();
71
220
 
@@ -74,42 +223,41 @@ async function main() {
74
223
  cancelled = true;
75
224
  abort.abort();
76
225
  });
226
+
77
227
  let server, serverOk;
228
+
78
229
  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
- );
230
+ const serverOptions = {
231
+ SERVER_PORT,
232
+ CLIENT_PORT,
233
+ sessionToken,
234
+ envVars,
235
+ abort,
236
+ command,
237
+ mcpServerArgs,
238
+ };
96
239
 
97
- // Make sure server started before starting client
98
- serverOk = await Promise.race([server, delay(2 * 1000)]);
240
+ const result = isDev
241
+ ? await startDevServer(serverOptions)
242
+ : await startProdServer(serverOptions);
243
+
244
+ server = result.server;
245
+ serverOk = result.serverOk;
99
246
  } catch (error) {}
100
247
 
101
248
  if (serverOk) {
102
249
  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
- });
250
+ const clientOptions = {
251
+ CLIENT_PORT,
252
+ authDisabled,
253
+ sessionToken,
254
+ abort,
255
+ cancelled,
256
+ };
257
+
258
+ await (isDev
259
+ ? startDevClient(clientOptions)
260
+ : startProdClient(clientOptions));
113
261
  } catch (e) {
114
262
  if (!cancelled || process.env.DEBUG) throw e;
115
263
  }
@@ -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-BNqYF5Na.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-BNqYF5Na.js";
2
2
  import { p as parseOAuthCallbackParams, g as generateOAuthErrorDescription } from "./oauthUtils-DTcoXpSP.js";
3
3
  const OAuthDebugCallback = ({ onConnect }) => {
4
4
  reactExports.useEffect(() => {