Skip to content

Commit 8a7da27

Browse files
committed
allow passing env vars to server from command line
1 parent 1797fbf commit 8a7da27

File tree

4 files changed

+115
-31
lines changed

4 files changed

+115
-31
lines changed

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@ To inspect an MCP server implementation, there's no need to clone this repo. Ins
1414
npx @modelcontextprotocol/inspector build/index.js
1515
```
1616

17-
You can also pass arguments along which will get passed as arguments to your MCP server:
17+
You can pass both arguments and environment variables to your MCP server. Arguments are passed directly to your server, while environment variables can be set using the `-e` flag:
1818

19-
```
20-
npx @modelcontextprotocol/inspector build/index.js arg1 arg2 ...
19+
```bash
20+
# Pass arguments only
21+
npx @modelcontextprotocol/inspector build/index.js arg1 arg2
22+
23+
# Pass environment variables only
24+
npx @modelcontextprotocol/inspector -e KEY=value -e KEY2=value2 build/index.js
25+
26+
# Pass both environment variables and arguments
27+
npx @modelcontextprotocol/inspector -e KEY=value -e KEY2=value2 build/index.js arg1 arg2
2128
```
2229

2330
The inspector runs both a client UI (default port 5173) and an MCP proxy server (default port 3000). Open the client UI in your browser to use the inspector. You can customize the ports if needed:

bin/cli.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,24 @@ function delay(ms) {
1111
}
1212

1313
async function main() {
14-
// Get command line arguments
15-
const [, , command, ...mcpServerArgs] = process.argv;
14+
// Parse command line arguments
15+
const args = process.argv.slice(2);
16+
const envVars = {};
17+
const mcpServerArgs = [];
18+
let command = null;
19+
20+
for (let i = 0; i < args.length; i++) {
21+
if (args[i] === "-e" && i + 1 < args.length) {
22+
const [key, value] = args[++i].split("=");
23+
if (key && value) {
24+
envVars[key] = value;
25+
}
26+
} else if (!command) {
27+
command = args[i];
28+
} else {
29+
mcpServerArgs.push(args[i]);
30+
}
31+
}
1632

1733
const inspectorServerPath = resolve(
1834
__dirname,
@@ -52,7 +68,11 @@ async function main() {
5268
...(mcpServerArgs ? [`--args=${mcpServerArgs.join(" ")}`] : []),
5369
],
5470
{
55-
env: { ...process.env, PORT: SERVER_PORT },
71+
env: {
72+
...process.env,
73+
PORT: SERVER_PORT,
74+
MCP_ENV_VARS: JSON.stringify(envVars),
75+
},
5676
signal: abort.signal,
5777
echoOutput: true,
5878
},

client/src/components/Sidebar.tsx

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { useState } from "react";
2-
import { Play, ChevronDown, ChevronRight, CircleHelp, Bug, Github } from "lucide-react";
2+
import {
3+
Play,
4+
ChevronDown,
5+
ChevronRight,
6+
Bug,
7+
CircleHelp,
8+
Github,
9+
Eye,
10+
EyeOff,
11+
} from "lucide-react";
312
import { Button } from "@/components/ui/button";
413
import { Input } from "@/components/ui/input";
514
import {
@@ -47,6 +56,7 @@ const Sidebar = ({
4756
}: SidebarProps) => {
4857
const [theme, setTheme] = useTheme();
4958
const [showEnvVars, setShowEnvVars] = useState(false);
59+
const [shownEnvVars, setShownEnvVars] = useState<Record<string, boolean>>({});
5060

5161
return (
5262
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
@@ -127,20 +137,40 @@ const Sidebar = ({
127137
{showEnvVars && (
128138
<div className="space-y-2">
129139
{Object.entries(env).map(([key, value], idx) => (
130-
<div key={idx} className="grid grid-cols-[1fr,auto] gap-2">
131-
<div className="space-y-1">
140+
<div key={idx} className="space-y-2 pb-4">
141+
<div className="flex gap-2">
132142
<Input
133143
placeholder="Key"
134144
value={key}
135145
onChange={(e) => {
146+
const newKey = e.target.value;
136147
const newEnv = { ...env };
137148
delete newEnv[key];
138-
newEnv[e.target.value] = value;
149+
newEnv[newKey] = value;
139150
setEnv(newEnv);
151+
setShownEnvVars((prev) => {
152+
const { [key]: shown, ...rest } = prev;
153+
return shown ? { ...rest, [newKey]: true } : rest;
154+
});
140155
}}
141156
className="font-mono"
142157
/>
158+
<Button
159+
variant="destructive"
160+
size="icon"
161+
className="h-9 w-9 p-0 shrink-0"
162+
onClick={() => {
163+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
164+
const { [key]: _removed, ...rest } = env;
165+
setEnv(rest);
166+
}}
167+
>
168+
×
169+
</Button>
170+
</div>
171+
<div className="flex gap-2">
143172
<Input
173+
type={shownEnvVars[key] ? "text" : "password"}
144174
placeholder="Value"
145175
value={value}
146176
onChange={(e) => {
@@ -150,25 +180,35 @@ const Sidebar = ({
150180
}}
151181
className="font-mono"
152182
/>
183+
<Button
184+
variant="outline"
185+
size="icon"
186+
className="h-9 w-9 p-0 shrink-0"
187+
onClick={() => {
188+
setShownEnvVars((prev) => ({
189+
...prev,
190+
[key]: !prev[key],
191+
}));
192+
}}
193+
>
194+
{shownEnvVars[key] ? (
195+
<Eye className="h-4 w-4" />
196+
) : (
197+
<EyeOff className="h-4 w-4" />
198+
)}
199+
</Button>
153200
</div>
154-
<Button
155-
variant="destructive"
156-
onClick={() => {
157-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
158-
const { [key]: removed, ...rest } = env;
159-
setEnv(rest);
160-
}}
161-
>
162-
Remove
163-
</Button>
164201
</div>
165202
))}
166203
<Button
167204
variant="outline"
205+
className="w-full mt-2"
168206
onClick={() => {
207+
const key = "";
169208
const newEnv = { ...env };
170-
newEnv[""] = "";
209+
newEnv[key] = "";
171210
setEnv(newEnv);
211+
setShownEnvVars({});
172212
}}
173213
>
174214
Add Environment Variable
@@ -243,18 +283,33 @@ const Sidebar = ({
243283
</Select>
244284

245285
<div className="flex items-center space-x-2">
246-
<a href="https://modelcontextprotocol.io/docs/tools/inspector" target="_blank" rel="noopener noreferrer">
286+
<a
287+
href="https://modelcontextprotocol.io/docs/tools/inspector"
288+
target="_blank"
289+
rel="noopener noreferrer"
290+
>
247291
<Button variant="ghost" title="Inspector Documentation">
248292
<CircleHelp className="w-4 h-4 text-gray-800" />
249293
</Button>
250294
</a>
251-
<a href="https://modelcontextprotocol.io/docs/tools/debugging" target="_blank" rel="noopener noreferrer">
295+
<a
296+
href="https://modelcontextprotocol.io/docs/tools/debugging"
297+
target="_blank"
298+
rel="noopener noreferrer"
299+
>
252300
<Button variant="ghost" title="Debugging Guide">
253301
<Bug className="w-4 h-4 text-gray-800" />
254302
</Button>
255303
</a>
256-
<a href="https://github.com/modelcontextprotocol/inspector" target="_blank" rel="noopener noreferrer">
257-
<Button variant="ghost" title="Report bugs or contribute on GitHub">
304+
<a
305+
href="https://github.com/modelcontextprotocol/inspector"
306+
target="_blank"
307+
rel="noopener noreferrer"
308+
>
309+
<Button
310+
variant="ghost"
311+
title="Report bugs or contribute on GitHub"
312+
>
258313
<Github className="w-4 h-4 text-gray-800" />
259314
</Button>
260315
</a>

server/src/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import express from "express";
1515
import mcpProxy from "./mcpProxy.js";
1616
import { findActualExecutable } from "spawn-rx";
1717

18+
const defaultEnvironment = {
19+
...getDefaultEnvironment(),
20+
...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
21+
};
22+
1823
// Polyfill EventSource for an SSE client in Node.js
1924
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2025
(global as any).EventSource = EventSource;
@@ -40,13 +45,12 @@ const createTransport = async (query: express.Request["query"]) => {
4045
if (transportType === "stdio") {
4146
const command = query.command as string;
4247
const origArgs = shellParseArgs(query.args as string) as string[];
43-
const env = query.env ? JSON.parse(query.env as string) : undefined;
48+
const queryEnv = query.env ? JSON.parse(query.env as string) : {};
49+
const env = { ...process.env, ...defaultEnvironment, ...queryEnv };
4450

4551
const { cmd, args } = findActualExecutable(command, origArgs);
4652

47-
console.log(
48-
`Stdio transport: command=${cmd}, args=${args}, env=${JSON.stringify(env)}`,
49-
);
53+
console.log(`Stdio transport: command=${cmd}, args=${args}`);
5054

5155
const transport = new StdioClientTransport({
5256
command: cmd,
@@ -136,8 +140,6 @@ app.post("/message", async (req, res) => {
136140

137141
app.get("/config", (req, res) => {
138142
try {
139-
const defaultEnvironment = getDefaultEnvironment();
140-
141143
res.json({
142144
defaultEnvironment,
143145
defaultCommand: values.env,

0 commit comments

Comments
 (0)