news 2026/10/15 15:03:35

如何给RhinoMCP添加自定义MCP命令:反射分发与Schema契约完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何给RhinoMCP添加自定义MCP命令:反射分发与Schema契约完整实战指南

【免费下载链接】rhinomcp

RhinoMCP connects Rhino 3D to AI Agent through the Model Context Protocol (MCP)

项目地址:https://gitcode.com/gh_mirrors/rh/rhinomcp
点击查看免费下载

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 结果 }

属性只有两个字段:

字段作用
NameJSON 命令类型名(约定用 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中,这里有三条非常关键的机制,你的自定义命令会自动继承:

  1. 强制 UI 线程:所有命令通过RhinoApp.InvokeOnUiThread执行,因为 RhinoCommon 和 Grasshopper 文档 API 必须在 Rhino 主线程运行;
  2. 自动撤销包裹:非只读命令会被doc.BeginUndoRecord($"MCP: {cmdType}")包裹,AI 的每次修改都能一键Ctrl+Z回退;
  3. 未知命令兜底:命令名不在分发表中时,返回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.json

3.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)

项目地址:https://gitcode.com/gh_mirrors/rh/rhinomcp
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 15:03:33

Spring Boot多数据源切换实战:从路由到事务的全链路避坑指南

简介&#xff1a;本资源是一份面向Java后端开发者与分布式系统学习者的分库分表实战项目包&#xff0c;聚焦企业级大数据量场景下的数据库水平扩展难题&#xff0c;重点解决多数据源动态切换、跨库路由与轻量级分片集成等核心问题。资源基于Sharding-JDBC框架构建SSMDemo示例工…

作者头像 李华
网站建设 2026/10/15 15:03:33

描述统计实战:从直方图、箱线图到均值、标准差与数据挖掘

先把话说在前面&#xff1a;数据挖掘这件事&#xff0c;真正动手之后你会发现&#xff0c;最花时间的往往不是算法本身&#xff0c;而是在喂算法之前那堆“理不清、说不明”的数据。而描述统计就是帮你把这堆数据理顺的第一步。系列这套“数据挖掘数学基础”课程里&#xff0c;…

作者头像 李华
网站建设 2026/10/15 15:03:29

SpringBoot生鲜电商团购平台技术实现与优化

1. 项目概述&#xff1a;生鲜电商团购平台的技术实现这个基于SpringBoot的生鲜团购平台&#xff0c;本质上是一个聚焦于高频、刚需消费场景的垂直电商解决方案。与综合类电商平台不同&#xff0c;我们专门针对生鲜商品短保质期、高损耗率的特性&#xff0c;设计了限时团购和社区…

作者头像 李华
网站建设 2026/10/15 15:03:23

B站动漫数据采集与分析:Python爬虫合规实践指南

简介&#xff1a;本资源是一套面向Python初学者与数据分析爱好者的B站番剧全链路分析实践项目&#xff0c;聚焦ACG文化数据采集、清洗、分析与可视化全流程&#xff0c;可作为爬虫与数据分析入门的完整学习范例。压缩包共23个文件&#xff0c;含3个Jupyter Notebook&#xff08…

作者头像 李华
网站建设 2026/10/15 15:03:22

翻页电子书放大就糊?不是分辨率不行,是你上传的PDF类型选错了

我以前一直以为翻页电子书放大就糊是行业通病&#xff0c;还总甩锅"压缩太狠"“屏幕不行”。直到朋友在 FLBOOK 上做了一本讲义发我&#xff0c;我放大到整页只剩四五个字&#xff0c;字还是字——才意识到&#xff1a;问题不在工具&#xff0c;也不在分辨率&#xf…

作者头像 李华
网站建设 2026/10/15 15:03:12

百亿量化私募数据清单:28家机构核心参数与选型逻辑

简介&#xff1a;这份PDF资料聚焦国内规模超百亿的量化私募机构&#xff0c;面向关注私募基金、量化投资与资产配置的投资者及金融从业者&#xff0c;帮助读者快速了解头部量化机构的团队背景、策略布局与业绩表现。包内共1个PDF文件&#xff0c;约800KB&#xff0c;内容以机构…

作者头像 李华