☰
用 CopilotKit 构建 Open MCP Client:Mastra Agent + E2B 沙箱驱动的 MCP App 生成器实战
2026/10/9 21:43:50 网站建设 项目流程

用 CopilotKit 构建 Open MCP Client:Mastra Agent + E2B 沙箱驱动的 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

本篇文章基于仓库中的 open-mcp-client 演示项目 展开,完整讲解一个可运行的MCP App 生成器(MCP App Builder)参考实现:Next.js 前端如何通过 CopilotKit v2 聊天界面驱动 Mastra Agent,后者如何在 E2B 云沙箱中按需装配运行mcp-use-server模板,并最终把生成的带 UI 的 MCP 工具以可点击、可预览、可下载的形式交付给用户。读完本文,你将掌握这个三端闭环的架构设计、环境配置、构建脚本、动态 MCP 侧边栏渲染机制以及 Render 云端部署的完整方案。

架构总览:从 Web UI 到 E2B 沙箱的完整链路

这个 monorepo 演示的是 CopilotKit 生态中"渲染 MCP Apps 与创建 MCP Apps"的完整闭环。核心组件共有三个:

  • apps/web:Next.js 前端,即MCP App Builder的 Web UI。它内置了 CopilotKit v2 聊天面板、MCP 服务器管理侧边栏、工具列表与工具预览模态框。
  • apps/mcp-use-server:运行在E2B沙箱里的 MCP 服务模板。Agent 通过预构建的 E2B 镜像快速启动沙箱,在沙箱内编写工具(tool)与小组件(widget),最终对外暴露一个标准的 MCP endpoint。
  • apps/threejs-server:本地可选的 Three.js MCP 示例(在本地运行整个项目时,它作为侧边栏的默认 MCP 服务器出现)。

三者通过一个后端路由串联:Web UI 的聊天请求发往/api/mastra-agent(实现见 apps/web/app/api/mastra-agent/route.ts),Mastra Agent 负责编排:既可以直接调用已连接的 MCP 工具,也可以调用一组内置的"工作区工具"(provision/read/write/edit/exec/restart/download)去 E2B 沙箱里生成新的 MCP 工具。

环境准备与快速开始

前置条件

  • Node.js 20+;
  • pnpm:workspace 必需(monorepo 的包管理统一用 pnpm);
  • OpenAI API Key:OPENAI_API_KEY必填,供聊天与 Agent 使用;可选OPENAI_MODEL指定模型。注意默认值存在两处差异:route.ts中代码默认值为gpt-5.2(见 route.ts),而.env.example与 render.yaml 中建议使用gpt-5.4-2026-03-05,部署或本地运行时以你实际设置的环境变量为准。

关于 Lockfile:pnpm-lock.yaml是被提交到版本库的,应当始终保留在版本控制中,以保证安装的可复现性(配合--frozen-lockfile)。本仓库的.gitignore只排除了package-lock.json、yarn.lock和bun.lockb,并不排除 pnpm 的 lockfile——这意味着不要用 npm/yarn/bun 生成自己的锁文件去污染这个 workspace。

安装与启动

从仓库根目录(即examples/showcases/open-mcp-client/)执行:

pnpm i Copy-Item .env.example .env # 编辑 .env:至少设置 OPENAI_API_KEY=sk-proj-...;如需沙箱供给再加 E2B_* 变量(见下文) pnpm dev

pnpm dev实际调用的是Turbo的turbo run dev,会并行启动 workspace 下所有配置了dev任务的包(主要是 Next.js 应用,见根 package.json 与 turbo.json)。启动完成后打开 Next 输出的地址(通常是http://localhost:3000)。

单独启动某个应用

目标命令
只启动 Web 应用pnpm --filter web dev(从仓库根目录)或cd apps/web && pnpm dev
启动 Three.js MCP 示例(本地侧边栏默认)cd apps/threejs-server && pnpm dev
启动mcp-use-server(本地 MCP,而非 E2B 镜像)cd apps/mcp-use-server && pnpm dev

注意三个子应用各自维护独立的package.json(apps/web/package.json、apps/mcp-use-server/package.json),依赖互不相同:Web 侧依赖@copilotkit/react-core、@copilotkit/runtime、@mastra/core、@mastra/mcp、@modelcontextprotocol/sdk与e2b;而mcp-use-server侧则依赖mcp-use、@openai/apps-sdk-ui与 React 19。

脚本参考

根工作区(package.json)

脚本说明
pnpm devTurbo 并发执行所有包的dev
pnpm buildTurbo 并发执行所有包的build(对web而言会先跑prebuild,见下)
pnpm lintTurbo 执行各包 lint
pnpm clean/pnpm fresh清理 installs / lockfile 的辅助脚本(fresh等价于clean && pnpm i)

apps/web

脚本说明
pnpm devNext.js 开发服务器(Turbopack)
pnpm build依次执行prebuild→pack-download-kit(生成.download-kit/base.tar.gz,供"完整 app kit"下载使用)→next build
pnpm pack-download-kit无需完整 Next 构建,单独重新生成.download-kit/base.tar.gz
pnpm start生产模式 Next 服务器
pnpm lintESLint
pnpm run test:download-kit集成测试:Next + E2B +POST /api/workspace/download
pnpm run test:e2b-download冒烟测试:仅验证 E2B tarball 产物
pnpm run dev:mcp从apps/threejs-server启动 Three.js 示例 MCP(本地 MCP 与 Web 同跑时使用)

其中pack-download-kit的实现在 scripts/pack-download-kit.mjs:它会以mcp-apps-starter为根目录名,把仓库外壳(显式跳过node_modules、.next、.turbo、dist、.vercel、.git等目录)打包为base.tar.gz。

E2B 沙箱模板(apps/mcp-use-server)

Agent 用于供给沙箱的 E2B模板定义在 template.ts。当你在该模板中修改了依赖、工具或小组件后,需要重建镜像:

脚本用途命令(仓库根目录执行)
Dev 模板(mcp-use-server-dev)日常迭代cd apps/mcp-use-server && npx tsx --env-file=../../.env build.dev.ts
Prod 模板(mcp-use-server)生产环境的稳定快照cd apps/mcp-use-server && npx tsx --env-file=../../.env build.prod.ts

两个构建脚本(build.dev.ts、build.prod.ts)都以 2 CPU / 2048MB 的规格调用Template.build,构建结束后会在控制台打印一个BuildInfo对象,其中templateId需要抄到.env的E2B_TEMPLATE(以及你的托管面板)中。注意:模板的名称(如mcp-use-server-dev)并不等于templateId,二者不要混淆。

E2B 沙箱模板:把 mcp-use-server 烘焙成镜像

模板的核心价值在于把依赖和构建产物提前烘焙进镜像,让Sandbox.create(templateId)在几秒内就能启动一个已经装好依赖、跑起服务的 MCP 服务器。从 template.ts 可以看到完整的构建链:

import { Template, waitForPort } from "e2b"; export const template = Template() .fromNodeImage("lts") // Node.js LTS 基础镜像 .setWorkdir("/home/user/workspace") // 工作目录 .copy(".", "/home/user/workspace") // 拷贝项目文件 .runCmd("npm install --no-audit --no-fund") // 把 node_modules 烤进镜像 .runCmd("npm run build") // 预构建 mcp-use 小组件 .setStartCmd( "npx tsx index.ts", // 沙箱启动时拉起服务 waitForPort(3109), // 等待 3109 端口就绪 );

mcp-use-server的入口 index.ts 会在 3109 端口启动一个MCPServer,并通过register()注册各个工具;新增小组件时按文件头注释的三步走:写resources/<widget-name>/widget.tsx、写tools/<tool-name>.ts、然后在index.ts两个标记段(// ADD NEW TOOL IMPORTS HERE与// ADD NEW TOOL REGISTRATIONS HERE)补上 import 与注册调用。

当E2B_TEMPLATE为空时,e2b.ts 会退回冷启动路径:git clone --depth 1克隆E2B_REPO_URL指定的仓库,再执行npm install与npm run dev,冷启动通常需要 60~90 秒。无论哪种路径,最终都会通过betaGetMcpUrl()(E2B 托管的 MCP URL)或回退到https://<sandbox-host>:3109/mcp拿到 endpoint,并默认给沙箱设置 60 分钟的生命周期上限(SANDBOX_TIMEOUT_MS)。

Agent 与 UI 的协作机制

聊天与建议(ChatSuggestions)

Starter prompts通过useCopilotChatSuggestions注册,实现见 ChatSuggestions.tsx,与 v2 的CopilotChat组件搭配使用。默认提供了四个建议条目(定义在 chatStarters.ts):Tic tac toe、Tip calculator、Dice roller 三个"有界的小组件构建演示",外加一个"Try Excalidraw"测试。可通过NEXT_PUBLIC_CHAT_STARTER_PROMPTS覆盖,JSON 数组格式为[{"title":"...","message":"..."}]。

构建后的测试芯片(show_mcp_test_prompts)

当 Agent 在沙箱里构建完一个新的 MCP 工具后,会调用一个纯前端 actionshow_mcp_test_prompts(实现见 McpTestPromptsAction.tsx)。该 action 接收一个{ label, message }[]的 JSON 字符串,前端解析后渲染成可点击的芯片;用户点击芯片时,通过appendMessage把对应 message 追加进同一条聊天线程,从而就地测试新生成的 MCP 工具(最多渲染 8 个芯片)。

下载:MCP-only 与 full app kit

restart_server/ 侧边栏下载能够返回完整 app kit(.tar.gz):当apps/web/.download-kit/base.tar.gz存在时(由pnpm build/prebuild生成),后端会把 E2B 工作区合并进mcp-apps-starter/骨架,产出"monorepo + 你的沙箱代码"的完整项目包;否则下载产物只包含MCP-only的工作区代码。合并逻辑实现在 merge-download-kit.ts:它把 E2B 导出的workspace/目录树替换进基础 kit 的apps/mcp-use-server位置,重新打成 gzip 流返回。下载入口路由为apps/web/app/api/workspace/download/route.ts,E2B 侧打包时会在 e2b.ts 的prepareDownload中先清理node_modules、dist、.agent等大目录,再以tar -czf归档(注释明确说明 E2B 环境常缺 GNUzip,故用 tar)。README 中关于合并细节的完整说明指向docs/HANDOFF.md。

调试 Agent 流量

在.env中设置MASTRA_AGENT_DEBUG=1,/api/mastra-agent就会输出逐请求级别的详细日志(每次请求的 MCP 加载情况、发现的 UI 工具、Agent 就绪状态等),实现在 route.ts 中,用mastraLog统一收口。

动态 MCP UI(侧边栏)

MCP 服务器管理

  • MCP servers:支持按 URL 添加/移除 MCP 服务器(可选serverId);前端维护的服务器列表会通过x-mcp-serversHTTP 头在每次请求时传给后端(/api/mastra-agent用它动态加载工具)。服务端缺省解析逻辑见 mcp-defaults.ts:无该头时回退到DEFAULT_MCP_SERVERS,再回退到内置默认Excalidraw(https://mcp.excalidraw.com,serverId: "excalidraw")。前端初始列表则来自 mcpServers.ts,由NEXT_PUBLIC_DEFAULT_MCP_SERVERS覆盖(注意这是客户端变量,必须带NEXT_PUBLIC_前缀;生产环境无托管 MCP 时可设为[],让用户自行在 UI 中添加)。
  • Tools:侧边栏以紧凑列表展示当前连接服务器发现到的全部工具;点击某个工具会在模态框中打开详情与预览(桌面与移动端共用一套ToolDetailModal,而非移动端第三个 Tab)。
  • Chat:CopilotKit v2 聊天,带建议芯片。

移动端布局

页面主布局在 page.tsx 中实现:

  • 移动端(<768px):两个 Tab——Chat与Tools(含服务器与工具列表);工具预览/详情在模态框打开;
  • 桌面端(md+):340px 固定宽度侧边栏 + 弹性聊天列(gridTemplateColumns: "340px minmax(0,1fr)");
  • 移动/桌面两套布局通过window.matchMedia("(min-width: 768px)")互斥挂载,避免同时渲染两份CopilotChat导致重复请求/api/mastra-agent;
  • 聊天 UX:专门处理了 spacing 与底部 padding,确保输入框不会遮挡最新消息;
  • 页面挂载时会尝试从localStorage(mcp_active_workspace)恢复上次的 E2B 工作区,调用/api/workspace/info校验后自动重新连上,避免刷新页面就要重新供给沙箱。

后端实现细节:/api/mastra-agent与/api/mcp-introspect

x-mcp-servers头解析与默认服务器

route.ts 的readMcpServersFromHeader解析x-mcp-servers,解析失败或缺失时回退到getDefaultMcpServers()。每个服务器配置形如{ type: "http" | "sse", url, serverId? },且会基于type + url计算一个 MD5 的serverHash用于后续工具归属定位。

MCP UI 元数据发现

fetchUIToolMetadata会对每个服务器发起 MCP 握手(SSE 用SSEClientTransport,其余用StreamableHTTPClientTransport),调用listTools()扫描工具的_meta["ui/resourceUri"],凡是声明了 UI 资源的工具都会登记为"UI 工具",并按 Mastra 的命名习惯把工具名规范为${serverId}_${tool.name}。这些信息构成了 AG-UI 中间件判断"哪个工具结果需要渲染成 App"的依据。

AG-UI 中间件与 ACTIVITY_SNAPSHOT

这是整个演示最关键的一层。createMcpUIMiddleware注册在 AG-UI 的Observable 层(而非 SSE 层),因此事件能顺利流经 CopilotKit v2 管线并触发内置的MCPAppsActivityRenderer。当某个工具返回TOOL_CALL_RESULT且该工具命中 UI 工具表时,中间件会拦截结果、把工具入参(toolInput)与结果包装成MCPAppsActivityContentSchema兼容的结构,并发射一条ACTIVITY_SNAPSHOT事件(activityType: "mcp-apps",携带resourceUri、serverHash、serverId),前端据此渲染小组件 iframe。

由于 CopilotKit runtime 在runAgent()前会调用registeredAgent.clone(),而MastraAgent.clone()会丢失.use()注册的中间件,源码中特意重写了clone()方法,让克隆体重新挂载同一份中间件——这是运行期最容易踩坑、也最值得复用的修复模式。

代理 MCP 请求与 HTML 重写

当MCPAppsActivityRenderer需要拉取小组件 HTML 时,会通过__proxiedMCPRequest把请求交给中间件代执行(支持tools/call、resources/read、notifications/message、ping四种方法)。executeProxiedMcpRequest在resources/read分支里还做了一轮CSP 安全的 HTML 修复:

  1. 从<base href="...">标签提取小组件内部的源站(如http://localhost:3109);
  2. 移除<base>标签(它会被 CSP 的base-uri 'self'拦截,且在--inline构建下本无必要);
  3. 把剩余的内部源站引用统一改写为外部 endpoint 的源站,保证 sandboxed iframe 内的图片、window.__mcpPublicUrl、window.__getFile等都能正确解析。

同样的改写逻辑也完整出现在 mcp-introspect/route.ts 中,用于离线预览。

工作区工具集

Agent 除了 MCP 工具外,还挂载了一组服务端工作区工具(全部在 route.ts 中用 zod 声明 schema):

工具作用
provision_workspace从预构建模板创建 E2B 沙箱(有模板约 3 秒),并自动清理模板默认的product-search工具、重启服务器,返回workspaceId与endpoint
read_file/write_file/edit_file在沙箱内读写/精准替换文件(edit_file支持一次多个 search/replace,逐条顺序应用)
exec在沙箱工作区执行 shell 命令(支持后台运行与超时;注意沙箱内没有fuser/lsof,查端口要用ss)
restart_server杀掉 3109 端口旧服务、后台重启npm run dev、每 5 秒轮询tools/list直到健康(最多 30 秒),失败则返回构建日志
get_workspace_info查询沙箱状态与 endpoint
download_workspace打包工作区为.tar.gz并返回签名下载 URL(约 1 小时有效)

Agent 配置上把maxSteps提到 25(默认 10),并设置了reasoningEffort: "minimal",以适配快速构建类任务。整个POST路由声明了export const maxDuration = 300,允许最长 5 分钟的 Agent 循环。

Agent 系统提示与工作流

内置的AGENT_SYSTEM_PROMPT(route.ts)为模型固化了三条典型工作流:

  • WORKFLOW A(构建新工具):provision_workspace→add_mcp_server→set_active_workspace→ 写 widget → 写 tool →edit_file注册到index.ts→restart_server→refresh_mcp_tools→show_mcp_test_prompts→ 告知用户;
  • WORKFLOW B(编辑/追加工具):跳过供给步骤,直接改文件后走restart_server → refresh_mcp_tools → show_mcp_test_prompts;
  • WORKFLOW C(使用已有 MCP 工具):直接调用工具即可,无需沙箱。

系统提示还明确约束了生成边界:优先"单工具 + 单小组件",不新增 npm 依赖,避免流程图/节点图/无限画布等重度需求(除非用户明确要求),并在需求模糊时先问一句澄清。工具文件的模板要求每个 widget 工具都必须带上_meta["ui/previewData"](对象形状需与小组件 props 一致),否则 Studio 没有演示预览。

消息 ID 去重

由于 Mastra 会复用同一个messageId同时作为TOOL_CALL_START.parentMessageId与TEXT_MESSAGE_*的messageId,CopilotKit 会根据两套事件各建一条消息,导致 React 侧出现重复 key。中间件用三张映射表(usedAsParentId、currentTextRemap、parentRemap)对冲突的messageId/parentMessageId重新生成 UUID,从事件流源头消除重复。

mcp-introspect:工具内省与 UI 预览

apps/web/app/api/mcp-introspect/route.ts 提供一个独立的POST { endpoint }接口,供前端钩子 useMcpIntrospect.ts 使用:

  1. 连接优先尝试Streamable HTTP,失败自动回退SSE;
  2. 分页调用listTools(),提取每个工具的name、description、inputSchema、_meta、hasUI、uiResourceUri、uiPreviewData;
  3. 对有 UI 的工具调用readResource拉取 HTML,并应用与代理层相同的<base>标签/内部源站改写;
  4. 最后列出全部原始资源,返回{ tools, resources }。

该接口的超时上限同样是 300 秒,因为工具枚举与 HTML 拉取可能较慢。侧边栏的工具列表、UI/Local/Modified徽标(见 page.tsx 中的渲染逻辑)都来自这条内省链路。

在 Render 上托管

Blueprint 部署步骤

  1. 把仓库推送到 GitHub/GitLab;
  2. 在 Render 控制台进入Blueprints,选择你的仓库——Render 会自动识别根目录的 render.yaml;
  3. 在面板中设置机密环境变量:至少OPENAI_API_KEY;需要沙箱时再加E2B_API_KEY与E2B_TEMPLATE;
  4. 部署。Blueprint 会自动配置构建/启动命令、NODE_VERSION与HOSTNAME。

从 render.yaml 可以看到:服务类型为web、runtime 为docker、plan 为standard,E2B_REPO_URL与OPENAI_MODEL(gpt-5.4-2026-03-05)带有默认值,其余均为sync: false的机密变量,需要在面板单独注入。此外还有一个配套的 Dockerfile 可供自托管。

长期运行进程说明

Render 运行的是常驻 Node.js 进程而非 serverless 函数,因此不存在逐函数超时限制——这也与maxDuration = 300的长 Agent 循环设计相匹配:在 serverless 平台上 5 分钟上限是硬约束,而 Render 上可以跑更久。

总结

Open MCP Client 演示项目把 CopilotKit 的前端 Agent 编排能力、Mastra 的工具调用生态、E2B 的云端沙箱供给和mcp-use-server的 UI 工具运行时整合在了一个可运行的 monorepo 里。从本仓库源码可以清晰看到一条可复用的工程链路:前端x-mcp-servers头声明服务器列表 → Mastra Agent 动态加载 MCP 工具并执行工作区工具 → AG-UI 中间件把工具结果转换为ACTIVITY_SNAPSHOT→ CopilotKit v2 渲染小组件 → 一键打包下载完整项目。若要在自己的项目中复刻这套模式,建议按顺序吃透三个文件:route.ts(Agent 与中间件)、template.ts(沙箱镜像)与 page.tsx(前端布局与状态管理)。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询