Skip to content

Commit 283371c

Browse files
2 parents 5b54ce1 + ad39ec2 commit 283371c

File tree

11 files changed

+398
-106
lines changed

11 files changed

+398
-106
lines changed

README.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,13 @@ The MCP Inspector includes a proxy server that can run and communicate with loca
122122

123123
The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
124124

125-
| Setting | Description | Default |
126-
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------- |
127-
| `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
128-
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
129-
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
130-
| `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 | "" |
125+
| Setting | Description | Default |
126+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
127+
| `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
128+
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
129+
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
130+
| `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 | "" |
131+
| `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts. Only as environment var, not configurable in browser. | true |
131132

132133
These settings can be adjusted in real-time through the UI and will persist across sessions.
133134

@@ -160,9 +161,24 @@ Example server configuration file:
160161
}
161162
}
162163
```
163-
164164
> **Tip:** You can easily generate this configuration format using the **Server Entry** and **Servers File** buttons in the Inspector UI, as described in the Servers File Export section above.
165165
166+
You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
167+
168+
```
169+
http://localhost:6274/?transport=sse&serverUrl=http://localhost:8787/sse
170+
http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:8787/mcp
171+
http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
172+
```
173+
174+
You can also set initial config settings via query params, for example:
175+
176+
```
177+
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
178+
```
179+
180+
Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
181+
166182
### From this repository
167183

168184
If you're working on the inspector itself:

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-cli",
3-
"version": "0.11.0",
3+
"version": "0.12.0",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

client/bin/start.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env node
22

3+
import open from "open";
34
import { resolve, dirname } from "path";
45
import { spawnPromise } from "spawn-rx";
56
import { fileURLToPath } from "url";
@@ -99,6 +100,9 @@ async function main() {
99100

100101
if (serverOk) {
101102
try {
103+
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
104+
open(`http://127.0.0.1:${CLIENT_PORT}`);
105+
}
102106
await spawnPromise("node", [inspectorClientPath], {
103107
env: { ...process.env, PORT: CLIENT_PORT },
104108
signal: abort.signal,

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-client",
3-
"version": "0.11.0",
3+
"version": "0.12.0",
44
"description": "Client-side application for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

client/src/App.tsx

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,15 @@ import RootsTab from "./components/RootsTab";
4949
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
5050
import Sidebar from "./components/Sidebar";
5151
import ToolsTab from "./components/ToolsTab";
52-
import { DEFAULT_INSPECTOR_CONFIG } from "./lib/constants";
5352
import { InspectorConfig } from "./lib/configurationTypes";
54-
import { getMCPProxyAddress } from "./utils/configUtils";
53+
import {
54+
getMCPProxyAddress,
55+
getInitialSseUrl,
56+
getInitialTransportType,
57+
getInitialCommand,
58+
getInitialArgs,
59+
initializeInspectorConfig,
60+
} from "./utils/configUtils";
5561

5662
const CONFIG_LOCAL_STORAGE_KEY = "inspectorConfig_v1";
5763

@@ -71,26 +77,13 @@ const App = () => {
7177
prompts: null,
7278
tools: null,
7379
});
74-
const [command, setCommand] = useState<string>(() => {
75-
return localStorage.getItem("lastCommand") || "mcp-server-everything";
76-
});
77-
const [args, setArgs] = useState<string>(() => {
78-
return localStorage.getItem("lastArgs") || "";
79-
});
80+
const [command, setCommand] = useState<string>(getInitialCommand);
81+
const [args, setArgs] = useState<string>(getInitialArgs);
8082

81-
const [sseUrl, setSseUrl] = useState<string>(() => {
82-
return localStorage.getItem("lastSseUrl") || "http://localhost:3001/sse";
83-
});
83+
const [sseUrl, setSseUrl] = useState<string>(getInitialSseUrl);
8484
const [transportType, setTransportType] = useState<
8585
"stdio" | "sse" | "streamable-http"
86-
>(() => {
87-
return (
88-
(localStorage.getItem("lastTransportType") as
89-
| "stdio"
90-
| "sse"
91-
| "streamable-http") || "stdio"
92-
);
93-
});
86+
>(getInitialTransportType);
9487
const [logLevel, setLogLevel] = useState<LoggingLevel>("debug");
9588
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
9689
const [stdErrNotifications, setStdErrNotifications] = useState<
@@ -99,27 +92,9 @@ const App = () => {
9992
const [roots, setRoots] = useState<Root[]>([]);
10093
const [env, setEnv] = useState<Record<string, string>>({});
10194

102-
const [config, setConfig] = useState<InspectorConfig>(() => {
103-
const savedConfig = localStorage.getItem(CONFIG_LOCAL_STORAGE_KEY);
104-
if (savedConfig) {
105-
// merge default config with saved config
106-
const mergedConfig = {
107-
...DEFAULT_INSPECTOR_CONFIG,
108-
...JSON.parse(savedConfig),
109-
} as InspectorConfig;
110-
111-
// update description of keys to match the new description (in case of any updates to the default config description)
112-
Object.entries(mergedConfig).forEach(([key, value]) => {
113-
mergedConfig[key as keyof InspectorConfig] = {
114-
...value,
115-
label: DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].label,
116-
};
117-
});
118-
119-
return mergedConfig;
120-
}
121-
return DEFAULT_INSPECTOR_CONFIG;
122-
});
95+
const [config, setConfig] = useState<InspectorConfig>(() =>
96+
initializeInspectorConfig(CONFIG_LOCAL_STORAGE_KEY),
97+
);
12398
const [bearerToken, setBearerToken] = useState<string>(() => {
12499
return localStorage.getItem("lastBearerToken") || "";
125100
});
@@ -575,11 +550,24 @@ const App = () => {
575550
{!serverCapabilities?.resources &&
576551
!serverCapabilities?.prompts &&
577552
!serverCapabilities?.tools ? (
578-
<div className="flex items-center justify-center p-4">
579-
<p className="text-lg text-gray-500">
580-
The connected server does not support any MCP capabilities
581-
</p>
582-
</div>
553+
<>
554+
<div className="flex items-center justify-center p-4">
555+
<p className="text-lg text-gray-500">
556+
The connected server does not support any MCP
557+
capabilities
558+
</p>
559+
</div>
560+
<PingTab
561+
onPingClick={() => {
562+
void sendMCPRequest(
563+
{
564+
method: "ping" as const,
565+
},
566+
EmptyResultSchema,
567+
);
568+
}}
569+
/>
570+
</>
583571
) : (
584572
<>
585573
<ResourcesTab

client/src/components/ToolsTab.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ const ToolsTab = ({
133133
}}
134134
setSelectedItem={setSelectedTool}
135135
renderItem={(tool) => (
136-
<>
136+
<div className="flex flex-col items-start">
137137
<span className="flex-1">{tool.name}</span>
138-
<span className="text-sm text-gray-500 text-right">
138+
<span className="text-sm text-gray-500 text-left">
139139
{tool.description}
140140
</span>
141-
</>
141+
</div>
142142
)}
143143
title="Tools"
144144
buttonText={nextCursor ? "List More Tools" : "List Tools"}

client/src/lib/hooks/useConnection.ts

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22
import {
33
SSEClientTransport,
44
SseError,
5+
SSEClientTransportOptions,
56
} from "@modelcontextprotocol/sdk/client/sse.js";
6-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7+
import {
8+
StreamableHTTPClientTransport,
9+
StreamableHTTPClientTransportOptions,
10+
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
711
import {
812
ClientNotification,
913
ClientRequest,
@@ -279,29 +283,6 @@ export function useConnection({
279283
setConnectionStatus("error-connecting-to-proxy");
280284
return;
281285
}
282-
let mcpProxyServerUrl;
283-
switch (transportType) {
284-
case "stdio":
285-
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`);
286-
mcpProxyServerUrl.searchParams.append("command", command);
287-
mcpProxyServerUrl.searchParams.append("args", args);
288-
mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env));
289-
break;
290-
291-
case "sse":
292-
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
293-
mcpProxyServerUrl.searchParams.append("url", sseUrl);
294-
break;
295-
296-
case "streamable-http":
297-
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
298-
mcpProxyServerUrl.searchParams.append("url", sseUrl);
299-
break;
300-
}
301-
(mcpProxyServerUrl as URL).searchParams.append(
302-
"transportType",
303-
transportType,
304-
);
305286

306287
try {
307288
// Inject auth manually instead of using SSEClientTransport, because we're
@@ -320,21 +301,82 @@ export function useConnection({
320301
}
321302

322303
// Create appropriate transport
323-
const transportOptions = {
324-
eventSourceInit: {
325-
fetch: (
326-
url: string | URL | globalThis.Request,
327-
init: RequestInit | undefined,
328-
) => fetch(url, { ...init, headers }),
329-
},
330-
requestInit: {
331-
headers,
332-
},
333-
};
304+
let transportOptions:
305+
| StreamableHTTPClientTransportOptions
306+
| SSEClientTransportOptions;
307+
308+
let mcpProxyServerUrl;
309+
switch (transportType) {
310+
case "stdio":
311+
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`);
312+
mcpProxyServerUrl.searchParams.append("command", command);
313+
mcpProxyServerUrl.searchParams.append("args", args);
314+
mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env));
315+
transportOptions = {
316+
authProvider: serverAuthProvider,
317+
eventSourceInit: {
318+
fetch: (
319+
url: string | URL | globalThis.Request,
320+
init: RequestInit | undefined,
321+
) => fetch(url, { ...init, headers }),
322+
},
323+
requestInit: {
324+
headers,
325+
},
326+
};
327+
break;
328+
329+
case "sse":
330+
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
331+
mcpProxyServerUrl.searchParams.append("url", sseUrl);
332+
transportOptions = {
333+
authProvider: serverAuthProvider,
334+
eventSourceInit: {
335+
fetch: (
336+
url: string | URL | globalThis.Request,
337+
init: RequestInit | undefined,
338+
) => fetch(url, { ...init, headers }),
339+
},
340+
requestInit: {
341+
headers,
342+
},
343+
};
344+
break;
345+
346+
case "streamable-http":
347+
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
348+
mcpProxyServerUrl.searchParams.append("url", sseUrl);
349+
transportOptions = {
350+
authProvider: serverAuthProvider,
351+
eventSourceInit: {
352+
fetch: (
353+
url: string | URL | globalThis.Request,
354+
init: RequestInit | undefined,
355+
) => fetch(url, { ...init, headers }),
356+
},
357+
requestInit: {
358+
headers,
359+
},
360+
// TODO these should be configurable...
361+
reconnectionOptions: {
362+
maxReconnectionDelay: 30000,
363+
initialReconnectionDelay: 1000,
364+
reconnectionDelayGrowFactor: 1.5,
365+
maxRetries: 2,
366+
},
367+
};
368+
break;
369+
}
370+
(mcpProxyServerUrl as URL).searchParams.append(
371+
"transportType",
372+
transportType,
373+
);
374+
334375
const clientTransport =
335376
transportType === "streamable-http"
336377
? new StreamableHTTPClientTransport(mcpProxyServerUrl as URL, {
337378
sessionId: undefined,
379+
...transportOptions,
338380
})
339381
: new SSEClientTransport(mcpProxyServerUrl as URL, transportOptions);
340382

0 commit comments

Comments
 (0)