MCP Server接入3D工作流:让AI真正读懂你的场景数据
1. 我的 3D 工作流里为什么非要塞进一个 MCP server做 3D 项目做久了你会发现一个特别拧巴的事AI 语言模型再聪明也看不懂我硬盘里的 .blend、.gltf、.fbx。它知道什么是漫反射贴图能跟你聊 PBR 材质聊得头头是道但让它帮我查一下当前场景里哪几个物体用了金属度超过 0.8 的材质它做不到因为它根本碰不到我的场景文件。以前的处理方式特别原始手动把场景导出成 JSON转成文本摘要再粘贴给 AI。一个 300MB 的角色场景光是整理一份像样的描述就够我喝一壶的。而且这事的痛还不只是麻烦——导出的是快照AI 问我那个物体局部坐标是多少的时候我经常得重新导一次。这种静态快照的方式根本没有对话感AI 拿到的信息永远是过时的。后来我接触到了 MCPModel Context Protocol模型上下文协议思路一下子通了。简单说MCP 就是给 AI 装一个标准化的接线口让它可以按统一的方式调用外部工具、读取外部数据。我不用再手动导出场景了只要写一个 MCP server把查询场景列表读取物体属性统计材质使用情况这些能力封装成工具函数AI 就能像点菜一样直接调用。这篇文章就记录我把 AI 接进自己 3D 工作流的完整过程。没有特别玄乎的东西核心是思路、代码和踩坑记录。无论你是 3D 美术、技术美术还是搞程序化生成的开发者只要手上有一堆 AI 理解不了的结构化数据这个方案都能给你参考。1.1 痛点复盘AI 确实聪明但看不见我的场景文件先说一个扎心事实大语言模型的强项是处理文本序列但 3D 文件几乎全是二进制——有变长头、有数据块偏移、有各种自定义扩展根本没法直接喂给模型。即便你把 glTF 这种自带 JSON 描述的文件硬塞给它token 数也会瞬间爆掉而且它读到的全是坐标数组根本形不成这张场景里有个角色手持一把武器这种高层认知。我梳理了一下自己的真实痛点大概是这三类查询类场景里总共有多少个物体哪些物体没挂碰撞体某个材质的 roughness 设的是多少这类问题频率最高但每次都要人手动去查。检查类导出前检查模型是否按规定命名、有没有多余的默认材质、法线方向是不是反的。这类工作重复且机械AI 其实很擅长干。生成类根据描述生成一个简单场景、批量改物体名字、统一替换材质属性。这类属于进阶玩法但前提依然是 AI 能访问到场景。这些需求有一个共性它们都需要 AI触达真实的数据而不是靠我口头描述。MCP 的价值恰恰在这里它让 AI 从接话员变成了能动手的助理。我见过很多人在群里问AI 能不能帮我看下这个模型有什么问题底下有人回答你可以把模型截图发给它看啊——可截图只能看个外表根本查不了拓扑、命名规范、材质属性这些只有数据结构里才有的东西。1.2 MCP 解决的不只是接口更是对话式操作的问题我在没接触 MCP 之前想的方案是自己写一套 HTTP API再给 AI 写一篇接口说明书让它照着调。听起来可行但实际用起来问题很多接口的鉴权方式各不相同、参数格式没有统一标准、AI 生成的调用代码经常带病而且最烦的是——你给我十个接口我还要告诉它先调 A 再调 B如果返回码是 403 就调 C 拿 token。MCP 的设计就把这些破事全收走了。它在模型和工具之间定了一层标准协议AI 只需要知道有哪些工具可用、每个工具收什么参数、返回什么格式剩下的握手、协商、消息转发、错误上报全由协议层处理。对我这种主要做 3D、不想把精力花在写一套 API 规范和鉴权体系上的人来说这个抽象非常救命。我理解它的方式是用USB 口来类比以前每个外设都要专门的线、专门的驱动现在统一了 USB 标准设备插上就能用。MCP 就是 AI 界的 USB它让场景数据材质库渲染农场这些不同形态的能力变成了同样插拔方式的工具包。所以这篇文章不是我对比了多少框架、调研了多少协议之后写出来的理论文章而是我实际把服务端跑起来、让 AI 成功查询到我的 3D 场景数据之后的一次完整复盘。接下来我会按我的实现路径来写先从 MCP 本身的原理说起再讲技术选型、代码、踩坑最后聊下一步方向。2. 先搞懂 MCP 的通信骨架工具、资源与提示词三件套网上讲 MCP 的文章不少但大多讲得特别抽象一上来就抛ToolResourcePrompt三个概念看完了还是一头雾水。我自己当时的理解路径是先用生活化的方式把骨架搭起来再去看协议细节所以这块我也用同样的方式来写。2.1 用生活化的方式理解 MCP 的三个核心原语MCP 协议定义了三个核心元语分别是工具Tools、资源Resources和提示词Prompts。我当时的理解是工具是 AI 的手资源是 AI 的眼睛提示词是 AI 的常用话术手册。工具就是可以调用的函数比如读取场景文件查询材质列表执行 Python 脚本。AI 可以根据你的自然语言自动决定调哪个工具、传什么参数。它解决的是AI 能动的问题。资源是暴露给 AI 读取的数据实体比如一个 glTF 场景文件、一份材质参数表、一份渲染日志。资源有自己的 URIAI 可以直接读取类似AI 能看。提示词是预置的交互模板类似用一句话总结当前场景状态列出所有未命名物体这些固定话术。它的作用是让 AI 知道面对某个任务时该按什么结构组织请求类似AI 知道怎么张嘴。三个元语通常是配合使用的。比如我的场景查询工具会暴露一个scene://default这样的资源 URI 让 AI 读取场景摘要同时注册一个list_objects工具让 AI 按条件过滤物体再提供一个帮我检查场景的提示词模板引导 AI 按先读摘要再查异常物体最后出报告的路径执行任务。区分这三个概念不用太死板。你在设计的时候只需要问自己这个能力是让 AI 读数据还是让 AI 做事读数据就做成资源做事就做成工具如果是固定套路就做成提示词。实际开发时我大部分功能都做成了工具资源主要用于暴露大的只读文件。2.2 一次完整请求的往返初始化握手与 JSON-RPC 消息流MCP 的通信底层用的是 JSON-RPC 2.0传输层支持 stdio标准输入输出管道和 HTTP 两种方式。我在本地跑的是 stdio 模式也就是把 MCP server 当做一个子进程AI 客户端通过标准输入输出跟它对话。每次完整通信大致走这几步初始化握手客户端启动 server 后先发initialize请求双方交换协议版本和能力声明比如我支持工具调用我支持资源订阅。这一步没走好后面所有功能都白搭。能力发现客户端再发tools/list或resources/listserver 返回当前可用的工具清单或资源清单包括名称、描述、参数 JSON Schema。AI 就是根据这些描述来决定调什么工具的。请求/响应AI 按用户意图构造一个tools/call请求带上参数server 执行对应函数把结果以 JSON 形式返回。错误与日志如果参数不合法或执行出错server 返回结构化错误信息日志则通过notifications/message推送给客户端方便调试。我把这个过程理解为AI 在跟一台电话交换机对话。它不认识 3D 文件是什么但它知道电话簿上有哪些号码工具清单拨哪个号、说什么话参数交换机那头收到之后把对应的能力执行完再把结果报给它。实际开发中你几乎不用手写这些 JSON-RPC 消息用现有的 SDK 封装就行。我用的是 Python 侧的 FastMCP 库它把工具的注册、参数的校验、生命周期管理都处理好了。但不代表协议细节可以不看——联调排查的时候大部分问题都出在握手失败、工具描述不够清晰、返回格式不合法这三点上后面我再展开讲。3. 技术选型推演Python、FastMCP 与 3D 数据格式的取舍逻辑选型这件事通常不是哪个最好而是哪个最省事、最贴合现有工作流。我在动手前犹豫过是用 TypeScript 还是 Python也纠结过要不要直接上 Blender 的 Python 接口最后才定下一个组合拳方案。这节我把当时推演的过程写下来。3.1 为什么选 Python 生态而非 Node.js先说结论如果你的主战场是 3D 工具链Python 生态优先。原因很简单3D 领域的绝大多数实用工具都长在 Python 生态里。Blender 的脚本接口是 Python工业级文件处理库比如处理 glTF/OBJ/PLY 的库也都是 Python 优先支持。MCP 开发端到端跑下来最耗时间的不是协议代码本身而是怎么从纷繁复杂的 3D 文件里提取出 AI 需要的信息这部分的现成工具 Python 明显更多。如果用 TypeScript也不是不行。Node.js 那边的 MCP SDK 同样成熟适合把服务发布到云端给多端调用。但我在本地做 AI 辅助 3D 创作脚本本来就在 Python 环境里跑再引入一套 Node 链路的 SDK等于让调试环境多了一倍复杂度。此外FastMCP 这个库让我省了不少事。它给每个工具函数自动生成 JSON Schema、自动做参数校验、自动处理 stdio 传输我写 server 的代码量大概只有手写 JSON-RPC 的十分之一。我这里直接对比一下手写协议和用库的差异对比项手写 JSON-RPC使用 FastMCP 封装工具注册自己维护 schema 字典装饰器一键注册参数校验手动写字段检查基于函数签名自动校验错误处理手工构造错误码和消息异常统一转换为协议错误开发耗时1-3 天1-2 小时注意这不是说手写协议没价值而是对你我这种想把精力留给3D 业务逻辑的人来说封装库的性价比高得多。等到你有钱有闲、要支持自定义传输层或者特殊鉴权的时候再考虑手写也不迟。3.2 3D 数据在 MCP 世界里应该长什么样第二个关键决策是MCP server 只是桥梁它要暴露什么数据给 AI总不能把整个 .blend 二进制文件丢过去吧。我的答案是尽量把 3D 场景转成 JSON 结构再给 AI让稠密几何数据留在本地把高层语义数据交给模型。那什么是高层语义数据举个例子AI 判断这个场景里有没有名为 main_character 的角色它不需要逐顶点坐标只需要场景节点树、节点名、层级关系、变换参数、材质引用。至于绘制三角形、GPU 烘焙这些事AI 本来也干不了交给离线渲染器和游戏引擎就行。所以我定下来的数据抽象至少包含三层场景摘要层物体总数、层级深度、材质总数、灯光数量、平均面数等统计信息。对象索引层每个对象的名字、类型、父子关系、世界/局部坐标、是否隐藏、是否参与渲染。属性查询层按需读取某个对象的完整属性包括材质参数、修改器栈、自定义属性等。我在实现时让 MCP 的每个资源对应一个 JSON 文档每个工具对应一次按需查询。这样既避免了把整个场景一次性压给模型导致 token 爆炸又保持了足够的灵活性。后面章节我会给具体代码这块先形成一个整体认知。再补充一点关于场景要不要经过 Blender的思考。如果你当前工作是围绕 Blender 展开的可以跟我一样在 server 里直接调用 Blender 的 Python API把场景转成 JSON如果你主要用的是游戏引擎或自研渲染器那就选择导出 glTF/OBJ 再解析。总之 MCP 要解决的本质问题是AI 如何拿到结构化语义数据至于底层是 Blender、Unity 还是自研引擎都可以退居为实现细节。4. 服务端代码拆解把 3D 查询能力封装成 AI 能调用的工具这一章是我写得最开心的一部分因为终于可以放代码了。为了让你能完整跑通我会从零开始写一个最小可运行的 MCP server。它不需要连接真实的 Blender而是用一个模拟的 3D 场景数据代替等你的环境搭好了再把数据源换成真实场景即可。4.1 最小可运行的服务端骨架这个骨架我用 FastMCP 实现。先假设你本地有 Python 3.10也装好了fastmcp和uv。如果没有可以直接用 pip 安装pip install fastmcp然后创建一个server_demo.py内容如下import json from fastmcp import FastMCP # 初始化一个 MCP server名字会显示在客户端连接列表里 mcp FastMCP(3D Scene Query Server) # 模拟一个场景数据实际开发时你可以改成读 glTF 或连接 Blender 接口 SCENE_DATA { name: demo_scene, objects: [ { id: obj_001, name: floor, type: mesh, visible: True, parent: None, materials: [concrete_floor], position: [0, 0, 0], rotation: [0, 0, 0], scale: [10, 1, 10], }, { id: obj_002, name: character_main, type: mesh, visible: True, parent: None, materials: [skin_mat, cloth_mat], position: [1.2, 0, 3.4], rotation: [0, 90, 0], scale: [1, 1, 1], }, { id: obj_003, name: lamp_fill, type: light, visible: True, parent: obj_002, intensity: 500, color: [255, 240, 220], }, ], materials: { concrete_floor: {metallic: 0.0, roughness: 0.9}, skin_mat: {metallic: 0.0, roughness: 0.55}, cloth_mat: {metallic: 0.0, roughness: 0.8}, }, } mcp.tool() def list_objects(visible_only: bool False) - str: 列出场景中所有对象的名称、类型和层级visible_only 为 True 时只返回可见对象。 objs [o for o in SCENE_DATA[objects] if not visible_only or o.get(visible, True)] return json.dumps(objs, ensure_asciiFalse, indent2) mcp.tool() def get_object_info(object_id: str) - str: 根据对象 ID 查询对象的详细信息包括名称、变换、材质等属性。 obj next((o for o in SCENE_DATA[objects] if o[id] object_id), None) if obj is None: raise ValueError(f找不到对象 {object_id}) return json.dumps(obj, ensure_asciiFalse, indent2) mcp.tool() def query_materials(metallic_threshold: float 0.5) - str: 查询所有材质并返回金属度超过指定阈值的材质列表。 included [ {name: name, **params} for name, params in SCENE_DATA[materials].items() if params[metallic] metallic_threshold ] return json.dumps(included, ensure_asciiFalse, indent2) if __name__ __main__: mcp.run(transportstdio)这个脚本放在任意目录下在命令行运行python server_demo.py看起来没有任何输出——别慌它正处于 stdio 监听状态等客户端来连接。接下来你在任何一个支持 MCP 的客户端里把启动命令填成python server_demo.py服务器目录填成脚本所在目录就能看到它广播出来的工具列表。我用到的数据结构极其简化但已经涵盖了工具注册、参数默认值、异常向上传递、JSON 序列化这些核心点。实际项目中你需要把这些静态数据替换成从 Blender 或其他引擎拿到的最新数据越接近实时AI 的回答越可信。4.2 工具函数的输入输出设计让模型少猜让工具多收工具函数设计得好不好直接决定了 AI 调用的成功率。我总结出一条核心经验描述要像给小学三年级学生写说明书参数要尽量给默认值输出要做成结构化 JSON。为什么描述要极其详细因为 AI 并不读你的代码它只读工具名和描述字符串。如果你给get_object_info的描述写得太短比如只写获取对象信息AI 就不确定这个信息包含哪些内容、对象 ID 从哪来容易传错参数。我后来把描述改成根据对象 ID 查询对象的详细信息包括名称、类型、变换、可见性、材质引用AI 的调用准确率一下子就上来了。参数设计也有讲究。我的原则是给每个参数定义好类型、默认值和取值范围。比如list_objects里的visible_only我给了默认False这样 AI 在用户没提只看可见对象时可以直接按默认值调用。再比如query_materials的metallic_threshold我给了0.5AI 就不需要每次都来问这个阈值应该给多少。输出格式方面我统一用json.dumps转成字符串返回。这是 MCP 工具的标准做法保证 AI 侧拿到的是一段可解析的文本。有两点要特别注意不要返回 Python 对象本身MCP 的响应层要求序列化好的数据。不要返回过于冗长的完整场景 JSONAI 的上下文窗口是有限的。我在实际工具里会做一个摘要函数把几百个对象压缩成物体名 类型 是否可见的表格而不是把位置、旋转、缩放全部铺开。4.3 错误处理与安全边界工具函数里最容易忽略的问题就是错误处理。你以为 AI 只会按你描述的样子传参数实际上它经常会给你来个 object_id-1 或者完全不存在的名字。如果不做校验服务端会直接抛异常客户端看到的是一个格式丑陋的内部错误体验很差。我在每个工具里都加了显式校验。比如get_object_info里如果找不到对象我会raise ValueError(找不到对象 ...)FastMCP 会把异常包装成结构化的 MCP 错误消息返回给客户端。这样 AI 就能理解原来是我查了一个不存在的 ID进而调整自己的调用逻辑。另外一点是安全边界。MCP server 一旦跑起来AI 就拥有了执行工具的能力这中间涉及读哪些文件、能不能执行 Python 脚本、能不能写回场景这些权限问题。我的原则是先做只读再考虑写。第一版所有工具都只读数据、不修改原场景跑稳定之后再加修改类工具而且修改类工具我特意加上了一个dry_run参数让 AI 先模拟执行确认无误后再真正落盘。5. AI 如何看懂3D 场景数据建模与智能检索的细节有了服务端骨架下一步关键就是数据建模。同一个 3D 场景直接丢给模型一个 5000 行的 JSON和丢给它一份精炼摘要得到的效果完全不一样。我的做法是建立摘要 按需查询两级数据通道这节把细节展开。5.1 场景摘要把一棵大场景树压成模型能读懂的摘要你想象一下一个文件里有几万个小物件每个物件还有几十个字段。如果 AI 一次性读全量 JSON先不提 token 够不够光是在一堆坐标里找到场景里有没有没命名材质这件事就够它晕的。所以我设计了一个get_scene_summary工具目标是让 AI 只需一次调用就能形成全局认知。其实就是一个压缩逻辑返回这些内容场景名、物体总数、对象类型分布网格多少个、灯光多少个、相机多少个顶层节点的层级树只保留名字和 ID不展开向量参数材质列表以及每个材质的关键参数金属度、粗糙度是否异常场景中明显的潜在问题比如存在 x 个无材质对象存在 y 个被隐藏的对象这个摘要我控制在 80 行以内AI 读完再决定要深入查哪个对象。我可以很负责任地说摘要的质量直接决定 AI 回答的质量。你宁可多花点时间设计摘要逻辑也别让 AI 自己去翻全部数据。摘要工具的实现思路很简单统计 裁剪数据源跟其他查询函数保持一致。如果你后面接的是真实 Blender 场景可以在此基础上加入纹理尺寸过大、面数异常等更多提示项。5.2 语义搜索与属性查询从红色的椅子到结构化查询AI 阅读自然语言的能力很强但红色的椅子这个词并不会自动变成material.color [1, 0, 0]这样的查询。想让 AI 理解这类请求需要两头配合服务端提供一套语义友好的查询参数AI 负责把自然语言翻译成结构化参数。我实现了一个search_objects工具支持按名称模糊搜索、按类型过滤、按材质参数范围过滤。比如用户说帮我找出所有 visible 的对象或者找找金属度大于 0.8 的材质AI 就会把它翻译成对应的布尔条件或阈值条件。mcp.tool() def search_objects( name_keyword: str , obj_type: str , material_name: str , only_visible: bool False, ) - str: 按条件搜索场景对象。name_keyword 支持名称子串匹配obj_type 可选 mesh/light/camera。 results [] for obj in SCENE_DATA[objects]: if name_keyword and name_keyword not in obj[name]: continue if obj_type and obj.get(type) ! obj_type: continue if material_name and material_name not in obj.get(materials, []): continue if only_visible and not obj.get(visible, False): continue results.append({id: obj[id], name: obj[name], type: obj[type]}) return json.dumps(results, ensure_asciiFalse, indent2)这个工具最实用的地方在于它把查询这件事做了归一化处理。AI 不擅长在长文本里做精确的字符串匹配但擅长提取关键词填参数。我把条件项拆开它只要做翻译即可。我实测里遇到过一个很有意思的情况用户问场景里的主光源是什么AI 先调用list_objects把对象全列出来发现类型为light的有好几个于是又调用search_objects按obj_typelight过滤最后根据intensity最大的那个给出回答。整个过程没有一个专门的查主光源函数全靠组合调用。这说明你的工具组合越灵活AI 解决问题的能力就越强。6. 联调实测与踩坑记录从连接失败到语义误判我按上面的骨架写完第一版后满心以为跑起来就能用结果联调阶段被各种问题轮番轰炸。这章挑几个最典型的坑记录一下每一个都是在真实调试中花过时间才搞明白的。6.1 打开客户端却连接不上配置与路径的坑第一个坑出现在连接阶段。我在本地装了一个支持 MCP 的客户端把启动命令填成 python server_demo.py然后点连接结果一直转圈最后报连接失败进程异常退出。排查了一会儿才意识到问题出在工作目录和 Python 模块路径上。客户端启动子进程时默认的工作目录跟脚本所在目录可能不同python server_demo.py找不到脚本文件自然就退出了。解决方法是把启动命令改为脚本的绝对路径同时把 working directory 也设为脚本所在目录。还有一个细节是FastMCP装饰器注册的工具会在首次连接时构建清单如果你的工具函数命名和变量有语法问题整个进程会在握手阶段崩溃而且报错信息不会滚动到 MCP 客户端界面只在终端一闪而过。所以联调时务必先在终端手动跑一遍python server_demo.py确认没有报错再去客户端连接。我后面所有修改都会先做这个快速自检能省下很多为什么连不上的排查时间。6.2 返回值过大撑爆上下文响应裁剪策略第二大坑是返回内容太大。第一版list_objects我把所有字段都返回了场景一多光这一个返回值就把 AI 的上下文窗口占得七七八八。客户端那边表现也很明显工具是调通了但 AI 开始忘记之前的对话内容回答越来越像复读机。后来我调整了策略默认返回只给精简字段深层次的信息按需再查。具体做法是在工具的输出层加一个detail: Literal[summary, full]参数默认选summary只返回名称、类型、可见性这些轻量字段只有用户明确询问某个对象的坐标或材质时AI 才会用get_object_info去拉完整 JSON。这一手改动之后同样的会话长度能支撑的查询深度翻了好几倍。我也顺手做了一个简单的 token 预估函数在日志里打印每次返回的大致字符数。看到单次响应超过 2000 字符我就会提醒自己该裁剪了。这个习惯对后面所有 MCP 工具都有帮助。6.3 语义误判工具描述写不好AI 就乱来第三个坑比较隐蔽属于你以为你写了但 AI 没看懂。那段时间我遇到一个怪事用户问场景里有几个材质是金属的AI 却不去调query_materials反而自己根据list_objects的结果瞎猜。我一开始以为是模型能力问题后来把工具列表打印出来一看原因特别简单——我的query_materials描述写得太学术了查询所有材质并返回金属度超过阈值的材质列表。AI 看到阈值这个词不确定阈值是什么单位、范围多少干脆放弃调用。后来我把描述改成查询材质库中所有金属材质。metallic_threshold 表示金属度下限范围为 0 到 1默认 0.5。金属度越高表示材质反射越像金属。改完之后AI 的理解瞬间到位调用成功率高了很多。这个坑给我留下一条重要规则每个工具的描述都要以向 AI 解释参数语义为目标来写而不是以向人描述函数功能为目标。另外还要注意工具之间的命名不能太相似。如果同时有get_object_info和get_object_detailAI 很容易搞混。我后来统一用了 get_object_info 和 get_object_material 这种功能边界清晰的命名组合歧义立刻少了很多。问题直观表现我的处理工作目录不对连接转圈后失败启动命令和工作目录均用绝对路径返回值过大AI 上下文被挤占忘记前置对话默认返回摘要字段详情按需查询工具描述模糊AI 不调用正确工具改为瞎猜在描述里补齐参数语义、范围、示例7. 后续玩法与个人建议让 AI 成为 3D 创作流程的编外成员联调成功后这个 MCP server 就不再只是一个玩具了它可以真正嵌入到我的 3D 工作流里。这章聊一些我后续想做的事以及给同样想入坑的开发者几条实用建议。7.1 从只读到写操作生成、修改、批处理的路径目前我的 server 只有只读查询能力但 3D 工作流里真正的价值在于写。举例来说如果我给 server 加上一个rename_selected_objects工具AI 就能按我定的规范批量重命名场景里的物体加上set_material_roughness工具AI 就能一键把一批材质的粗糙度统一调整到目标范围。我计划的路径是先把 dry-run 模式做扎实。任何写操作都先以dry_runTrue调用让 AI 生成一份我会执行的操作清单我确认没问题后再真正执行。这个设计能有效避免 AI 误解用户意图时直接改了场景数据造成事故。比如用户说帮我把地面换成焦散材质如果 AI 把地面理解成整个场景所有平面dry-run 就很容易发现意图偏差。再往后我打算接入一些程序化生成能力。比如根据一段文字描述让 AI 调用 server 里的 create_primitive_stack 工具生成一组基元物体堆叠的场景。这类功能听起来炫酷但技术路径跟查询类工具其实是相通的只是数据源从读场景变成构建场景节点并注册进场景树。7.2 给同样想做 MCP 的人几条实用建议最后给几条我踩完坑之后的真心话不一定多专业但绝对实用。第一条从小闭环开始别一上来就想搞全量场景接入。我第一次就想把完整 Blender 场景接入结果既要处理坐标转换又要处理各种材质类型折腾一周还没跑通一个简单用例。后来我把数据源换成一个只有三个对象的静态 JSON先把链路从工具定义到AI 调用跑通再逐步替换成真实数据效率高很多。第二条工具要少而精不要多而滥。MCP 的工具列表越长AI 选择工具的决策越慢、越容易出错。我给自己定的规则是每个场景最好不超过 10 个工具。如果你发现工具数量要往 20 个去了先检讨一下是不是很多工具其实是同一功能的不同参数。第三条给工具加上人会问什么的思考。做工具表的时候别只站在数据角度想要站在用户角度想。用户不会问提供 scene.objects 数组他只会问这个场景里有多少个没贴图的物体。你要把这个自然语言问题翻译成工具签名和描述这才是 MCP 工具设计里最有技术含量的事。第四条日志不要嫌多。MCP 联调的很长一段时间里我都是靠着服务端日志活着。每次用户问一个问题我都去看对应的工具调用记录看 AI 到底传了什么参数。没有这些日志你根本不知道 AI 是哪里理解错了。FastMCP 有日志钩子我建议从一开始就接上。我在实际使用中发现MCP 真正让 AI走进我的 3D 世界靠的并不是复杂算法而是把场景数据的访问权交到 AI 手里再给它一套清晰的工具说明书。这件事做完之后很多以前必须人肉操作的检查、统计、筛选工作现在一句话就能完成。如果你手上也有一堆 AI 看不懂的结构化数据不妨照着这个思路做一个自己的 MCP server从最小的场景开始跑通第一个真正有用的工具调用。那一步迈出去你会发现AI 接入专业工作流这件事其实没想象中那么远。