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

Conversation

aaronpowell
Copy link

@aaronpowell aaronpowell commented Apr 3, 2025

This allows you to set the SERVER_PORT or MCP_PROXY_FULL_ADDRESS as an environment variable and the value will be injected into the static app when it's run. Also making the transport type provided by the config.

Fixes #256 and #255

Motivation and Context

How Has This Been Tested?

I've tested this by setting the SERVER_PORT env var before running and verifying that that is passed through correctly to the client app:

$> SERVER_PORT=12354 npm start -- npx -y @modelcontextprotocol/server-everything

> @modelcontextprotocol/[email protected] start
> node client/bin/start.js npx -y @modelcontextprotocol/server-everything

Starting MCP inspector...
⚙️ Proxy server listening on port 12354
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀

image

I can then connect and run successfully:

image

When started without an env var it starts on the default port:

$> npm start

> @modelcontextprotocol/[email protected] start
> node client/bin/start.js

Starting MCP inspector...
⚙️ Proxy server listening on port 6277
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀

And the browser reflects that:

image

Breaking Changes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

This allows you to set the SERVER_PORT or MCP_PROXY_FULL_ADDRESS as an environment variable and the value will be injected into the static app when it's run.

Fixes modelcontextprotocol#256
import http from "http";
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");

const server = http.createServer((request, response) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would a better approach be to expose an endpoint in this server to return the config ? That would simplify this part of the code (and not have to deal with runtime injection).

Something like that is being done here (although it contacts the proxy server):

fetch(`${getMCPProxyAddress(config)}/config`)

Copy link
Contributor

Choose a reason for hiding this comment

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

The runtime config stuff further down in the client code definitely would be simpler.

Copy link
Author

Choose a reason for hiding this comment

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

As in, create an API in the client/bin/cli.js that provides an endpoint in which you load the config file? It would but doesn't that add overhead to the startup of the app in that you have to make an API call to get the config, then make an API call to get the config from the server.

Maybe they could be combined, make the /config exist on the client app, which then proxies the SERVER/config.

Copy link
Contributor

Choose a reason for hiding this comment

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

@aaronpowell Could we get a mermaid sequence diagram showing how this is intended to work? That would give us a better starting point for understanding how best to plumb this.

Copy link
Author

Choose a reason for hiding this comment

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

sequenceDiagram
    participant SPA
    participant ClientHost
    participant ProxyServer
    
    Note over SPA: Application starts
    SPA->>ClientHost: GET /config
    activate ClientHost
    ClientHost->>ProxyServer: GET /config
    activate ProxyServer
    ProxyServer-->>ClientHost: Return configuration data
    deactivate ProxyServer
    ClientHost-->>SPA: Return configuration data
    deactivate ClientHost
    Note over SPA: Process configuration
Loading

With this we can simplify the "Client Host" (the server run from client/bin/cli.js to not have to worry about much heavy lifting other than "where is the server endpoint" and pass through the request.

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't the proxy server address part of the config?
Screenshot 2025-04-08 at 1 19 31 PM

Copy link
Contributor

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

Reimplementing as an endpoint that the app calls rather than the runtime injection scheme into the window seems simpler.

: DEFAULT_INSPECTOR_CONFIG;

// Override with runtime injected values if available
if (runtimeConfig.MCP_PROXY_FULL_ADDRESS) {
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 be more like:

    if (runtimeConfig.MCP_PROXY_FULL_ADDRESS) {
      configFromStorage = {
        ...configFromStorage,
        MCP_PROXY_FULL_ADDRESS: {
          value: runtimeConfig.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.

are there properties other than value on MCP_PROXY_FULL_ADDRESS that are important?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is a ConfigItem.

import http from "http";
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");

const server = http.createServer((request, response) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The runtime config stuff further down in the client code definitely would be simpler.

@cliffhall cliffhall added enhancement New feature or request waiting on submitter Waiting for the submitter to provide more info labels Apr 3, 2025
import http from "http";
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");

const server = http.createServer((request, response) => {
Copy link
Author

Choose a reason for hiding this comment

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

As in, create an API in the client/bin/cli.js that provides an endpoint in which you load the config file? It would but doesn't that add overhead to the startup of the app in that you have to make an API call to get the config, then make an API call to get the config from the server.

Maybe they could be combined, make the /config exist on the client app, which then proxies the SERVER/config.

: DEFAULT_INSPECTOR_CONFIG;

// Override with runtime injected values if available
if (runtimeConfig.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.

are there properties other than value on MCP_PROXY_FULL_ADDRESS that are important?


// Create a runtime config object with environment variables
const runtimeConfig = {
MCP_PROXY_FULL_ADDRESS: process.env.MCP_PROXY_FULL_ADDRESS || "",
Copy link
Contributor

Choose a reason for hiding this comment

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

MCP_PROXY_FULL_ADDRESS and MCP_PROXY_PORT should be ConfigItems.

This means that when you set environment variables we read them on the server and add them to the config info obtained by the proxy, resulting in the proxy server being the 'winner' in terms of what the config should look like.
@aaronpowell aaronpowell force-pushed the aaronpowell/issue-256 branch from e041581 to d76a79a Compare April 8, 2025 01:20
@aaronpowell aaronpowell requested a review from cliffhall April 8, 2025 01:21
Copy link
Contributor

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

Please include detailed steps for testing in the PR header.

@aaronpowell
Copy link
Author

Please include detailed steps for testing in the PR header.

Updated with how I've tested so far. Let me know if there are more scenarios you'd like me to test against.

Copy link
Contributor

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

Reviewed again and had a few observations.

// 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.


// 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.

Copy link
Author

@aaronpowell aaronpowell left a comment

Choose a reason for hiding this comment

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

Thanks for the review, just some clarifications before I make the changes (I'll admit I have gotten a little lost across some of the merges, given there's been a few releases since I started the PR 🤣)


// Add the MCP_PROXY_FULL_ADDRESS to the response
jsonResponse.config = {
MCP_PROXY_FULL_ADDRESS: { value: mcpServerUrl },
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request waiting on submitter Waiting for the submitter to provide more info
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Setting SERVER_PORT doesn't cascade through to launching the client endpoint
3 participants