rhino-mcp
rhino-mcp
Drive McNeel Rhino 8 and Grasshopper from Claude through the Model Context Protocol.
Overview
rhino-mcp is a Model Context Protocol server that lets Claude (or any MCP client) drive Rhino 8 — creating geometry, manipulating layers and materials, baking Grasshopper output, exporting STEP/IGES/STL/OBJ — through plain natural-language tool calls.
It runs in two modes:
Standalone (default): backed by
rhino3dmfor headless.3dmfile authoring; works without Rhino installed and exposes ~242 tools (geometry, file-I/O, transform, layer, material, analysis, RhinoScript docs, composition, document hygiene, geometry validation, GH template catalogue, freeform skin / panelisation / curvature / fields, drawing-set sheet + title block, schedule / quantity, block definition, sun-position + shadow projection, BIM metadata tagging, 19 physical material presets, BRE daylight factor + Bird DNI clear-sky model, and multi-Rhino announcement-file discovery).Bridge: when the C# bridge plugin is loaded in a live Rhino 8 session, the server transparently forwards every call (booleans, lofts, sweeps, viewport image capture + zoom-to-object / zoom-to-layer, render, scripting (Python and gated Roslyn C# with timeouts and extra references), deformation, NURBS editing, SubD, paneling, freeform analysis with true Gaussian curvature, drawing-set view placement + section cuts + PDF export, accurate Brep-area schedules, block explode/redefine, ray-cast solar exposure + Brep ray probe, IFC / gbXML import-export, HDRI environment, camera / light / render / turntable automation, layer-material binding, and every Grasshopper operation including batched
connect_manywiring and slider placement) to RhinoCommon and Grasshopper.Instances, exposing ~245 tools.
Features
Full geometry catalogue — points, lines, polylines, arcs, circles, ellipses, polygons, helixes, NURBS curves, rebuilds.
Solids and meshes — boxes, spheres, cylinders, cones, tori, mesh boxes; booleans / sweeps / lofts / fillets via the bridge.
Transforms — move, rotate, scale, mirror, plane-to-plane orient, linear/polar/rectangular arrays.
Script execution — run arbitrary RhinoScript Python (IronPython) or RhinoCommon C# (Roslyn) code in a live Rhino session, with built-in RhinoScript API documentation search (899 functions).
Advanced modeling — deformation (bend/twist/taper/flow), NURBS editing (rebuild/unroll/evaluate), SubD, surface matching, extraction (dup edge/border/isocurve/Make2D), control points, and paneling tools.
Drawing-set authoring — sheet / title block / north arrow / scale bar in standalone; multi-view placement, section cuts, and PDF export through the bridge.
Quantity & schedule — per-layer / per-material / per-
user_textaggregation with CSV export; bridge mode usesAreaMassProperties/VolumeMassPropertiesfor accurate Brep area + volume.Block / instance reuse — define / insert / list blocks in standalone; explode / redefine in bridge mode for synchronized component edits.
Environmental analysis — sun position + monthly sun-path polylines + shadow projection (NOAA SPA approximation); ray-cast solar exposure in bridge mode. Daylight precision adds Bird DNI clear-sky model with Kasten-Young air mass and Linke turbidity, plus BRE simplified daylight factor.
BIM interchange — IFC2x3 / IFC4 / IFC4x3 import & export, gbXML export, and IFC PropertySet metadata tagging that round-trips through
user_text.Material presets + HDRI — 19 bundled physical presets (concrete / brick / steel / aluminium / glass / timber / stone / plaster / fabric / landscape) keyed by category; HDRI environment for rendering.
Render automation — camera / light / render-engine setup, render-to-file, and parametric turntable sequences (bridge mode).
Undo / redo and batch operations — every bridge operation is wrapped in undo records; batch modify changes many objects in a single call.
Layers, materials, blocks, groups — full attribute control on the document.
File I/O — open/save
.3dm, export OBJ/STL standalone; STEP/IGES/DXF and screenshots via the bridge.Inline base64 screenshots —
rhino_screenshot(as_base64=True)returns the PNG payload inline for visual verification by the LLM.Rich object selection —
rhino_object_selectfilters by glob name pattern, layer, RGB color, object type, and user-text key/value pairs.Pagination on bulk queries —
rhino_list_objectsreturns apagination: {total, offset, limit, returned, has_more}block so large documents stay manageable.Scene composition shortcuts (v0.2) —
rhino_place_grid,rhino_stack_floors,rhino_scatter,rhino_replicate_along_curvecollapse "loop + transform" into a single tool call.Document hygiene tools (v0.2) — query/set units, tolerances, and base point (
rhino_document_units_*,rhino_tolerance_*,rhino_origin_set);rhino_document_summaryexposes them so the LLM can verify scale before issuing geometry calls.Geometry validation (v0.2) —
rhino_validate_brep,rhino_report_mesh_health,rhino_curve_continuity, plusrhino_check_naked_edges(bridge) for explicit topology diagnostics before booleans / exports.Grasshopper template loader (v0.2) —
gh_template_listreads a manifest of pre-wired definitions;gh_load_template,gh_bind_template_parameter,gh_run_template(bridge) load, parameterise, and bake them.Free-form architecture toolkit —
rhino_skin_from_sections,rhino_uv_grid_panels,rhino_panel_planarity,rhino_panel_curvature_classify,rhino_surface_developable_score,rhino_attractor_displace_points,rhino_smooth_polyline. Bridge mode adds true Gaussian / mean / principal curvature, world-axis slicing, and waffle ribs.Strategy prompts for the LLM — seven
@mcp.prompt()guides (general_strategy,rhinoscript_workflow,viewport_workflow,parametric_workflow,bim_authoring_workflow,design_dialogue_workflow,freeform_workflow) help Claude pick the right tool, avoid hallucinated APIs, and keep the user in the loop on design decisions.Async-capable tools — read-only query tools run on
async defso concurrent bridge round-trips don't stall the MCP transport.Robust connection layer — JSON-RPC over named pipe / Unix socket / TCP with
MSG_PEEKliveness probes and exponential backoff with jitter on reconnect.Grasshopper automation — open
.ghfiles, drop components, wire them, set sliders/panels/toggles, run, bake, read DataTrees.Three transports — stdio (Claude Desktop), Streamable HTTP, Docker/TCP.
Capability-aware registration — bridge-only tools are simply not registered when no bridge is reachable; no stub functions, ever.
Actionable errors — every failure carries a category, a remediation hint, and details the agent can act on.
Architecture
┌──────────────┐ stdio | HTTP ┌────────────────┐
│ Claude / │ ◀──────────────────▶ │ rhino-mcp │
│ MCP client │ │ (Python) │
└──────────────┘ └─────┬──────────┘
│
JSON-RPC 2.0 │ (named pipe / unix socket / TCP)
│
┌─────────▼──────────┐
│ C# Bridge Plugin │
│ (inside Rhino 8) │
│ ──────────────── │
│ RhinoCommon │
│ Grasshopper │
│ Roslyn (C# exec) │
│ IronPython (Py) │
└────────────────────┘In standalone mode the right-hand side is replaced by an in-process rhino3dm.File3dm document; bridge-only tools are unregistered.
Requirements
Python 3.11 or newer
rhino3dm >= 8.9,mcp[cli] >= 1.2,pydantic >= 2.6(installed automatically)Optional: McNeel Rhino 8 (any platform) — needed only for bridge mode
Optional: .NET 8 SDK — to build the C# bridge plugin (
dotnet build)Optional:
pywin32on Windows for the named-pipe transport
Installation
Using uv (recommended)
uv tool install rhino-mcp
# or, from a checkout:
uv sync && uv run rhino-mcpUsing pip
pip install rhino3dm-mcp
rhino-mcp --versionOne-shot Claude Desktop wiring
After installing the package, register the server with Claude Desktop in a single command instead of editing claude_desktop_config.json by hand:
rhino-mcp install # auto-detect launcher (uvx / rhino-mcp / python)
rhino-mcp install --mode bridge # force bridge mode in the env block
rhino-mcp install --force # overwrite an existing entry
rhino-mcp install --dry-run # preview the JSON without writingThe command writes a timestamped .bak.* copy of the existing config before saving, and is idempotent — re-running with the same flags is a no-op.
Restart Claude Desktop afterwards to pick up the change.
From inside Rhino — _McpInstall
If the bridge plugin is loaded, type _McpInstall on the Rhino command line.
The command is non-interactive: it locates uvx / rhino-mcp / python on PATH (with ~/.local/bin, ~/.cargo/bin, /opt/homebrew/bin, and /usr/local/bin searched on macOS so the GUI-app PATH is enough), runs the same install pipeline as rhino-mcp install, and streams the outcome to the Rhino command line.
The MCP server is registered with --mode auto so Claude Desktop attaches to the bridge when Rhino is running and falls back to standalone (rhino3dm) when it is not.
Docker
docker compose -f docker/docker-compose.yml up --buildThe container exposes the server over Streamable HTTP on TCP :8765.
Running the server
rhino-mcp is normally launched by an MCP client (Claude Desktop / Cursor / claude.ai connector) via the command + args you put into the client's config.
You can also run it manually for debugging.
Launch modes
Use case | Command |
Stdio (Claude Desktop default) |
|
Streamable HTTP (local dev / Cursor) |
|
Streamable HTTP (claude.ai connector) |
|
Docker (HTTP on |
|
Force standalone (no Rhino needed) |
|
Force bridge (fail fast if Rhino down) |
|
Force bridge with HTTP fallback |
|
Show all CLI flags |
|
The HTTP endpoint is http://<host>:<port>/mcp.
Use --allow-external only when exposing the server through ngrok / Cloudflare Tunnel / etc.
— it disables DNS-rebinding protection.
Claude Desktop (stdio)
claude_desktop_config.json lives at:
OS | Path |
macOS |
|
Windows |
|
Linux |
|
Paste this entry under mcpServers:
{
"mcpServers": {
"rhino-mcp": {
"command": "uvx",
"args": ["rhino3dm-mcp"],
"env": {
"RHINO_MCP_TRANSPORT": "stdio",
"RHINO_HOST": "127.0.0.1",
"RHINO_PORT": "4242",
"RHINO_MCP_LOG_LEVEL": "INFO"
}
}
}
}Prefer a checkout?
Point command at uv and run from the repo:
{
"mcpServers": {
"rhino-mcp": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/rhino-mcp", "run", "rhino-mcp"],
"env": { "RHINO_MCP_LOG_LEVEL": "DEBUG" }
}
}
}Restart Claude Desktop; the rhino-mcp tools and the three strategy prompts (general_strategy, rhinoscript_workflow, viewport_workflow) appear in the palette.
Cursor IDE
Cursor reads ~/.cursor/mcp.json (global) or <project>/.cursor/mcp.json (per-project):
{
"mcpServers": {
"rhino-mcp": {
"command": "uvx",
"args": ["rhino3dm-mcp"],
"env": { "RHINO_MCP_FORCE_MODE": "bridge" }
}
}
}claude.ai remote connector (Streamable HTTP)
Run the server with HTTP + external access:
rhino-mcp --transport http --host 0.0.0.0 --port 8765 --allow-external --statelessTunnel it (e.g.
ngrok http 8765) and grab the public HTTPS URL.In claude.ai → Settings → Connectors → Add custom connector, set the URL to
https://<your-tunnel>/mcp.
Generic stdio MCP clients
Any MCP-compatible client (mcp-inspector, Continue, Claude Code, etc.) that can spawn a subprocess will work — give it uvx rhino3dm-mcp or the path to your rhino-mcp entry-point and inherit env vars from the table below.
Rhino-side bridge plugin (C# — recommended)
Build and install the C# plugin for full 235 tool support.
The included helper script wraps dotnet build and verifies the post-build install location:
./scripts/build-plugin.sh # debug build + install
./scripts/build-plugin.sh --release # release build
./scripts/build-plugin.sh --clean # clean + rebuild
# Or invoke dotnet directly:
dotnet build rhino_plugin/csharp/RhinoMCPPlugin.csproj -c ReleasePost-build targets copy the .rhp:
OS | Path |
macOS |
|
Windows |
|
Restart Rhino 8 — the bridge starts automatically on load (TCP :4242 by default).
Then restart rhino-mcp (or set RHINO_MCP_FORCE_MODE=bridge) and the bridge-only tools become available.
Quick start
# After "rhino-mcp" is configured in Claude Desktop, ask Claude:
# "Create a 10-unit sphere at the origin and save the document to /tmp/demo.3dm"
# Behind the scenes Claude calls rhino_sphere → rhino_save.Strategy prompts
Three MCP prompts are registered automatically and surfaced in the client's prompt picker:
Prompt | When to use |
| Decision tree for orienting in a document → choosing the right tool → best practices. |
| Mandatory steps before invoking |
| How to combine |
Telling Claude "use the general_strategy prompt before you start" gives much better tool selection on long sessions.
Visual verification with base64 screenshots
"Build a 5x5x5 box on layer 'Demo', then take a 1280x720 base64 screenshot so you can confirm it landed."Claude calls rhino_box → rhino_zoom_extent → rhino_screenshot(as_base64=True) and reads the inline PNG from the response (image_base64 field) on the next turn.
Pagination & rich filtering
"List the first 50 curves on the 'Walls' layer, then select every object whose name matches 'Pillar_*' on layer 'Structure'."rhino_list_objects returns a pagination block; iterate with offset += limit until has_more is false. rhino_object_select accepts name_pattern, layer, color, object_type, user_text, and deselect_first.
Standalone smoke run from the shell:
RHINO_MCP_FORCE_MODE=standalone uv run python - <<'PY'
import asyncio
from rhino_mcp.server import build_server
from rhino_mcp.utils.registry import Mode
mcp, count = build_server(runtime_mode=Mode.STANDALONE, bridge_client=None)
print(f"{count} tools registered")
sphere = asyncio.run(
mcp._tool_manager._tools["rhino_sphere"].run(
{"args": {"center": {"x": 0, "y": 0, "z": 0}, "radius": 10.0}}
)
)
print(sphere)
PYTools reference (summary)
Category | Standalone | + Bridge | Notable members |
Geometry | 14 | 14 |
|
Curves | 3 | 3 |
|
Solids | 5 | 10 |
|
Surfaces | 0 | 11 |
|
Mesh | 1 | 8 |
|
Transform | 9 | 11 |
|
Annotation | 2 | 8 |
|
Layer / Object | 6 | 8 |
|
Material | 2 | 3 |
|
File I/O | 5 | 9 |
|
Analysis | 4 | 9 |
|
Query | 7 | 8 |
|
Display | 0 | 5 |
|
Scripting | 0 | 2 |
|
RS Docs | 4 | 4 |
|
History | 0 | 2 |
|
Batch | 0 | 1 |
|
Deformation | 0 | 4 |
|
NURBS | 0 | 5 |
|
SubD | 0 | 2 |
|
Srf Match | 0 | 3 |
|
Extraction | 0 | 4 |
|
Ctrl Points | 0 | 2 |
|
Paneling | 0 | 3 |
|
Grasshopper | 0 | 22 |
|
Drawing | 2 | 5 |
|
Schedule | 5 | 5 |
|
Blocks | 3 | 5 |
|
Environment | 3 | 4 |
|
Freeform | 8 | 11 |
|
BIM I/O | 1 | 4 |
|
Materials+ | 2 | 3 |
|
Render | 0 | 5 |
|
Daylight | 2 | 2 |
|
Detailed signatures live in docs/en/tools-reference.md.
Grasshopper integration
1. Start Rhino 8 → open Grasshopper → load your `.gh` definition.
2. Ensure the C# bridge plugin (`rhino-mcp.rhp`) is loaded in Rhino.
3. Set `RHINO_MCP_FORCE_MODE=bridge` and restart rhino-mcp.
4. From Claude:
- "Open /work/wing.gh, set the 'span' slider to 12.5, run, bake to layer 'Wing'."
- "Read the output of the 'Voronoi' component as a DataTree."See docs/en/grasshopper-guide.md for canvas/component/cluster recipes.
Configuration
Every knob is controlled by an environment variable. CLI flags override env where applicable.
Variable | Default | Purpose |
|
|
|
| (auto) | Force |
|
| When |
| (auto) | Bridge transport selector: |
|
| TCP transport endpoint |
|
| Windows named-pipe name |
| (XDG runtime) | Unix socket path override |
|
| Seconds to wait for a bridge ping |
|
| Max reconnect attempts after a transient transport failure |
|
| Base delay (seconds) for exponential backoff |
|
| Symmetric jitter ratio ( |
|
| Stderr log level. Set |
Development
git clone https://github.com/easehee/rhino-mcp.git
cd rhino-mcp
uv venv && source .venv/bin/activate
uv pip install -e '.[dev]'
# Tests + coverage
uv run pytest --cov=src/rhino_mcp
# Lint + type check
uv run ruff check src/ tests/
uv run mypy src/rhino_mcp
# Build the C# bridge plugin (auto-installs into Rhino on macOS/Windows)
./scripts/build-plugin.sh --release
# Wheel + sdist
./scripts/build.shManual smoke test against a running server
# Terminal A — start the server with verbose logging
RHINO_MCP_LOG_LEVEL=DEBUG rhino-mcp --transport http --port 8765
# Terminal B — list tools via the MCP inspector or a curl probe
npx @modelcontextprotocol/inspector http://127.0.0.1:8765/mcpTroubleshooting
Symptom | Likely cause / remediation |
| Start Rhino 8 with the C# bridge plugin loaded; verify |
Bridge-only tool absent in Claude | Server is in standalone mode — load the bridge plugin or set |
| Standalone supports only |
| Standalone import is |
| The bridge crashed or Rhino was closed — |
Stdout corrupted under stdio transport | Always log to stderr; rhino-mcp does this — verify no third-party libraries print to stdout. |
| Caller passed |
Pagination loops forever | Always advance |
| Filters AND together — try removing one filter at a time; the |
More in docs/en/troubleshooting.md.
License
MIT — see LICENSE.