A Model Context Protocol (MCP) server that gives Claude, ChatGPT, Codex, Gemini or a local Ollama model full control of Rhino 8 / Rhinoceros 3D. Describe a building in plain language and watch it get modelled — massing, floor plates, cores, facades, vaults, plans, sections and area schedules — with the AI checking its own work as it goes.
Built by an architect, for architects. No coding required to use it.
Keywords: rhino mcp · rhino 3d mcp server · rhinoceros mcp · model context protocol rhino · rhino 8 ai plugin · claude rhino integration · ai architectural modelling · ai 3d modelling · generative architecture · computational design · rhinocommon automation · architectural bim ai
Several Rhino MCP servers exist. This one is built for long, real modelling sessions — one user drove it to ~8,000 objects reconstructing a Gothic cathedral at 1:1, with zero invalid breps.
| This project | |
|---|---|
| Tool surface | 123 tools, curated into lean / standard / full profiles so small models aren't drowned |
| Transport | Protocol 5 — multiplexed, so reads, ping and cancel answer instantly while a 3-minute script runs |
| Reliability | Idempotent retries (no duplicate geometry on reconnect), atomic batches with rollback, write-ahead-log crash recovery |
| Correctness | Intent validation — asserts what the geometry means, not just that it parses |
| Geometry stdlib | rab helpers auto-imported into every script: walls, slabs, grids, pointed arches, rib vaults, rose tracery, mouldings |
| Vision loop | Auto-thumbnails after every edit, multi-angle review sets, before/after pixel diffs, one-call section previews |
| Install | Pre-built plugin — no .NET SDK needed, one double-click, and it verifies itself |
If Rhino MCP saves you modelling time, please star the repo — stars are how other Rhino users find it.
Valid geometry is not correct geometry. An AI generating thousands of parametric objects makes arithmetic and wiring errors — a doubled base height, swapped arguments, a boolean cutter added instead of subtracted — and every one of those produces a perfectly closed, valid solid that validate_objects would happily pass.
So this server validates intent:
Failures come back with the offending object IDs, at the moment of creation — while the fix is one parameter away instead of twenty tool calls later.
assert_geometry— bbox / envelope / count / count_delta / watertight / supported post-conditionsfind_unsupported— finds floating spires, pinnacles and statuary (nothing beneath them)section_preview— cut the model at any station and look inside, in one call, leaving no geometry behindvalidate_objects— separates real corruption from intentionally open shells, with naked-edge lengths so a hairline gap stands out
If you can describe a building, you can model it:
"Create a 4-storey office massing on a 30×18 m footprint, taller ground floor, derive the floor slabs, add a core with two lifts and a stair, then put windows on the north facade every 3 metres."
You can ask for:
- Massing studies — footprints, levels, setbacks, options side by side
- Floor plans and sections — cut, captured and restored without touching your viewport
- Area schedules — GFA/NFA by layer, level or name, ready to paste into a report
- Historical work — pointed and equilateral arches, rib vaults, rose windows, mouldings
- Materials and views — "make the facade glass, the core concrete, show me a hero shot"
- Drawing imports — trace a PDF site plan or import a DWG and build on top of it
- Download this repository (green Code button → Download ZIP, then extract — or
git clone). - Close Rhino and your AI client, then double-click
INSTALL.bat. It installs the plugin, the Python server and configures every AI client it finds, then prints a PASS/FAIL verification report and writesinstall-log.txt. It will not close without telling you what happened. - Open Rhino 8. First time only: run
PlugInManager, click Install, and choose%APPDATA%\McNeel\Rhinoceros\8.0\Plug-ins\RhinoAIBridge\RhinoAIBridge.rhp. After that the bridge starts automatically with Rhino — no command to type. - Restart your AI client and ask it: "ping Rhino".
Requirements: Windows, Rhino 8, and one MCP-capable AI client. No .NET SDK required.
Access modes are set with the RHINO_AIBRIDGE_MODE environment variable — safe (blocks code and destructive edits), standard (allows deletes/booleans), or developer (everything, required for execute_python3).
Every execute_script call gets rab auto-imported, so the model writes intent instead of boilerplate:
rab.wall((0,0,0), (12000,0,0), height=3000, thickness=200, layer_path="Wall")
rab.slab([(0,0),(30000,0),(30000,18000),(0,18000)], thickness=250, z=3600)
for pt in rab.grid((0,0), 4, 3, 8400, 8400):
rab.column(pt, h=3600)
# Historical geometry, solved exactly - not approximated
void = rab.arch(3000, 2000, 800, pier=2500, kind="pointed", origin=(4000,0,0))
rab.boolean_diff(wall_id, void)
rab.vault_quadripartite(corners, springing_z=5000, crown_z=9000)
rab.rose_window((6000,0,14000), 2400, spokes=12, foils=12)The two-centred arch solver uses c = (h²−s²)/2s, R = c+s, and the extrados is concentric (same centres) — which is what makes archivolt offsets exact. Vault webs blend to a flat fold at the boss so they land precisely on the crown, where a naive Coons patch overshoots.
Reusable code: write_module(name, source) saves a library that later scripts import with rab.use('name'). The server writes the file, so nothing inside Rhino ever holds a file handle open.
Set RHINO_TOOLS=lean|standard|full in the server environment:
| Profile | Tools | For |
|---|---|---|
lean |
~21 | small/local models (Ollama), minimal context |
standard (default) |
~71 | Claude / GPT-class daily driving |
full |
123 | everything, including JSON twins and compatibility aliases |
Anything not exposed in the active profile is still callable as a batch sub-command, and the live command list is always available from list_commands and the rhino://capabilities resource.
| Tool | What it does |
|---|---|
ping |
Health check — Rhino version, units, scene_version etag, and which script engines are actually available |
query_scene |
Universal query: filter by type, layer, name, bbox; columnar format saves 40–60% tokens |
list_commands |
Live plugin dispatch table (never drifts from the code) |
get_scene_diff / get_change_log / get_tracker_version |
Incremental sync — catch up on user edits cheaply |
get_state / set_state / clear_state |
Session scratchpad for derived data |
| Tool | What it does |
|---|---|
create_object |
Universal create: wall, slab, column, opening, roof, massing, core + primitives |
derive_floors_from_mass |
Section a massing solid at floor heights into real slabs (variable storey heights) |
create_core |
Lift/stair/shaft core, with punch_through to carve real voids |
place_openings_on_facade |
A whole facade of openings in one call |
loft_surface · sweep1 · sweep2 · pipe_curve · extrude_curve · network_surface · sphere_patch · revolve_profile |
Surface and solid modelling |
boolean_operation · trim_with_planes |
Constructive solid geometry |
| Tool | What it does |
|---|---|
assert_geometry |
Post-condition contracts — bbox, envelope, count, count_delta, watertight, supported |
find_unsupported |
Objects floating with nothing beneath them |
section_preview |
Instant interior inspection at any station; no permanent geometry |
validate_objects |
Corruption vs. intentional open shells, scoped by layer / name / since_version |
detect_clashes |
Real clash detection: RTree broad phase + Brep–Brep narrow phase |
select_by_semantic |
"all south-facing windows on level 3" |
analyze_architecture · get_building_systems · get_level_summary · detect_design_patterns · find_unassigned_geometry |
Semantic understanding of the model |
measure_object · measure_distance · check_intersection · report_areas |
Measurement and schedules |
| Tool | What it does |
|---|---|
capture_viewport · thumbnail · get_viewport_image |
Images straight into the conversation |
capture_review_set |
Hero, plan, elevations and detail in one multi-image call |
compare_before_after |
Capture → edit → capture, with pixel-change metrics |
capture_inspection_view · set_camera · set_view |
Precise camera control (bbox framing or explicit) |
create_section · cut_section · create_elevation · create_plan · create_all_plans · list_sections |
Drawing production |
create_display_mode · apply_display_mode · capture_illustration |
8 illustration presets: diagram, technical, blueprint, sketch, axonometric, atmospheric, monochrome, cutaway |
| Tool | What it does |
|---|---|
create_layer_tree · setup_arch_layers · create_layer · batch_layer_visibility |
Layer discipline in one call |
set_pbr_material · set_layer_material · search_materials · download_material · edit_material |
PBR materials + CC0 AmbientCG library with unit-aware UV scaling |
set_design_brief · add_design_rule · tag_object · get_provenance · search_memory · name_group · log_session |
Design memory persisted inside the .3dm — survives save/reload |
| Tool | What it does |
|---|---|
import_dwg · trace_pdf · get_pdf_info · calibrate_scale · export_objects |
Get existing drawings in, get geometry out |
execute_script |
IronPython 2 inside Rhino, with rab auto-imported |
execute_python3 |
CPython 3 via RhinoCode (Rhino 8.11+, Developer mode) |
write_module · list_modules · read_module |
Reusable code libraries, no file locking |
batch · batch_preview |
Atomic multi-op transactions and dry runs |
save_checkpoint · restore_checkpoint · undo · cancel_operation · get_recovery_log |
Rollback, cancellation and crash recovery |
skills/rhino-architect/ is an Agent Skill that teaches the model how architects work: phase ordering (brief → layers → massing → verify → structure → envelope → drawings), millimetre defaults, when to batch vs. step, verification cadence, anti-patterns, and debugged parametric generators for stairs and curtain walls. Point your client at that folder, or install the packaged .skill.
INSTALL.bat configures whatever it detects. Manual configuration:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"rhino-architect": {
"command": "uv",
"args": ["--directory", "C:/path/to/rhino-mcp/server", "run", "--frozen", "rhino-architect"],
"env": { "RHINO_TOOLS": "standard" }
}
}
}~/.codex/config.toml
[mcp_servers.rhino_architect]
command = "uv"
args = ["--directory", "C:\\path\\to\\rhino-mcp\\server", "run", "--frozen", "rhino-architect"]
startup_timeout_sec = 20
tool_timeout_sec = 120
enabled = true
[mcp_servers.rhino_architect.env]
RHINO_HOST = "127.0.0.1"
RHINO_PORT = "9544"Configured automatically at %USERPROFILE%\.gemini\antigravity\mcp_config.json. Restart Antigravity, then ask "ping Rhino".
Use RHINO_TOOLS=lean so a smaller model isn't overwhelmed.
ollama pull qwen2.5-coder:7b
cd server && uv run python chat.py --provider ollama --model qwen2.5-coder:7b
Best local models: qwen2.5-coder:32b, deepseek-r1:32b, llama3.1:70b.
| Variable | Default | Purpose |
|---|---|---|
RHINO_TOOLS |
standard |
Tool profile: lean, standard, full |
RHINO_AIBRIDGE_MODE |
safe |
Plugin access mode: safe, standard, developer |
RHINO_HOST / RHINO_PORT |
127.0.0.1 / 9544 |
Bridge address |
RHINO_SAFE_MODE |
unset | 1 blocks destructive tools on the Python side too |
RHINO_TIMING |
unset | 1 adds elapsed_ms to every response |
RHINO_RAB |
1 |
0 disables the auto-imported rab helpers |
Claude Desktop / ChatGPT / Codex / Antigravity / Ollama
| MCP (stdio)
v
server/src/rhino_architect/server.py <- FastMCP Python server (123 tools, profile-gated)
| TCP 127.0.0.1:9544
| per-user auth token + [1-byte flag][4-byte len][payload]
v
plugin/RhinoAIBridge.rhp <- C# Rhino 8 plugin (.NET 8), 157 commands
| UI-thread dispatch + deferred redraw
v
Rhino 8 document
Protocol 5 multiplexes by request_id, so reads and ping answer from the plugin's socket thread while the UI thread runs a long script. Mutating requests are registered for idempotent replay: if the connection drops after delivery, re-sending the same request_id replays the cached result instead of duplicating geometry. Viewport captures travel as raw binary frames rather than inflated base64.
Atomic batches run inside a single Rhino undo record; any failure triggers one Doc.Undo().
Checkpoint economics: snapshots are skipped when the scene hasn't changed, throttled on large documents, and controllable per call with checkpoint="off" | "auto" | "force".
Only needed to modify the C# plugin. Target machines never need the .NET SDK.
cd plugin && dotnet build --configuration Release
cd ../server && uv sync --group dev && uv run pytest -q && uv run ruff check src tests
CI builds the plugin, lints, and runs the test suite on every push.
"Cannot connect to Rhino" — open Rhino. The bridge auto-starts; if it doesn't, run AIBridge once and re-check PlugInManager shows the plugin as loaded.
The installer closed instantly / I don't know if it worked — fixed in v4.11.0. The installer now prints a verification report and always waits for ENTER. Every run writes install-log.txt; attach that to an issue.
"Rhino is running" / "MCP server is running" during install — both hold their files open. Quit Rhino and your AI client, then re-run.
Tool not found in my client — check your profile. RHINO_TOOLS=standard hides ~50 long-tail tools; they remain callable via batch, or set RHINO_TOOLS=full.
execute_python3 unavailable — call ping and read script_engines.python3.reason. It states exactly what's missing (Rhino 8.11+, the rhinocode CLI, or Developer mode).
Health check — powershell install/rhino-mcp-healthcheck.ps1, or cd server && uv run python ../scripts/doctor.py.
- Installer rewritten — verification report,
install-log.txt, detects locked files from a running Rhino or AI client, and never closes without explaining itself .gitattributespins Windows scripts to CRLF — LF endings madecmd.exemis-seek ongoto/call, which is why the installer appeared to close instantly for ZIP downloads- Shipped plugin refreshed —
dist/plugin/had drifted a month and a half behind the source - CI now fails if
dist/is out of sync with a fresh build
- Intent validation:
assert_geometry,find_unsupported,section_preview rabgeometry stdlib auto-imported into every script, including the exact two-centred arch solver, rib vaults, rose tracery and named mouldings- Reusable modules:
write_module/list_modules/read_module - Tool profiles
lean/standard/full; checkpoint economics (zero-delta skip, throttling, per-call policy) - Zero-touch bridge — the plugin loads with Rhino and starts its server automatically
validate_objectsseparates corruption from open shells and takes scoping filterspingreports live script-engine availability- Fixed: event-loop blocking during PDF tracing, display-mode capture race, camera-dict schema, in-flight registry leak, Safe-mode gaps, binary frames in single-command batches
- Protocol 5: multiplexed connection, idempotent retries, cooperative cancellation, binary image frames, write-ahead-log crash recovery
- Offscreen ViewCapture rendering, columnar queries (40–60% fewer tokens)
- Authenticated localhost bridge, access modes, sections/plans, illustration engine, material intelligence, PDF tracing, DWG import, Codex + Antigravity support
Older releases
- v4.6 — auth token, 3-tier trust modes, dry-run support, viewport metadata
- v4.5 — pre-built plugin (no .NET SDK), auto-thumbnails, atomic batch rollback
- v4.0 — scene snapshot cache (O(1) reads), deferred redraw, architect intelligence layer
Issues and pull requests welcome — especially field reports. The most valuable contribution this project has received was a detailed write-up of an 8,000-object modelling session, which produced seven confirmed bug fixes and an entire new category of tooling. If you push this thing hard and it bends, please tell us how.
MIT — see LICENSE. Free for personal and commercial use.
Built by Tanishq Bhattad — https://github.com/tanishqbhattad
assert_geometry(assertions=[ {"kind": "bbox", "selector": ["by_name:nave_web"], "z_max": 33000, "tol": 10}, {"kind": "count", "selector": ["by_layer:Vault"], "expect": 60}, {"kind": "supported", "selector": ["last_created"], "max_gap": 150}, {"kind": "envelope", "selector": ["all"], "box": [[0,-24000,-1000],[130000,24000,97000]]} ])