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()