Skip to content

v2 remove console warns on middleware#1776

Open
KKonstantinov wants to merge 4 commits intomainfrom
middleware-remove-console-warn
Open

v2 remove console warns on middleware#1776
KKonstantinov wants to merge 4 commits intomainfrom
middleware-remove-console-warn

Conversation

@KKonstantinov
Copy link
Contributor

Remove console.warn log pollution and add skipHostHeaderValidation option

Motivation and Context

Fixes #1515. The middleware packages (@modelcontextprotocol/express, @modelcontextprotocol/hono) emit a console.warn when binding to 0.0.0.0 or :: without allowedHosts. This pollutes server logs for users who intentionally bind to all interfaces (e.g., behind a reverse proxy in a container). Libraries should not produce unsolicited log output in environments they don't own.

Additionally, there was no way to opt out of the automatic localhost host-header validation — useful when running behind a reverse proxy that rewrites the Host header.

This PR:

  1. Removes the console.warn entirely. The allowedHosts option is already well-documented in JSDoc.
  2. Adds skipHostHeaderValidation — an explicit opt-out from all automatic host-header validation middleware.
  3. Uses a discriminated union type (HostHeaderValidationOptions) so that allowedHosts and skipHostHeaderValidation: true are mutually exclusive at the type level — passing both is a compile error, not a silent precedence rule.

How Has This Been Tested?

  • All existing tests updated and passing for both @modelcontextprotocol/express and @modelcontextprotocol/hono
  • New tests added for skipHostHeaderValidation: true on both localhost and 0.0.0.0 hosts
  • Hono tests verify at the HTTP level that validation middleware is actually skipped (requests with arbitrary Host headers succeed)
  • Full build, typecheck, and lint pass across both packages

Breaking Changes

  • The console.warn when binding to 0.0.0.0/:: without allowedHosts is removed. This is a behavior change but not a breaking API change — no user code depended on this warning.
  • CreateMcpExpressAppOptions and CreateMcpHonoAppOptions changed from interface to type (intersection with HostHeaderValidationOptions). This is source-compatible for all usage patterns except extends/implements on the interface, which is unlikely for options objects.

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

The HostHeaderValidationOptions discriminated union type is defined independently in both packages (no shared import) to avoid coupling the middleware packages. The pattern is identical:

type HostHeaderValidationOptions =
    | { skipHostHeaderValidation: true; allowedHosts?: never }
    | { skipHostHeaderValidation?: false; allowedHosts?: string[] };

This ensures that allowedHosts is only accepted when validation is enabled, catching misconfiguration at compile time rather than silently ignoring one option.

@changeset-bot
Copy link

changeset-bot bot commented Mar 26, 2026

⚠️ No Changeset found

Latest commit: 9ee628f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@KKonstantinov KKonstantinov marked this pull request as ready for review March 26, 2026 22:27
@KKonstantinov KKonstantinov requested a review from a team as a code owner March 26, 2026 22:27
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 26, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1776

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1776

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1776

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1776

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1776

commit: 9ee628f

Comment on lines 88 to +97

// If allowedHosts is explicitly provided, use that for validation
if (allowedHosts) {
app.use(hostHeaderValidation(allowedHosts));
} else {
// Apply DNS rebinding protection automatically for localhost hosts
const localhostHosts = ['127.0.0.1', 'localhost', '::1'];
if (localhostHosts.includes(host)) {
app.use(localhostHostValidation());
} else if (host === '0.0.0.0' || host === '::') {
// Warn when binding to all interfaces without DNS rebinding protection
// eslint-disable-next-line no-console
console.warn(
`Warning: Server is binding to ${host} without DNS rebinding protection. ` +
'Consider using the allowedHosts option to restrict allowed hosts, ' +
'or use authentication to protect your server.'
);
if (!skipHostHeaderValidation) {
// If allowedHosts is explicitly provided, use that for validation
if (allowedHosts) {
app.use(hostHeaderValidation(allowedHosts));
} else {
// Apply DNS rebinding protection automatically for localhost hosts
const localhostHosts = ['127.0.0.1', 'localhost', '::1'];
if (localhostHosts.includes(host)) {
app.use(localhostHostValidation());
Copy link

Choose a reason for hiding this comment

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

🔴 Integration tests in test/integration/test/server.test.ts (lines 2311-2322) still assert that console.warn is called when binding to 0.0.0.0 or :: without allowedHosts, but this PR removes those console.warn calls. These two tests will fail, breaking CI. They need to be removed or replaced with tests for the new skipHostHeaderValidation behavior.

Extended reasoning...

What the bug is

This PR removes the console.warn calls from createMcpExpressApp (in packages/middleware/express/src/express.ts) and createMcpHonoApp (in packages/middleware/hono/src/hono.ts) that previously fired when binding to 0.0.0.0 or :: without providing allowedHosts. However, the integration test file test/integration/test/server.test.ts was not updated to reflect this change.

The specific failing tests

Two integration tests will fail:

  1. Line 2311: "should warn when binding to 0.0.0.0" — creates a console.warn spy, calls createMcpExpressApp({ host: "0.0.0.0" }), then asserts expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("0.0.0.0")). Since the console.warn call no longer exists in the production code, the spy will never be called, and this assertion will fail.

  2. Line 2318: "should warn when binding to :: (IPv6 all interfaces)" — identical pattern with host: "::" and expect.stringContaining("::"). Same failure for the same reason.

Step-by-step proof

  1. Before this PR, createMcpExpressApp({ host: "0.0.0.0" }) would hit the else-if branch (host === "0.0.0.0" || host === "::") at the old line 93, which called console.warn(...).
  2. After this PR, that entire else-if branch is removed. When host is "0.0.0.0" and no allowedHosts is provided, the code simply falls through without any warning — the localhostHosts.includes(host) check fails (since "0.0.0.0" is not in ["127.0.0.1", "localhost", "::1"]), and no other action is taken.
  3. The integration test at line 2311 spies on console.warn and asserts it was called. Since console.warn is never invoked, the spy records zero calls, and toHaveBeenCalledWith(...) fails.
  4. The same applies to the test at line 2318 for host: "::".

Why existing code does not prevent it

The PR correctly updated the unit tests in packages/middleware/express/test/express.test.ts and packages/middleware/hono/test/hono.test.ts — both had their warn-related tests removed and replaced with skipHostHeaderValidation tests. However, the integration tests in test/integration/test/server.test.ts were missed. This file is not listed among the 4 changed files in the PR.

Impact

This will cause CI test failures when the integration test suite runs. Additionally, the test at line 2326 that creates a warnSpy for the allowedHosts test and asserts expect(warnSpy).not.toHaveBeenCalled() will pass vacuously (since the warn is gone), but the spy setup is now unnecessary dead code.

How to fix

Remove the two failing tests (lines 2311-2323) from test/integration/test/server.test.ts. Optionally, add integration tests for the new skipHostHeaderValidation behavior. The warnSpy at line 2326 can also be cleaned up since console.warn is no longer called.

…extprotocol/typescript-sdk into middleware-remove-console-warn
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.

Log pollution by unrequested warning output

1 participant