PrefectHQ/fastmcp · error · ValueError

host_origin_protection must be True, False, or 'auto'.

Error message

host_origin_protection must be True, False, or 'auto'.

What it means

create_streamable_http_app validates the host_origin_protection flag against the allowed values True, False, and 'auto' (where 'auto' enables DNS-rebinding protection only for non-localhost binds). Any other value raises ValueError before middleware is assembled.

Source

Thrown at fastmcp_slim/fastmcp/server/http.py:650

    else:
        # No auth required
        http_methods = ["POST", "DELETE"] if stateless_http else None
        server_routes.append(
            Route(
                streamable_http_path,
                endpoint=streamable_http_app,
                methods=http_methods,
            )
        )

    # Add custom routes with lowest precedence
    if routes:
        server_routes.extend(routes)
    server_routes.extend(server._get_additional_http_routes())

    # Add middleware
    if host_origin_protection not in (True, False, "auto"):
        raise ValueError("host_origin_protection must be True, False, or 'auto'.")

    if host_origin_protection is not False:
        server_middleware.insert(
            0,
            Middleware(
                HostOriginGuardMiddleware,
                allowed_hosts=allowed_hosts,
                allowed_origins=allowed_origins,
                mode="strict" if host_origin_protection is True else "auto",
            ),
        )
    if middleware:
        server_middleware.extend(middleware)

    # Create a lifespan manager to start and stop the session manager
    @asynccontextmanager
    async def lifespan(app: Starlette) -> AsyncGenerator[None, None]:
        streamable_http_app.session_manager = FastMCPStreamableHTTPSessionManager(

View on GitHub (pinned to 1f02114297)

Solutions

  1. Coerce config values to bool before passing: host_origin_protection = raw in (True, 'true', '1') or raw == 'auto' and 'auto'.
  2. Pass exactly True, False, or 'auto' (lowercase string) as the argument.
  3. Normalize env/YAML strings: value = {'true': True, 'false': False}.get(str(raw).lower(), 'auto') when raw is a string.
  4. Omit the argument to use the default instead of passing None.

Example fix

// before
app = server.http_app(host_origin_protection="true")  # ValueError
// after
raw = os.getenv("HOST_ORIGIN_PROTECTION", "auto")
hop = {"true": True, "false": False}.get(raw.lower(), "auto")
app = server.http_app(host_origin_protection=hop)
Defensive patterns

Strategy: validation

Validate before calling

RAW = os.getenv("HOST_ORIGIN_PROTECTION", "auto")
value = {"true": True, "false": False}.get(str(RAW).lower(), "auto")
assert value in (True, False, "auto")

Type guard

def is_valid_hop(v) -> bool:
    return v is True or v is False or v == "auto"

Try / catch

try:
    app = server.http_app(host_origin_protection=raw)
except ValueError:
    app = server.http_app(host_origin_protection="auto")

Prevention

When it happens

Trigger: Calling server.create_streamable_http_app(host_origin_protection=...) or http_app(...) with a truthy/falsy non-boolean like a string 'true', 'yes', 1, or None.

Common situations: Config-driven deployments where the setting is parsed from YAML/env as a string ('false' is neither True/False/'auto'); typos like 'Auto'; passing CLI string flags without coercion.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/9a41d849fdd86cb0. Report an issue: GitHub.