From c02d1d89741c123ea7c629aa01dcac877a1afa8a Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Mon, 2 Mar 2026 19:14:32 -0600 Subject: [PATCH] fix: configure MCP transport security with env-driven allowed hosts FastMCP auto-enables DNS rebinding protection when host=127.0.0.1 (the default). Production requests from brain.woltje.com were rejected with 421 Invalid Host header because the allowed_hosts list was empty. Added MCP_ALLOWED_HOSTS config field (comma-separated). When set, DNS rebinding protection is enabled with those hosts; when empty, protection is disabled. Set MCP_ALLOWED_HOSTS=brain.woltje.com in Portainer stack env. Co-Authored-By: Claude Sonnet 4.6 --- src/config.py | 5 +++++ src/main.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/config.py b/src/config.py index 28f3239..1a2d807 100644 --- a/src/config.py +++ b/src/config.py @@ -19,5 +19,10 @@ class Settings(BaseSettings): port: int = 8000 log_level: str = "info" + # MCP transport security — comma-separated allowed Host header values. + # Set to the public hostname (e.g. "brain.woltje.com") in production. + # Empty disables DNS rebinding protection. + mcp_allowed_hosts: str = "" + settings = Settings() diff --git a/src/main.py b/src/main.py index 99346d8..94f7999 100644 --- a/src/main.py +++ b/src/main.py @@ -6,6 +6,7 @@ import logging from fastapi import Depends, FastAPI, HTTPException, Security from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from mcp.server.fastmcp import FastMCP +from mcp.server.transport_security import TransportSecuritySettings from src import brain, db from src.config import settings @@ -29,7 +30,12 @@ def require_api_key(credentials: HTTPAuthorizationCredentials = Security(bearer) # --------------------------------------------------------------------------- # MCP server # --------------------------------------------------------------------------- -mcp = FastMCP("openbrain", stateless_http=True) +_allowed_hosts = [h.strip() for h in settings.mcp_allowed_hosts.split(",") if h.strip()] +_transport_security = TransportSecuritySettings( + enable_dns_rebinding_protection=bool(_allowed_hosts), + allowed_hosts=_allowed_hosts, +) +mcp = FastMCP("openbrain", stateless_http=True, transport_security=_transport_security) @mcp.tool()