import bpy, math
from mathutils import Vector

info = {}
info["blender"] = bpy.app.version_string

# ---------- clean slate ----------
try:
    if bpy.context.object and bpy.context.object.mode != 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT')
except Exception:
    pass
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for blklist in (bpy.data.meshes, bpy.data.curves, bpy.data.materials, bpy.data.lights,
                bpy.data.cameras, bpy.data.images, bpy.data.worlds):
    for blk in list(blklist):
        if blk.users == 0:
            blklist.remove(blk)

scene = bpy.context.scene

def make_coll(name):
    c = bpy.data.collections.get(name) or bpy.data.collections.new(name)
    if c.name not in [ch.name for ch in scene.collection.children]:
        try:
            scene.collection.children.link(c)
        except RuntimeError:
            pass
    return c

COLLS = {n: make_coll(n) for n in ["Terrain","Water","Cabin","Chimney","Dock","Trees","Props","Lights","Camera"]}

def link_to(obj, cname):
    for c in list(obj.users_collection):
        c.objects.unlink(obj)
    COLLS[cname].objects.link(obj)

# ---------- units / render ----------
scene.unit_settings.system = 'METRIC'
scene.unit_settings.length_unit = 'METERS'
scene.render.engine = 'CYCLES'
cy = scene.cycles
cy.samples = 128
cy.use_denoising = True
cy.caustics_reflective = False
cy.caustics_refractive = False
cy.sample_clamp_indirect = 8.0
scene.render.resolution_x = 1536
scene.render.resolution_y = 1536

prefs = bpy.context.preferences.addons['cycles'].preferences
gpu = None
for dt in ('OPTIX','CUDA','HIP','METAL','ONEAPI'):
    try:
        prefs.compute_device_type = dt
        prefs.get_devices()
        if any(d.type == dt for d in prefs.devices):
            gpu = dt
            break
    except Exception:
        pass
if gpu:
    scene.cycles.device = 'GPU'
    for d in prefs.devices:
        d.use = (d.type == gpu) or (d.type == 'CPU')
info["gpu"] = gpu

vt = scene.view_settings
try:
    vt.view_transform = 'AgX'
except Exception:
    vt.view_transform = 'Filmic'
look_set = None
for lk in ('AgX - Punchy','Punchy','AgX - Medium High Contrast','Medium High Contrast'):
    try:
        vt.look = lk
        look_set = lk
        break
    except Exception:
        pass
info["look"] = look_set
vt.exposure = 0.35

# ---------- world: soft sky + subtle aerial haze ----------
world = bpy.data.worlds.get('World') or bpy.data.worlds.new('World')
scene.world = world
world.use_nodes = True
nt = world.node_tree
nt.nodes.clear()
o = nt.nodes.new('ShaderNodeOutputWorld')
b = nt.nodes.new('ShaderNodeBackground')
sky = nt.nodes.new('ShaderNodeTexSky')
sky.sky_type = 'NISHITA'
sky.sun_disc = False
sky.sun_elevation = math.radians(38)
sky.sun_rotation = math.radians(115)
sky.altitude = 60
sky.air_density = 1.1
sky.dust_density = 1.6
nt.links.new(sky.outputs['Color'], b.inputs['Color'])
b.inputs['Strength'].default_value = 0.55
nt.links.new(b.outputs['Background'], o.inputs['Surface'])
vw = nt.nodes.new('ShaderNodeVolumeScatter')
vw.inputs['Density'].default_value = 0.0012
vw.inputs['Anisotropy'].default_value = 0.25
nt.links.new(vw.outputs['Volume'], o.inputs['Volume'])

# ---------- warm key sun from ESE ----------
ld = bpy.data.lights.new('KeySun', 'SUN')
ld.energy = 5.5
ld.angle = math.radians(1.2)
ld.color = (1.0, 0.92, 0.80)
sun = bpy.data.objects.new('KeySun', ld)
link_to(sun, 'Lights')
Ldir = Vector((-0.72, 0.28, -0.63)).normalized()
sun.rotation_euler = Ldir.to_track_quat('-Z', 'Y').to_euler()
sun.location = (12, -6, 10)

info["objects"] = len(bpy.data.objects)
result = info


Traceback (most recent call last):
  File "<repo>\.runner\blender-mcp-v1.0.0\addon\mcp_to_blender_server.py", line 230, in _execute_code
    exec(code, namespace)
    ~~~~^^^^^^^^^^^^^^^^^
  File "<string>", line 94, in <module>
TypeError: bpy_struct: item.attr = val: enum "NISHITA" not found in ('SINGLE_SCATTERING', 'MULTIPLE_SCATTERING', 'PREETHAM', 'HOSEK_WILKIE')