diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 467e5d166c8..6a014ee5e13 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -3208,6 +3208,15 @@ if MCP_AVAILABLE: verbose_logger.debug( f"MCP server auth headers: {list(mcp_server_auth_headers.keys()) if mcp_server_auth_headers else None}" ) + # https://datatracker.ietf.org/doc/html/rfc9728#name-www-authenticate-response + await _raise_preemptive_401_for_unauthenticated_servers( + scope=scope, + mcp_servers=mcp_servers, + oauth2_headers=oauth2_headers, + mcp_server_auth_headers=mcp_server_auth_headers, + user_api_key_auth=user_api_key_auth, + client_ip=_sse_client_ip, + ) set_auth_context( user_api_key_auth=user_api_key_auth, mcp_auth_header=mcp_auth_header, diff --git a/litellm/proxy/auth/auth_utils.py b/litellm/proxy/auth/auth_utils.py index 637a4a070c4..44266203446 100644 --- a/litellm/proxy/auth/auth_utils.py +++ b/litellm/proxy/auth/auth_utils.py @@ -506,13 +506,16 @@ def get_request_route(request: Request) -> str: if not isinstance(scope, dict): return str(request.url.path) raw_path: str = str(scope.get("path", request.url.path)) - root_path: str = str(scope.get("app_root_path", scope.get("root_path", ""))) + root_path: str = str( + scope.get("app_root_path", scope.get("root_path", "")) + ).rstrip("/") if not isinstance(raw_path, str): return str(request.url.path) - # Only strip root_path when it is a meaningful prefix (not bare "/"). - # Stripping bare "/" would remove the leading slash from every path - # e.g. "/team/new" → "team/new", breaking route matching. - if root_path and root_path != "/" and raw_path.startswith(root_path): + # Only strip root_path when it is a meaningful prefix. Trailing + # slashes are stripped above so the result always keeps its leading + # "/" — stripping a bare "/" or "/prefix/" would otherwise produce + # paths like "team/new" and break route matching. + if root_path and raw_path.startswith(root_path): return raw_path[len(root_path) :] return raw_path except Exception as e: diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index e9d9c243e7c..07f515eff61 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -1542,7 +1542,7 @@ if MCP_AVAILABLE: master_key, algorithms=["HS256"], # UI session cookies may omit exp; don't require it. - options={"verify_exp": False}, + options={"verify_exp": False, "verify_aud": False}, ) if decoded.get("login_method") in ("sso", "username_password"): cookie_key = decoded.get("key", "")