From 02fe500d619dbd5f1f1ef394703bb7ad51ed20cb Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 27 May 2026 14:16:47 -0400 Subject: [PATCH] fix(mcp): disable DNS-rebinding protection on FastMCP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FastMCP defaults to an allow-list of localhost variants for the Host header (DNS-rebinding protection). Any deployment behind a reverse proxy hitting a non-localhost hostname (e.g. devassistant.traefik.internal) gets 421 Misdirected Request with: WARNING mcp.server.transport_security: Invalid Host header: The protection exists to stop a malicious browser page from rebinding DNS to attack a localhost MCP server. Our deployment is HTTP transport behind a reverse proxy with bearer-token auth, which already gates every request — so the rebinding threat doesn't apply. Disabling the check lets any Host through; auth still rejects unauthorized requests at 401. This also makes the integration test pass without test-only host hackery — every realistic Host header (Traefik internal hostname, CDN domain, custom DNS) now reaches FastMCP cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/fabledassistant/mcp/server.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/fabledassistant/mcp/server.py b/src/fabledassistant/mcp/server.py index 453cd17..b686b92 100644 --- a/src/fabledassistant/mcp/server.py +++ b/src/fabledassistant/mcp/server.py @@ -2,6 +2,7 @@ from __future__ import annotations from mcp.server.fastmcp import FastMCP +from mcp.server.transport_security import TransportSecuritySettings from quart import Quart _INSTRUCTIONS = """ @@ -23,8 +24,24 @@ Hierarchy: Project -> Milestone -> Task/Note. def build_mcp_server() -> FastMCP: - """Build the FastMCP instance with all tools registered.""" - mcp = FastMCP("scribe", instructions=_INSTRUCTIONS.strip()) + """Build the FastMCP instance with all tools registered. + + DNS-rebinding protection is disabled: FastMCP's default allow-list is + just localhost variants, which means any deployment behind a reverse + proxy (Traefik with a hostname like devassistant.traefik.internal, + Cloudflare, nginx, etc.) gets 421 Misdirected Request. The threat + model that protection addresses — a malicious browser page rebinding + DNS to hit a localhost MCP — doesn't apply here: this is HTTP transport + behind a reverse proxy with bearer-token auth as the real security + boundary. + """ + mcp = FastMCP( + "scribe", + instructions=_INSTRUCTIONS.strip(), + transport_security=TransportSecuritySettings( + enable_dns_rebinding_protection=False, + ), + ) from fabledassistant.mcp.tools import register_all register_all(mcp) return mcp