bonsai-mcp
Provides tools to view and interact with IFC models inside Blender's viewport, including focusing and isolating elements, setting views, taking screenshots, and reloading the scene.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@bonsai-mcp List all walls and their property sets in the IFC model"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
bonsai-mcp
MCP server to read, view and edit IFC models with IfcOpenShell and Bonsai (Blender).
Query a model, look at it through Blender's viewport, and edit it with IFC semantics — profiles, placements, property sets — never raw mesh operations. The read tools run with Blender closed.
Status: Stage 3 — read tools, Blender/Bonsai viewport, and IFC-semantic editing.
Table of contents
Related MCP server: IFC Bonsai MCP
1. Requirements
Python | >= 3.11 (3.13 recommended) |
| >= 0.5 |
OS | Windows 11 (primary), macOS, Linux |
Blender | >= 4.2, only needed for the |
Bonsai | the build matching Blender's Python (Blender 5.x → |
The ifc_* tools run with Blender closed. The bl_* tools need Blender + Bonsai + the add-on.
2. Install
git clone https://github.com/nhantruong96/bonsai-mcp.git
cd bonsai-mcp
uv syncTo pin Python 3.13:
uv sync --python 3.13Sample model. --selfcheck and pytest use KIT's FZK-Haus model. It is not committed — it is not this project's file — and is downloaded automatically on first run. Nothing to do.
Fetch it manually if the machine is offline at run time:
curl -L -o tests/fixtures/AC20-FZK-Haus.ifc https://www.ifcwiki.org/images/e/e3/AC20-FZK-Haus.ifcOr point the selfcheck at any IFC file:
uv run python -m bonsai_mcp --selfcheck --file "D:\path\to\your.ifc"3. Install the Blender add-on
Only needed for the bl_* tools. Skip this if you only use the read tools.
3.1. Install Bonsai (if it is not installed yet)
Blender 5.x runs Python 3.13 → take the bonsai_py313 build. Blender 4.2 runs Python 3.11 → bonsai_py311.
Blender → Edit > Preferences > Get Extensions → the ˅ button top right → Install from Disk... → pick the Bonsai zip.
3.2. Build the add-on zip
Compress-Archive -Path C:\Tools\bonsai-mcp\addon\bonsai_mcp_addon -DestinationPath C:\Tools\bonsai-mcp\bonsai_mcp_addon.zip -Force3.3. Install the add-on
Blender → Edit > Preferences > Get Extensions → the ˅ button top right → Install from Disk... → pick bonsai_mcp_addon.zip.
3.4. Point the add-on at src
Edit > Preferences > Add-ons → Bonsai MCP Bridge → expand → fill in bonsai-mcp src:
C:\Tools\bonsai-mcp\srcThis is what lets the ifc_* tools read the model Blender has open. Without it the bl_* tools still work but ifc_* cannot read through Blender.
Alternatively set the BONSAI_MCP_SRC environment variable, which takes precedence.
3.5. Run
Open an IFC file:
Bonsai > Project > Load Project.In the 3D viewport press
N→ Bonsai MCP tab → Start Server.The panel shows
Running, the address127.0.0.1:9876, request/error counts and the last 20 log lines.
Change the port under Edit > Preferences > Add-ons > Bonsai MCP Bridge > Port, then set BONSAI_MCP_BLENDER_PORT to the same value on the MCP side.
4. Selfcheck
uv run python -m bonsai_mcp --selfcheckuv run python -m bonsai_mcp --list-toolsuv run python -m pytestSelfcheck exits 0 on success. All logging goes to stderr.
The Blender integration tests skip themselves when no bridge is running. To run them, Start Server in Blender then:
uv run python -m pytest tests/test_blender_integration.py -vuv run python -m pytest tests/test_blender_edit_integration.py -v5. Claude Desktop config
File: %APPDATA%\Claude\claude_desktop_config.json (Windows) ·
~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
{
"mcpServers": {
"bonsai-mcp": {
"command": "uv",
"args": [
"--directory",
"C:\\Tools\\bonsai-mcp",
"run",
"python",
"-m",
"bonsai_mcp"
],
"env": {
"BONSAI_MCP_BLENDER_HOST": "127.0.0.1",
"BONSAI_MCP_BLENDER_PORT": "9876",
"BONSAI_MCP_LOG_LEVEL": "INFO"
}
}
}
}The env block is optional; leaving it out uses exactly those defaults.
Restart Claude Desktop after editing the file.
6. Claude Code config
claude mcp add bonsai-mcp -- uv --directory C:\Tools\bonsai-mcp run python -m bonsai_mcpVerify:
claude mcp list7. Usage procedure
7.1. Without Blender
ifc_open— open the file; check schema, units and element counts.ifc_list_property_names— always run this before writing any property query.ifc_model_infoorifc_spatial_tree— get the storey structure and per-class counts.ifc_list_entities/ifc_select— filter to the elements you need; page withlimit+offset.ifc_get_element— full detail for one element.ifc_geometry_info— geometry, profile and absolute placement of that element.ifc_representation_stats/ifc_validate— whole-file QC.
7.2. With Blender
Load Project in Bonsai, then Start Server in the Bonsai MCP panel.
bl_status— confirm the file, the versions, and that the session switched toblendermode.From here every
ifc_*tool reads the model Blender holds. Do not callifc_open— it is refused by design.bl_set_viewfor an overview,bl_focus_element(guid)for one element.For anything internal pass
isolate=true, or usebl_isolate, thenbl_isolate(restore=true).
8. Tool reference
8.1. IFC read tools
Tool | Parameters | Returns |
|
| schema, exporter, creation dates, units, entity/element totals, |
|
| the above plus a per-class histogram and the storey list |
|
| real total plus one page: guid, name, class, storey |
|
| attributes, psets, quantities, material, type, container |
|
|
|
|
| Project → Site → Building → Storey with element counts per node |
|
| every pset/property name that actually exists, with occurrence counts |
|
| representations, items, profiles, absolute 4×4 matrix, bbox, volume, area; plus flat |
|
| breakdown by |
|
| condensed |
Query syntax for ifc_select:
IfcWall
IfcWall, IfcSlab
IfcBeam, Name=/B-.*/
IfcSpace, Pset_SpaceCommon.IsExternal=TRUE8.2. Blender tools
Tool | Parameters | Returns |
|
| whether Blender is reachable, Bonsai version, the IFC file it has open, |
|
| viewport PNG plus its real dimensions |
|
| selects and zooms to the element, returns the image and what was focused |
|
| hides everything else, returns the image and hidden/kept counts |
|
| sets the viewpoint, returns the image |
|
| resyncs the viewport from the in-memory IFC data |
direction: front, back, left, right, top, bottom, axo.
width bounds the image's longest side, not literally its width. The viewport aspect ratio is preserved. Clamped to 320–2048.
bl_reload does not reload from disk; it rebuilds the viewport from the in-memory IFC data.
8.3. Edit tools, group A: attributes
No geometry is touched. Each takes guid for one element or query for many.
Tool | Parameters |
|
|
|
|
|
|
|
|
8.4. Edit tools, group B: placement
Tool | Parameters |
|
|
|
|
|
|
ifc_rotate_element spins the element about its own origin, not the project origin.
8.5. Edit tools, group C: parametric geometry
Tool | Parameters |
|
|
|
|
|
|
Recipes: ResetAbsoluteCoordinates, OffsetObjectPlacements, SetWorldCoordinateSystem, ConvertLengthUnit, Optimise.
args uses the recipe's own parameter names; a wrong name is answered with the accepted list and their defaults.
8.6. Saving
Tool | Parameters |
|
|
| — |
9. Safe editing procedure
Run
ifc_geometry_infobefore any geometry edit — check the real parameter names and whether the element shares a representation map.Edit. The session's first edit automatically writes
<name>.backup-<timestamp>.ifcbeside the original.Read the
before/aftervalues in the result. Every edit tool returns both.In blender mode, use
bl_focus_elementorbl_screenshotto look at the result. The viewport is already resynced.ifc_saveto a new path. Overwriting requires an explicitoverwrite=true.If it went wrong,
ifc_revertreturns to the state before the first edit.
Guardrails
Guardrail | Behaviour |
Automatic backup | Once per session, taken just before the first edit |
Never overwrite the original |
|
Shared geometry | Group C refuses when a map or profile is shared, reporting how many elements would be affected; pass |
No tessellation | Editing tessellated geometry is refused, and no tool creates a |
Bulk ceiling | A |
Before and after | Every edit tool returns |
Viewport sync | In blender mode, Bonsai rebuilds exactly the affected elements after each edit |
Note: Blender's own undo (Ctrl+Z) does not cover MCP edits. Use ifc_revert.
10. Unit conventions
Quantity | Unit returned | Note |
Coordinates, dimensions | metres ( | already multiplied by |
Profile parameters | both | |
Extrusion depth |
| |
Volume / area |
| from the IfcOpenShell kernel (SI) |
Storey elevation |
| |
Angles | degrees | |
4×4 matrix |
| row-major, absolute |
Values passed into edit tools | metres and degrees |
|
| the file's own units | use the numbers shown in |
The placement matrix is absolute: the whole nested IfcLocalPlacement chain is multiplied through.
11. Environment variables
Variable | Set where | Default | Effect |
| MCP side |
|
|
| MCP side |
| add-on address |
| MCP side |
| add-on port; must match the preference set in Blender |
| Blender side | — | path to |
12. Troubleshooting
Symptom | Fix |
| Call |
| Use an absolute path; escape |
| Run |
| Wrong class name; see the suggestions in the error, or |
Result looks truncated | Read |
Coordinates are in the wrong place | Use |
| The element has no geometry the kernel could build; read |
No profile found under | Clipping booleans nest; use the flat |
| It is an |
| The map scan was capped; raise |
|
|
| Offline. Fetch it manually with the |
| A captive portal or error page answered instead of the file; nothing was written |
| The upstream file changed; tests asserting exact counts may now fail |
pytest reports many | Blender integration tests, or a missing sample model; see the reasons with |
Server does not appear in Claude | Wrong |
No log output anywhere | Logging goes to stderr, not stdout |
Blender
Symptom | Fix |
| In Blender: 3D viewport → |
The | The add-on is not enabled: |
| The port is taken; change Port in the preferences and set |
| Pin them together: set |
| The add-on has no path to |
| Load one first with |
| By design — Blender holds the model; load the other file in Blender instead |
| It is behind other geometry; call again with |
The image is very tall or very narrow | The image follows the viewport aspect ratio; make the Blender window landscape |
| Blender is headless, or the workspace has no VIEW_3D area |
| Blender has a dialog open or a modal operator running; close it, or raise |
Screenshots show the old geometry after an edit | Call |
Editing
Symptom | Fix |
| Shared geometry. Check with |
| The element has no parameters left; re-export it upstream as a swept solid |
| Narrow the |
| Run |
| Create it first with |
| The error already lists the profile's real parameter names |
| Save to a different path, or pass |
| Nothing has been edited yet, so no backup exists |
|
|
| The folder holding the original is not writable; nothing was changed |
| Blender's undo does not cover MCP edits; use |
13. Development
uv sync
uv run python -m pytest -v
uv run python -m bonsai_mcp --selfcheckConventions:
uv.lockis committed;.venv/never is.No
print(). stdout is the JSON-RPC channel; log throughconfig.pyto stderr.Nothing in
src/may importbpy— the server must run with Blender closed. A test enforces it.addon/bonsai_mcp_addon/protocol.pyruns on the socket thread and must never touchbpy; allbpycalls live inhandlers.pyand run only from the main-thread timer.bpyis not thread safe — a call from the wrong thread crashes Blender dozens of commands later, somewhere unrelated. An AST test enforces it.Failing tools return the full traceback in the
tracebackfield; Blender-side failures carry Blender's own traceback too.Every write goes through
ifcopenshell.api, and throughSession.write; no tool touches the model directly.There are no raw-mesh tools. Editing a mesh freely and writing it back turns a
SweptSolidinto aTessellation— parameters gone, file inflated, recipient unable to edit. A test enforces it.The guardrail fixture is generated by
tests/steel_fixture.py: 4 beams sharing oneIfcRepresentationMap, 1 column with its own profile, 1 tessellated slab.
License: MIT — see LICENSE.
Available Tools
28 toolsbl_focus_elementA
Select the element with this GlobalId, zoom the viewport to it, and return the image together with what was focused.
Zooming to an element does not make it visible: anything inside the building — a wall under a roof, a beam inside a slab — ends up behind other geometry and the image shows the obstruction instead. Pass isolate=true to hide everything else, then bl_isolate(restore=true) to bring it all back.
Inputs: guid (required), width, isolate, timeout.
bl_isolateA
Hide everything except the given GlobalIds and return the image.
Pass restore=true to unhide everything again. Hiding is viewport-only; no IFC data is touched.
Inputs: guids, width, restore, timeout.
bl_reloadA
Rebuild the viewport geometry from the IFC data currently in memory.
Not a reload from disk — that would discard unsaved edits. Pass guids to reload only those elements, or omit it for every IFC-backed object.
Inputs: guids, timeout. Returns: object
bl_screenshotA
Render the current 3D viewport and return the image.
This is an OpenGL viewport render, not a screen grab, so no panels or other
windows appear in it. PNG. width bounds the image's longest side (clamped
to 320..2048); the viewport's aspect ratio is preserved.
Inputs: width, timeout.
bl_set_viewC
Point the viewport camera along one axis and return the image.
direction: front, back, left, right, top, bottom, axo.
Inputs: width, timeout, direction, frame_all.
bl_statusA
Whether Blender is reachable, which Bonsai and ifcopenshell it runs, and which IFC file it currently has open.
Compares the ifcopenshell build behind Blender with the one this server uses and warns when they differ — Bonsai vendors its own copy, and a version gap produces errors that look like data problems.
Calling this also syncs the session: once Blender has a project loaded it becomes the authority, every ifc_* read tool starts reading through it, and ifc_open refuses to open a different file.
Inputs: timeout. Returns: object
ifc_add_psetB
Create a property set with the given properties, or extend it if it exists.
Inputs: pset (required), properties (required), guid, query, +1 more. Returns: object
ifc_edit_profileA
Change the parameters of an element's parameterised profile.
Refused when the geometry is shared — through an IfcRepresentationMap or a reused profile entity — because the change would silently apply to every element sharing it. The refusal reports how many. Pass apply_to_all=true only when changing all of them is what you actually want.
Refused outright on tessellated geometry: there are no parameters left, and nothing here will tessellate to work around that.
Inputs: guid (required), params (required), apply_to_all. Returns: object
ifc_geometry_infoB
Geometry of one element: representation type, items, profile parameters, the absolute 4x4 placement matrix, bounding box, volume and surface area.
The placement is absolute — the whole nested IfcLocalPlacement chain has been multiplied through. Elements whose geometry comes from a shared IfcRepresentationMap are flagged, together with how many products share it.
Inputs: guid, step_id, include_metrics. Returns: object
ifc_get_elementA
Full detail for one element: attributes, property sets, quantities, material, type and spatial container. Identify it by GlobalId or STEP id.
Inputs: guid, step_id. Returns: object
ifc_list_entitiesA
List instances of an IFC class (subclasses included), paginated.
Always reports the real total alongside the page actually returned.
Inputs: ifc_class (required), limit, offset. Returns: object
ifc_list_property_namesA
Every property set and property name that actually exists in this file, with occurrence counts.
Call this before writing any property query. Exporters routinely ignore the
standard names — ArchiCAD writes BaseQuantities where the standard says
Qto_WallBaseQuantities — and a query against a name that is not there
returns an empty set with no error at all.
Inputs: limit, properties_per_set. Returns: object
ifc_model_infoC
Model summary plus a per-class entity histogram and the list of storeys.
Inputs: class_limit, storey_limit. Returns: object
ifc_move_elementB
Translate elements by a delta in metres. Reports origin before and after.
Inputs: dx, dy, dz, guid, +2 more. Returns: object
ifc_openA
Open an IFC file and make it the active model.
Returns the schema, exporting application, creation dates, units, total entity and element counts, and the ifcopenshell version in use.
Refused when Blender already holds a model through Bonsai: opening it here would create a second, independent copy of the same file.
Inputs: path (required). Returns: object
ifc_patchA
Run an ifcpatch recipe over the whole model.
Recipes: ResetAbsoluteCoordinates, OffsetObjectPlacements, SetWorldCoordinateSystem, ConvertLengthUnit, Optimise.
args is an object keyed by the recipe's own parameter names; a wrong name
is answered with the list the recipe accepts and their defaults.
ConvertLengthUnit and Optimise rebuild the model into a new file object. Standalone they need allow_file_replacement=true; in blender mode they are refused, because Blender's objects would still point into the discarded file.
Inputs: recipe (required), args, allow_file_replacement. Returns: object
ifc_remove_psetC
Detach a property set from an element.
Inputs: pset (required), guid, query, max_elements. Returns: object
ifc_representation_statsB
Whole-file geometry breakdown by RepresentationIdentifier/RepresentationType, by item class, and by parametric vs tessellated share. Also lists the representation maps shared by the most products.
Inputs: limit, map_scan_limit, shared_map_limit. Returns: object
ifc_revertA
Restore the file from the safety copy taken before this session's first edit.
Every edit since then is discarded. In blender mode Blender reloads the restored file too, so memory and disk do not end up disagreeing.
Inputs: no named parameters. Returns: object
ifc_rotate_elementB
Rotate elements about their own origin, around world x, y or z.
The element spins in place; it does not orbit the project origin.
Inputs: axis (required), degrees (required), guid, query, +1 more. Returns: object
ifc_saveA
Write the model to path.
A new path is required by default. Overwriting anything — including the file the model was opened from — needs overwrite=true, stated explicitly.
With validate=true the saved file is reopened and checked, and the result reports any issue that was not already in the model before saving.
Inputs: path (required), validate, overwrite. Returns: object
ifc_selectB
Select elements with an ifcopenshell selector query.
Examples: IfcWall, IfcWall, IfcSlab, IfcBeam, Name=/B-.*/,
IfcSpace, Pset_SpaceCommon.IsExternal=TRUE.
Inputs: query (required), limit, offset. Returns: object
ifc_set_attributeA
Set one IFC attribute (Name, Description, ObjectType, Tag, ...).
Target one element with guid, or many with a selector query. A query
matching more than max_elements is refused with the count, so a typo
cannot rewrite the model.
Inputs: value (required), attribute (required), guid, query, +1 more. Returns: object
ifc_set_extrusion_depthC
Set the extrusion depth in metres. Same sharing guard as ifc_edit_profile.
Inputs: guid (required), depth (required), apply_to_all. Returns: object
ifc_set_placementA
Set an element's absolute 4x4 placement matrix.
Row-major, translation in the last column in metres, bottom row [0, 0, 0, 1]. Get the current one from ifc_geometry_info.
Inputs: guid (required), matrix (required). Returns: object
ifc_set_pset_valueB
Set one property inside an existing property set.
Run ifc_list_property_names first: exporters rarely use the standard names, and this fails loudly rather than creating a near-miss duplicate.
Inputs: pset (required), value (required), property (required), guid, +2 more. Returns: object
ifc_spatial_treeC
Spatial hierarchy IfcProject -> Site -> Building -> Storey, with the number of elements contained in each node.
Inputs: max_depth, class_limit. Returns: object
ifc_validateB
Run ifcopenshell.validate and return a condensed report: counts by level, the most frequent messages, and a page of individual issues.
Inputs: limit, express_rules. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 28 tools
The bl_/ifc_ prefixes and read-vs-write verbs separate most tools cleanly, and each tool has a specific resource or action. A few adjacent reading tools and geometry-edit tools (e.g. ifc_model_info vs ifc_representation_stats, ifc_edit_profile vs ifc_set_extrusion_depth) could be confused, but their descriptions disambiguate them well.
All names are snake_case and consistently use bl_/ifc_ prefixes, which makes the set predictable. However, the verb convention is not uniform: mutation tools use verb_object patterns like ifc_set_attribute and ifc_move_element, while several read tools are noun-like (ifc_model_info, ifc_spatial_tree, ifc_geometry_info) rather than get_*.
At 28 tools, the set is above the 25-tool threshold where an agent's tool-selection burden becomes steep. Most tools are individually purposeful, but several read/reporting and viewport tools could plausibly be consolidated without losing capability.
The set covers a substantial IFC workflow: open, inspect, edit properties and pset values, transform geometry, save, validate, and revert. Notable gaps remain, however, including no creation or deletion of IFC elements, no material/type assignment tools, and no spatial-structure editing, so full modelling workflows are incomplete.
Related MCP Connectors
Create, inspect, validate, and save editable 3D building scenes with Pascal's MCP server.
Headless CAD engine & MCP server for 2D/3D drafting, solids, DXF/SVG export, and automation.
Read and edit the Revit 2024–2027 model on your PC. Needs the free Revit MCP add-in.
Official MCP server for Softhills 3D/2D Visualization SDKs, API documentation, and code examples.
Related MCP Servers
- AlicenseAqualityCmaintenanceA Model Context Protocol server that integrates Claude with Blender, enabling users to analyze and interact with IFC (Industry Foundation Classes) building models through natural language commands.1766MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that connects AI language models with the Bonsai Blender add-on to read, create, and edit IFC models through high-level tool calls.67MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that enables AI assistants like Codex or ChatGPT to inspect and control a local Blender scene, including creating objects, editing materials, setting cameras and lights, rendering previews, and saving safe copies of .blend files.1MIT
- AlicenseAqualityDmaintenanceConnects MCP clients to a local Blender + Bonsai (BlenderBIM) session for scene inspection, IFC project querying, viewport screenshots, and executing Python code in Blender.813MIT