@modelcontextprotocol/inspector 0.16.4 → 0.16.6

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/README.md CHANGED
@@ -29,6 +29,14 @@ npx @modelcontextprotocol/inspector
29
29
 
30
30
  The server will start up and the UI will be accessible at `http://localhost:6274`.
31
31
 
32
+ ### Docker Container
33
+
34
+ You can also start it in a Docker container with the following command:
35
+
36
+ ```bash
37
+ docker run --rm --network host -p 6274:6274 -p 6277:6277 ghcr.io/modelcontextprotocol/inspector:latest
38
+ ```
39
+
32
40
  ### From an MCP server repository
33
41
 
34
42
  To inspect an MCP server implementation, there's no need to clone this repo. Instead, use `npx`. For example, if your server is built at `build/index.js`:
@@ -166,6 +174,16 @@ If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGER
166
174
  DANGEROUSLY_OMIT_AUTH=true npm start
167
175
  ```
168
176
 
177
+ ---
178
+
179
+ **🚨 WARNING 🚨**
180
+
181
+ Disabling authentication with `DANGEROUSLY_OMIT_AUTH` is incredibly dangerous! Disabling auth leaves your machine open to attack not just when exposed to the public internet, but also **via your web browser**. Meaning, visiting a malicious website OR viewing a malicious advertizement could allow an attacker to remotely compromise your computer. Do not disable this feature unless you truly understand the risks.
182
+
183
+ Read more about the risks of this vulnerability on Oligo's blog: [Critical RCE Vulnerability in Anthropic MCP Inspector - CVE-2025-49596](https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596)
184
+
185
+ ---
186
+
169
187
  You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
170
188
 
171
189
  ```bash
@@ -377,6 +395,9 @@ npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/lis
377
395
  # Call a specific tool
378
396
  npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg key=value --tool-arg another=value2
379
397
 
398
+ # Call a tool with JSON arguments
399
+ npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg 'options={"format": "json", "max_tokens": 100}'
400
+
380
401
  # List available resources
381
402
  npx @modelcontextprotocol/inspector --cli node build/index.js --method resources/list
382
403
 
@@ -389,6 +410,9 @@ npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
389
410
  # Connect to a remote MCP server (with Streamable HTTP transport)
390
411
  npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list
391
412
 
413
+ # Connect to a remote MCP server (with custom headers)
414
+ npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list --header "X-API-Key: your-api-key"
415
+
392
416
  # Call a tool on a remote server
393
417
  npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
394
418
 
package/cli/build/cli.js CHANGED
@@ -54,6 +54,10 @@ async function runWebClient(args) {
54
54
  await spawnPromise("node", [inspectorClientPath, ...startArgs], {
55
55
  signal: abort.signal,
56
56
  echoOutput: true,
57
+ // pipe the stdout through here, prevents issues with buffering and
58
+ // dropping the end of console.out after 8192 chars due to node
59
+ // closing the stdout pipe before the output has finished flushing
60
+ stdio: "inherit",
57
61
  });
58
62
  }
59
63
  catch (e) {
@@ -73,18 +77,28 @@ async function runCli(args) {
73
77
  try {
74
78
  // Build CLI arguments
75
79
  const cliArgs = [cliPath];
80
+ // Add target URL/command first
81
+ cliArgs.push(args.command, ...args.args);
76
82
  // Add transport flag if specified
77
83
  if (args.transport && args.transport !== "stdio") {
78
84
  // Convert streamable-http back to http for CLI mode
79
85
  const cliTransport = args.transport === "streamable-http" ? "http" : args.transport;
80
86
  cliArgs.push("--transport", cliTransport);
81
87
  }
82
- // Add command and remaining args
83
- cliArgs.push(args.command, ...args.args);
88
+ // Add headers if specified
89
+ if (args.headers) {
90
+ for (const [key, value] of Object.entries(args.headers)) {
91
+ cliArgs.push("--header", `${key}: ${value}`);
92
+ }
93
+ }
84
94
  await spawnPromise("node", cliArgs, {
85
95
  env: { ...process.env, ...args.envArgs },
86
96
  signal: abort.signal,
87
97
  echoOutput: true,
98
+ // pipe the stdout through here, prevents issues with buffering and
99
+ // dropping the end of console.out after 8192 chars due to node
100
+ // closing the stdout pipe before the output has finished flushing
101
+ stdio: "inherit",
88
102
  });
89
103
  }
90
104
  catch (e) {
@@ -126,6 +140,18 @@ function parseKeyValuePair(value, previous = {}) {
126
140
  }
127
141
  return { ...previous, [key]: val };
128
142
  }
143
+ function parseHeaderPair(value, previous = {}) {
144
+ const colonIndex = value.indexOf(":");
145
+ if (colonIndex === -1) {
146
+ throw new Error(`Invalid header format: ${value}. Use "HeaderName: Value" format.`);
147
+ }
148
+ const key = value.slice(0, colonIndex).trim();
149
+ const val = value.slice(colonIndex + 1).trim();
150
+ if (key === "" || val === "") {
151
+ throw new Error(`Invalid header format: ${value}. Use "HeaderName: Value" format.`);
152
+ }
153
+ return { ...previous, [key]: val };
154
+ }
129
155
  function parseArgs() {
130
156
  const program = new Command();
131
157
  const argSeparatorIndex = process.argv.indexOf("--");
@@ -144,7 +170,8 @@ function parseArgs() {
144
170
  .option("--server <n>", "server name from config file")
145
171
  .option("--cli", "enable CLI mode")
146
172
  .option("--transport <type>", "transport type (stdio, sse, http)")
147
- .option("--server-url <url>", "server URL for SSE/HTTP transport");
173
+ .option("--server-url <url>", "server URL for SSE/HTTP transport")
174
+ .option("--header <headers...>", 'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)', parseHeaderPair, {});
148
175
  // Parse only the arguments before --
149
176
  program.parse(preArgs);
150
177
  const options = program.opts();
@@ -186,6 +213,7 @@ function parseArgs() {
186
213
  envArgs: { ...(config.env || {}), ...(options.e || {}) },
187
214
  cli: options.cli || false,
188
215
  transport: "stdio",
216
+ headers: options.header,
189
217
  };
190
218
  }
191
219
  else if (config.type === "sse" || config.type === "streamable-http") {
@@ -196,6 +224,7 @@ function parseArgs() {
196
224
  cli: options.cli || false,
197
225
  transport: config.type,
198
226
  serverUrl: config.url,
227
+ headers: options.header,
199
228
  };
200
229
  }
201
230
  else {
@@ -206,6 +235,7 @@ function parseArgs() {
206
235
  envArgs: { ...(config.env || {}), ...(options.e || {}) },
207
236
  cli: options.cli || false,
208
237
  transport: "stdio",
238
+ headers: options.header,
209
239
  };
210
240
  }
211
241
  }
@@ -224,6 +254,7 @@ function parseArgs() {
224
254
  cli: options.cli || false,
225
255
  transport: transport,
226
256
  serverUrl: options.serverUrl,
257
+ headers: options.header,
227
258
  };
228
259
  }
229
260
  async function main() {
@@ -11,9 +11,24 @@ export async function listPrompts(client) {
11
11
  // Get a prompt
12
12
  export async function getPrompt(client, name, args) {
13
13
  try {
14
+ // Convert all arguments to strings for prompt arguments
15
+ const stringArgs = {};
16
+ if (args) {
17
+ for (const [key, value] of Object.entries(args)) {
18
+ if (typeof value === "string") {
19
+ stringArgs[key] = value;
20
+ }
21
+ else if (value === null || value === undefined) {
22
+ stringArgs[key] = String(value);
23
+ }
24
+ else {
25
+ stringArgs[key] = JSON.stringify(value);
26
+ }
27
+ }
28
+ }
14
29
  const response = await client.getPrompt({
15
30
  name,
16
- arguments: args || {},
31
+ arguments: stringArgs,
17
32
  });
18
33
  return response;
19
34
  }
@@ -49,8 +49,18 @@ export async function callTool(client, name, args) {
49
49
  const tool = tools.find((t) => t.name === name);
50
50
  let convertedArgs = args;
51
51
  if (tool) {
52
- // Convert parameters based on the tool's schema
53
- convertedArgs = convertParameters(tool, args);
52
+ // Convert parameters based on the tool's schema, but only for string values
53
+ // since we now accept pre-parsed values from the CLI
54
+ const stringArgs = {};
55
+ for (const [key, value] of Object.entries(args)) {
56
+ if (typeof value === "string") {
57
+ stringArgs[key] = value;
58
+ }
59
+ }
60
+ if (Object.keys(stringArgs).length > 0) {
61
+ const convertedStringArgs = convertParameters(tool, stringArgs);
62
+ convertedArgs = { ...args, ...convertedStringArgs };
63
+ }
54
64
  }
55
65
  const response = await client.callTool({
56
66
  name: name,
@@ -4,7 +4,8 @@ import { Command } from "commander";
4
4
  import { callTool, connect, disconnect, getPrompt, listPrompts, listResources, listResourceTemplates, listTools, readResource, setLoggingLevel, validLogLevels, } from "./client/index.js";
5
5
  import { handleError } from "./error-handler.js";
6
6
  import { createTransport } from "./transport.js";
7
- function createTransportOptions(target, transport) {
7
+ import { awaitableLog } from "./utils/awaitable-log.js";
8
+ function createTransportOptions(target, transport, headers) {
8
9
  if (target.length === 0) {
9
10
  throw new Error("Target is required. Specify a URL or a command to execute.");
10
11
  }
@@ -46,10 +47,11 @@ function createTransportOptions(target, transport) {
46
47
  command: isUrl ? undefined : command,
47
48
  args: isUrl ? undefined : commandArgs,
48
49
  url: isUrl ? command : undefined,
50
+ headers,
49
51
  };
50
52
  }
51
53
  async function callMethod(args) {
52
- const transportOptions = createTransportOptions(args.target, args.transport);
54
+ const transportOptions = createTransportOptions(args.target, args.transport, args.headers);
53
55
  const transport = createTransport(transportOptions);
54
56
  const client = new Client({
55
57
  name: "inspector-cli",
@@ -101,7 +103,7 @@ async function callMethod(args) {
101
103
  else {
102
104
  throw new Error(`Unsupported method: ${args.method}. Supported methods include: tools/list, tools/call, resources/list, resources/read, resources/templates/list, prompts/list, prompts/get, logging/setLevel`);
103
105
  }
104
- console.log(JSON.stringify(result, null, 2));
106
+ await awaitableLog(JSON.stringify(result, null, 2));
105
107
  }
106
108
  finally {
107
109
  try {
@@ -119,6 +121,27 @@ function parseKeyValuePair(value, previous = {}) {
119
121
  if (val === undefined || val === "") {
120
122
  throw new Error(`Invalid parameter format: ${value}. Use key=value format.`);
121
123
  }
124
+ // Try to parse as JSON first
125
+ let parsedValue;
126
+ try {
127
+ parsedValue = JSON.parse(val);
128
+ }
129
+ catch {
130
+ // If JSON parsing fails, keep as string
131
+ parsedValue = val;
132
+ }
133
+ return { ...previous, [key]: parsedValue };
134
+ }
135
+ function parseHeaderPair(value, previous = {}) {
136
+ const colonIndex = value.indexOf(":");
137
+ if (colonIndex === -1) {
138
+ throw new Error(`Invalid header format: ${value}. Use "HeaderName: Value" format.`);
139
+ }
140
+ const key = value.slice(0, colonIndex).trim();
141
+ const val = value.slice(colonIndex + 1).trim();
142
+ if (key === "" || val === "") {
143
+ throw new Error(`Invalid header format: ${value}. Use "HeaderName: Value" format.`);
144
+ }
122
145
  return { ...previous, [key]: val };
123
146
  }
124
147
  function parseArgs() {
@@ -171,7 +194,11 @@ function parseArgs() {
171
194
  throw new Error(`Invalid transport type: ${value}. Valid types are: ${validTransports.join(", ")}`);
172
195
  }
173
196
  return value;
174
- });
197
+ })
198
+ //
199
+ // HTTP headers
200
+ //
201
+ .option("--header <headers...>", 'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)', parseHeaderPair, {});
175
202
  // Parse only the arguments before --
176
203
  program.parse(preArgs);
177
204
  const options = program.opts();
@@ -184,6 +211,7 @@ function parseArgs() {
184
211
  return {
185
212
  target: finalArgs,
186
213
  ...options,
214
+ headers: options.header, // commander.js uses 'header' field, map to 'headers'
187
215
  };
188
216
  }
189
217
  async function main() {
@@ -15,8 +15,8 @@ function createStdioTransport(options) {
15
15
  }
16
16
  const defaultEnv = getDefaultEnvironment();
17
17
  const env = {
18
- ...processEnv,
19
18
  ...defaultEnv,
19
+ ...processEnv,
20
20
  };
21
21
  const { cmd: actualCommand, args: actualArgs } = findActualExecutable(options.command ?? "", args);
22
22
  return new StdioClientTransport({
@@ -38,10 +38,24 @@ export function createTransport(options) {
38
38
  }
39
39
  const url = new URL(options.url);
40
40
  if (transportType === "sse") {
41
- return new SSEClientTransport(url);
41
+ const transportOptions = options.headers
42
+ ? {
43
+ requestInit: {
44
+ headers: options.headers,
45
+ },
46
+ }
47
+ : undefined;
48
+ return new SSEClientTransport(url, transportOptions);
42
49
  }
43
50
  if (transportType === "http") {
44
- return new StreamableHTTPClientTransport(url);
51
+ const transportOptions = options.headers
52
+ ? {
53
+ requestInit: {
54
+ headers: options.headers,
55
+ },
56
+ }
57
+ : undefined;
58
+ return new StreamableHTTPClientTransport(url, transportOptions);
45
59
  }
46
60
  throw new Error(`Unsupported transport type: ${transportType}`);
47
61
  }
@@ -0,0 +1,7 @@
1
+ export function awaitableLog(logValue) {
2
+ return new Promise((resolve) => {
3
+ process.stdout.write(logValue, () => {
4
+ resolve();
5
+ });
6
+ });
7
+ }
@@ -1,4 +1,4 @@
1
- import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-MFoeZZBS.js";
1
+ import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-B_qh-Btq.js";
2
2
  const OAuthCallback = ({ onConnect }) => {
3
3
  const { toast } = useToast();
4
4
  const hasProcessedRef = reactExports.useRef(false);
@@ -1,4 +1,4 @@
1
- import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-MFoeZZBS.js";
1
+ import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-B_qh-Btq.js";
2
2
  const OAuthDebugCallback = ({ onConnect }) => {
3
3
  reactExports.useEffect(() => {
4
4
  let isProcessed = false;
@@ -21,6 +21,14 @@ const OAuthDebugCallback = ({ onConnect }) => {
21
21
  if (storedState) {
22
22
  try {
23
23
  restoredState = JSON.parse(storedState);
24
+ if (restoredState && typeof restoredState.resource === "string") {
25
+ restoredState.resource = new URL(restoredState.resource);
26
+ }
27
+ if (restoredState && typeof restoredState.authorizationUrl === "string") {
28
+ restoredState.authorizationUrl = new URL(
29
+ restoredState.authorizationUrl
30
+ );
31
+ }
24
32
  sessionStorage.removeItem(SESSION_KEYS.AUTH_DEBUGGER_STATE);
25
33
  } catch (e) {
26
34
  console.error("Failed to parse stored auth state:", e);
@@ -1,3 +1,6 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
4
  function _mergeNamespaces(n, m2) {
2
5
  for (var i = 0; i < m2.length; i++) {
3
6
  const e = m2[i];
@@ -15118,6 +15121,7 @@ const enumType = ZodEnum.create;
15118
15121
  ZodPromise.create;
15119
15122
  const optionalType = ZodOptional.create;
15120
15123
  ZodNullable.create;
15124
+ const NEVER = INVALID;
15121
15125
  const LATEST_PROTOCOL_VERSION = "2025-06-18";
15122
15126
  const SUPPORTED_PROTOCOL_VERSIONS = [
15123
15127
  LATEST_PROTOCOL_VERSION,
@@ -16020,7 +16024,7 @@ unionType([
16020
16024
  CallToolRequestSchema,
16021
16025
  ListToolsRequestSchema
16022
16026
  ]);
16023
- const ClientNotificationSchema = unionType([
16027
+ unionType([
16024
16028
  CancelledNotificationSchema,
16025
16029
  ProgressNotificationSchema,
16026
16030
  InitializedNotificationSchema,
@@ -16038,7 +16042,7 @@ unionType([
16038
16042
  ElicitRequestSchema,
16039
16043
  ListRootsRequestSchema
16040
16044
  ]);
16041
- const ServerNotificationSchema = unionType([
16045
+ unionType([
16042
16046
  CancelledNotificationSchema,
16043
16047
  ProgressNotificationSchema,
16044
16048
  LoggingMessageNotificationSchema,
@@ -16067,9 +16071,22 @@ class McpError extends Error {
16067
16071
  this.name = "McpError";
16068
16072
  }
16069
16073
  }
16074
+ const SafeUrlSchema = stringType().url().superRefine((val, ctx) => {
16075
+ if (!URL.canParse(val)) {
16076
+ ctx.addIssue({
16077
+ code: ZodIssueCode.custom,
16078
+ message: "URL must be parseable",
16079
+ fatal: true
16080
+ });
16081
+ return NEVER;
16082
+ }
16083
+ }).refine((url) => {
16084
+ const u2 = new URL(url);
16085
+ return u2.protocol !== "javascript:" && u2.protocol !== "data:" && u2.protocol !== "vbscript:";
16086
+ }, { message: "URL cannot use javascript:, data:, or vbscript: scheme" });
16070
16087
  const OAuthProtectedResourceMetadataSchema = objectType({
16071
16088
  resource: stringType().url(),
16072
- authorization_servers: arrayType(stringType().url()).optional(),
16089
+ authorization_servers: arrayType(SafeUrlSchema).optional(),
16073
16090
  jwks_uri: stringType().url().optional(),
16074
16091
  scopes_supported: arrayType(stringType()).optional(),
16075
16092
  bearer_methods_supported: arrayType(stringType()).optional(),
@@ -16085,17 +16102,17 @@ const OAuthProtectedResourceMetadataSchema = objectType({
16085
16102
  }).passthrough();
16086
16103
  const OAuthMetadataSchema = objectType({
16087
16104
  issuer: stringType(),
16088
- authorization_endpoint: stringType(),
16089
- token_endpoint: stringType(),
16090
- registration_endpoint: stringType().optional(),
16105
+ authorization_endpoint: SafeUrlSchema,
16106
+ token_endpoint: SafeUrlSchema,
16107
+ registration_endpoint: SafeUrlSchema.optional(),
16091
16108
  scopes_supported: arrayType(stringType()).optional(),
16092
16109
  response_types_supported: arrayType(stringType()),
16093
16110
  response_modes_supported: arrayType(stringType()).optional(),
16094
16111
  grant_types_supported: arrayType(stringType()).optional(),
16095
16112
  token_endpoint_auth_methods_supported: arrayType(stringType()).optional(),
16096
16113
  token_endpoint_auth_signing_alg_values_supported: arrayType(stringType()).optional(),
16097
- service_documentation: stringType().optional(),
16098
- revocation_endpoint: stringType().optional(),
16114
+ service_documentation: SafeUrlSchema.optional(),
16115
+ revocation_endpoint: SafeUrlSchema.optional(),
16099
16116
  revocation_endpoint_auth_methods_supported: arrayType(stringType()).optional(),
16100
16117
  revocation_endpoint_auth_signing_alg_values_supported: arrayType(stringType()).optional(),
16101
16118
  introspection_endpoint: stringType().optional(),
@@ -16105,11 +16122,11 @@ const OAuthMetadataSchema = objectType({
16105
16122
  }).passthrough();
16106
16123
  const OpenIdProviderMetadataSchema = objectType({
16107
16124
  issuer: stringType(),
16108
- authorization_endpoint: stringType(),
16109
- token_endpoint: stringType(),
16110
- userinfo_endpoint: stringType().optional(),
16111
- jwks_uri: stringType(),
16112
- registration_endpoint: stringType().optional(),
16125
+ authorization_endpoint: SafeUrlSchema,
16126
+ token_endpoint: SafeUrlSchema,
16127
+ userinfo_endpoint: SafeUrlSchema.optional(),
16128
+ jwks_uri: SafeUrlSchema,
16129
+ registration_endpoint: SafeUrlSchema.optional(),
16113
16130
  scopes_supported: arrayType(stringType()).optional(),
16114
16131
  response_types_supported: arrayType(stringType()),
16115
16132
  response_modes_supported: arrayType(stringType()).optional(),
@@ -16137,8 +16154,8 @@ const OpenIdProviderMetadataSchema = objectType({
16137
16154
  request_parameter_supported: booleanType().optional(),
16138
16155
  request_uri_parameter_supported: booleanType().optional(),
16139
16156
  require_request_uri_registration: booleanType().optional(),
16140
- op_policy_uri: stringType().optional(),
16141
- op_tos_uri: stringType().optional()
16157
+ op_policy_uri: SafeUrlSchema.optional(),
16158
+ op_tos_uri: SafeUrlSchema.optional()
16142
16159
  }).passthrough();
16143
16160
  const OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchema.merge(OAuthMetadataSchema.pick({
16144
16161
  code_challenge_methods_supported: true
@@ -16158,18 +16175,18 @@ const OAuthErrorResponseSchema = objectType({
16158
16175
  error_uri: stringType().optional()
16159
16176
  });
16160
16177
  const OAuthClientMetadataSchema = objectType({
16161
- redirect_uris: arrayType(stringType()).refine((uris) => uris.every((uri) => URL.canParse(uri)), { message: "redirect_uris must contain valid URLs" }),
16178
+ redirect_uris: arrayType(SafeUrlSchema),
16162
16179
  token_endpoint_auth_method: stringType().optional(),
16163
16180
  grant_types: arrayType(stringType()).optional(),
16164
16181
  response_types: arrayType(stringType()).optional(),
16165
16182
  client_name: stringType().optional(),
16166
- client_uri: stringType().optional(),
16167
- logo_uri: stringType().optional(),
16183
+ client_uri: SafeUrlSchema.optional(),
16184
+ logo_uri: SafeUrlSchema.optional(),
16168
16185
  scope: stringType().optional(),
16169
16186
  contacts: arrayType(stringType()).optional(),
16170
- tos_uri: stringType().optional(),
16187
+ tos_uri: SafeUrlSchema.optional(),
16171
16188
  policy_uri: stringType().optional(),
16172
- jwks_uri: stringType().optional(),
16189
+ jwks_uri: SafeUrlSchema.optional(),
16173
16190
  jwks: anyType().optional(),
16174
16191
  software_id: stringType().optional(),
16175
16192
  software_version: stringType().optional(),
@@ -16629,7 +16646,7 @@ async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) {
16629
16646
  return await fetchWithCorsRetry(url, headers, fetchFn);
16630
16647
  }
16631
16648
  function shouldAttemptFallback(response, pathname) {
16632
- return !response || response.status === 404 && pathname !== "/";
16649
+ return !response || response.status >= 400 && response.status < 500 && pathname !== "/";
16633
16650
  }
16634
16651
  async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
16635
16652
  var _a, _b;
@@ -16885,6 +16902,19 @@ const generateOAuthErrorDescription = (params) => {
16885
16902
  errorUri ? `More info: ${errorUri}.` : ""
16886
16903
  ].filter(Boolean).join("\n");
16887
16904
  };
16905
+ function validateRedirectUrl(url) {
16906
+ try {
16907
+ const parsedUrl = new URL(url);
16908
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
16909
+ throw new Error("Authorization URL must be HTTP or HTTPS");
16910
+ }
16911
+ } catch (error) {
16912
+ if (error instanceof Error && error.message === "Authorization URL must be HTTP or HTTPS") {
16913
+ throw error;
16914
+ }
16915
+ throw new Error(`Invalid URL: ${url}`);
16916
+ }
16917
+ }
16888
16918
  const discoverScopes = async (serverUrl, resourceMetadata) => {
16889
16919
  try {
16890
16920
  const metadata = await discoverAuthorizationServerMetadata(
@@ -16935,8 +16965,10 @@ const clearClientInformationFromSessionStorage = ({
16935
16965
  sessionStorage.removeItem(key);
16936
16966
  };
16937
16967
  class InspectorOAuthClientProvider {
16938
- constructor(serverUrl) {
16968
+ constructor(serverUrl, scope) {
16969
+ __publicField(this, "scope");
16939
16970
  this.serverUrl = serverUrl;
16971
+ this.scope = scope;
16940
16972
  sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
16941
16973
  }
16942
16974
  get redirectUrl() {
@@ -16949,7 +16981,8 @@ class InspectorOAuthClientProvider {
16949
16981
  grant_types: ["authorization_code", "refresh_token"],
16950
16982
  response_types: ["code"],
16951
16983
  client_name: "MCP Inspector",
16952
- client_uri: "https://github.com/modelcontextprotocol/inspector"
16984
+ client_uri: "https://github.com/modelcontextprotocol/inspector",
16985
+ scope: this.scope ?? ""
16953
16986
  };
16954
16987
  }
16955
16988
  state() {
@@ -16990,9 +17023,7 @@ class InspectorOAuthClientProvider {
16990
17023
  sessionStorage.setItem(key, JSON.stringify(tokens));
16991
17024
  }
16992
17025
  redirectToAuthorization(authorizationUrl) {
16993
- if (authorizationUrl.protocol !== "http:" && authorizationUrl.protocol !== "https:") {
16994
- throw new Error("Authorization URL must be HTTP or HTTPS");
16995
- }
17026
+ validateRedirectUrl(authorizationUrl.href);
16996
17027
  window.location.href = authorizationUrl.href;
16997
17028
  }
16998
17029
  saveCodeVerifier(codeVerifier) {
@@ -17179,7 +17210,7 @@ const oauthTransitions = {
17179
17210
  authorizationCode: context.state.authorizationCode,
17180
17211
  codeVerifier,
17181
17212
  redirectUri: context.provider.redirectUrl,
17182
- resource: context.state.resource ?? void 0
17213
+ resource: context.state.resource ? context.state.resource instanceof URL ? context.state.resource : new URL(context.state.resource) : void 0
17183
17214
  });
17184
17215
  context.provider.saveTokens(tokens);
17185
17216
  context.updateState({
@@ -24047,7 +24078,7 @@ function splitLines(chunk) {
24047
24078
  const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
24048
24079
  `, searchIndex);
24049
24080
  let lineEnd = -1;
24050
- if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
24081
+ if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
24051
24082
  incompleteLine = chunk.slice(searchIndex);
24052
24083
  break;
24053
24084
  } else {
@@ -24835,16 +24866,7 @@ class StreamableHTTPClientTransport {
24835
24866
  return this._protocolVersion;
24836
24867
  }
24837
24868
  }
24838
- const StdErrNotificationSchema = NotificationSchema.extend({
24839
- method: literalType("notifications/stderr"),
24840
- params: objectType({
24841
- content: stringType()
24842
- })
24843
- });
24844
- ClientNotificationSchema.or(
24845
- StdErrNotificationSchema
24846
- ).or(ServerNotificationSchema).or(NotificationSchema);
24847
- const version$1 = "0.16.4";
24869
+ const version$1 = "0.16.6";
24848
24870
  const packageJson = {
24849
24871
  version: version$1
24850
24872
  };
@@ -24976,7 +24998,6 @@ function useConnection({
24976
24998
  oauthScope,
24977
24999
  config,
24978
25000
  onNotification,
24979
- onStdErrNotification,
24980
25001
  onPendingRequest,
24981
25002
  onElicitationRequest,
24982
25003
  getRoots,
@@ -25030,7 +25051,7 @@ function useConnection({
25030
25051
  mcpRequestOptions.onprogress = (params) => {
25031
25052
  if (onNotification) {
25032
25053
  onNotification({
25033
- method: "notification/progress",
25054
+ method: "notifications/progress",
25034
25055
  params
25035
25056
  });
25036
25057
  }
@@ -25147,7 +25168,6 @@ function useConnection({
25147
25168
  };
25148
25169
  const handleAuthError = async (error) => {
25149
25170
  if (is401Error(error)) {
25150
- const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
25151
25171
  let scope = oauthScope == null ? void 0 : oauthScope.trim();
25152
25172
  if (!scope) {
25153
25173
  let resourceMetadata;
@@ -25159,6 +25179,10 @@ function useConnection({
25159
25179
  }
25160
25180
  scope = await discoverScopes(sseUrl, resourceMetadata);
25161
25181
  }
25182
+ const serverAuthProvider = new InspectorOAuthClientProvider(
25183
+ sseUrl,
25184
+ scope
25185
+ );
25162
25186
  const result = await auth(serverAuthProvider, {
25163
25187
  serverUrl: sseUrl,
25164
25188
  scope
@@ -25212,11 +25236,18 @@ function useConnection({
25212
25236
  let transportOptions;
25213
25237
  let mcpProxyServerUrl;
25214
25238
  switch (transportType) {
25215
- case "stdio":
25239
+ case "stdio": {
25216
25240
  mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`);
25217
25241
  mcpProxyServerUrl.searchParams.append("command", command);
25218
25242
  mcpProxyServerUrl.searchParams.append("args", args);
25219
25243
  mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env));
25244
+ const proxyFullAddress = config.MCP_PROXY_FULL_ADDRESS.value;
25245
+ if (proxyFullAddress) {
25246
+ mcpProxyServerUrl.searchParams.append(
25247
+ "proxyFullAddress",
25248
+ proxyFullAddress
25249
+ );
25250
+ }
25220
25251
  transportOptions = {
25221
25252
  authProvider: serverAuthProvider,
25222
25253
  eventSourceInit: {
@@ -25230,9 +25261,17 @@ function useConnection({
25230
25261
  }
25231
25262
  };
25232
25263
  break;
25233
- case "sse":
25264
+ }
25265
+ case "sse": {
25234
25266
  mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
25235
25267
  mcpProxyServerUrl.searchParams.append("url", sseUrl);
25268
+ const proxyFullAddressSSE = config.MCP_PROXY_FULL_ADDRESS.value;
25269
+ if (proxyFullAddressSSE) {
25270
+ mcpProxyServerUrl.searchParams.append(
25271
+ "proxyFullAddress",
25272
+ proxyFullAddressSSE
25273
+ );
25274
+ }
25236
25275
  transportOptions = {
25237
25276
  eventSourceInit: {
25238
25277
  fetch: (url, init) => fetch(url, {
@@ -25245,6 +25284,7 @@ function useConnection({
25245
25284
  }
25246
25285
  };
25247
25286
  break;
25287
+ }
25248
25288
  case "streamable-http":
25249
25289
  mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
25250
25290
  mcpProxyServerUrl.searchParams.append("url", sseUrl);
@@ -25288,12 +25328,6 @@ function useConnection({
25288
25328
  return Promise.resolve();
25289
25329
  };
25290
25330
  }
25291
- if (onStdErrNotification) {
25292
- client2.setNotificationHandler(
25293
- StdErrNotificationSchema,
25294
- onStdErrNotification
25295
- );
25296
- }
25297
25331
  let capabilities;
25298
25332
  try {
25299
25333
  const transport = transportType === "streamable-http" ? new StreamableHTTPClientTransport(mcpProxyServerUrl, {
@@ -25354,6 +25388,15 @@ function useConnection({
25354
25388
  if ((capabilities == null ? void 0 : capabilities.logging) && defaultLoggingLevel) {
25355
25389
  lastRequest = "logging/setLevel";
25356
25390
  await client2.setLoggingLevel(defaultLoggingLevel);
25391
+ pushHistory(
25392
+ {
25393
+ method: "logging/setLevel",
25394
+ params: {
25395
+ level: defaultLoggingLevel
25396
+ }
25397
+ },
25398
+ {}
25399
+ );
25357
25400
  lastRequest = "";
25358
25401
  }
25359
25402
  if (onElicitationRequest) {
@@ -26558,6 +26601,7 @@ const OAuthFlowProgress = ({
26558
26601
  updateAuthState,
26559
26602
  proceedToNextStep
26560
26603
  }) => {
26604
+ const { toast: toast2 } = useToast();
26561
26605
  const provider = reactExports.useMemo(
26562
26606
  () => new DebugInspectorOAuthClientProvider(serverUrl),
26563
26607
  [serverUrl]
@@ -26688,11 +26732,24 @@ const OAuthFlowProgress = ({
26688
26732
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
26689
26733
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs break-all", children: authState.authorizationUrl }),
26690
26734
  /* @__PURE__ */ jsxRuntimeExports.jsx(
26691
- "a",
26735
+ "button",
26692
26736
  {
26693
- href: authState.authorizationUrl,
26694
- target: "_blank",
26695
- rel: "noopener noreferrer",
26737
+ onClick: () => {
26738
+ try {
26739
+ validateRedirectUrl(authState.authorizationUrl);
26740
+ window.open(
26741
+ authState.authorizationUrl,
26742
+ "_blank",
26743
+ "noopener noreferrer"
26744
+ );
26745
+ } catch (error) {
26746
+ toast2({
26747
+ title: "Invalid URL",
26748
+ description: error instanceof Error ? error.message : "The authorization URL is not valid",
26749
+ variant: "destructive"
26750
+ });
26751
+ }
26752
+ },
26696
26753
  className: "flex items-center text-blue-500 hover:text-blue-700",
26697
26754
  "aria-label": "Open authorization URL in new tab",
26698
26755
  title: "Open authorization URL",
@@ -26778,7 +26835,18 @@ const OAuthFlowProgress = ({
26778
26835
  Button,
26779
26836
  {
26780
26837
  variant: "outline",
26781
- onClick: () => window.open(authState.authorizationUrl, "_blank"),
26838
+ onClick: () => {
26839
+ try {
26840
+ validateRedirectUrl(authState.authorizationUrl);
26841
+ window.open(authState.authorizationUrl, "_blank");
26842
+ } catch (error) {
26843
+ toast2({
26844
+ title: "Invalid URL",
26845
+ description: error instanceof Error ? error.message : "The authorization URL is not valid",
26846
+ variant: "destructive"
26847
+ });
26848
+ }
26849
+ },
26782
26850
  children: "Open in New Tab"
26783
26851
  }
26784
26852
  )
@@ -26906,6 +26974,20 @@ const AuthDebugger = ({
26906
26974
  while (currentState.oauthStep !== "complete") {
26907
26975
  await oauthMachine.executeStep(currentState);
26908
26976
  if (currentState.oauthStep === "authorization_code" && currentState.authorizationUrl) {
26977
+ try {
26978
+ validateRedirectUrl(currentState.authorizationUrl);
26979
+ } catch (error) {
26980
+ updateAuthState({
26981
+ ...currentState,
26982
+ isInitiatingAuth: false,
26983
+ latestError: error instanceof Error ? error : new Error(String(error)),
26984
+ statusMessage: {
26985
+ type: "error",
26986
+ message: `Invalid authorization URL: ${error instanceof Error ? error.message : String(error)}`
26987
+ }
26988
+ });
26989
+ return;
26990
+ }
26909
26991
  sessionStorage.setItem(
26910
26992
  SESSION_KEYS.AUTH_DEBUGGER_STATE,
26911
26993
  JSON.stringify(currentState)
@@ -30313,15 +30395,9 @@ function getWindowScrollBarX(element, rect) {
30313
30395
  }
30314
30396
  return rect.left + leftScroll;
30315
30397
  }
30316
- function getHTMLOffset(documentElement, scroll, ignoreScrollbarX) {
30317
- if (ignoreScrollbarX === void 0) {
30318
- ignoreScrollbarX = false;
30319
- }
30398
+ function getHTMLOffset(documentElement, scroll) {
30320
30399
  const htmlRect = documentElement.getBoundingClientRect();
30321
- const x = htmlRect.left + scroll.scrollLeft - (ignoreScrollbarX ? 0 : (
30322
- // RTL <body> scrollbar.
30323
- getWindowScrollBarX(documentElement, htmlRect)
30324
- ));
30400
+ const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);
30325
30401
  const y = htmlRect.top + scroll.scrollTop;
30326
30402
  return {
30327
30403
  x,
@@ -30359,7 +30435,7 @@ function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
30359
30435
  offsets.y = offsetRect.y + offsetParent.clientTop;
30360
30436
  }
30361
30437
  }
30362
- const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll, true) : createCoords(0);
30438
+ const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
30363
30439
  return {
30364
30440
  width: rect.width * scale.x,
30365
30441
  height: rect.height * scale.y,
@@ -30388,6 +30464,7 @@ function getDocumentRect(element) {
30388
30464
  y
30389
30465
  };
30390
30466
  }
30467
+ const SCROLLBAR_MAX = 25;
30391
30468
  function getViewportRect(element, strategy) {
30392
30469
  const win = getWindow(element);
30393
30470
  const html = getDocumentElement(element);
@@ -30405,6 +30482,19 @@ function getViewportRect(element, strategy) {
30405
30482
  y = visualViewport.offsetTop;
30406
30483
  }
30407
30484
  }
30485
+ const windowScrollbarX = getWindowScrollBarX(html);
30486
+ if (windowScrollbarX <= 0) {
30487
+ const doc = html.ownerDocument;
30488
+ const body = doc.body;
30489
+ const bodyStyles = getComputedStyle(body);
30490
+ const bodyMarginInline = doc.compatMode === "CSS1Compat" ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;
30491
+ const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);
30492
+ if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {
30493
+ width -= clippingStableScrollbarWidth;
30494
+ }
30495
+ } else if (windowScrollbarX <= SCROLLBAR_MAX) {
30496
+ width += windowScrollbarX;
30497
+ }
30408
30498
  return {
30409
30499
  width,
30410
30500
  height,
@@ -36382,7 +36472,7 @@ const useTheme = () => {
36382
36472
  [theme, setThemeWithSideEffect]
36383
36473
  );
36384
36474
  };
36385
- const version = "0.16.4";
36475
+ const version = "0.16.6";
36386
36476
  var [createTooltipContext, createTooltipScope] = createContextScope("Tooltip", [
36387
36477
  createPopperScope
36388
36478
  ]);
@@ -36887,8 +36977,6 @@ const Sidebar = ({
36887
36977
  setOauthScope,
36888
36978
  onConnect,
36889
36979
  onDisconnect,
36890
- stdErrNotifications,
36891
- clearStdErrNotifications,
36892
36980
  logLevel,
36893
36981
  sendLogLevelRequest,
36894
36982
  loggingSupported,
@@ -37030,6 +37118,7 @@ const Sidebar = ({
37030
37118
  placeholder: "Command",
37031
37119
  value: command,
37032
37120
  onChange: (e) => setCommand(e.target.value),
37121
+ onBlur: (e) => setCommand(e.target.value.trim()),
37033
37122
  className: "font-mono"
37034
37123
  }
37035
37124
  )
@@ -37499,30 +37588,7 @@ const Sidebar = ({
37499
37588
  ]
37500
37589
  }
37501
37590
  )
37502
- ] }),
37503
- stdErrNotifications.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 border-t border-gray-200 pt-4", children: [
37504
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
37505
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: "Error output from MCP server" }),
37506
- /* @__PURE__ */ jsxRuntimeExports.jsx(
37507
- Button,
37508
- {
37509
- variant: "outline",
37510
- size: "sm",
37511
- onClick: clearStdErrNotifications,
37512
- className: "h-8 px-2",
37513
- children: "Clear"
37514
- }
37515
- )
37516
- ] }),
37517
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 max-h-80 overflow-y-auto", children: stdErrNotifications.map((notification, index2) => /* @__PURE__ */ jsxRuntimeExports.jsx(
37518
- "div",
37519
- {
37520
- className: "text-sm text-red-500 font-mono py-2 border-b border-gray-200 last:border-b-0",
37521
- children: notification.params.content
37522
- },
37523
- index2
37524
- )) })
37525
- ] }) })
37591
+ ] })
37526
37592
  ] })
37527
37593
  ] }) }),
37528
37594
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 border-t", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
@@ -38512,7 +38578,6 @@ const App = () => {
38512
38578
  const [transportType, setTransportType] = reactExports.useState(getInitialTransportType);
38513
38579
  const [logLevel, setLogLevel] = reactExports.useState("debug");
38514
38580
  const [notifications, setNotifications] = reactExports.useState([]);
38515
- const [stdErrNotifications, setStdErrNotifications] = reactExports.useState([]);
38516
38581
  const [roots, setRoots] = reactExports.useState([]);
38517
38582
  const [env, setEnv] = reactExports.useState({});
38518
38583
  const [config, setConfig] = reactExports.useState(
@@ -38591,12 +38656,6 @@ const App = () => {
38591
38656
  onNotification: (notification) => {
38592
38657
  setNotifications((prev) => [...prev, notification]);
38593
38658
  },
38594
- onStdErrNotification: (notification) => {
38595
- setStdErrNotifications((prev) => [
38596
- ...prev,
38597
- notification
38598
- ]);
38599
- },
38600
38659
  onPendingRequest: (request, resolve, reject) => {
38601
38660
  setPendingSampleRequests((prev) => [
38602
38661
  ...prev,
@@ -39034,9 +39093,6 @@ const App = () => {
39034
39093
  );
39035
39094
  setLogLevel(level);
39036
39095
  };
39037
- const clearStdErrNotifications = () => {
39038
- setStdErrNotifications([]);
39039
- };
39040
39096
  const AuthDebuggerWrapper = () => /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "auth", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
39041
39097
  AuthDebugger,
39042
39098
  {
@@ -39048,13 +39104,13 @@ const App = () => {
39048
39104
  ) });
39049
39105
  if (window.location.pathname === "/oauth/callback") {
39050
39106
  const OAuthCallback = React.lazy(
39051
- () => __vitePreload(() => import("./OAuthCallback-DyaAiF-O.js"), true ? [] : void 0)
39107
+ () => __vitePreload(() => import("./OAuthCallback-Cmffn8om.js"), true ? [] : void 0)
39052
39108
  );
39053
39109
  return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
39054
39110
  }
39055
39111
  if (window.location.pathname === "/oauth/callback/debug") {
39056
39112
  const OAuthDebugCallback = React.lazy(
39057
- () => __vitePreload(() => import("./OAuthDebugCallback-BD-zJllm.js"), true ? [] : void 0)
39113
+ () => __vitePreload(() => import("./OAuthDebugCallback-BhzcPtuF.js"), true ? [] : void 0)
39058
39114
  );
39059
39115
  return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
39060
39116
  }
@@ -39096,11 +39152,9 @@ const App = () => {
39096
39152
  setOauthScope,
39097
39153
  onConnect: connectMcpServer,
39098
39154
  onDisconnect: disconnectMcpServer,
39099
- stdErrNotifications,
39100
39155
  logLevel,
39101
39156
  sendLogLevelRequest,
39102
- loggingSupported: !!(serverCapabilities == null ? void 0 : serverCapabilities.logging) || false,
39103
- clearStdErrNotifications
39157
+ loggingSupported: !!(serverCapabilities == null ? void 0 : serverCapabilities.logging) || false
39104
39158
  }
39105
39159
  ),
39106
39160
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -944,9 +944,6 @@ video {
944
944
  .h-screen {
945
945
  height: 100vh;
946
946
  }
947
- .max-h-80 {
948
- max-height: 20rem;
949
- }
950
947
  .max-h-96 {
951
948
  max-height: 24rem;
952
949
  }
@@ -1407,9 +1404,6 @@ video {
1407
1404
  .pr-8 {
1408
1405
  padding-right: 2rem;
1409
1406
  }
1410
- .pt-4 {
1411
- padding-top: 1rem;
1412
- }
1413
1407
  .text-left {
1414
1408
  text-align: left;
1415
1409
  }
@@ -1625,6 +1619,10 @@ video {
1625
1619
  .ring-offset-background {
1626
1620
  --tw-ring-offset-color: hsl(var(--background));
1627
1621
  }
1622
+ .blur {
1623
+ --tw-blur: blur(8px);
1624
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1625
+ }
1628
1626
  .filter {
1629
1627
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1630
1628
  }
@@ -1768,10 +1766,6 @@ h1 {
1768
1766
  color: hsl(var(--muted-foreground));
1769
1767
  }
1770
1768
 
1771
- .last\:border-b-0:last-child {
1772
- border-bottom-width: 0px;
1773
- }
1774
-
1775
1769
  .hover\:border-\[\#646cff\]:hover {
1776
1770
  --tw-border-opacity: 1;
1777
1771
  border-color: rgb(100 108 255 / var(--tw-border-opacity, 1));
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/mcp.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MCP Inspector</title>
8
- <script type="module" crossorigin src="/assets/index-MFoeZZBS.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BWZfLo5E.css">
8
+ <script type="module" crossorigin src="/assets/index-B_qh-Btq.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-C217Ot8c.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root" class="w-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelcontextprotocol/inspector",
3
- "version": "0.16.4",
3
+ "version": "0.16.6",
4
4
  "description": "Model Context Protocol inspector",
5
5
  "license": "MIT",
6
6
  "author": "Anthropic, PBC (https://anthropic.com)",
@@ -41,16 +41,16 @@
41
41
  "prettier-fix": "prettier --write .",
42
42
  "prettier-check": "prettier --check .",
43
43
  "lint": "prettier --check . && cd client && npm run lint",
44
- "prepare": "npm run build",
44
+ "prepare": "husky && npm run build",
45
45
  "publish-all": "npm publish --workspaces --access public && npm publish --access public",
46
46
  "update-version": "node scripts/update-version.js",
47
47
  "check-version": "node scripts/check-version-consistency.js"
48
48
  },
49
49
  "dependencies": {
50
- "@modelcontextprotocol/inspector-cli": "^0.16.4",
51
- "@modelcontextprotocol/inspector-client": "^0.16.4",
52
- "@modelcontextprotocol/inspector-server": "^0.16.4",
53
- "@modelcontextprotocol/sdk": "^1.17.2",
50
+ "@modelcontextprotocol/inspector-cli": "^0.16.6",
51
+ "@modelcontextprotocol/inspector-client": "^0.16.6",
52
+ "@modelcontextprotocol/inspector-server": "^0.16.6",
53
+ "@modelcontextprotocol/sdk": "^1.17.5",
54
54
  "concurrently": "^9.2.0",
55
55
  "open": "^10.2.0",
56
56
  "shell-quote": "^1.8.3",
@@ -63,12 +63,19 @@
63
63
  "@types/jest": "^29.5.14",
64
64
  "@types/node": "^22.17.0",
65
65
  "@types/shell-quote": "^1.7.5",
66
+ "husky": "^9.1.7",
66
67
  "jest-fixed-jsdom": "^0.0.9",
68
+ "lint-staged": "^16.1.5",
67
69
  "prettier": "^3.6.2",
68
70
  "rimraf": "^6.0.1",
69
71
  "typescript": "^5.4.2"
70
72
  },
71
73
  "engines": {
72
74
  "node": ">=22.7.5"
75
+ },
76
+ "lint-staged": {
77
+ "**/*.{js,ts,jsx,tsx,json,md}": [
78
+ "prettier --write"
79
+ ]
73
80
  }
74
81
  }
@@ -131,10 +131,10 @@ const createTransport = async (req) => {
131
131
  console.log("Query parameters:", JSON.stringify(query));
132
132
  const transportType = query.transportType;
133
133
  if (transportType === "stdio") {
134
- const command = query.command;
134
+ const command = query.command.trim();
135
135
  const origArgs = shellParseArgs(query.args);
136
136
  const queryEnv = query.env ? JSON.parse(query.env) : {};
137
- const env = { ...process.env, ...defaultEnvironment, ...queryEnv };
137
+ const env = { ...defaultEnvironment, ...process.env, ...queryEnv };
138
138
  const { cmd, args } = findActualExecutable(command, origArgs);
139
139
  console.log(`STDIO transport: command=${cmd}, args=${args}`);
140
140
  const transport = new StdioClientTransport({
@@ -280,7 +280,6 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
280
280
  let serverTransport;
281
281
  try {
282
282
  serverTransport = await createTransport(req);
283
- console.log("Created server transport");
284
283
  }
285
284
  catch (error) {
286
285
  if (error instanceof SseError && error.code === 401) {
@@ -290,32 +289,89 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
290
289
  }
291
290
  throw error;
292
291
  }
293
- const webAppTransport = new SSEServerTransport("/message", res);
294
- console.log("Created client transport");
292
+ const proxyFullAddress = req.query.proxyFullAddress || "";
293
+ const prefix = proxyFullAddress || "";
294
+ const endpoint = `${prefix}/message`;
295
+ const webAppTransport = new SSEServerTransport(endpoint, res);
295
296
  webAppTransports.set(webAppTransport.sessionId, webAppTransport);
297
+ console.log("Created client transport");
296
298
  serverTransports.set(webAppTransport.sessionId, serverTransport);
299
+ console.log("Created server transport");
297
300
  await webAppTransport.start();
298
301
  serverTransport.stderr.on("data", (chunk) => {
299
302
  if (chunk.toString().includes("MODULE_NOT_FOUND")) {
303
+ // Server command not found, remove transports
304
+ const message = "Command not found, transports removed";
300
305
  webAppTransport.send({
301
306
  jsonrpc: "2.0",
302
- method: "notifications/stderr",
307
+ method: "notifications/message",
303
308
  params: {
304
- content: "Command not found, transports removed",
309
+ level: "emergency",
310
+ logger: "proxy",
311
+ data: {
312
+ message,
313
+ },
305
314
  },
306
315
  });
307
316
  webAppTransport.close();
308
317
  serverTransport.close();
309
318
  webAppTransports.delete(webAppTransport.sessionId);
310
319
  serverTransports.delete(webAppTransport.sessionId);
311
- console.error("Command not found, transports removed");
320
+ console.error(message);
312
321
  }
313
322
  else {
323
+ // Inspect message and attempt to assign a RFC 5424 Syslog Protocol level
324
+ let level;
325
+ let message = chunk.toString().trim();
326
+ let ucMsg = chunk.toString().toUpperCase();
327
+ if (ucMsg.includes("DEBUG")) {
328
+ level = "debug";
329
+ }
330
+ else if (ucMsg.includes("INFO")) {
331
+ level = "info";
332
+ }
333
+ else if (ucMsg.includes("NOTICE")) {
334
+ level = "notice";
335
+ }
336
+ else if (ucMsg.includes("WARN")) {
337
+ level = "warning";
338
+ }
339
+ else if (ucMsg.includes("ERROR")) {
340
+ level = "error";
341
+ }
342
+ else if (ucMsg.includes("CRITICAL")) {
343
+ level = "critical";
344
+ }
345
+ else if (ucMsg.includes("ALERT")) {
346
+ level = "alert";
347
+ }
348
+ else if (ucMsg.includes("EMERGENCY")) {
349
+ level = "emergency";
350
+ }
351
+ else if (ucMsg.includes("SIGINT")) {
352
+ message = "SIGINT received. Server shutdown.";
353
+ level = "emergency";
354
+ }
355
+ else if (ucMsg.includes("SIGHUP")) {
356
+ message = "SIGHUP received. Server shutdown.";
357
+ level = "emergency";
358
+ }
359
+ else if (ucMsg.includes("SIGTERM")) {
360
+ message = "SIGTERM received. Server shutdown.";
361
+ level = "emergency";
362
+ }
363
+ else {
364
+ level = "info";
365
+ }
314
366
  webAppTransport.send({
315
367
  jsonrpc: "2.0",
316
- method: "notifications/stderr",
368
+ method: "notifications/message",
317
369
  params: {
318
- content: chunk.toString(),
370
+ level,
371
+ logger: "stdio",
372
+ data: {
373
+ message,
374
+ },
319
375
  },
320
376
  });
321
377
  }
@@ -332,7 +388,7 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
332
388
  });
333
389
  app.get("/sse", originValidationMiddleware, authMiddleware, async (req, res) => {
334
390
  try {
335
- console.log("New SSE connection request. NOTE: The sse transport is deprecated and has been replaced by StreamableHttp");
391
+ console.log("New SSE connection request. NOTE: The SSE transport is deprecated and has been replaced by StreamableHttp");
336
392
  let serverTransport;
337
393
  try {
338
394
  serverTransport = await createTransport(req);
@@ -357,7 +413,10 @@ app.get("/sse", originValidationMiddleware, authMiddleware, async (req, res) =>
357
413
  }
358
414
  }
359
415
  if (serverTransport) {
360
- const webAppTransport = new SSEServerTransport("/message", res);
416
+ const proxyFullAddress = req.query.proxyFullAddress || "";
417
+ const prefix = proxyFullAddress || "";
418
+ const endpoint = `${prefix}/message`;
419
+ const webAppTransport = new SSEServerTransport(endpoint, res);
361
420
  webAppTransports.set(webAppTransport.sessionId, webAppTransport);
362
421
  console.log("Created client transport");
363
422
  serverTransports.set(webAppTransport.sessionId, serverTransport);