Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-fastmcp-slim for 
openSUSE:Factory checked in at 2026-07-09 22:20:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fastmcp-slim (Old)
 and      /work/SRC/openSUSE:Factory/.python-fastmcp-slim.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-fastmcp-slim"

Thu Jul  9 22:20:26 2026 rev:4 rq:1364635 version:3.4.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-fastmcp-slim/python-fastmcp-slim.changes  
2026-07-06 12:37:38.654481303 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-fastmcp-slim.new.1991/python-fastmcp-slim.changes
        2026-07-09 22:21:11.853839732 +0200
@@ -1,0 +2,10 @@
+Thu Jul  9 05:28:00 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 3.4.4:
+  * Relax the Streamable HTTP host/origin guard defaults added in
+    3.4.3 to restore compatibility with existing ASGI, serverless
+    and reverse-proxy deployments
+  * Add Hugging Face OAuth authentication provider (PKCE, Dynamic
+    Client Registration)
+
+-------------------------------------------------------------------

Old:
----
  fastmcp_slim-3.4.3.tar.gz

New:
----
  fastmcp_slim-3.4.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-fastmcp-slim.spec ++++++
--- /var/tmp/diff_new_pack.xWG3se/_old  2026-07-09 22:21:12.513862278 +0200
+++ /var/tmp/diff_new_pack.xWG3se/_new  2026-07-09 22:21:12.517862414 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-fastmcp-slim
-Version:        3.4.3
+Version:        3.4.4
 Release:        0
 Summary:        The fast, Pythonic way to build MCP servers and clients (slim)
 License:        Apache-2.0

++++++ fastmcp_slim-3.4.3.tar.gz -> fastmcp_slim-3.4.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fastmcp_slim-3.4.3/PKG-INFO 
new/fastmcp_slim-3.4.4/PKG-INFO
--- old/fastmcp_slim-3.4.3/PKG-INFO     2020-02-02 01:00:00.000000000 +0100
+++ new/fastmcp_slim-3.4.4/PKG-INFO     2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: fastmcp-slim
-Version: 3.4.3
+Version: 3.4.4
 Summary: The dependency-slim FastMCP package.
 Project-URL: Homepage, https://gofastmcp.com
 Project-URL: Repository, https://github.com/PrefectHQ/fastmcp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fastmcp_slim-3.4.3/fastmcp/server/auth/providers/huggingface.py 
new/fastmcp_slim-3.4.4/fastmcp/server/auth/providers/huggingface.py
--- old/fastmcp_slim-3.4.3/fastmcp/server/auth/providers/huggingface.py 
1970-01-01 01:00:00.000000000 +0100
+++ new/fastmcp_slim-3.4.4/fastmcp/server/auth/providers/huggingface.py 
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,279 @@
+"""Hugging Face OAuth provider for FastMCP."""
+
+from __future__ import annotations
+
+import contextlib
+from collections.abc import Mapping
+from typing import Any, Literal
+
+import httpx
+from key_value.aio.protocols import AsyncKeyValue
+from pydantic import AnyHttpUrl
+
+from fastmcp.server.auth import TokenVerifier
+from fastmcp.server.auth.auth import AccessToken
+from fastmcp.server.auth.oauth_proxy import OAuthProxy
+from fastmcp.utilities.auth import parse_scopes
+from fastmcp.utilities.logging import get_logger
+
+logger = get_logger(__name__)
+
+HUGGINGFACE_AUTHORIZATION_ENDPOINT = "https://huggingface.co/oauth/authorize";
+HUGGINGFACE_TOKEN_ENDPOINT = "https://huggingface.co/oauth/token";
+HUGGINGFACE_USERINFO_ENDPOINT = "https://huggingface.co/oauth/userinfo";
+HUGGINGFACE_WHOAMI_ENDPOINT = "https://huggingface.co/api/whoami-v2";
+
+DEFAULT_HUGGINGFACE_SCOPES = ["openid", "profile"]
+
+
+def _extract_scopes(data: Mapping[str, Any]) -> list[str]:
+    scope_value = data.get("scope") or data.get("scopes")
+    if isinstance(scope_value, str):
+        return parse_scopes(scope_value) or []
+    if isinstance(scope_value, list):
+        return [str(scope).strip() for scope in scope_value if 
str(scope).strip()]
+
+    auth = data.get("auth")
+    if not isinstance(auth, Mapping):
+        return []
+    access_token = auth.get("accessToken")
+    if not isinstance(access_token, Mapping):
+        return []
+
+    nested_scopes = access_token.get("scopes") or access_token.get("scope")
+    if isinstance(nested_scopes, str):
+        return parse_scopes(nested_scopes) or []
+    if isinstance(nested_scopes, list):
+        return [
+            str(scope.get("name") if isinstance(scope, Mapping) else 
scope).strip()
+            for scope in nested_scopes
+            if str(scope.get("name") if isinstance(scope, Mapping) else 
scope).strip()
+        ]
+    return []
+
+
+class HuggingFaceTokenVerifier(TokenVerifier):
+    """Token verifier for Hugging Face OAuth access tokens.
+
+    Hugging Face OAuth access tokens are opaque, so validation is performed by
+    calling Hugging Face's userinfo endpoint.
+    """
+
+    def __init__(
+        self,
+        *,
+        required_scopes: list[str] | None = None,
+        timeout_seconds: int = 10,
+        http_client: httpx.AsyncClient | None = None,
+    ):
+        super().__init__(required_scopes=required_scopes)
+        self.timeout_seconds = timeout_seconds
+        self._http_client = http_client
+
+    async def verify_token(self, token: str) -> AccessToken | None:
+        """Verify a Hugging Face OAuth token using the userinfo endpoint."""
+        try:
+            async with (
+                contextlib.nullcontext(self._http_client)
+                if self._http_client is not None
+                else httpx.AsyncClient(timeout=self.timeout_seconds)
+            ) as client:
+                userinfo_response = await client.get(
+                    HUGGINGFACE_USERINFO_ENDPOINT,
+                    headers={
+                        "Authorization": f"Bearer {token}",
+                        "User-Agent": "FastMCP-HuggingFace-OAuth",
+                    },
+                )
+                if userinfo_response.status_code != 200:
+                    logger.debug(
+                        "Hugging Face token verification failed: %d",
+                        userinfo_response.status_code,
+                    )
+                    return None
+
+                userinfo = userinfo_response.json()
+                sub = userinfo.get("sub")
+                if not sub:
+                    logger.debug("Hugging Face userinfo missing 'sub' claim")
+                    return None
+
+                token_scopes = _extract_scopes(userinfo)
+                whoami: dict[str, Any] | None = None
+                if not token_scopes or (
+                    self.required_scopes
+                    and not 
set(self.required_scopes).issubset(set(token_scopes))
+                ):
+                    whoami = await self._fetch_whoami(client, token)
+                    if whoami:
+                        token_scopes = list(
+                            dict.fromkeys([*token_scopes, 
*_extract_scopes(whoami)])
+                        )
+
+                if not token_scopes:
+                    token_scopes = list(DEFAULT_HUGGINGFACE_SCOPES)
+
+                if self.required_scopes and not 
set(self.required_scopes).issubset(
+                    set(token_scopes)
+                ):
+                    logger.debug(
+                        "Hugging Face token missing required scopes. Has %d, 
needs %d",
+                        len(token_scopes),
+                        len(self.required_scopes),
+                    )
+                    return None
+
+                username = (
+                    userinfo.get("preferred_username")
+                    or userinfo.get("nickname")
+                    or userinfo.get("name")
+                )
+                return AccessToken(
+                    token=token,
+                    client_id=str(sub),
+                    scopes=token_scopes,
+                    expires_at=None,
+                    claims={
+                        "sub": str(sub),
+                        "name": userinfo.get("name"),
+                        "preferred_username": username,
+                        "email": userinfo.get("email"),
+                        "email_verified": userinfo.get("email_verified"),
+                        "profile": userinfo.get("profile"),
+                        "picture": userinfo.get("picture"),
+                        "organizations": userinfo.get("organizations"),
+                        "huggingface_userinfo": userinfo,
+                        "huggingface_whoami": whoami,
+                    },
+                )
+
+        except httpx.RequestError as e:
+            logger.debug("Failed to verify Hugging Face token: %s", e)
+            return None
+        except Exception as e:
+            logger.debug("Hugging Face token verification error: %s", e)
+            return None
+
+    async def _fetch_whoami(
+        self, client: httpx.AsyncClient, token: str
+    ) -> dict[str, Any] | None:
+        response = await client.get(
+            HUGGINGFACE_WHOAMI_ENDPOINT,
+            headers={
+                "Authorization": f"Bearer {token}",
+                "User-Agent": "FastMCP-HuggingFace-OAuth",
+            },
+        )
+        if response.status_code != 200:
+            logger.debug("Hugging Face whoami lookup failed: %d", 
response.status_code)
+            return None
+        return response.json()
+
+
+class HuggingFaceProvider(OAuthProxy):
+    """Complete Hugging Face OAuth provider for FastMCP."""
+
+    def __init__(
+        self,
+        *,
+        client_id: str,
+        client_secret: str | None = None,
+        base_url: AnyHttpUrl | str,
+        resource_base_url: AnyHttpUrl | str | None = None,
+        issuer_url: AnyHttpUrl | str | None = None,
+        redirect_path: str | None = None,
+        required_scopes: list[str] | None = None,
+        valid_scopes: list[str] | None = None,
+        timeout_seconds: int = 10,
+        allowed_client_redirect_uris: list[str] | None = None,
+        client_storage: AsyncKeyValue | None = None,
+        jwt_signing_key: str | bytes | None = None,
+        require_authorization_consent: bool | Literal["remember", "external"] 
= True,
+        consent_csp_policy: str | None = None,
+        forward_resource: bool = True,
+        fallback_refresh_token_expiry_seconds: int | None = None,
+        fastmcp_access_token_expiry_seconds: int | None = None,
+        token_expiry_threshold_seconds: int = 0,
+        extra_authorize_params: dict[str, str] | None = None,
+        extra_token_params: dict[str, str] | None = None,
+        http_client: httpx.AsyncClient | None = None,
+        enable_cimd: bool = True,
+    ):
+        """Initialize Hugging Face OAuth provider.
+
+        Args:
+            client_id: Hugging Face OAuth app client ID. Public apps and CIMD
+                client IDs are supported.
+            client_secret: Hugging Face OAuth app client secret. Optional for
+                public PKCE apps; when omitted, ``jwt_signing_key`` is 
required.
+            base_url: Public URL where OAuth endpoints will be accessible.
+            required_scopes: Required Hugging Face scopes. Defaults to
+                ``["openid", "profile"]``.
+            valid_scopes: Scopes clients may request. Defaults to required 
scopes.
+            extra_authorize_params: Extra authorization parameters, such as
+                ``{"orgIds": "your-org-id"}`` for organization grants.
+        """
+        required_scopes_final = (
+            parse_scopes(required_scopes)
+            if required_scopes is not None
+            else list(DEFAULT_HUGGINGFACE_SCOPES)
+        ) or []
+        valid_scopes_final = parse_scopes(valid_scopes)
+
+        # Do not pass provider-level required_scopes into the verifier here.
+        # Hugging Face's userinfo endpoint validates opaque access tokens and
+        # returns identity claims, but granted scopes are carried reliably in
+        # the upstream token response. OAuthProxy stores those scopes, enforces
+        # provider.required_scopes against FastMCP-issued tokens, and
+        # _uses_alternate_verification() patches the stored upstream scopes
+        # onto the returned AccessToken.
+        token_verifier = HuggingFaceTokenVerifier(
+            timeout_seconds=timeout_seconds,
+            http_client=http_client,
+        )
+
+        super().__init__(
+            upstream_authorization_endpoint=HUGGINGFACE_AUTHORIZATION_ENDPOINT,
+            upstream_token_endpoint=HUGGINGFACE_TOKEN_ENDPOINT,
+            upstream_client_id=client_id,
+            upstream_client_secret=client_secret,
+            token_verifier=token_verifier,
+            base_url=base_url,
+            resource_base_url=resource_base_url,
+            redirect_path=redirect_path,
+            issuer_url=issuer_url or base_url,
+            allowed_client_redirect_uris=allowed_client_redirect_uris,
+            client_storage=client_storage,
+            jwt_signing_key=jwt_signing_key,
+            require_authorization_consent=require_authorization_consent,
+            consent_csp_policy=consent_csp_policy,
+            forward_resource=forward_resource,
+            
fallback_refresh_token_expiry_seconds=fallback_refresh_token_expiry_seconds,
+            
fastmcp_access_token_expiry_seconds=fastmcp_access_token_expiry_seconds,
+            token_expiry_threshold_seconds=token_expiry_threshold_seconds,
+            extra_authorize_params=extra_authorize_params,
+            extra_token_params=extra_token_params,
+            token_endpoint_auth_method="client_secret_basic"
+            if client_secret
+            else "none",
+            valid_scopes=valid_scopes_final,
+            enable_cimd=enable_cimd,
+        )
+
+        logger.debug(
+            "Initialized Hugging Face OAuth provider for client %s with 
scopes: %s",
+            client_id,
+            required_scopes_final,
+        )
+
+        self.required_scopes = required_scopes_final
+        self.update_default_scopes(valid_scopes_final or required_scopes_final)
+
+    def _uses_alternate_verification(self) -> bool:
+        """Patch returned token scopes from the upstream token response.
+
+        Hugging Face OAuth access tokens are opaque. The userinfo endpoint
+        validates the token and returns identity claims, but scope information 
is
+        carried by the token response stored in OAuthProxy's upstream token 
set.
+        """
+        return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fastmcp_slim-3.4.3/fastmcp/server/http.py 
new/fastmcp_slim-3.4.4/fastmcp/server/http.py
--- old/fastmcp_slim-3.4.3/fastmcp/server/http.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/fastmcp_slim-3.4.4/fastmcp/server/http.py       2020-02-02 
01:00:00.000000000 +0100
@@ -5,7 +5,7 @@
 from contextvars import ContextVar
 from fnmatch import fnmatchcase
 from ipaddress import ip_address
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, Literal
 from urllib.parse import urlsplit
 from uuid import uuid4
 
@@ -36,6 +36,8 @@
 logger = get_logger(__name__)
 
 DEFAULT_HOSTS = ("127.0.0.1", "localhost", "::1")
+HostOriginProtection = bool | Literal["auto"]
+HostOriginProtectionMode = Literal["auto", "strict"]
 
 
 class FastMCPStreamableHTTPSessionManager(StreamableHTTPSessionManager):
@@ -228,34 +230,81 @@
         app: ASGIApp,
         allowed_hosts: Sequence[str] | None = None,
         allowed_origins: Sequence[str] | None = None,
+        mode: HostOriginProtectionMode = "auto",
     ) -> None:
         self.app = app
         self.allowed_hosts = tuple(allowed_hosts or ())
         self.allowed_origins = tuple(allowed_origins or ())
+        self.mode = mode
+        self.has_explicit_allowed_hosts = allowed_hosts is not None
+        self.has_explicit_allowed_origins = allowed_origins is not None
 
     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> 
None:
         if scope["type"] != "http":
             await self.app(scope, receive, send)
             return
 
-        allowed_hosts = self._allowed_hosts_for_scope(scope)
         headers = Headers(scope=scope)
         host = headers.get("host", "")
 
-        if not _host_matches(host, allowed_hosts):
+        if self._should_validate_host(scope) and not _host_matches(
+            host,
+            self._allowed_hosts_for_scope(scope),
+        ):
             response = Response("Misdirected Request", status_code=421)
             await response(scope, receive, send)
             return
 
         origin = headers.get("origin")
         request_origin = _request_origin(scope, host)
-        if origin and not self._origin_allowed(origin, request_origin, host):
+        if (
+            origin
+            and self._should_validate_origin(scope, host)
+            and not self._origin_allowed(
+                origin,
+                request_origin,
+                host,
+                allow_same_origin_fallback=self._allow_same_origin_fallback(
+                    scope,
+                    host,
+                ),
+            )
+        ):
             response = Response("Forbidden Origin", status_code=403)
             await response(scope, receive, send)
             return
 
         await self.app(scope, receive, send)
 
+    def _should_validate_host(self, scope: Scope) -> bool:
+        if self.mode == "strict" or self.has_explicit_allowed_hosts:
+            return True
+
+        server = scope.get("server")
+        return bool(server and _is_loopback_host(server[0]))
+
+    def _should_validate_origin(self, scope: Scope, host: str) -> bool:
+        if (
+            self.mode == "strict"
+            or self.has_explicit_allowed_hosts
+            or self.has_explicit_allowed_origins
+            or _is_loopback_host(host)
+        ):
+            return True
+
+        server = scope.get("server")
+        return bool(server and _is_loopback_host(server[0]))
+
+    def _allow_same_origin_fallback(self, scope: Scope, host: str) -> bool:
+        if not self.has_explicit_allowed_origins:
+            return True
+
+        if self.mode == "strict" or self.has_explicit_allowed_hosts:
+            return True
+
+        server = scope.get("server")
+        return _is_loopback_host(host) or bool(server and 
_is_loopback_host(server[0]))
+
     def _allowed_hosts_for_scope(self, scope: Scope) -> tuple[str, ...]:
         allowed_hosts = list(DEFAULT_HOSTS)
         allowed_hosts.extend(self.allowed_hosts)
@@ -268,10 +317,19 @@
 
         return tuple(allowed_hosts)
 
-    def _origin_allowed(self, origin: str, request_origin: str, host: str) -> 
bool:
+    def _origin_allowed(
+        self,
+        origin: str,
+        request_origin: str,
+        host: str,
+        allow_same_origin_fallback: bool,
+    ) -> bool:
         if _origin_matches(origin, self.allowed_origins):
             return True
 
+        if not allow_same_origin_fallback:
+            return False
+
         origin_host = _origin_host(origin)
         if _is_loopback_host(origin_host) and _is_loopback_host(host):
             return True
@@ -491,7 +549,7 @@
     debug: bool = False,
     routes: list[BaseRoute] | None = None,
     middleware: list[Middleware] | None = None,
-    host_origin_protection: bool = True,
+    host_origin_protection: HostOriginProtection = False,
     allowed_hosts: Sequence[str] | None = None,
     allowed_origins: Sequence[str] | None = None,
 ) -> StarletteWithLifespan:
@@ -511,7 +569,9 @@
         routes: Optional list of custom routes
         middleware: Optional list of middleware
         host_origin_protection: Whether to validate Host and Origin headers
-            before requests reach the MCP endpoint.
+            before requests reach the MCP endpoint. Defaults to False for
+            compatibility. "auto" protects localhost-bound servers and explicit
+            host/origin allowlists.
         allowed_hosts: Additional hostnames that may appear in the Host header.
         allowed_origins: Additional browser origins trusted by the request 
guard.
             Configure CORS separately when browser JavaScript must read
@@ -576,13 +636,17 @@
     server_routes.extend(server._get_additional_http_routes())
 
     # Add middleware
-    if host_origin_protection:
+    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:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fastmcp_slim-3.4.3/fastmcp/server/mixins/transport.py 
new/fastmcp_slim-3.4.4/fastmcp/server/mixins/transport.py
--- old/fastmcp_slim-3.4.3/fastmcp/server/mixins/transport.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/fastmcp_slim-3.4.4/fastmcp/server/mixins/transport.py   2020-02-02 
01:00:00.000000000 +0100
@@ -19,7 +19,9 @@
 import fastmcp
 from fastmcp.server.event_store import EventStore
 from fastmcp.server.http import (
+    HostOriginProtection,
     StarletteWithLifespan,
+    _is_loopback_host,
     create_sse_app,
     create_streamable_http_app,
 )
@@ -48,6 +50,22 @@
     return host
 
 
+def _resolve_allowed_hosts_for_run(
+    *,
+    host: str,
+    host_origin_protection: HostOriginProtection,
+    allowed_hosts: list[str] | None,
+    configured_allowed_hosts: list[str] | None,
+) -> list[str] | None:
+    if allowed_hosts is not None:
+        return allowed_hosts
+
+    if host_origin_protection == "auto" and _is_loopback_host(host):
+        return [*(configured_allowed_hosts or []), host]
+
+    return configured_allowed_hosts
+
+
 class TransportMixin:
     """Mixin providing transport-related methods for FastMCP.
 
@@ -250,7 +268,7 @@
         json_response: bool | None = None,
         stateless_http: bool | None = None,
         stateless: bool | None = None,
-        host_origin_protection: bool | None = None,
+        host_origin_protection: HostOriginProtection | None = None,
         allowed_hosts: list[str] | None = None,
         allowed_origins: list[str] | None = None,
         sockets: list[socket.socket] | None = None,
@@ -269,7 +287,9 @@
             stateless_http: Whether to use stateless HTTP (defaults to 
settings.stateless_http)
             stateless: Alias for stateless_http for CLI consistency
             host_origin_protection: Whether to validate Host and Origin headers
-                before requests reach the MCP endpoint.
+                before requests reach the MCP endpoint. Defaults to
+                settings.http_host_origin_protection. "auto" protects
+                localhost-bound servers and explicit host/origin allowlists.
             allowed_hosts: Additional hostnames that may appear in the Host 
header.
             allowed_origins: Additional browser origins trusted by the request 
guard.
                 Configure CORS separately when browser JavaScript must read
@@ -290,6 +310,17 @@
 
         host = host if host is not None else fastmcp.settings.host
         port = port if port is not None else fastmcp.settings.port
+        resolved_host_origin_protection = (
+            host_origin_protection
+            if host_origin_protection is not None
+            else fastmcp.settings.http_host_origin_protection
+        )
+        resolved_allowed_hosts = _resolve_allowed_hosts_for_run(
+            host=host,
+            host_origin_protection=resolved_host_origin_protection,
+            allowed_hosts=allowed_hosts,
+            configured_allowed_hosts=fastmcp.settings.http_allowed_hosts,
+        )
         default_log_level_to_use = (
             log_level if log_level is not None else fastmcp.settings.log_level
         ).lower()
@@ -300,8 +331,8 @@
             middleware=middleware,
             json_response=json_response,
             stateless_http=stateless_http,
-            host_origin_protection=host_origin_protection,
-            allowed_hosts=allowed_hosts,
+            host_origin_protection=resolved_host_origin_protection,
+            allowed_hosts=resolved_allowed_hosts,
             allowed_origins=allowed_origins,
         )
 
@@ -345,7 +376,7 @@
         transport: Literal["http", "streamable-http", "sse"] = "http",
         event_store: EventStore | None = None,
         retry_interval: int | None = None,
-        host_origin_protection: bool | None = None,
+        host_origin_protection: HostOriginProtection | None = None,
         allowed_hosts: list[str] | None = None,
         allowed_origins: list[str] | None = None,
     ) -> StarletteWithLifespan:
@@ -365,7 +396,9 @@
                 disconnections. Requires event_store to be set. Only used with
                 streamable-http transport.
             host_origin_protection: Whether to validate Host and Origin headers
-                before requests reach the MCP endpoint.
+                before requests reach the MCP endpoint. Defaults to
+                settings.http_host_origin_protection. "auto" protects
+                localhost-bound servers and explicit host/origin allowlists.
             allowed_hosts: Additional hostnames that may appear in the Host 
header.
             allowed_origins: Additional browser origins trusted by the request 
guard.
                 Configure CORS separately when browser JavaScript must read
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fastmcp_slim-3.4.3/fastmcp/settings.py 
new/fastmcp_slim-3.4.4/fastmcp/settings.py
--- old/fastmcp_slim-3.4.3/fastmcp/settings.py  2020-02-02 01:00:00.000000000 
+0100
+++ new/fastmcp_slim-3.4.4/fastmcp/settings.py  2020-02-02 01:00:00.000000000 
+0100
@@ -320,7 +320,7 @@
     stateless_http: bool = (
         False  # If True, uses true stateless mode (new transport per request)
     )
-    http_host_origin_protection: bool = True
+    http_host_origin_protection: bool | Literal["auto"] = False
     http_allowed_hosts: list[str] | None = None
     http_allowed_origins: list[str] | None = None
 

Reply via email to