Skip to content

flowing env vars to the client app for inspector #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 103 additions & 7 deletions client/bin/client.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,114 @@
#!/usr/bin/env node

import { join, dirname } from "path";
import { fileURLToPath } from "url";
import handler from "serve-handler";
import fs from "fs";
import http from "http";
import https from "https";
import { dirname, join } from "path";
import handler from "serve-handler";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const distPath = join(__dirname, "../dist");

// Function to determine the MCP server URL
const getMcpServerUrl = () => {
if (process.env.MCP_PROXY_FULL_ADDRESS) {
return process.env.MCP_PROXY_FULL_ADDRESS;
}

// Use current host with custom port if specified
const port = process.env.SERVER_PORT || "6277";
// Default to http://localhost:port
return `http://localhost:${port}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be 127.0.0.1 instead of localhost. The are generally interchangeable, however the OAuth spec recommends to use 127.0.0.1 instead of localhost:

While redirect URIs using localhost (i.e., "http://localhost:{port}/{path}") function similarly to loopback IP redirects described in Section 7.3, the use of localhost is NOT RECOMMENDED. Specifying a redirect URI with the loopback IP literal rather than localhost avoids inadvertently listening on network interfaces other than the loopback interface. It is also less susceptible to client-side firewalls and misconfigured host name resolution on the user's device.

};

const server = http.createServer((request, response) => {
return handler(request, response, {
public: distPath,
rewrites: [{ source: "/**", destination: "/index.html" }],
});
// Handle the /config endpoint as a proxy to the MCP server
if (request.url === "/config") {
const mcpServerUrl = getMcpServerUrl();
const configUrl = `${mcpServerUrl}/config`;

try {
const clientModule = mcpServerUrl.startsWith("https:") ? https : http;
const proxyReq = clientModule.request(configUrl, (proxyRes) => {
// Capture the response data to modify it
let data = "";
proxyRes.on("data", (chunk) => {
data += chunk;
});

proxyRes.on("end", () => {
try {
// Parse the JSON response
const jsonResponse = JSON.parse(data);

// Add the MCP_PROXY_FULL_ADDRESS to the response
jsonResponse.config = {
MCP_PROXY_FULL_ADDRESS: { value: mcpServerUrl },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this contain all the env vars that the client is interested in, i.e;

  • MCP_SERVER_REQUEST_TIMEOUT
  • MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS
  • MCP_REQUEST_MAX_TOTAL_TIMEOUT
  • MCP_PROXY_FULL_ADDRESS

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can they all be set an environment variables? If so, I'll update here.

};

// Send the modified response
response.writeHead(proxyRes.statusCode, {
"Content-Type": "application/json",
});
response.end(JSON.stringify(jsonResponse));
} catch (e) {
// If parsing fails, just forward the original response
response.writeHead(proxyRes.statusCode, proxyRes.headers);
response.end(data);
}
});
});

proxyReq.on("error", (err) => {
console.error(`Error proxying request to ${configUrl}:`, err.message);
response.statusCode = 500;
response.end(
JSON.stringify({
error: "Failed to connect to MCP server",
defaultEnvironment: {},
mcpServerUrl: mcpServerUrl,
}),
);
});

request.pipe(proxyReq);
} catch (error) {
console.error(`Error setting up proxy to ${configUrl}:`, error.message);
response.statusCode = 500;
response.end(
JSON.stringify({
error: "Failed to connect to MCP server",
defaultEnvironment: {},
mcpServerUrl: mcpServerUrl,
}),
);
}
}
// Check if this is a request for index.html (either directly or via SPA routing)
else if (
request.url === "/" ||
request.url === "/index.html" ||
!request.url.includes(".")
) {
const indexPath = join(distPath, "index.html");
fs.readFile(indexPath, "utf-8", (err, data) => {
if (err) {
response.statusCode = 500;
response.end(`Error loading index.html: ${err.message}`);
return;
}

response.setHeader("Content-Type", "text/html");
response.end(data);
});
} else {
// For all other assets, use serve-handler as before
return handler(request, response, {
public: distPath,
rewrites: [{ source: "/**", destination: "/index.html" }],
});
}
});

const port = process.env.PORT || 6274;
Expand Down
10 changes: 8 additions & 2 deletions client/bin/start.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import { resolve, dirname } from "path";
import { dirname, resolve } from "path";
import { spawnPromise } from "spawn-rx";
import { fileURLToPath } from "url";

Expand Down Expand Up @@ -63,6 +63,7 @@ async function main() {

const CLIENT_PORT = process.env.CLIENT_PORT ?? "6274";
const SERVER_PORT = process.env.SERVER_PORT ?? "6277";
const MCP_PROXY_FULL_ADDRESS = process.env.MCP_PROXY_FULL_ADDRESS ?? "";

console.log("Starting MCP inspector...");

Expand Down Expand Up @@ -100,7 +101,12 @@ async function main() {
if (serverOk) {
try {
await spawnPromise("node", [inspectorClientPath], {
env: { ...process.env, PORT: CLIENT_PORT },
env: {
...process.env,
PORT: CLIENT_PORT,
MCP_PROXY_FULL_ADDRESS,
SERVER_PORT,
},
signal: abort.signal,
echoOutput: true,
});
Expand Down
16 changes: 14 additions & 2 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import Sidebar from "./components/Sidebar";
import ToolsTab from "./components/ToolsTab";
import { DEFAULT_INSPECTOR_CONFIG } from "./lib/constants";
import { InspectorConfig } from "./lib/configurationTypes";
import { getMCPProxyAddress } from "./utils/configUtils";

const CONFIG_LOCAL_STORAGE_KEY = "inspectorConfig_v1";

Expand Down Expand Up @@ -239,16 +238,29 @@ const App = () => {
);

useEffect(() => {
fetch(`${getMCPProxyAddress(config)}/config`)
fetch(`/config`)
.then((response) => response.json())
.then((data) => {
setEnv(data.defaultEnvironment);
setConfig((prev) => {
return {
...prev,
...data.config,
};
});
if (data.defaultCommand) {
setCommand(data.defaultCommand);
}
if (data.defaultArgs) {
setArgs(data.defaultArgs);
}

if (data.defaultTransportType) {
setTransportType(data.defaultTransportType);
if (data.defaultTransportType === "sse") {
setSseUrl(data.defaultCommand);
}
}
})
.catch((error) =>
console.error("Error fetching default environment:", error),
Expand Down
4 changes: 2 additions & 2 deletions client/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const DEFAULT_INSPECTOR_CONFIG: InspectorConfig = {
MCP_PROXY_FULL_ADDRESS: {
label: "Inspector Proxy Address",
description:
"Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577",
value: "",
"The endpoint of the MCP Proxy server. This is used to connect to the MCP server.",
value: `http://${window.location.hostname}:${DEFAULT_MCP_PROXY_LISTEN_PORT}`,
},
} as const;
9 changes: 6 additions & 3 deletions client/src/lib/hooks/useConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
import { InspectorOAuthClientProvider } from "../auth";
import packageJson from "../../../package.json";
import {
getMCPProxyAddress,
getMCPServerRequestMaxTotalTimeout,
resetRequestTimeoutOnProgress,
} from "@/utils/configUtils";
Expand Down Expand Up @@ -232,7 +231,9 @@ export function useConnection({

const checkProxyHealth = async () => {
try {
const proxyHealthUrl = new URL(`${getMCPProxyAddress(config)}/health`);
const proxyHealthUrl = new URL(
`${config.MCP_PROXY_FULL_ADDRESS.value}/health`,
);
const proxyHealthResponse = await fetch(proxyHealthUrl);
const proxyHealth = await proxyHealthResponse.json();
if (proxyHealth?.status !== "ok") {
Expand Down Expand Up @@ -278,7 +279,9 @@ export function useConnection({
setConnectionStatus("error-connecting-to-proxy");
return;
}
const mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
const mcpProxyServerUrl = new URL(
`${config.MCP_PROXY_FULL_ADDRESS.value}/sse`,
);
mcpProxyServerUrl.searchParams.append("transportType", transportType);
if (transportType === "stdio") {
mcpProxyServerUrl.searchParams.append("command", command);
Expand Down
9 changes: 0 additions & 9 deletions client/src/utils/configUtils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import { InspectorConfig } from "@/lib/configurationTypes";
import { DEFAULT_MCP_PROXY_LISTEN_PORT } from "@/lib/constants";

export const getMCPProxyAddress = (config: InspectorConfig): string => {
const proxyFullAddress = config.MCP_PROXY_FULL_ADDRESS.value as string;
if (proxyFullAddress) {
return proxyFullAddress;
}
return `${window.location.protocol}//${window.location.hostname}:${DEFAULT_MCP_PROXY_LISTEN_PORT}`;
};

export const getMCPServerRequestTimeout = (config: InspectorConfig): number => {
return config.MCP_SERVER_REQUEST_TIMEOUT.value as number;
Expand Down
1 change: 0 additions & 1 deletion client/src/vite-env.d.ts

This file was deleted.

Loading