Add configurable allowed hosts for MCP remote access (KB_MCP_ALLOWED_HOSTS)

The MCP SDK's DNS rebinding protection rejects remote clients with 421
when the Host header isn't in the allowlist. Add KB_MCP_ALLOWED_HOSTS env
var (comma-separated IPs/FQDNs) to configure additional allowed hosts
while keeping localhost always permitted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 12:39:43 +01:00
parent e39e00a2c0
commit da5b8435bc
10 changed files with 203 additions and 0 deletions
+8
View File
@@ -7,3 +7,11 @@ KB_ENGINE_URL = os.environ.get("KB_ENGINE_URL", "http://localhost:8000")
KB_API_KEY = os.environ.get("KB_API_KEY", "")
KB_MCP_API_KEY = os.environ.get("KB_MCP_API_KEY", "")
KB_MCP_PORT = int(os.environ.get("KB_MCP_PORT", "3000"))
KB_MCP_ALLOWED_HOSTS = os.environ.get("KB_MCP_ALLOWED_HOSTS", "")
def parse_allowed_hosts() -> list[str]:
"""Parse KB_MCP_ALLOWED_HOSTS into a list of host strings."""
if not KB_MCP_ALLOWED_HOSTS:
return []
return [h.strip() for h in KB_MCP_ALLOWED_HOSTS.split(",") if h.strip()]
+19
View File
@@ -5,6 +5,7 @@ import json
import logging
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
@@ -81,6 +82,23 @@ async def _ensure_exclusive_collection(doc_id: int, collection: str) -> None:
engine.update_tags(doc_id, add=[new_tag])
# ---------------------------------------------------------------------------
# Transport security — DNS rebinding protection with configurable allowed hosts
# ---------------------------------------------------------------------------
_LOCALHOST_HOSTS = ["127.0.0.1:*", "localhost:*", "[::1]:*"]
_LOCALHOST_ORIGINS = ["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"]
_extra_hosts = config.parse_allowed_hosts()
_allowed_hosts = _LOCALHOST_HOSTS + [f"{h}:*" for h in _extra_hosts]
_allowed_origins = _LOCALHOST_ORIGINS + [f"http://{h}:*" for h in _extra_hosts]
_transport_security = TransportSecuritySettings(
enable_dns_rebinding_protection=True,
allowed_hosts=_allowed_hosts,
allowed_origins=_allowed_origins,
)
# ---------------------------------------------------------------------------
# FastMCP server
# ---------------------------------------------------------------------------
@@ -93,6 +111,7 @@ mcp = FastMCP(
"authentication — all requests are authenticated via the Authorization "
"header at the HTTP transport layer."
),
transport_security=_transport_security,
)