Skip to content

Commit 89d721e

Browse files
authored
Merge pull request modelcontextprotocol#333 from kentcdodds/patch-1
feat(client): add search param configuration
2 parents 24e8861 + b09d0e1 commit 89d721e

File tree

3 files changed

+132
-41
lines changed

3 files changed

+132
-41
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ Example server configuration file:
9393
}
9494
```
9595

96+
You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
97+
98+
```
99+
http://localhost:6274/?transport=sse&serverUrl=http://localhost:8787/sse
100+
http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:8787/mcp
101+
http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
102+
```
103+
104+
You can also set initial config settings via query params, for example:
105+
106+
```
107+
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
108+
```
109+
110+
Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
111+
96112
### From this repository
97113

98114
If you're working on the inspector itself:

client/src/App.tsx

Lines changed: 15 additions & 40 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
});

client/src/utils/configUtils.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { InspectorConfig } from "@/lib/configurationTypes";
2-
import { DEFAULT_MCP_PROXY_LISTEN_PORT } from "@/lib/constants";
2+
import {
3+
DEFAULT_MCP_PROXY_LISTEN_PORT,
4+
DEFAULT_INSPECTOR_CONFIG,
5+
} from "@/lib/constants";
36

47
export const getMCPProxyAddress = (config: InspectorConfig): string => {
58
const proxyFullAddress = config.MCP_PROXY_FULL_ADDRESS.value as string;
@@ -24,3 +27,100 @@ export const getMCPServerRequestMaxTotalTimeout = (
2427
): number => {
2528
return config.MCP_REQUEST_MAX_TOTAL_TIMEOUT.value as number;
2629
};
30+
31+
const getSearchParam = (key: string): string | null => {
32+
try {
33+
const url = new URL(window.location.href);
34+
return url.searchParams.get(key);
35+
} catch {
36+
return null;
37+
}
38+
};
39+
40+
export const getInitialTransportType = ():
41+
| "stdio"
42+
| "sse"
43+
| "streamable-http" => {
44+
const param = getSearchParam("transport");
45+
if (param === "stdio" || param === "sse" || param === "streamable-http") {
46+
return param;
47+
}
48+
return (
49+
(localStorage.getItem("lastTransportType") as
50+
| "stdio"
51+
| "sse"
52+
| "streamable-http") || "stdio"
53+
);
54+
};
55+
56+
export const getInitialSseUrl = (): string => {
57+
const param = getSearchParam("serverUrl");
58+
if (param) return param;
59+
return localStorage.getItem("lastSseUrl") || "http://localhost:3001/sse";
60+
};
61+
62+
export const getInitialCommand = (): string => {
63+
const param = getSearchParam("serverCommand");
64+
if (param) return param;
65+
return localStorage.getItem("lastCommand") || "mcp-server-everything";
66+
};
67+
68+
export const getInitialArgs = (): string => {
69+
const param = getSearchParam("serverArgs");
70+
if (param) return param;
71+
return localStorage.getItem("lastArgs") || "";
72+
};
73+
74+
// Returns a map of config key -> value from query params if present
75+
export const getConfigOverridesFromQueryParams = (
76+
defaultConfig: InspectorConfig,
77+
): Partial<InspectorConfig> => {
78+
const url = new URL(window.location.href);
79+
const overrides: Partial<InspectorConfig> = {};
80+
for (const key of Object.keys(defaultConfig)) {
81+
const param = url.searchParams.get(key);
82+
if (param !== null) {
83+
// Try to coerce to correct type based on default value
84+
const defaultValue = defaultConfig[key as keyof InspectorConfig].value;
85+
let value: string | number | boolean = param;
86+
if (typeof defaultValue === "number") {
87+
value = Number(param);
88+
} else if (typeof defaultValue === "boolean") {
89+
value = param === "true";
90+
}
91+
overrides[key as keyof InspectorConfig] = {
92+
...defaultConfig[key as keyof InspectorConfig],
93+
value,
94+
};
95+
}
96+
}
97+
return overrides;
98+
};
99+
100+
export const initializeInspectorConfig = (
101+
localStorageKey: string,
102+
): InspectorConfig => {
103+
const savedConfig = localStorage.getItem(localStorageKey);
104+
let baseConfig: InspectorConfig;
105+
if (savedConfig) {
106+
// merge default config with saved config
107+
const mergedConfig = {
108+
...DEFAULT_INSPECTOR_CONFIG,
109+
...JSON.parse(savedConfig),
110+
} as InspectorConfig;
111+
112+
// update description of keys to match the new description (in case of any updates to the default config description)
113+
for (const [key, value] of Object.entries(mergedConfig)) {
114+
mergedConfig[key as keyof InspectorConfig] = {
115+
...value,
116+
label: DEFAULT_INSPECTOR_CONFIG[key as keyof InspectorConfig].label,
117+
};
118+
}
119+
baseConfig = mergedConfig;
120+
} else {
121+
baseConfig = DEFAULT_INSPECTOR_CONFIG;
122+
}
123+
// Apply query param overrides
124+
const overrides = getConfigOverridesFromQueryParams(DEFAULT_INSPECTOR_CONFIG);
125+
return { ...baseConfig, ...overrides };
126+
};

0 commit comments

Comments
 (0)