【免费下载链接】rhinomcp
RhinoMCP connects Rhino 3D to AI Agent through the Model Context Protocol (MCP)
RhinoMCP 通过 Model Context Protocol(MCP)把 Rhino 3D 和 Grasshopper 连接到 AI Agent,让 AI 能用自然语言直接建模、读图、执行脚本。想在它上面添加自定义 MCP 命令?好消息是:RhinoMCP 用**反射分发([McpCommand]特性)**自动注册处理器,用JSON Schema 契约约束两端数据格式,你只需照着一份"五步清单"走,新命令即可被 AI 直接调用。本文带你从原理到落地完整走一遍。
一、先搞懂:一条MCP命令的完整旅程
RhinoMCP 的运行时链路是单向桥接的:
AI 客户端 → Python FastMCP 服务端 → TCP JSON (127.0.0.1:1999) → Rhino C# 插件 → Rhino / Grasshopper每当 AI 调用一个工具,数据流向如下(详见 docs/IMPLEMENTATION.md):
MCP 工具调用 → @mcp.tool 的 Python 包装函数 → RhinoConnection.send_command(command_type, params) → {"type": command_type, "params": {...}} → RhinoMCPServer.ExecuteCommandInternal(...) → 带 [McpCommand] 特性的 C# 处理器 → {"status": "success", "result": {...}} 或 {"status": "error", "message": "..."}所以"添加一个自定义 MCP 命令"本质上是同时维护四层一致性:Python 工具包装层、JSON Schema 契约层、C# 反射分发层、协议枚举层。理解了这条链路,后面每一步修改就都有据可循了。
二、反射分发:[McpCommand]如何让新命令"自动上线"
2.1 一个特性搞定注册
核心机制在 plugin/McpCommandAttribute.cs:
[McpCommand("create_layer")] public JObject CreateLayer(JObject parameters) { // 解析 parameters,操作 RhinoDoc,返回 JObject 结果 }属性只有两个字段:
| 字段 | 作用 |
|---|---|
Name | JSON 命令类型名(约定用 snake_case),如create_layer |
ReadOnly | 为true时,分发器不创建 Rhino 撤销记录,适用于纯查询类命令(get_*、undo、capture_viewport等) |
2.2 反射扫描与分发表缓存
扫描逻辑位于 plugin/Functions/_Registry.cs:
GetDispatchTable()在首次访问时惰性构建,之后缓存,启动开销只有一次;- 它用
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly扫描RhinoMCPFunctions所有公共实例方法,凡是挂了[McpCommand]的方法都会通过Delegate.CreateDelegate包装成Func<JObject, JObject>进入分发表; - 重复命令名会直接抛异常(
Duplicate [McpCommand("xxx")]),从源头杜绝命名冲突。
2.3 命令执行:UI 线程 + 自动撤销记录
真正的分发发生在 plugin/RhinoMCPServer.cs 的ExecuteCommandInternal中,这里有三条非常关键的机制,你的自定义命令会自动继承:
- 强制 UI 线程:所有命令通过
RhinoApp.InvokeOnUiThread执行,因为 RhinoCommon 和 Grasshopper 文档 API 必须在 Rhino 主线程运行; - 自动撤销包裹:非只读命令会被
doc.BeginUndoRecord($"MCP: {cmdType}")包裹,AI 的每次修改都能一键Ctrl+Z回退; - 未知命令兜底:命令名不在分发表中时,返回
Unknown command type: xxx错误而不是崩溃。
还有个"白送"的福利:describe_capabilities命令直接读取这份活的分发表(见 plugin/Functions/DescribeCapabilities.cs),你添加的[McpCommand]会自动出现在 AI 的能力清单里,无需任何额外登记。
三、Schema 契约:Python 与 C# 之间的"合同"
contracts/目录是两端数据交换的唯一事实来源(single source of truth),结构见 contracts/README.md:
contracts/ ├── protocol.json # 协议信封(命令/响应包装)+ 全部命令类型枚举 ├── common/definitions.json # 共享类型(color、point3d、guid 等) ├── commands/ # 每条命令的参数 Schema,如 create_layer.json └── responses/ # 响应 Schema,如 layer_info.json3.1 命令信封与响应格式
协议信封定义在 contracts/protocol.json,其中type字段的enum列出了当前全部 60 个命令(35 个 Rhino + 25 个 Grasshopper)。响应统一为两种形态:
{"status": "success", "result": { ... }} {"status": "error", "message": "错误描述"}3.2 参数 Schema 长什么样
以 contracts/commands/create_layer.json 为例,共享类型通过$ref复用common/definitions.json:
{ "type": "object", "properties": { "name": { "type": "string", "description": "新图层名称(省略则自动生成)" }, "color": { "$ref": "../common/definitions.json#/$defs/color" }, "parent": { "type": "string", "description": "父图层名称" } } }3.3 校验时机与三种模式
Python 端的校验器 server/src/rhinomcp/validation.py 基于jsonschema(Draft 2020-12 + 跨文件$ref注册表)工作,在send_command前后各拦一道:
- Pre-flight:命令发出前用
commands/<type>.json校验params; - Post-flight:命令返回后用
responses/中对应的 Schema 校验result——这是唯一能捕捉"插件与契约漂移"的地方。
通过环境变量RHINO_MCP_VALIDATE控制严格程度(默认warn):
| 模式 | 行为 |
|---|---|
off | 完全跳过校验 |
warn | 记录日志但仍发送(安全默认值) |
strict | 校验失败直接抛ValueError,非法负载永远到不了 Rhino(推荐 CI 使用) |
一个容易踩的坑:如果新命令有响应 Schema,需要在validation.py的response_schema_map中登记"命令名 → 响应 Schema 文件"的映射(validation.py),否则 Post-flight 校验会静默跳过。
四、实战:五步添加一条自定义MCP命令
官方推荐的完整清单如下(出处:contracts/README.md 与 docs/IMPLEMENTATION.md)。以新增set_object_transparency命令为例:
第1步:编写命令参数 Schema
新建contracts/commands/set_object_transparency.json,声明参数名、类型、是否必填。能复用共享类型的(guid、color、point3d)一律用$ref,保持契约风格统一。
第2步:编写响应 Schema(如需要)
新建contracts/responses/set_transparency_result.json,并用required明确必填字段——这会让 Post-flight 校验真正发挥作用。
第3步:登记到协议枚举
把新命令名加入 contracts/protocol.json 中type的enum数组。这一步保证协议层与实现层同步。
第4步:两端实现处理器
C# 端:在plugin/Functions/下新建一个 partial 类文件,方法签名固定为public JObject 方法名(JObject parameters):
[McpCommand("set_object_transparency")] public JObject SetObjectTransparency(JObject parameters) { string id = parameters["id"]?.ToString(); double value = parameters["value"]?.ToObject<double>() ?? 0; // 解析参数(可复用 _utils.cs 的 castTo* 辅助方法)、 // 操作 RhinoDoc、最后返回 JObject 结果 // 注意:解析失败时直接抛异常, // 分发器会自动捕获并转成 {"status":"error"} 返回 }Python 端:在server/src/rhinomcp/tools/下新建对应模块,用@mcp.tool()注册一个尽量薄的包装函数:
@mcp.tool() def set_object_transparency(ctx: Context, obj_id: str, value: float = 0.5) -> str: """设置对象透明度的 MCP 工具说明(AI 靠这段 docstring 理解何时调用它)。""" return get_rhino_connection().send_command( "set_object_transparency", {"id": obj_id, "value": value} )tools/目录的模块会被server/src/rhinomcp/__init__.py自动导入注册(下划线开头的文件除外),所以不需要额外接线——这和 C# 端的反射分发是同一个哲学:加文件即生效。
💡 小提醒:value的取值范围如果要在运行时拦截,应写进命令 Schema 的minimum/maximum;而"什么情况下该用这个工具"应写进 docstring,因为那是 AI 唯一看到的说明。
第5步:跑验证
python contracts/test_schemas.py # 校验所有 Schema 是否为合法 JSON Schema然后在 Rhino 里重新编译加载插件、输入mcpstart重启桥接,再用describe_capabilities确认新命令已出现在分发表中。
五、避坑清单:高频问题速查
| 症状 | 原因与解法 |
|---|---|
启动时抛Duplicate [McpCommand] | 两个方法用了同名命令,重命名其一 |
| 命令永远不在 UI 线程生效/偶发空引用 | 检查是否绕过RhinoMCPFunctions自行建了 TCP 入口——所有命令必须走反射分发 |
| 想让 AI 的修改可撤销 | 去掉ReadOnly = true,只读标记只留给get_*、capture_viewport等纯查询命令 |
strict模式下命令被拦下 | 对照commands/<type>.json检查params的字段名与类型;先切warn看日志定位 |
| 响应总是跳过 Post-flight 校验 | 忘了在validation.py的response_schema_map中登记响应 Schema |
| AI 从不主动调用新工具 | docstring 写得不够"操作性",写清楚用途、参数含义和返回内容 |
🧭 记住一条总原则:分发表是活的,契约是静态的合同。C# 端靠反射"即插即用",contracts/保证跨语言数据格式永远对齐——两端任何一处漂移,都会在校验日志或describe_capabilities的清单里立刻现形。
结语
至此你已经掌握了 RhinoMCP 扩展命令的完整方法论:[McpCommand]反射分发负责"自动注册",JSON Schema 契约负责"格式对齐",五步清单负责"落地闭环"。接下来不妨从一条简单的只读查询命令练手(记得标ReadOnly = true),再尝试一条带撤销记录的变更类命令——当 AI 在对话里自然调用出你的新工具时,这套架构的价值就真正闭环了。更多架构细节可深入阅读 docs/IMPLEMENTATION.md。
【免费下载链接】rhinomcp
RhinoMCP connects Rhino 3D to AI Agent through the Model Context Protocol (MCP)
相关推荐
如何为 Rust 数学库 libm 贡献代码:从挑选 issue 到提交 PR 的完整指南
如何为 Rust 数学库 libm 贡献代码:从挑选 issue 到提交 PR 的完整指南 libm 是将 C 标准数学库 MUSL 的 libm 完整移植到纯
如何为Spike添加自定义RISC-V指令:完整实现教程
如何为Spike添加自定义RISC V指令:完整实现教程 Spike作为RISC V ISA模拟器,是开发和测试RISC V架构的重要工具。本文将详细介绍如何在
指令集开发工具测试git-bug 给 Bug 添加评论:`bug comment new` 命令完整实战指南
git bug 给 Bug 添加评论: bug comment new 命令完整实战指南 git bug bug comment new 是分布式 Bug 跟踪
开发工具研发协作
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考