CopilotKit 实战用 Three.js 构建可流式预览的 MCP App 三维场景服务端【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit本篇指南以仓库中的 examples/integrations/mcp-apps/threejs-server/README.md 为主体围绕一个可直接运行的 Three.js MCP 服务示例展开它把 Three.js 的 3D 渲染能力包装成 MCPModel Context Protocol工具并配有一个 React 前端组件支持边生成代码、边流式预览场景。读完本文你将掌握该示例的运行方法、两个工具show_threejs_scene与learn_threejs的输入输出契约、可用的 Three.js 全局变量以及流式预览在前端是如何实现的——并能够以此为模板快速搭建自己的代码生成型 MCP App。示例概览一个交互式 3D 场景渲染器threejs-server是一个完整的 MCP App 示例。所谓 MCP App指的是一个同时具备服务端工具与**前端 UIApp**的 MCP 应用Agent 调用服务端暴露的工具服务端通过资源Resource把前端界面交付给支持 MCP Apps 的宿主host前端组件再根据工具输入在宿主内完成渲染。该示例的核心价值在于交互式 3D 渲染Agent 可以通过写 JavaScript 代码来创建并动画化 Three.js 场景流式预览在代码尚未生成完毕时前端就能一边收到不完整的输入partial input一边实时展示场景构建过程内置助手预置了OrbitControls相机控制、后处理特效Bloom 泛光与渲染通道RenderPass文档工具learn_threejs按需返回 Three.js API 文档与代码示例帮助 Agent 写出符合规范的代码。整个示例的代码结构如下仓库相对路径server.ts —— MCP 服务端注册工具与 UI 资源server-utils.ts —— Streamable HTTP 传输的通用启动工具src/threejs-app.tsx —— 实际渲染 3D 场景的 React 组件src/mcp-app-wrapper.tsx —— 负责建立 MCP App 连接的通用包装层test-input.json —— 用于测试工具输入的 JSON 样例mcp-app.html —— Vite 构建入口 HTML。快速运行安装依赖在示例目录下安装依赖npm install该示例依赖的运行时关键包见 package.jsonmodelcontextprotocol/sdkMCP SDK示例锁定^1.24.0、modelcontextprotocol/ext-appsMCP Apps 扩展^0.4.0、three^0.181.0即文档中THREE全局变量对应的版本、react/react-dom^19.2.0以及用于输入校验的zod^4.1.13。构建并启动服务npm run start:http # Streamable HTTP 传输 # 或 npm run start:stdio # stdio 传输两条命令都会先执行npm run build对应 package.json 中的start:http/start:stdio脚本构建产物输出到dist/目录。start:http最终通过bun --watch server.ts启动 HTTP 服务默认监听端口由环境变量PORT控制未设置时取3108见 server.ts 中的process.env.PORT ?? 3108start:stdio则以bun server.ts --stdio启动服务端检测到--stdio参数后改用 StdioServerTransport见 server-utils.ts 与 server.ts 的--stdio分支。注意该示例的启动脚本使用bun作为运行时见 package.json 的serve:http/serve:stdio运行前需确认环境已安装 Bun。在宿主中查看使用basic-host示例来自 MCP Apps 生态的兼容宿主或其他支持 MCP Apps 的宿主连接即可。启动后HTTP 模式下服务端会打印Three.js Server listening on http://localhost:3108/mcp/mcp端点即为 Streamable HTTP 的接入地址。测试工具输入将 test-input.json 的内容复制到basic-host的工具输入tool input输入框中即可验证show_threejs_scene工具。该输入构造了一个带旋转立方体的简单场景完整代码见下文示例其中code字段即要执行的 Three.js 代码。工具契约show_threejs_scene 与 learn_threejs服务端 server.ts 的createServer()函数创建了一个名为Three.js Server、版本1.0.0的McpServer实例并注册两个工具。Tool 1show_threejs_scene通过registerAppTool注册来自modelcontextprotocol/ext-apps/server它既是一个普通工具也携带 UI 元数据_meta.ui.resourceUri指向ui://threejs/mcp-app.html因此属于UI 工具——Agent 调用它后宿主会加载并展示对应的前端组件。其输入输出定义如下字段类型默认值说明codestringDEFAULT_THREEJS_CODE要渲染 3D 场景的 JavaScript 代码heightnumber整数正数400渲染画布高度像素输出字段类型说明codestring透传的代码字符串heightnumber透传的高度值从源码看工具的执行函数并不会真的去运行 Three.js 代码而是把{ code, height }序列化后同时放入content文本与structuredContent结构化数据返回server.ts。实际渲染发生在前端组件中宿主收到结构化输出后将其作为工具输入传给 App 组件执行。服务端还为该工具维护了一份兜底的默认代码DEFAULT_THREEJS_CODEserver.ts当 Agent 未提供code时使用内容是一个深色背景、旋转立方体加双向照明的演示场景。前端组件内部也维护了一份略有不同的默认代码见 src/threejs-app.tsx当组件未收到任何代码输入时使用。Tool 2learn_threejs通过标准的server.registerTool注册不是 UI 工具只是返回文档文本。它没有输入参数调用后返回一段内置的 Markdown 文档THREEJS_DOCUMENTATION见 server.ts内容包含可用全局变量清单、基础模板、旋转立方体示例、OrbitControls 交互示例以及若干编码建议。该工具的价值在于让 Agent 在生成场景代码前先查阅文档从而写出与示例约束如光照强度 ≤ 1、深色背景一致的代码。UI 资源ui://threejs/mcp-app.htmlregisterAppResource将资源 URIui://threejs/mcp-app.html映射到构建产物dist/mcp-app.htmlserver.tsMIME 类型为RESOURCE_MIME_TYPE。这个 HTML 正是由 Vite 打包出的单文件前端见 vite.config.ts 使用vite-plugin-singlefile入口由环境变量INPUTmcp-app.html指定内部挂载src/mcp-app-wrapper.tsx。测试输入示例一个带阴影的旋转立方体场景test-input.json 对应的可读版本如下。它创建一个透视相机、启用阴影的渲染器、旋转立方体与地面、一盏投射阴影的平行光与一盏环境光const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera(60, width / height, 0.1, 100); camera.position.set(2, 2, 2); const renderer new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setSize(width, height); renderer.shadowMap.enabled true; const cube new THREE.Mesh( new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({ color: 0x00ff88 }), ); cube.castShadow true; cube.position.y 0.5; scene.add(cube); const floor new THREE.Mesh( new THREE.PlaneGeometry(5, 5), new THREE.MeshStandardMaterial({ color: 0x222233 }), ); floor.rotation.x -Math.PI / 2; floor.receiveShadow true; scene.add(floor); const light new THREE.DirectionalLight(0xffffff, 2); light.position.set(3, 5, 3); light.castShadow true; scene.add(light); scene.add(new THREE.AmbientLight(0x404040)); function animate() { requestAnimationFrame(animate); cube.rotation.y 0.01; renderer.render(scene, camera); } animate();几个关键点代码中直接使用width、height、canvas三个全局变量无需自行创建画布或计算尺寸演示了阴影配置的完整链路渲染器开启shadowMap→ 网格体castShadow→ 地面receiveShadow→ 灯光castShadowPerspectiveCamera(60, width / height, 0.1, 100)的宽高比直接由全局width/height提供相机参数与 README 中的示例一致。可用的 Three.js 全局变量编写自定义代码时以下全局变量在代码执行环境中始终可用THREE; // Three.js 库r181 canvas; // 预创建的 canvas 元素 width; // canvas 宽度像素 height; // canvas 高度像素 OrbitControls; // 相机控制three/examples/jsm/controls/OrbitControls.js EffectComposer; // 后处理合成器 RenderPass; // 渲染通道 UnrealBloomPass; // Bloom 泛光特效从源码可以确认这些全局变量的注入方式src/threejs-app.tsx 的executeThreeCode使用new Function(ctx, canvas, width, height, ...)构造执行函数其中THREE、OrbitControls、EffectComposer、RenderPass、UnrealBloomPass通过ctx解构传入来自threeContext对象import 自three包及其 examples/jsm 子路径canvas、width、height作为独立参数传入width取自组件容器的offsetWidth取不到时回退为800height取自工具输入默认400。因此你的自定义代码可以直接引用这些标识符不需要任何 import 语句。架构剖析整个示例可以拆成服务端 前端 App两层它们通过 MCP Apps 协议协作。服务端server.ts暴露两个工具show_threejs_scene渲染 3D 场景与learn_threejs返回文档通过资源ui://threejs/mcp-app.html提供 UI同时支持Streamable HTTP与stdio两种传输方式。服务端还遵循一个值得注意的模式createServer()是一个工厂函数每次请求都新建独立的McpServer实例。这在 server.ts 的注释中说明了原因McpServer只支持一个 transport所以每个 HTTP 会话都需要自己的实例。配合 server-utils.ts 中sessionIdGenerator: undefined无状态模式的配置每次/mcp请求都会createServer()→new StreamableHTTPServerTransport()→server.connect(transport)→ 处理请求并在响应关闭时清理传输与服务端res.on(close, ...)。这个工厂 无状态模式可以直接复用到其他 MCP 服务中。Appsrc/threejs-app.tsxReact 组件ThreeJSApp从包装层 src/mcp-app-wrapper.tsx 接收全部 MCP App 属性属性类型含义toolInputsTToolInput \| null完整工具输入流式结束后toolInputsPartialTToolInput \| null部分工具输入流式进行中toolResultCallToolResult \| null服务端工具执行结果hostContextMcpUiHostContext \| null宿主上下文主题、尺寸、locale 等callServerToolApp[callServerTool]调用服务端工具sendMessage/openLink/sendLog回调与宿主聊天、打开链接、发送日志组件行为分两个阶段流式预览阶段当toolInputs尚为null而toolInputsPartial有值时isStreaming !toolInputs !!toolInputsPartial组件渲染一个LoadingShimmer占位块——一个带 shimmer 动画的深色面板内部实时滚动展示已到达的toolInputsPartial.code片段useEffect中把pre的scrollTop设为scrollHeight实现自动滚动到底部视觉上就是代码一边生成、预览面板一边在长的效果执行阶段流式结束后toolInputs.code作为最终代码交给executeThreeCode执行useEffect依赖[code, height]渲染到预创建的canvas元素上画布高度由工具输入中的height决定、宽度自适应容器若执行抛出异常则在画布上方显示error-overlay错误层。此外组件还读取hostContext.safeAreaInsets作为容器内边距适配宿主的刘海屏/安全区并会从app.getHostContext()拉取初始宿主上下文见 src/mcp-app-wrapper.tsx。WidgetProps这个通用接口被定义在包装层中注释明确说明该接口可复用于其他 widget意味着你写新的 MCP App 组件时可以沿用同一套属性约定。编写自定义场景代码的实用建议结合服务端内置文档THREEJS_DOCUMENTATION与示例代码可以沉淀出几条直接可用的编码约定总是设置深色背景调用renderer.setClearColor(0x1a1a2e)或类似深色避免默认白色背景造成画面过曝光照强度控制在 1 或以下内置文档建议Keep light intensity ≤ 1过强的平行光会让MeshStandardMaterial表面被洗白示例中也有使用0x8888ff补光灯与AmbientLight(0x404040)的搭配形成主光 补光 环境光的三光源方案优先使用MeshStandardMaterial以获得真实光照表现动画使用requestAnimationFrame配合controls.update()若使用 OrbitControls驱动渲染循环利用learn_threejs工具当不确定某个 Three.js API 的写法时先让 Agent 调用该工具获取文档与示例再生成代码。从示例到自己的 MCP App如果你想把该模式复用到自己的可视化场景可以按以下思路迁移服务端仿照 server.ts 的createServer()用registerAppTool注册你的执行型工具并让_meta.ui.resourceUri指向自己的单文件 HTML 资源用registerAppResource暴露dist/*.html前端复用 src/mcp-app-wrapper.tsx 的通用包装层连接useApp、订阅ontoolinput/ontoolinputpartial/ontoolresult/onhostcontextchanged把WidgetProps传给自己的组件构建沿用 vite.config.ts 的单文件构建配置vite-plugin-singlefileINPUT环境变量确保mcp-app.html能被打包成自包含资源协议适配需要 HTTP 时复制 server-utils.ts 的无状态 Streamable HTTP 启动器需要进程内/本地集成时使用--stdio分支。这样你就能快速产出Agent 写代码 → 宿主实时预览 → 场景动态渲染的完整 MCP App 链路而 threejs-server 正是这一模式最小可用的参考实现。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考