OpenClaw.NET 的 MCP App 实践——不只是“连上“,还要“好用“ MCP 解决了“能不能连“的问题,MCP Apps 则解决“连上了怎么交互“的问题。当 AI Agent 从“调用工

发布时间:2026/7/19 4:10:12
OpenClaw.NET 的 MCP App 实践——不只是“连上“,还要“好用“ MCP 解决了“能不能连“的问题,MCP Apps 则解决“连上了怎么交互“的问题。当 AI Agent 从“调用工 场景引入当 AI 遇到复杂业务想象一下这个场景你的公司刚上线了一个 AI 助手骄傲地接入了 ERP 系统。销售小李在企业微信里问帮我查一下华东区上季度的退货明细。AI 爽快地调用了查询工具然后——在聊天窗口里返回了 800 行 JSON。小李懵了。他真正想做的不是看到数据而是选几条走审批流程。更麻烦的是审批环节。他问 AI把退货金额超过 5 万的单子提交给张总审批。AI 回复请从以下 47 个审批模板中选择1. 标准退货审批 2. 大额退款流程 3. 跨区调拨退库……小李翻了两屏还没看完最后默默打开浏览器登录 ERP 网页端手动操作了 15 分钟。这不是 AI 不够聪明而是纯文本对话本身就不适合复杂的任务型交互。选模板、填表单、点确认、传附件、勾选明细行——这些在 Web 页面上点两下就能完成的事在聊天窗口里变成了灾难。每次多轮选择都在消耗 token每次歧义理解都可能触发错误操作。一个原本 30 秒能完成的审批提交在 AI 对话里可能消耗 10 轮交互、烧掉几千个 token最后还因为理解偏差填错了审批人。2025 年MCPModel Context Protocol让 AI Agent 连上了万千工具。但它解决的是能不能连的问题——像 USB 统一了接口形状。当工具需要人机协作、需要复杂输入输出时MCP 的标准工具调用接口就显得力不从心了。MCP Apps 应运而生。它不是在 Agent 层再造一套 UI 框架而是让 SaaS 厂商直接把既有的扩展能力——PaaS、低代码、插件、行业包——暴露为标准化的 UI 资源。想象一下小李在企业微信里说提交退货审批对话窗口直接弹出一个 400x600 的小界面——退货单列表、审批人选择、备注输入框、提交按钮全部内嵌在聊天上下文中。表单用的是客户自己配置的审批模板字段校验和客户在 Web 端用的一模一样。点完提交界面关闭AI 继续对话“已提交 3 条退货申请给张总预计 2 小时内审批完成。”这就是 MCP Apps 要解决的问题。不是让 AI 更聪明而是让 AI 更懂得在什么时候闭嘴把界面交给业务系统。二、MCP Apps 到底是什么MCP Apps 是 MCP 规范 2026 年 1 月版本引入的正式扩展。它的核心机制可以用三个关键词概括ui:// 资源协议、沙箱 iframe 渲染、标准桥接协议。ui:// 资源把 UI 当作一种 MCP 资源在 MCP 中资源Resource通常指文档、数据库记录、日志文件——用 file:// 或 https:// 这样的 URI 标识。MCP Apps 引入了一种新类型ui://。一个 MCP App 在注册时声明自己提供 UI 能力并给出一个类似 ui://grocery/store-dashboard.html 的 URI。当 AI Agent 判断当前任务需要人机协作界面时它会向 MCP App Server 请求这个资源。Server 返回的内容类型mimeType是特殊的text/html;profilemcp-app这个 profilemcp-app 标记告诉 Agent“这不是普通网页这是一个需要在沙箱中渲染的 MCP 应用界面。”这里的巧妙之处在于复用了 MCP 已有的资源抽象。SaaS 厂商不需要学习新的 API 规范只需要按照资源提供的方式返回 HTML——只不过这次的资源是一张可交互的界面。沙箱 iframe 桥接协议Agent 宿主比如一个支持 MCP 的企业微信机器人或 Slack 客户端收到这个 HTML 后会把它放进一个严格隔离的 iframe 沙箱中运行。沙箱限制了脚本访问外部 DOM、Cookie 和本地存储的能力确保第三方 App 不会窃取对话上下文或用户凭证。但沙箱里的界面需要和 Agent 通信——获取上下文、提交结果、触发后续工具调用。这就需要一套标准桥接协议。MCP App 通过 window.parent.postMessage 与宿主 Agent 交换消息协议涵盖了初始化握手与上下文传递App 启动时获取当前会话上下文如用户信息、租户 IDUI 事件上报按钮点击、表单提交、选项切换等事件通知 Agent工具调用请求App 内的操作可以反向触发 MCP 工具调用生命周期状态同步加载中、就绪、错误、关闭等状态通知这套桥接协议是 MCP Apps 的灵魂。它让沙箱里的界面和 Agent 形成了双向通道——不是单向的展示而是真正的协作。四个不是厘清边界MCP Apps 很容易和一些既有概念混淆。明确说它不是以下四种东西不是 A2UI。 A2UI 的思路是在 Agent 层再造一套 UI 描述语言让 AI 动态生成界面。问题是 AI 生成的界面不稳定今天和明天长得不一样而且很难对接复杂的客制化逻辑。MCP Apps 则是 SaaS 厂商自己写 HTML/CSS/JS完全控制呈现逻辑。界面是开发者写的不是 AI 编的。不是网页链接。 ui:// 返回的 HTML 不直接暴露给用户浏览器而是在 Agent 宿主内渲染。它依赖 MCP 桥接协议通信脱离 Agent 环境就无法运行。你不可能把这个链接贴到浏览器地址栏里打开。不是小程序。 不需要微信/钉钉/飞书的审核和宿主支持也不绑定任何特定平台。只要有 MCP 兼容的 Agent 客户端就能运行。这意味着跨平台的真正可能性——同一个 MCP App 可以在企业微信、Slack、自建 Portal 中无缝运行。不是传统 iframe 嵌入。 虽然底层用了 iframe但它遵循 MCP 标准协议具备完整的生命周期管理、工具命名空间隔离和上下文传递机制。不是简单地在页面里嵌一个框而是在 AI 对话流中嵌入一个有身份、有状态、有协议约束的应用单元。一张表说清楚差异维度 MCP Apps A2UI 网页链接 私有小程序 传统 iframeUI 由谁定义 SaaS 开发者 AI 动态生成 任意 Web 开发者 平台审核的开发者 SaaS 开发者运行环境 MCP Agent 宿主 Agent 内部渲染 外部浏览器 企业微信/钉钉等 任意网页与 Agent 交互 标准桥接协议 原生集成 无 平台私有 JS-SDK 自定义 postMessage上下文传递 内置 内置 需手动透传 需平台适配 需自行实现部署成本 低复用现有能力 高需训练/UI 生成 中 高平台绑定 中标准程度 MCP 开放规范 厂商私有 开放 Web 标准 平台私有 无统一标准一句话总结MCP Apps 是 SaaS 厂商把既有业务界面切片成标准化组件嵌入到 AI 对话中的桥梁机制。三、为什么老 SaaS 需要它中大型企业的客制化现实做过中大型 SaaS 交付的人都知道一个事实没有一个客户会原封不动地用你的标准产品。字段要扩展——客户在标准订单上加 20 个自定义字段。表单布局要调整——采购部要求把供应商信息放在第一屏。校验逻辑要自定义——医药行业需要额外的合规性检查。审批节点要按组织架构重新画——集团有 7 级审批子公司只有 3 级。甚至整个行业模块都要替换——制造业客户要替换掉默认的库存计算逻辑。Salesforce 靠 Force.com 成了万亿级公司北森花十年从 HR SaaS 转型 PaaS 平台用友金蝶的技术架构越来越像低代码平台——这背后的逻辑是一样的软件的价值最终由客户业务逻辑定义而不是由软件厂商定义。这些客制化能力——字段扩展、低代码表单、审批流设计器、行业包/插件市场——是 SaaS 厂商花了十几年、投入 billions 积累起来的核心资产。扩展能力是另类护城河为什么 Salesforce 的客户迁移成本那么高因为客户的业务逻辑已经长进了系统里。几百条自定义校验规则、几十个审批模板、几千个扩展字段、几十个工作流——迁移这些客制化配置的成本往往比重新买一套软件还高。但 AI 来了以后这些护城河面临一个尴尬的局面大模型厂商不愿意碰扩展能力。为什么扩展能力是苦力活。它需要理解每个客户的客制化配置、需要对接复杂的表单渲染引擎、需要处理多租户的权限和数据隔离。重交付、低毛利和 AI 厂商追求的一次训练、无限复制商业模式天然冲突。结果就是AI 助手可以帮你查数据——因为查询是标准化的但很难帮你走流程——因为流程已经被客户改得和标准产品完全不同了。AI 面对客制化的 ERP就像一个会讲通用英语的人被扔进了方言区——能听懂大意但搞不定细节。MCP Apps 的桥梁价值MCP Apps 提供了一条优雅的出路SaaS 厂商不需要为 AI 重写一套界面只需要把现有的扩展能力——PaaS 页面、低代码表单、审批设计器——包装成 ui:// 资源。对客户的价值是直观的AI 助手终于能理解他们的客制化流程了。不是在对话里猜来猜去而是直接弹出客户自己配置过的业务界面——那个用了三年的审批表单、那个加了二十个自定义字段的订单页面原封不动地出现在 AI 对话里。对 SaaS 厂商的价值更深层稳定基于既有的、经过生产验证的扩展架构不是 AI 生成的不确定代码。表单校验通过了十年的生产考验不会因为模型版本更新而出错。不消耗 token复杂交互在本地 iframe 里完成不需要多轮 LLM 调用。一个五步审批流程用户点五次鼠标完成而不是和 AI 对话十轮。成本低复用现有 PaaS/低代码能力不需要额外投入 AI 工程团队。懂业务扩展配置的老人就能干好不需要prompt 工程师。老 SaaS 的客制化能力不是负担是待开采的金矿。MCP Apps 给了它一把铲子。四、OpenClaw.NET 是怎么实现的项目背景OpenClaw.NET 是一个 .NET 生态的原生 AI Agent 运行时近期在 mcpapp-ext 分支中完成了对 MCP Apps 的完整支持。选择 .NET 并非偶然——大量企业级 SaaS尤其是 ERP、CRM、HR 系统的后端正是基于 .NET 构建的。原生支持意味着这些系统可以在不引入额外技术栈的情况下直接把业务界面暴露为 MCP Apps。这不是追逐技术潮流而是务实的工程判断企业 SaaS 的扩展能力本来就写在 .NET 里何必为了 AI 再造一个轮子核心组件一条从发现到服务的流水线OpenClaw.NET 的 MCP App 支持由四个核心组件构成形成一条完整的流水线Discovery发现→ Registry注册→ Server服务→ ToolProvider工具提供McpAppDiscovery负责扫描所有 MCP App 的 Manifest 文件openclaw.mcpapp.json。支持文件系统监控和热更新——App 的 Manifest 变更后自动重新发现不需要重启服务。这意味着你可以在不中断运行的情况下新增或更新 MCP Apps。McpAppRegistry维护已发现 App 的注册表管理 App 的元数据、能力和状态。它是整个生命周期的指挥中心协调组件之间的状态同步。McpAppServer作为 MCP 协议的服务端处理 Agent 的工具调用请求和资源请求。当 Agent 请求 ui:// 资源时由 Server 返回对应的 HTML 内容。Server 同时管理着与各个 App 后端stdio 或 HTTP的长连接。McpAppNativeTool把每个 App 的工具集封装为标准的 MCP Tool注入到 Agent 的工具调用体系中。关键特性是工具命名前缀隔离——每个 App 的工具名都带有前缀如 grocery.query-inventory避免不同 App 之间的命名冲突。想象一下 20 个 MCP App 同时注册没有命名空间隔离的话query 工具来自哪个 App 就完全混乱了。Manifest一个 JSON 定义全部每个 MCP App 只需要一个 openclaw.mcpapp.json 文件{“id”: “grocery-inventory”,“name”: “Grocery Inventory Dashboard”,“transport”: “http”,“url”: “https://localhost:5001/mcp”,“hasUi”: true,“uiResourceUri”: “ui://grocery/store-dashboard.html”,“capabilities”: [“tools”, “resources”, “prompts”],“toolNamePrefix”: “grocery.”}字段含义一目了然transport 和 url 定义 MCP 连接方式支持 stdio 和 http 两种hasUi 和 uiResourceUri 声明 UI 能力capabilities 列出该 App 支持的 MCP 特性工具、资源、提示词toolNamePrefix 确保工具命名空间隔离。这个设计的简洁性是有意为之。SaaS 厂商不需要理解 MCP 协议的细节只需要填好这个 JSONOpenClaw.NET 负责处理剩下的一切。七态生命周期每个 MCP App 在 OpenClaw.NET 中经历严格的生命周期管理状态 含义 触发条件Discovered Discovery 扫描到 Manifest 文件 文件系统扫描Validated Manifest 格式和依赖校验通过 JSON Schema 校验Loaded App 的 MCP Server 连接成功 首次连接成功Running 完全可用可被 Agent 调用 工具和资源就绪Stopped 被管理员手动停用 管理操作Failed 连接失败或运行中出错 异常捕获Disabled 配置标记为禁用 配置项设置状态转换由 SemaphoreSlim 并发锁保护确保高并发场景下不会出现竞态条件。一个 App 从 Discovered 到 Running 的完整路径每一步都有日志记录和错误回退机制。UI 代理Host Proxy Browser RoutesMCP App 的 UI 资源如何安全地暴露给 AgentOpenClaw.NET 采用了一套双层代理机制Gateway Host Proxy所有 ui:// 请求先经过网关层的 Host Proxy负责身份验证、权限校验和请求路由。App 的实际服务端地址不直接暴露给 Agent 端。Browser RoutesAgent 端通过标准化的 Browser Routes 请求 UI 资源URL 格式统一为 /mcpapp/{app-id}/ui/{resource-path}。路由层负责把请求转发到对应的 MCP App Server。// Browser Routes 的核心路由配置app.MapGet(“/mcpapp/{appId}/ui/{*resourcePath}”,async (string appId, string resourcePath) {var app registry.GetApp(appId);if (!app.HasUi) return Results.NotFound();var html await proxy.FetchUiResourceAsync( app.UiResourceUri, resourcePath ); return Results.Content(html, text/html;profilemcp-app);});这套机制的核心目的是隔离与代理App 服务端运行在各自的地址空间Agent 永远不会直接访问它们。所有通信都通过受控的代理层安全性和可观测性都得到保障。关键接口设计OpenClaw.NET 定义了一个简洁的 IMcpAppInfoProvider 接口用于向 Agent 暴露 App 的 UI 信息public interface IMcpAppInfoProvider{bool HasUi { get; } // 是否提供 UI 能力string UiResourceUri { get; } // ui:// 资源地址}Agent 在工具调用前可以通过这个接口判断当前 App 是否支持界面渲染从而决定走纯文本工具调用还是UI 嵌入路径。这个判断是动态的——同一个 App 的同一个工具在某些场景下纯文本返回就够了在另一些场景下则需要弹出 UI。技术选型上回归 .NET 原生不是情怀而是务实的工程判断——企业 SaaS 的扩展能力本来就写在这里何必为了 AI 再造一个轮子五、一个真实的例子GroceryInventory理论说完了看一个从配置到运行的完整实例。假设你经营一个连锁杂货店库存系统想把库存管理面板暴露为 MCP App。第一步写 Manifest在项目的根目录或指定的扫描路径创建 openclaw.mcpapp.json{“id”: “grocery-inventory”,“name”: “Grocery Inventory Dashboard”,“transport”: “http”,“url”: “https://localhost:5001/mcp”,“hasUi”: true,“uiResourceUri”: “ui://grocery/store-dashboard.html”,“capabilities”: [“tools”, “resources”, “prompts”],“toolNamePrefix”: “grocery.”}注意 toolNamePrefix: “grocery.”——这个 App 的所有工具都会以 grocery. 开头比如 grocery.query-inventory、grocery.update-stock、grocery.check-expiry不会和其他 App 的工具冲突。