Skip to content

fix: bind client to localhost to match server #529

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 2 commits into
base: main
Choose a base branch
from

Conversation

felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Jun 18, 2025

Bind client to localhost instead of all interfaces to match server

Motivation and Context

Complete the security hardening started in e8e9909 by also binding the client to localhost only.
Previously only the server was protected while the client remained exposed to the network,
allowing attackers to access the server through the client as a proxy.

Changes:

  • Add HOST environment variable support to client (prod mode)
  • Configure Vite dev server to bind to localhost by default
  • Update browser auto-open URLs to use actual host instead of hardcoded 127.0.0.1
  • Fix missing cancelled parameter in startProdClient function
Before After
CleanShot 2025-06-18 at 17 56 03@2x CleanShot 2025-06-18 at 17 59 25@2x

How Has This Been Tested?

prod: npm run build && npm run start - works
dev: npm run dev - works
test: npm test - works

Breaking Changes

There may be instances where clients were inadvertently relying on clients binding to *:6274 in their setup - e.g. if accessing the inspector UI via a remotely hosted machine or similar.

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

@felixweinberger felixweinberger changed the base branch from main to fweinberger/auto-open June 18, 2025 17:02
@felixweinberger felixweinberger force-pushed the fweinberger/client-localhost branch from 0345463 to bdfc9e7 Compare June 18, 2025 17:08
@felixweinberger felixweinberger marked this pull request as ready for review June 18, 2025 17:11
@felixweinberger felixweinberger force-pushed the fweinberger/client-localhost branch from bdfc9e7 to 58661be Compare June 18, 2025 17:21
Base automatically changed from fweinberger/auto-open to main June 18, 2025 22:13
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.

Suggested a simple refactor of some duplicated lines.

Also... can we align the way we refer to the loopback, either make the link with the token be 127.0.0.1 instead of localhost (or make the host in the client and start script be localhost instead of 127.0.0.1).

Screenshot 2025-06-18 at 6 21 37 PM

Previously we were using 127.0.0.1 since it refers directly to the loopback and doesn't require DNS lookup, thus being slightly safer. However 127.0.0.1 is an IPv4 specific address and in IPv6 environments the loopback is ::1 and so could possibly lead to ECONNREFUSED. So localhost is arguably better for that purpose. An attacker would have to edit your hosts file to redirect 127.0.0.1 to an evil address, so you'd already have to have some compromise.

Comment on lines 117 to 120
const clientHost = process.env.HOST || "127.0.0.1";
const url = authDisabled
? `http://127.0.0.1:${CLIENT_PORT}`
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
? `http://${clientHost}:${CLIENT_PORT}`
: `http://${clientHost}:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

These lines could be extract/refactored into a function and called from line 117 and line 156.

@felixweinberger felixweinberger force-pushed the fweinberger/client-localhost branch 2 times, most recently from 8b66a9e to 4a0225e Compare June 19, 2025 11:00
felixweinberger and others added 2 commits June 19, 2025 19:25
…acks

Complete the security hardening started in e8e9909 by also binding the client to localhost only.
Previously only the server was protected while the client remained exposed to the network,
allowing attackers to access the server through the client as a proxy.

Changes:
- Add HOST environment variable support to client (prod mode)
- Configure Vite dev server to bind to localhost by default
- Update browser auto-open URLs to use actual host instead of hardcoded 127.0.0.1
- Fix missing cancelled parameter in startProdClient function

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Extract duplicated URL generation code into getClientUrl() helper function in start.js
- Replace all 127.0.0.1 references with localhost for consistency across codebase
- Update server to respect HOST environment variable for URL generation
- Remove 127.0.0.1 from default allowed origins in CORS configuration
- Update documentation to use localhost instead of 127.0.0.1

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@felixweinberger felixweinberger force-pushed the fweinberger/client-localhost branch from 4a0225e to f1525aa Compare June 19, 2025 18:26
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.

Added suggestions to fix a couple of PORT remnants from before we codified SERVER_PORT and CLIENT_PORT env vars. Below is how we want to control ports:

Screenshot 2025-06-19 at 4 25 19 PM

NOTE: Another (prior) issue exists where the client looks only for DEFAULT_MCP_PROXY_LISTEN_PORT unless MCP_PROXY_FULL_ADDRESS is set in config. A bit of a chicken/egg problem anyway since the config is fetched from the proxy's /config address. Thus if the proxy server is started on a port other than default, the client won't be able to find it.

Clearly that part needs to be revisited, since we now have a MCP_PROXY_TOKEN on the querystring, but that's for another PR, unless you feel like tackling it in this one. If SERVER_PORT was set in the environment (i.e., is not the default), we need to add it to the querystring and used in place of DEFAULT_MCP_PROXY_LISTEN_PORT in the client.

@@ -531,7 +529,7 @@ app.get("/config", originValidationMiddleware, authMiddleware, (req, res) => {
});

const PORT = parseInt(process.env.PORT || "6277", 10);
Copy link
Contributor

@cliffhall cliffhall Jun 19, 2025

Choose a reason for hiding this comment

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

Suggested change
const PORT = parseInt(process.env.PORT || "6277", 10);
const PORT = parseInt(process.env.SERVER_PORT || "6277", 10);

We're using CLIENT_PORT and SERVER_PORT everywhere except here and . Thus you get a mismatch of the actual port the server is started on and what is reported on the terminal console if you just set PORT.

Screenshot 2025-06-19 at 4 20 26 PM

@@ -40,18 +40,19 @@ const server = http.createServer((request, response) => {
});

const port = process.env.PORT || 6274;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const port = process.env.PORT || 6274;
const port = parseInt(process.env.CLIENT_PORT || "6274", 10);

We're using CLIENT_PORT and SERVER_PORT everywhere except here and server/src/index.ts. Thus you get a mismatch of what's reported on the command line if you just set PORT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants