@modelcontextprotocol/inspector 0.17.1 → 0.17.5

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
@@ -236,13 +236,15 @@ ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
236
236
 
237
237
  The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
238
238
 
239
- | Setting | Description | Default |
240
- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
241
- | `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
242
- | `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
243
- | `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
244
- | `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
245
- | `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts (works with authentication enabled). Only as environment var, not configurable in browser. | true |
239
+ | Setting | Description | Default |
240
+ | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
241
+ | `MCP_SERVER_REQUEST_TIMEOUT` | Client-side timeout (ms) - Inspector will cancel the request if no response is received within this time. Note: servers may have their own timeouts | 300000 |
242
+ | `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
243
+ | `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
244
+ | `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
245
+ | `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts (works with authentication enabled). Only as environment var, not configurable in browser. | true |
246
+
247
+ **Note on Timeouts:** The timeout settings above control when the Inspector (as an MCP client) will cancel requests. These are independent of any server-side timeouts. For example, if a server tool has a 10-minute timeout but the Inspector's timeout is set to 30 seconds, the Inspector will cancel the request after 30 seconds. Conversely, if the Inspector's timeout is 10 minutes but the server times out after 30 seconds, you'll receive the server's timeout error. For tools that require user interaction (like elicitation) or long-running operations, ensure the Inspector's timeout is set appropriately.
246
248
 
247
249
  These settings can be adjusted in real-time through the UI and will persist across sessions.
248
250
 
@@ -361,7 +363,7 @@ http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
361
363
  You can also set initial config settings via query params, for example:
362
364
 
363
365
  ```
364
- http://localhost:6274/?MCP_SERVER_REQUEST_TIMEOUT=10000&MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS=false&MCP_PROXY_FULL_ADDRESS=http://10.1.1.22:5577
366
+ http://localhost:6274/?MCP_SERVER_REQUEST_TIMEOUT=60000&MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS=false&MCP_PROXY_FULL_ADDRESS=http://10.1.1.22:5577
365
367
  ```
366
368
 
367
369
  Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
@@ -456,6 +458,17 @@ npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --me
456
458
  | **Automation** | N/A | Ideal for CI/CD pipelines, batch processing, and integration with coding assistants |
457
459
  | **Learning MCP** | Rich visual interface helps new users understand server capabilities | Simplified commands for focused learning of specific endpoints |
458
460
 
461
+ ## Tool Input Validation Guidelines
462
+
463
+ When implementing or modifying tool input parameter handling in the Inspector:
464
+
465
+ - **Omit optional fields with empty values** - When processing form inputs, omit empty strings or null values for optional parameters, UNLESS the field has an explicit default value in the schema that matches the current value
466
+ - **Preserve explicit default values** - If a field schema contains an explicit default (e.g., `default: null`), and the current value matches that default, include it in the request. This is a meaningful value the tool expects
467
+ - **Always include required fields** - Preserve required field values even when empty, allowing the MCP server to validate and return appropriate error messages
468
+ - **Defer deep validation to the server** - Implement basic field presence checking in the Inspector client, but rely on the MCP server for parameter validation according to its schema
469
+
470
+ These guidelines maintain clean parameter passing and proper separation of concerns between the Inspector client and MCP servers.
471
+
459
472
  ## License
460
473
 
461
474
  This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
@@ -1,7 +1,8 @@
1
1
  // List available prompts
2
- export async function listPrompts(client) {
2
+ export async function listPrompts(client, metadata) {
3
3
  try {
4
- const response = await client.listPrompts();
4
+ const params = metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {};
5
+ const response = await client.listPrompts(params);
5
6
  return response;
6
7
  }
7
8
  catch (error) {
@@ -9,7 +10,7 @@ export async function listPrompts(client) {
9
10
  }
10
11
  }
11
12
  // Get a prompt
12
- export async function getPrompt(client, name, args) {
13
+ export async function getPrompt(client, name, args, metadata) {
13
14
  try {
14
15
  // Convert all arguments to strings for prompt arguments
15
16
  const stringArgs = {};
@@ -26,10 +27,14 @@ export async function getPrompt(client, name, args) {
26
27
  }
27
28
  }
28
29
  }
29
- const response = await client.getPrompt({
30
+ const params = {
30
31
  name,
31
32
  arguments: stringArgs,
32
- });
33
+ };
34
+ if (metadata && Object.keys(metadata).length > 0) {
35
+ params._meta = metadata;
36
+ }
37
+ const response = await client.getPrompt(params);
33
38
  return response;
34
39
  }
35
40
  catch (error) {
@@ -1,7 +1,8 @@
1
1
  // List available resources
2
- export async function listResources(client) {
2
+ export async function listResources(client, metadata) {
3
3
  try {
4
- const response = await client.listResources();
4
+ const params = metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {};
5
+ const response = await client.listResources(params);
5
6
  return response;
6
7
  }
7
8
  catch (error) {
@@ -9,9 +10,13 @@ export async function listResources(client) {
9
10
  }
10
11
  }
11
12
  // Read a resource
12
- export async function readResource(client, uri) {
13
+ export async function readResource(client, uri, metadata) {
13
14
  try {
14
- const response = await client.readResource({ uri });
15
+ const params = { uri };
16
+ if (metadata && Object.keys(metadata).length > 0) {
17
+ params._meta = metadata;
18
+ }
19
+ const response = await client.readResource(params);
15
20
  return response;
16
21
  }
17
22
  catch (error) {
@@ -19,9 +24,10 @@ export async function readResource(client, uri) {
19
24
  }
20
25
  }
21
26
  // List resource templates
22
- export async function listResourceTemplates(client) {
27
+ export async function listResourceTemplates(client, metadata) {
23
28
  try {
24
- const response = await client.listResourceTemplates();
29
+ const params = metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {};
30
+ const response = await client.listResourceTemplates(params);
25
31
  return response;
26
32
  }
27
33
  catch (error) {
@@ -1,6 +1,7 @@
1
- export async function listTools(client) {
1
+ export async function listTools(client, metadata) {
2
2
  try {
3
- const response = await client.listTools();
3
+ const params = metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {};
4
+ const response = await client.listTools(params);
4
5
  return response;
5
6
  }
6
7
  catch (error) {
@@ -42,9 +43,9 @@ function convertParameters(tool, params) {
42
43
  }
43
44
  return result;
44
45
  }
45
- export async function callTool(client, name, args) {
46
+ export async function callTool(client, name, args, generalMetadata, toolSpecificMetadata) {
46
47
  try {
47
- const toolsResponse = await listTools(client);
48
+ const toolsResponse = await listTools(client, generalMetadata);
48
49
  const tools = toolsResponse.tools;
49
50
  const tool = tools.find((t) => t.name === name);
50
51
  let convertedArgs = args;
@@ -62,9 +63,21 @@ export async function callTool(client, name, args) {
62
63
  convertedArgs = { ...args, ...convertedStringArgs };
63
64
  }
64
65
  }
66
+ // Merge general metadata with tool-specific metadata
67
+ // Tool-specific metadata takes precedence over general metadata
68
+ let mergedMetadata;
69
+ if (generalMetadata || toolSpecificMetadata) {
70
+ mergedMetadata = {
71
+ ...(generalMetadata || {}),
72
+ ...(toolSpecificMetadata || {}),
73
+ };
74
+ }
65
75
  const response = await client.callTool({
66
76
  name: name,
67
77
  arguments: convertedArgs,
78
+ _meta: mergedMetadata && Object.keys(mergedMetadata).length > 0
79
+ ? mergedMetadata
80
+ : undefined,
68
81
  });
69
82
  return response;
70
83
  }
@@ -71,36 +71,36 @@ async function callMethod(args) {
71
71
  let result;
72
72
  // Tools methods
73
73
  if (args.method === "tools/list") {
74
- result = await listTools(client);
74
+ result = await listTools(client, args.metadata);
75
75
  }
76
76
  else if (args.method === "tools/call") {
77
77
  if (!args.toolName) {
78
78
  throw new Error("Tool name is required for tools/call method. Use --tool-name to specify the tool name.");
79
79
  }
80
- result = await callTool(client, args.toolName, args.toolArg || {});
80
+ result = await callTool(client, args.toolName, args.toolArg || {}, args.metadata, args.toolMeta);
81
81
  }
82
82
  // Resources methods
83
83
  else if (args.method === "resources/list") {
84
- result = await listResources(client);
84
+ result = await listResources(client, args.metadata);
85
85
  }
86
86
  else if (args.method === "resources/read") {
87
87
  if (!args.uri) {
88
88
  throw new Error("URI is required for resources/read method. Use --uri to specify the resource URI.");
89
89
  }
90
- result = await readResource(client, args.uri);
90
+ result = await readResource(client, args.uri, args.metadata);
91
91
  }
92
92
  else if (args.method === "resources/templates/list") {
93
- result = await listResourceTemplates(client);
93
+ result = await listResourceTemplates(client, args.metadata);
94
94
  }
95
95
  // Prompts methods
96
96
  else if (args.method === "prompts/list") {
97
- result = await listPrompts(client);
97
+ result = await listPrompts(client, args.metadata);
98
98
  }
99
99
  else if (args.method === "prompts/get") {
100
100
  if (!args.promptName) {
101
101
  throw new Error("Prompt name is required for prompts/get method. Use --prompt-name to specify the prompt name.");
102
102
  }
103
- result = await getPrompt(client, args.promptName, args.promptArgs || {});
103
+ result = await getPrompt(client, args.promptName, args.promptArgs || {}, args.metadata);
104
104
  }
105
105
  // Logging methods
106
106
  else if (args.method === "logging/setLevel") {
@@ -207,7 +207,12 @@ function parseArgs() {
207
207
  //
208
208
  // HTTP headers
209
209
  //
210
- .option("--header <headers...>", 'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)', parseHeaderPair, {});
210
+ .option("--header <headers...>", 'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)', parseHeaderPair, {})
211
+ //
212
+ // Metadata options
213
+ //
214
+ .option("--metadata <pairs...>", "General metadata as key=value pairs (applied to all methods)", parseKeyValuePair, {})
215
+ .option("--tool-metadata <pairs...>", "Tool-specific metadata as key=value pairs (for tools/call method only)", parseKeyValuePair, {});
211
216
  // Parse only the arguments before --
212
217
  program.parse(preArgs);
213
218
  const options = program.opts();
@@ -221,6 +226,18 @@ function parseArgs() {
221
226
  target: finalArgs,
222
227
  ...options,
223
228
  headers: options.header, // commander.js uses 'header' field, map to 'headers'
229
+ metadata: options.metadata
230
+ ? Object.fromEntries(Object.entries(options.metadata).map(([key, value]) => [
231
+ key,
232
+ String(value),
233
+ ]))
234
+ : undefined,
235
+ toolMeta: options.toolMetadata
236
+ ? Object.fromEntries(Object.entries(options.toolMetadata).map(([key, value]) => [
237
+ key,
238
+ String(value),
239
+ ]))
240
+ : undefined,
224
241
  };
225
242
  }
226
243
  async function main() {
@@ -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-9_nBaufL.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-CneFQkmB.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-9_nBaufL.js";
1
+ import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-CneFQkmB.js";
2
2
  const OAuthDebugCallback = ({ onConnect }) => {
3
3
  reactExports.useEffect(() => {
4
4
  let isProcessed = false;