Water is in. Now the biggest piece: the log cabin — stacked log walls, gabled roof, floor, door, windows, and porch.

import bpy, math, random
from mathutils import Vector

scene = bpy.context.scene
CX = 6.8
random.seed(11)

def link_to(obj, cname):
    col = bpy.data.collections.get(cname)
    if col is None:
        col = bpy.data.collections.new(cname); scene.collection.children.link(col)
    for c in list(obj.users_collection): c.objects.unlink(obj)
    col.objects.link(obj)

def new_obj(name, mesh, cname):
    ob = bpy.data.objects.new(name, mesh)
    link_to(ob, cname)
    return ob

# ---------------- materials ----------------
def pmat(name, color, rough=0.7, spec=0.3):
    m = bpy.data.materials.get(name) or bpy.data.materials.new(name)
    if not m.use_nodes: m.use_nodes=True
    nt = m.node_tree; nt.nodes.clear()
    out = nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(500,0)
    b = nt.nodes.new('ShaderNodeBsdfPrincipled'); b.location=(250,0)
    nt.links.new(b.outputs['BSDF'], out.inputs['Surface'])
    b.inputs['Base Color'].default_value = (*color,1)
    b.inputs['Roughness'].default_value = rough
    texco = nt.nodes.new('ShaderNodeTexCoord'); texco.location=(-600,-300)
    noise = nt.nodes.new('ShaderNodeTexNoise'); noise.location=(-400,-300)
    noise.inputs['Scale'].default_value=14.0; noise.inputs['Detail'].default_value=8
    noise.inputs['Distortion'].default_value=0.4
    ramp = nt.nodes.new('ShaderNodeValToRGB'); ramp.location=(-200,-300)
    ramp.color_ramp.elements[0].position=0.35
    ramp.color_ramp.elements[0].color = (*[c*0.72 for c in color],1)
    ramp.color_ramp.elements[1].position=0.65
    ramp.color_ramp.elements[1].color = (*[min(1,c*1.25) for c in color],1)
    nt.links.new(texco.outputs['Object'], noise.inputs['Vector'])
    nt.links.new(noise.outputs['Fac'], ramp.inputs['Fac'])
    nt.links.new(ramp.outputs['Color'], b.inputs['Base Color'])
    bmp = nt.nodes.new('ShaderNodeBump'); bmp.location=(40,-260)
    bmp.inputs['Strength'].default_value=0.10
    nt.links.new(noise.outputs['Fac'], bmp.inputs['Height'])
    nt.links.new(bmp.outputs['Normal'], b.inputs['Normal'])
    return m

MAT_LOG   = pmat("LogMat",  (0.30,0.185,0.095), rough=0.68)
MAT_PLANK = pmat("PlankMat",(0.235,0.145,0.075), rough=0.60)
MAT_DARK  = pmat("DarkWood",(0.085,0.052,0.028), rough=0.62)
MAT_STONE = None
MAT_ROOF  = pmat("RoofMat", (0.115,0.065,0.042), rough=0.55)
MAT_TRIM  = pmat("TrimWood",(0.16,0.105,0.06), rough=0.55)

def stone_mat():
    global MAT_STONE
    if MAT_STONE: return MAT_STONE
    m = bpy.data.materials.new("StoneMat"); m.use_nodes=True
    nt=m.node_tree; nt.nodes.clear()
    out=nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(520,0)
    b=nt.nodes.new('ShaderNodeBsdfPrincipled'); b.location=(280,0)
    nt.links.new(b.outputs['BSDF'], out.inputs['Surface'])
    b.inputs['Roughness'].default_value=0.85
    tc=nt.nodes.new('ShaderNodeTexCoord'); tc.location=(-700,-200)
    # voronoi cells -> per-stone gray tint + mortar gaps via bump
    vor=nt.nodes.new('ShaderNodeTexVoronoi'); vor.location=(-500,-100)
    vor.feature='F1'; vor.distance='EUCLIDEAN'
    vor.inputs['Scale'].default_value=9.0
    vor.inputs['Randomness'].default_value=1.0
    ramp=nt.nodes.new('ShaderNodeValToRGB'); ramp.location=(-300,-100)
    e0=ramp.color_ramp.elements[0]; e0.position=0.15; e0.color=(0.32,0.31,0.295,1)
    e1=ramp.color_ramp.elements[1]; e1.position=0.85; e1.color=(0.52,0.505,0.475,1)
    nt.links.new(tc.outputs['Object'], vor.inputs['Vector'])
    nt.links.new(vor.outputs['Distance'], ramp.inputs['Fac'])
    nt.links.new(ramp.outputs['Color'], b.inputs['Base Color'])
    bmp=nt.nodes.new('ShaderNodeBump'); bmp.location=(-80,-240)
    bmp.inputs['Strength'].default_value=0.45
    inv=nt.nodes.new('ShaderNodeInvert'); inv.location=(-140,-320)
    nt.links.new(vor.outputs['Distance'], inv.inputs['Color'])
    nt.links.new(inv.outputs['Color'], bmp.inputs['Height'])
    nt.links.new(bmp.outputs['Normal'], b.inputs['Normal'])
    MAT_STONE = m
    return m
stone_mat()

def glass_mat():
    m = bpy.data.materials.get("GlassPane") or bpy.data.materials.new("GlassPane")
    m.use_nodes=True
    nt=m.node_tree; nt.nodes.clear()
    out=nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(420,0)
    t=nt.nodes.new('ShaderNodeBsdfTransparent'); t.location=(120,120)
    g=nt.nodes.new('ShaderNodeBsdfGlossy'); g.location=(120,-40)
    g.inputs['Roughness'].default_value=0.03
    g.inputs['Color'].default_value=(0.85,0.88,0.9,1)
    add=nt.nodes.new('ShaderNodeAddShader'); add.location=(280,40)
    nt.links.new(t.outputs['BSDF'], add.inputs[0])
    nt.links.new(g.outputs['BSDF'], add.inputs[1])
    mix=nt.nodes.new('ShaderNodeMixShader'); mix.location=(330,40)   # unused spare
    nt.links.new(add.outputs['Shader'], out.inputs['Surface'])
    return m
glass_mat()

def window_light_mat():
    m = bpy.data.materials.get("WinLight") or bpy.data.materials.new("WinLight")
    m.use_nodes=True
    nt=m.node_tree; nt.nodes.clear()
    out=nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(400,0)
    em=nt.nodes.new('ShaderNodeEmission'); em.location=(180,20)
    em.inputs['Color'].default_value=(1.0,0.62,0.28,1)
    em.inputs['Strength'].default_value=14.0
    nt.links.new(em.outputs['Emission'], out.inputs['Surface'])
    return m
window_light_mat()

# ---------------- helpers ----------------
def cyl_log(name, r, length, mat=MAT_LOG, verts=14):
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=r, depth=length,
                                        location=(0,0,0), rotation=(math.pi/2,0,0),
                                        cap_endpoints=True)
    ob = bpy.context.active_object
    ob.name=name
    for p in ob.data.polygons: p.use_smooth=True
    ob.data.materials.append(mat)
    return ob

def box(name, sx,sy,sz, loc, rot=(0,0,0), mat=None):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc, rotation=rot)
    ob=bpy.context.active_object; ob.name=name
    ob.scale=(sx/2, sy/2, sz/2)
    bpy.ops.object.transform_apply(scale=True)
    if mat: ob.data.materials.append(mat)
    return ob

CAB = "Cabin"
logs = []
W = 8.4          # wall width along X
D = 6.2          # wall depth along Y
LOG_R = 0.155
z = LOG_R*0.9
row = 0
while z < 3.02:
    y_front = -D/2; y_back = D/2
    x_left = -W/2; x_right = W/2
    if row % 2 == 0:
        logs.append(("F", z)); logs.append(("B", z))
        # side walls: two half-logs to interlock corners
        for sgn in (-1,1):
            l1 = cyl_log(f"LogS{row}_{sgn}", LOG_R, D/2-W/2, verts=12)
            l1.rotation_euler=(math.pi/2,0,0)
            l1.location=(sgn*(W/2-LOG_R), -D/4, z); link_to(l1,CAB); logs.append(l1)
            l2 = cyl_log(f"LogS{row}b_{sgn}", LOG_R, D/2-W/2, verts=12)
            l2.rotation_euler=(math.pi/2,0,0)
            l2.location=(sgn*(W/2-LOG_R), D/4, z); link_to(l2,CAB); logs.append(l2)
        lf = cyl_log(f"LogF{row}", LOG_R, W, verts=14); lf.location=(0,y_front,z); link_to(lf,CAB)
        lb = cyl_log(f"LogB{row}", LOG_R, W, verts=14); lb.location=(0,y_back,z);  link_to(lb,CAB)
        logs += [lf, lb]
    else:
        ls = cyl_log(f"LogL{row}", LOG_R, D, verts=14); ls.rotation_euler=(0,math.pi/2,0)
        ls.location=(x_left,0,z); link_to(ls,CAB)
        rs_ = cyl_log(f"LogR{row}", LOG_R, D, verts=14); rs_.rotation_euler=(0,math.pi/2,0)
        rs_.location=(x_right,0,z); link_to(rs_,CAB)
        logs += [ls, rs_]
        # front/back short logs between side walls
        seglen = W - 2*LOG_R
        for sgn in (-1,1):
            fseg = cyl_log(f"LogFs{row}_{sgn}", LOG_R*0.92, seglen/2, verts=12)
            fseg.location=(sgn*(W/4+LOG_R/2), y_front+LOG_R*0.15, z+LOG_R*0.55); link_to(fseg,CAB)
            bseg = cyl_log(f"LogBs{row}_{sgn}", LOG_R*0.92, seglen/2, verts=12)
            bseg.location=(sgn*(W/4+LOG_R/2), y_back-LOG_R*0.15, z+LOG_R*0.55); link_to(bseg,CAB)
            logs += [fseg, bseg]
    row += 1
    z += LOG_R*1.62

TOP_Z = z  # top of walls
info_rows = row

# ---- gable ends (triangular log stacks) ----
peak_h = 2.35
steps = 9
for s in range(steps):
    tz = TOP_Z + (s+0.5)*peak_h/steps
    halfw = (W/2) * (1 - (s+0.5)/steps) + 0.18
    ln = halfw*2
    fg = cyl_log(f"GableF{s}", LOG_R*0.95, ln, verts=12); fg.location=(0,-D/2+LOG_R*0.1,tz); link_to(fg,CAB)
    bg = cyl_log(f"GableB{s}", LOG_R*0.95, ln, verts=12); bg.location=(0, D/2-LOG_R*0.1,tz); link_to(bg,CAB)

# ---- floor slab ----
floor = box("Floor", W+0.7, D+0.7, 0.22, (CX,0,-0.02), mat=MAT_PLANK)
link_to(floor,"Cabin")

# ---- foundation stones ----
found = box("Foundation", W+1.1, D+1.1, 0.34, (CX,0,-0.26), mat=MAT_STONE)
link_to(found,"Cabin")

# ---- door opening on front (-Y): carve by placing dark inset + frame ----
DOOR_W, DOOR_H = 1.06, 2.12
door_x = CX-2.35
door_z = DOOR_H/2 + 0.09
dframe_l = box("DoorFrameL", 0.14, 0.24, DOOR_H+0.24, (door_x-DOOR_W/2-0.05, -D/2-0.06, door_z), mat=MAT_TRIM)
dframe_r = box("DoorFrameR", 0.14, 0.24, DOOR_H+0.24, (door_x+DOOR_W/2+0.05, -D/2-0.06, door_z), mat=MAT_TRIM)
dframe_t = box("DoorFrameT", DOOR_W+0.38, 0.24, 0.14, (door_x, -D/2-0.06, DOOR_H+0.16), mat=MAT_TRIM)
for o in (dframe_l,dframe_r,dframe_t): link_to(o,"Cabin")
# recess behind door (dark)
recess = box("DoorRecess", DOOR_W+0.1, 0.5, DOOR_H+0.05, (door_x, -D/2+0.18, door_z))
rm = bpy.data.materials.new("NearBlack"); rm.use_nodes=True
nt2=rm.node_tree; nt2.nodes.clear()
o2=nt2.nodes.new('ShaderNodeOutputMaterial'); b2=nt2.nodes.new('ShaderNodeBsdfPrincipled')
b2.inputs['Base Color'].default_value=(0.01,0.008,0.006,1)
b2.inputs['Roughness'].default_value=1.0
nt2.links.new(b2.outputs['BSDF'], o2.inputs['Surface'])
recess.data.materials.append(rm); link_to(recess,"Cabin")
# door leaf slightly open
door = box("DoorLeaf", DOOR_W-0.06, 0.075, DOOR_H-0.04, (0,0,0), mat=MAT_DARK)
door.location=(door_x-0.42, -D/2-0.42, door_z+0.02)
door.rotation_euler=(0,0,math.radians(28))
link_to(door,"Cabin")
# handle
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.045, location=(door_x+0.28, -D/2-0.78, 1.15))
h=bpy.context.active_object; h.name="DoorHandle"; h.data.materials.append(glass_mat() if False else MAT_TRIM)
bpy.ops.object.shade_smooth(); link_to(h,"Cabin")

# ---- window openings front & sides: dark recess + frame + cross bars ----
def make_window(name, cx_, cy_, cz_, w, h, rotz=0.0, light=False):
    grp=[]
    rec = box(f"{name}_Rec", w, 0.46, h, (cx_,cy_,cz_), rot=(0,0,rotz))
    rec.data.materials.append(rm); link_to(rec,"Cabin"); grp.append(rec)
    fr_b = box(f"{name}_FrB", w+0.22, 0.16, 0.13, (cx_+ (0)*math.cos(rotz), cy_, cz_-h/2-0.05), rot=(0,0,rotz), mat=MAT_TRIM)
    fr_t = box(f"{name}_FrT", w+0.22, 0.16, 0.13, (cx_,cy_,cz_+h/2+0.05), rot=(0,0,rotz), mat=MAT_TRIM)
    fr_l = box(f"{name}_FrL", 0.13, 0.16, h+0.22, (cx_-(w/2+0.05)*math.cos(rotz), cy_-(w/2+0.05)*math.sin(rotz), cz_), rot=(0,0,rotz), mat=MAT_TRIM)
    fr_r = box(f"{name}_FrR", 0.13, 0.16, h+0.22, (cx_+(w/2+0.05)*math.cos(rotz), cy_+(w/2+0.05)*math.sin(rotz), cz_), rot=(0,0,rotz), mat=MAT_TRIM)
    mb_h = box(f"{name}_BarH", w, 0.09, 0.06, (cx_,cy_,cz_), rot=(0,0,rotz), mat=MAT_TRIM)
    mb_v = box(f"{name}_BarV", 0.06, 0.09, h, (cx_,cy_,cz_), rot=(0,0,rotz), mat=MAT_TRIM)
    pane = box(f"{name}_Pane", w+0.02, 0.02, h+0.02, (cx_ - math.sin(rotz)*0.0, cy_, cz_), rot=(0,0,rotz))
    pane.data.materials.append(bpy.data.materials["GlassPane"])
    lit  = box(f"{name}_Lit", w-0.04, 0.01, h-0.04, (cx_, cy_+0.06*math.cos(rotz)+0.06, cz_+0.35*h), rot=(0,0,rotz))
    lit.data.materials.append(bpy.data.materials["WinLight"])
    for o in (fr_b,fr_t,fr_l,fr_r,mb_h,mb_v,pane,lit): link_to(o,"Cabin"); grp.append(o)
    return grp

# front window right of door
make_window("WinF1", CX+1.75, -D/2-0.02, 1.55, 1.15, 1.05)
# side window left wall
make_window("WinL1", CX-W/2+0.02, 0.4, 1.55, 1.05, 1.15, rotz=math.pi/2)
# side window right wall (near chimney)
make_window("WinR1", CX+W/2-0.02, -0.9, 1.55, 1.05, 1.15, rotz=-math.pi/2)
# back window
make_window("WinB1", CX-0.6, D/2+0.02, 1.55, 1.15, 1.05, rotz=math.pi)

result={"rows":info_rows, "top_z":TOP_Z}


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 148, in <module>
  File "<string>", line 117, in cyl_log
TypeError: Converting py args to operator properties:: keyword "cap_endpoints" unrecognized