Skip to content

v0.5.0

Latest

Choose a tag to compare

@github-actions github-actions released this 07 Nov 03:20
fc53b84

Security

Every version before 0.5.0 let any program that could reach the add-on's port drive Blender and run
Python as you, and a web page you opened could do it too (some browsers block or ask first). 0.5.0
requires a token on every connection and keeps execute_script off until you click Allow Python.
Upgrade, and remove the old blender_mcp_addon add-on. Details are in
SECURITY.md.

Added

  • The add-on installs as a Blender extension. The release ZIP is now meshwire-addon-<version>.zip,
    with a blender_manifest.toml at its root, and installs from Edit > Preferences > Get Extensions >
    dropdown > Install from Disk... on Blender 4.2 and later. CI validates the built ZIP, installs it into a
    fresh Blender and runs the handshake and ping against it, on 4.5 and 5.2. The manifest declares the
    network and files permissions, and bl_info is gone. An older Meshwire add-on still enabled
    (installed from a pre-0.5.0 ZIP as blender_mcp_addon) makes the new one show a warning in its sidebar
    tab and refuse to start its server, so two servers never run together. Remove the old copy in
    Preferences > Add-ons and restart Blender. The Start and Stop operators and the panel were renamed
    (meshwire.start_server, meshwire.stop_server, MESHWIRE_PT_server) so they cannot collide with the old
    copy's, which would otherwise replace them while both are enabled. A keymap entry or script that calls
    bpy.ops.mcp.start_server or bpy.ops.mcp.stop_server must call the meshwire operator instead.
  • Several Blenders at once. Each running add-on now writes a small file, instances/<port>.json, in the
    Meshwire config folder next to the token, and removes it when its server stops or Blender quits normally. The file names the port,
    process, Blender version, add-on version and .blend file, and holds no token. With no --port, the
    server reads these files, tries each port, deletes the files of Blenders it finds are gone (on Windows a file left by a
    crash stays until a Blender binds that port again), and connects to
    the most recently started live one. It looks again at every connection, and logs the port, Blender version
    and file it chose. It moves on to an older Blender only past a program that is not a Meshwire add-on with
    your token (connection refused, a reply that is not Meshwire's, or no valid proof). A newest Blender that
    is busy, full, closes before answering, or needs updating gives an error naming its port, instance file
    and process ID instead of a switch. Pass --port or set MESHWIRE_PORT to pick one. The order is now --port, then the new MESHWIRE_PORT variable, then discovery, then
    9876. Discovery runs only for a loopback --host. The --port default is no longer 9876, so a setup that
    relied on that exact default still works whenever Blender is on 9876, and finds the right Blender when it is not.
  • Two Blenders no longer fight over a port. The add-on binds the first free port from 9876 to 9885 when
    Preferences says 0, and exactly the port you set otherwise, failing with a message that says why. On
    Windows the port is now bound exclusively, so another program cannot share it. CI also runs the token,
    instance file and socket tests on Windows and macOS.

Fixed

  • Only a client with the token can use the add-on's socket. Until now any program on the machine could
    connect to the port and drive Blender, and execute_script ran any Python it was sent. This affects
    every version before this one (the releases were v0.2.0, v0.3.0, v0.4.0 and v0.5.0a1); see SECURITY.md.
    What testing confirmed: on the last commit before authentication, a raw object_create sent with no hello
    was served and the connection stayed open (unauthenticated_call_refused fails there, on Blender 4.5.15).
    On v0.4.0 in Blender 4.5.15, a raw object_create and an HTTP POST (as a browser fetch with
    Content-Type: text/plain sends it, with one command line as the body) both created an object, so a web
    page could make Blender run a command, and with execute_script, any Python. v0.5.0a1 has the same socket
    code. Some current browsers block or ask before a public page requests a local address, so this depends
    on the browser. The host was also a Scene property saved in the .blend, so a shared file could carry a
    non-loopback host; it is now an add-on Preference.
    The add-on now makes a token in your Meshwire config folder (~/.config/meshwire/token on Linux, mode 0600). A connection
    must open with a hello and then a hello_proof, each step within 2 seconds, in lines of at most 4 KiB. The two
    sides prove they hold the token with HMAC-SHA256 over fresh nonces, so the token itself never crosses the
    socket. The add-on's proof also covers the address it received the connection at, and the server refuses a
    connection that reached a different address, so a program relaying the traffic from another port or
    address is caught (an SSH tunnel on purpose needs MESHWIRE_ADDON_ADDRESS). Anything else is closed at
    once; a wrong proof, or a client that does not speak hello, gets one error that says what to update. Sockets
    that have not finished the handshake have their own pool, so idle ones cannot lock out the real client, and
    a flood evicts the oldest of them, and a client that is evicted gets a clear error instead of being sent to another Blender. A client that failed its
    handshake can make the add-on read at most 64 KiB more from it. SECURITY.md now says how to rotate the
    token: stop the server in every Blender that runs Meshwire (each keeps the token it started with), delete
    the token file, start the servers again, and restart the MCP clients.
    The server reads the token file on every connect, so it needs no setup. For another machine, set
    MESHWIRE_TOKEN there (it overrides the file) and use Copy token in the Meshwire tab. After the
    handshake the traffic is neither encrypted nor integrity-protected, so a non-loopback host sends commands
    and results in plaintext; use an SSH tunnel for remote use. A new server
    against an older add-on, and the reverse, now say which side to update instead of hanging.

  • exit() in a script no longer ends the session. A script that calls exit(), sys.exit() or raises
    SystemExit used to escape the add-on's timer, which could close Blender or leave the server deaf. It now
    returns a normal error ("The script called exit() or sys.exit(); Blender keeps running.") with the output so
    far, and any other handler that does the same gets an error reply.

  • Large replies arrive whole, and bad requests get an answer. The add-on used to cut off any reply
    bigger than the socket buffer; a 32 MiB reply now arrives complete. A malformed, non-object, non-UTF-8 or
    oversized request, and a client past the connection limit, now get an error reply that says what was wrong,
    and the add-on then closes that connection. The server reports that reason instead of "Disconnected".

  • execute_script now needs the panel switch. It refuses every call, before running anything, until you
    click Allow Python in the Meshwire sidebar tab in Blender. The switch is off every time Blender starts
    and stays on only until it closes. No tool, socket message or saved file can turn it on. Anything that
    called execute_script must be allowed once per Blender session.

  • Settings moved from the scene to add-on Preferences. Host, port and the new Rodin key, Meshy key and
    ComfyUI workflow path are stored per user in Preferences, not inside the .blend file, so a shared file
    no longer carries them. Port 0, the new default, picks the first free port from 9876 to 9885. Starting the
    server now reports an error in Blender when it cannot bind, instead of claiming it started.

  • ai_configure_backend takes only safe keys. It accepts enabled, api_key, host, timeout, device and
    model (an Ollama model name), and host must be an http or https URL with no user name or password. Any other key, such as
    workflow, python_path, model_path or api_base_url, is refused with a message that names it and
    points to Preferences (Meshwire add-on).

  • An MCP client can now see that a tool failed. A reply from Blender that carries an error, or reports
    success: false, now comes back with isError set, so the client treats it as a failure. The JSON text
    is unchanged. The server's argument log also no longer holds API keys or whole scripts: values under
    keys such as api_key, token, secret and password are logged as <redacted>, and any string over
    200 characters is logged as its length.

  • The socket lane runs nightly on 4.2, 4.3, 4.4, 5.0 and 5.1. Before, the viewport and sculpt tools
    that the background driver cannot reach were checked on 4.5 and 5.2 only. The nightly job now runs the
    lane after the driver as a best-effort step, and socket-lane.json holds a baseline for all seven
    series. The README says which series run the lane, and lists each failing socket check with the series it
    fails on. All of this is generated by scripts/tool_status.py.

  • sculpt_mesh_filter with ERASE_DISPLACEMENT works on Blender 4.2. 4.2 spells that filter type
    ERASE_DISCPLACEMENT in sculpt.mesh_filter, so the call failed with an enum error there. The add-on
    now reads the operator's enum items and uses whichever spelling the running Blender offers.

  • The annotation_add note names the right series. The harness reason and the README table said
    Blender 4.3 to 4.5 has no Python call for annotation strokes. The baselines show 4.3, 4.4, 4.5, 5.0 and
    5.1 return that message, and only 4.2 and 5.2 draw the stroke, so the text now says 4.3 to 5.1.

  • The truth harness no longer lets an allowed failure hide a new one. Each row in
    SOFT_FAIL_ALLOWED and KNOWN_UNREACHED now carries a regex for the message the tool really returns
    ("No AI backends are available", "needs an open 3D viewport", and so on). A SOFT_FAIL whose message
    does not match its tool's pattern is classed BUG, so an allowed tool that starts failing for another
    reason shows up in the next run. The README table reads the reason from the same row.

  • The nightly harness fails on too many session crashes. --retry-crashes still forgives the known
    Blender 4.2 to 4.4 segfault, which lands on a different case each run and passes alone. A run with
    more than 3 such crashes now exits non-zero, so a crash of ours that happens to pass alone cannot hide
    behind it.

  • The truth harness no longer touches your Blender user files. The driver and the socket lane set
    BLENDER_USER_RESOURCES to a directory under the run's temp dir, and drop BLENDER_USER_CONFIG,
    BLENDER_USER_SCRIPTS and the other user path variables. Before, a local run of ai_queue_clear or
    ai_clear_history could delete your real AI job history, and ai_get_history could return it.

  • Undo and replies that fail. A reply of {"success": true, "error": null}, which execute_script
    returns, now pushes its undo step; only a truthy error or success: false counts as a failure.
    ai_model_status pushes one undo step when it imports the finished model and none for a poll that
    imports nothing, so undoing the import no longer takes an earlier tool call with it.

  • mesh_knife_project says when it cut nothing. It compares the vertex, edge and face counts and
    returns success: false ("the cutter does not overlap the target") when they did not change, instead
    of claiming success. A new view_axis parameter (TOP by default, or BOTTOM, FRONT, BACK,
    LEFT, RIGHT) sets the direction the cutter is projected along. For the call only, the first 3D view is turned
    to that axis and framed on the target and cutter, then restored, so the cut no longer depends on
    where the viewport happens to look. The reply reports the axis used.

  • Long jobs no longer time out at 30 seconds through the server. Every bake_* tool,
    msfs_export_model, msfs_batch_export_lods, physics_simulate, physics_fluid_quick,
    render_image and material_preview_render now wait 600 seconds for Blender by default and declare
    an optional timeout (seconds the server waits) in their schemas. Only ai_generate_texture_sync,
    whose handler reads timeout as its polling limit, still receives it, and the server waits 30 seconds
    longer than that limit so the handler's own timeout reply arrives. For other tools the server keeps
    timeout to itself. This replaces the earlier rule that forwarded timeout to every tool declaring it.

  • ai_redo_generation replays the right kind of job. Jobs now record whether they are a 3D model or
    a texture. A texture job is redone as a texture instead of being submitted as a paid 3D model, with
    the backend inputs it was first sent (an inpaint job's image and mask, size, seed), and a job kind
    that cannot be replayed gives a clear error. ai_generate_variations accepts at most 8
    variations, in the schema and in the handler.

  • object_separate returns to Object mode when the operator raises, for example MATERIAL on a
    mesh with one material slot, instead of leaving the scene in Edit mode.

  • The pipeline's MSFS stage exports. ai_pipeline_generate called msfs_export_model with
    output_dir, which the handler does not take, so the export always failed unnoticed. It now passes a
    filepath built from output_dir and the object name, and an export error fails the stage.

  • tools/list and the addon handlers now agree. Seven handlers had no tool and are now listed:
    ai_get_backend_info, ai_generate_variations, ai_cancel_generation, ai_redo_generation,
    ai_queue_list, ai_queue_clear and ai_get_history. ping stays a protocol call. export_fbx
    and export_obj list the options they already read (scale, axes, bake_anim, apply_unit_scale,
    export_materials) with the enum values both Blender 4.5 and 5.2 accept. ai_generate_texture and
    ai_generate_texture_sync no longer read texture_types, which no workflow used.
    ai_pipeline_status drops resume, which nothing implemented. A tool that declares timeout
    now receives it: the server used to strip it before forwarding, so ai_generate_texture_sync always
    polled for its default 300 seconds. execute_script says timeout is how long the server waits, not a
    script limit. material_set_color requires color as its schema says, and measure documents which
    mode needs which argument. ai_generate_variations reports success: False when no variation was
    generated. New static gate required_matches compares each schema's required with the handler.

  • Defects that hid behind SOFT_FAIL and PASS. ai_auto_uv LIGHTMAP no longer passes the removed
    PREF_APPLY_IMAGE and PREF_IMG_PX_SIZE keywords. ai_mesh_decimate maps UN_SUBDIVIDE to
    UNSUBDIV and PLANAR to DISSOLVE. constraint_add sets target only on constraints that have
    one, so LIMIT_LOCATION and LIMIT_ROTATION work. physics_fluid_quick maps GAS flows to the
    SMOKE flow type. physics_simulate reads RigidBodyWorld.collection in place of the removed
    group. export_gltf drops GLTF_EMBEDDED from its schema, since the exporter never accepted it.
    ai_pipeline_generate returns success: False and an error when a stage fails, and a failed
    generation, import or texture reply now marks its stage failed. analyze_viewport and ai_refine
    return success: False when the vision analysis fails, in place of a successful reply that
    carries a failed analysis; refine_iteration follows.

  • ai_texture_from_render and grease_pencil_markup do the work they report. The depth and
    normal control images are now the real passes: the object renders in a throwaway scene through the
    compositor (depth ramped so near is light with a clear background, normals mapped to 0-1 RGB), so
    the caller's scene, view layer and compositor are never touched. A failed render or a missing object
    returns a clear error, and the temp file comes from tempfile.mkstemp. grease_pencil_markup now
    draws arrows, circles and rectangles into the image pixels with numpy when Pillow is missing, which
    is always the case in Blender's Python, in place of a compositor fallback that drew nothing yet
    reported success. Colour and thickness are honoured, output_path is validated, annotations_drawn
    is the real count, text items are listed under skipped (Blender's blf draws nothing into an
    image buffer), and success is false when nothing was drawn. The socket lane checks both from the
    output pixels.

  • Blender 5.x differences go through compat.py, by feature detection. No handler or compat
    function compares a version number. render_set_engine accepts EEVEE, BLENDER_EEVEE and
    BLENDER_EEVEE_NEXT and sets the one this Blender registers; ai_texture_from_render no longer
    hard-codes BLENDER_EEVEE_NEXT, so its depth and normal renders work on 5.x and the tool reaches
    the AI backend. boolean_op maps the FAST solver to FLOAT where that is the name.
    geonode_apply and geonode_inspect read and write modifier inputs through
    modifier.properties.inputs where Blender offers it. keyframe_list reads curves from the
    action's channelbags (only the object's own slot) in place of the removed slot.layers.
    mesh_select_edge_loops calls select_edge_loop_multi or select_edge_ring_multi where
    loop_multi_select is gone. grease_pencil_markup's compositor fallback finds the compositor
    tree in Scene.compositing_node_group and uses a group output where the Composite node is gone.

  • Annotation and grease pencil tools work on 4.5 and 5.x. grease_pencil_create draws through
    the v3 drawing API (bpy.data.grease_pencils_v3 on 4.3 to 4.5). annotation_add and
    annotation_clear use Scene.annotation where annotations are their own datablock. Blender 4.3
    to 4.5 has no Python call that creates an annotation stroke, so there annotation_add returns an
    error and leaves nothing behind; a new socket test covers both outcomes.

  • Behaviour changes in the grease pencil tools. grease_pencil_create returns an error when no
    stroke has two or more points, where it used to create an empty object. On the v3 data API the
    point radius is the stroke thickness divided by 2000 (the legacy API used a pixel line_width).
    boolean_op still takes FAST or EXACT as the solver; FAST is the cross-version name and
    becomes FLOAT where Blender calls it that.

  • get_version_info reports detected features. It no longer returns is_5_0 and is_4_2;
    features says which API shapes this build has.

  • Sculpt tools use Blender's real identifiers. sculpt_face_set_create calls
    sculpt.face_sets_init, whose modes the criteria map to (LINKED is LOOSE_PARTS);
    face_sets_create never took them. sculpt_mask_by_topology uses sculpt.mask_from_cavity and
    sculpt.mask_init in place of the removed dirty_mask and vertex_paint_masks, and blurs with
    one mask_filter call. sculpt_extract_mask runs sculpt.paint_mask_extract and reads the
    .sculpt_mask attribute, so a mesh with no mask gets a clear error. sculpt_multires_reshape
    no longer lists DELETE_LOWER, which Blender never had. sculpt_to_retopo bakes a tangent-space
    normal map, since bake has no DISPLACEMENT type. The face set, mask and blur operators crash
    Blender in background mode, so those tools answer "needs an open 3D viewport" there.

  • A failing sculpt tool leaves Object mode. The sculpt tools used to return an error after
    entering Sculpt mode and stay there. restores_object_mode(error_replies=True) now also covers
    error replies, and a successful call still keeps Sculpt mode.

  • mesh_select combines criteria with AND (#38). Several criteria used to be unioned, so a
    second criterion could only add elements. An element now has to match every criterion given; a
    criterion that does not apply to the mode is ignored. The tool description says so.

  • Mesh tools use Blender's real identifiers. mesh_subdivide maps INNERVERT to the
    INNER_VERT that bmesh.ops.subdivide_edges takes. mesh_edge_slide passes use_even, not
    even, and returns "needs an open 3D viewport" in background mode, where the slide operator
    crashes Blender 4.5. mesh_crease writes the crease_edge attribute, since
    BMLayerAccessEdge.crease is gone. curve_to_mesh no longer lists fill_type NONE, which
    Blender rejects for 3D curves; a 2D curve still accepts it.

  • Each successful tool call is one undo step in the GUI. Operators called from the socket
    timer push no undo step, so undo reverted nothing or too much. CommandHandlers.handle now
    calls bpy.ops.ed.undo_push(message=<tool>) after a tool that does not report an error or
    success: false, after any rename the tool made. Read-only tools (NON_MUTATING), undo,
    redo and ping never push, so redo keeps working. execute_script no longer pushes a step
    of its own before running, which left an extra no-op step. Background mode has undo disabled
    and is unchanged.

  • Viewport-only tools no longer crash Blender, and work in a GUI session (closes #37).
    mesh_loop_cut, mesh_knife_project, sculpt_mesh_filter, sculpt_mask_by_topology
    (ALL, NONE and invert) and render_screenshot call operators that need a 3D viewport.
    In background mode they segfaulted or raised. They now find a viewport by walking the window
    manager, because the socket server runs handlers from a timer where bpy.context.area is
    None, and run the operator under temp_override. With no viewport they return
    {"success": false, "error": "<tool> needs an open 3D viewport"}. sculpt_mesh_filter maps
    ENHANCE_DETAIL to Blender's ENHANCE_DETAILS and SURFACE_NOISE to RANDOM.
    mesh_knife_project lost its boolean INTERSECT fallback, which deleted everything outside
    the cutter, and now selects the cutter after entering Edit mode so the operator sees it.
    Mesh tools that enter Edit mode (mesh_loop_cut, mesh_bridge, mesh_fill,
    mesh_edge_slide, mesh_knife_project, mesh_separate_selected,
    mesh_select_shortest_path, mesh_select_edge_loops) return to Object mode when they fail.

  • Missing arguments are rejected with a validation error, and ~ paths work. Sixteen
    tools (object_create, object_delete, object_get, object_transform,
    object_duplicate, object_join, object_separate, object_parent,
    material_set_principled, material_add_texture, render_animation,
    render_screenshot, render_set_engine, render_set_resolution, polyhaven_download,
    ai_model_status) and ai_pipeline_status raised a bare KeyError or accepted {}.
    They now report the missing parameter through require_param. validate_filepath returns
    the absolute path with ~ expanded and checks existence after expansion. ai_inpaint_texture
    and reference_image_setup called it without a name or discarded the result; a static gate
    now catches an unused result.

  • Every path parameter resolves ~ and //. save_as, the bake tools, render_multi_angle,
    material_preview_render, sculpt_to_retopo, the MSFS export and livery tools, the AI
    tools that take an image or output directory, and the five tools that take reference_image
    passed the raw string to Python file I/O, so ~/bakes became a folder named ~ in Blender's
    working directory. They now resolve the path first and report the absolute path they used. A
    // path resolves against the saved .blend file, and is refused with a clear error when the file
    has never been saved. A static gate fails the build when a path parameter skips the resolver.

  • ai_texture_from_render builds its depth and normal control images on Blender 4.2 to 4.4.
    Those compositors refuse shader math nodes, so the tool failed before it reached the AI backend.
    It now uses the compositor's own nodes where the shader nodes are refused.

  • bake_curvature frees its image and mesh data when it fails, and saves through the same
    checked writer as the other bake tools, so a failed write is an error, not a silent success.

  • Five of the six baking tools never worked. bake_pbr_batch, bake_highpoly_to_lowpoly,
    bake_from_multires, bake_to_vertex_colors and bake_id_map called eight _bake_*
    helpers that were never written, so each raised AttributeError once past argument
    checks. The helpers now exist. The target image node and the emission wiring that
    METALLIC and DISPLACEMENT bake through are tracked as they are added and removed
    in a finally, including when the bake raises. A material is left with the same
    nodes, links, socket values, selection, active node and active Material Output
    (including a Cycles-only output) it had; this is checked on a linked and an unlinked
    Metallic socket, a displacement chain, a shared material and a multi-slot object, on
    Blender 4.2, 4.5 and 5.2. The render engine, the bake settings and the object
    selection are also put back after each bake, which they were not. A setting that cannot be
    put back, such as an add-on render engine that is no longer installed, is named in
    warnings. bake_id_map now
    restores material slots and face material indices in every colour mode. An object
    with no node material gets a clear error from bake_pbr_batch; the other tools add a
    temporary material and remove it again. A bake that writes no file, including
    channels: [], returns an error rather than success, and empty material slots are
    named in a warnings entry. bake_from_multires called an operator that rejects its
    map types; it now uses the multires baker, and bakes to level 0 when the modifier is
    at its top level, since a level baked to itself carries no displacement.
    bake_highpoly_to_lowpoly now lists the normal_space parameter it already read.
    bake_curvature never called the helpers and is unchanged.

  • All 38 msfs_* tools failed on every call. handlers/msfs.py imported
    from .msfs, which resolves to the handler module itself, so each call raised an
    ImportError or "not a package". It now imports the msfs package. These tools
    now run; their output is not yet verified. Five more defects behind the import:
    msfs_create_glass_material set Material.shadow_method, absent from Blender
    4.5 and later; msfs_export_model passed the removed export_colors option and
    the format GLTF, which the exporter spells GLTF_SEPARATE;
    msfs_livery_extract_colors, msfs_livery_transfer and
    msfs_livery_map_elements did not accept the parameters their schemas offer;
    and msfs_livery_export_uv_layout now says it needs a GPU context in background
    mode instead of raising, and no longer leaves the object in Edit mode.

  • ai_auto_uv read freed memory. It held a UV layer reference across a mode change, then
    read its name. The outcome depended on what was left in that memory: it crashed Blender 5.2
    every time and failed unpredictably on 4.5.

  • validate_enum() called with swapped arguments in 20 handlers. Every
    affected handler rejected all of its documented enum values, because the
    allowed list landed in the name slot and the parameter-name string landed
    in allowed, so iterating a str made the check compare against its individual
    characters. mesh_fill, curve_create, curve_to_mesh, mesh_extrude,
    mesh_select, mesh_select_shortest_path and others were unusable as a
    result, and the error messages they raised named the wrong parameter.

  • import_file could not import .stl. It called bpy.ops.import_mesh.stl,
    which Blender 4.x removed; now uses bpy.ops.wm.stl_import, matching the
    wm.stl_export already used on the export side.

  • render_set_engine failed on Blender 5.x. compat.get_eevee_engine_name()
    hard-coded BLENDER_EEVEE_NEXT for 4.2+, but 5.0 renamed the engine back to
    BLENDER_EEVEE. It now queries the engine enum instead of mapping from the
    version number.

Thanks to Muhammet Ali (muhammetali) for this fix.

Added

  • Every Blender series from 4.2 through 5.2 is now covered. Blender 4.3.2, 4.4.3, 5.0.1 and 5.1.2
    join 4.2.23 as nightly series, each with its own harness baseline. 4.5 and 5.2 stay gated on every
    pull request. The nightly job is now a matrix over the five best-effort series. The baseline sync
    test finds baselines by glob, so a new series needs no test edit.
  • The code is formatted with ruff format, and CI checks it. The lint job fails on an
    unformatted Python file. .git-blame-ignore-revs lets git blame skip the format commit.
  • The harness driver takes --retry-crashes. It reruns each crashed case alone. A case that
    passes alone is reported as a session crash instead of a regression. Only the nightly job uses it,
    because Blender 4.2 and 4.3 crash inside Blender late in a full run, on a different case each time.
    The 4.2 baseline is re-recorded with every repair so far.
  • tests/test_handler_signatures.py: Blender-free static checks that guard
    against validate_enum argument-order regressions and against calling
    Blender operators removed in 4.x.
  • Integration tests for the STL export/import round-trip and for mesh_fill
    enum handling.

What's Changed

  • fix: re-land #41 (validate_enum, STL import, EEVEE on 5.x) by @RFingAdam in #51
  • chore(deps): bump the python-minor-patch group across 1 directory with 4 updates by @dependabot[bot] in #48
  • fix: stop ai_auto_uv reading freed memory by @RFingAdam in #52
  • fix: make the MSFS tools reach their modules by @RFingAdam in #53
  • fix: write the missing bake helpers so the baking tools run by @RFingAdam in #54
  • docs: generate the README tool status from the harness baselines by @RFingAdam in #55
  • fix: reject missing required arguments and expand ~ in file paths by @RFingAdam in #56
  • fix: run viewport-only tools under a 3D view override by @RFingAdam in #57
  • fix: make each successful tool call one undo step by @RFingAdam in #58
  • fix: use Blender's real identifiers in the mesh tools, AND mesh_select criteria by @RFingAdam in #59
  • fix: use Blender's real identifiers in the sculpt tools by @RFingAdam in #60
  • fix: route Blender 4.x and 5.x API differences through compat.py by @RFingAdam in #61
  • Fix defects hidden behind SOFT_FAIL and false passes; count only tools that ran by @RFingAdam in #62
  • Render real depth and normal passes and draw real markup; empty the defect tables by @RFingAdam in #63
  • Make tools/list and the addon handlers agree by @RFingAdam in #64
  • ci: cover Blender 4.3, 4.4, 5.0 and 5.1 nightly by @RFingAdam in #65
  • Format with ruff, gate it, and retry session crashes on nightly series by @RFingAdam in #66
  • Resolve ~ and // for every path parameter, and fix control images on 4.2 to 4.4 by @RFingAdam in #67
  • Fix undo steps, long tool timeouts and mesh_knife_project's projection by @RFingAdam in #68
  • Make allowed harness failures name their message, and run the socket lane nightly by @RFingAdam in #69
  • Replay a texture job's backend inputs on redo, and outwait handler timeouts by @RFingAdam in #70
  • Report tool failures as isError and redact secrets from the argument log by @RFingAdam in #71
  • Turn Python execution off by default, move settings to Preferences, and limit ai_configure_backend by @RFingAdam in #72
  • Bound each socket client's buffers and replies by @RFingAdam in #73
  • Require a token handshake before the add-on serves a client by @RFingAdam in #74
  • Find the right Blender when several are open by @RFingAdam in #75
  • Ship the add-on as a Blender extension zip by @RFingAdam in #76
  • M4 Task 7: SECURITY.md and what older versions exposed by @RFingAdam in #77
  • Don't drop a client that answered while Blender was busy by @RFingAdam in #78
  • M4 final review fixes: deterministic discovery, bounded pre-auth input, exit() safety, meshwire idnames by @RFingAdam in #79
  • Release prep: 0.5.0 by @RFingAdam in #80

New Contributors

Full Changelog: v0.5.0a1...v0.5.0