OpenClaw.NET 的 MCP App 实践------不只是"连上",还要"好用"
MCP 解决了"能不能连"的问题,MCP Apps 则解决"连上了怎么交互"的问题。当 AI Agent 从"调用工具"进化到"嵌入应用",老 SaaS 的客制化能力终于有了一条不烧 token 的出路。
一、场景引入:当 AI 遇到复杂业务
想象一下这个场景:
你的公司刚上线了一个 AI 助手,骄傲地接入了 ERP 系统。销售小李在企业微信里问:"帮我查一下华东区上季度的退货明细。"AI 爽快地调用了查询工具,然后------在聊天窗口里返回了 800 行 JSON。
小李懵了。他真正想做的不是"看到数据",而是"选几条走审批流程"。
更麻烦的是审批环节。他问 AI:"把退货金额超过 5 万的单子提交给张总审批。"AI 回复:"请从以下 47 个审批模板中选择:1. 标准退货审批 2. 大额退款流程 3. 跨区调拨退库......"小李翻了两屏还没看完,最后默默打开浏览器,登录 ERP 网页端,手动操作了 15 分钟。
这不是 AI 不够聪明,而是纯文本对话本身就不适合复杂的任务型交互。
选模板、填表单、点确认、传附件、勾选明细行------这些在 Web 页面上点两下就能完成的事,在聊天窗口里变成了灾难。每次多轮选择都在消耗 token,每次歧义理解都可能触发错误操作。一个原本 30 秒能完成的审批提交,在 AI 对话里可能消耗 10 轮交互、烧掉几千个 token,最后还因为理解偏差填错了审批人。
2025 年,MCP(Model 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;profile=mcp-app
这个 profile=mcp-app 标记告诉 Agent:"这不是普通网页,这是一个需要在沙箱中渲染的 MCP 应用界面。"
这里的巧妙之处在于复用了 MCP 已有的资源抽象。SaaS 厂商不需要学习新的 API 规范,只需要按照资源提供的方式返回 HTML------只不过这次的"资源"是一张可交互的界面。
沙箱 iframe + 桥接协议
Agent 宿主(比如一个支持 MCP 的企业微信机器人或 Slack 客户端)收到这个 HTML 后,会把它放进一个严格隔离的 iframe 沙箱中运行。沙箱限制了脚本访问外部 DOM、Cookie 和本地存储的能力,确保第三方 App 不会窃取对话上下文或用户凭证。
但沙箱里的界面需要和 Agent 通信------获取上下文、提交结果、触发后续工具调用。这就需要一套标准桥接协议 。MCP App 通过 window.parent.postMessage 与宿主 Agent 交换消息,协议涵盖了:
- 初始化握手与上下文传递:App 启动时获取当前会话上下文(如用户信息、租户 ID)
- UI 事件上报:按钮点击、表单提交、选项切换等事件通知 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 | 网页链接 | 私有小程序 | 传统 iframe |
|---|---|---|---|---|---|
| UI 由谁定义 | 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 文件:
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 协议的细节,只需要填好这个 JSON,OpenClaw.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 Routes
MCP App 的 UI 资源如何安全地暴露给 Agent?OpenClaw.NET 采用了一套双层代理机制:
Gateway Host Proxy :所有 ui:// 请求先经过网关层的 Host Proxy,负责身份验证、权限校验和请求路由。App 的实际服务端地址不直接暴露给 Agent 端。
Browser Routes :Agent 端通过标准化的 Browser Routes 请求 UI 资源,URL 格式统一为 /mcpapp/{app-id}/ui/{resource-path}。路由层负责把请求转发到对应的 MCP App Server。
csharp
// 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;profile=mcp-app");
});
这套机制的核心目的是隔离与代理:App 服务端运行在各自的地址空间,Agent 永远不会直接访问它们。所有通信都通过受控的代理层,安全性和可观测性都得到保障。
关键接口设计
OpenClaw.NET 定义了一个简洁的 IMcpAppInfoProvider 接口,用于向 Agent 暴露 App 的 UI 信息:
csharp
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:
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 的工具冲突。
第二步:实现 UI 资源
在服务端提供 ui://grocery/store-dashboard.html 对应的内容。这是一个标准的 HTML 页面,包含库存查询表单、数据表格和操作按钮。它通过 MCP 桥接协议与 Agent 通信------初始化时获取上下文(比如当前店铺 ID),用户提交时上报事件。
这个 HTML 页面可以用任何前端技术栈编写:React、Vue、原生 JavaScript,甚至是你的 PaaS 低代码平台自动生成的页面。唯一的约束是它需要通过 postMessage 遵循 MCP 桥接协议。
第三步:OpenClaw.NET 自动完成其余工作
OpenClaw.NET 启动时会:
- Discovery 扫描并发现这个 Manifest
- Registry 校验格式、注册元数据
- Server 建立 与
https://localhost:5001/mcp的 HTTP 连接 - ToolProvider 提取 工具列表,前缀设为
grocery. - 状态流转:Discovered → Validated → Loaded → Running
现在,当用户在 Agent 对话中说"查看库存",Agent 调用 grocery.query-inventory 获取数据,同时发现该 App 有 UI 能力。它会请求 ui://grocery/store-dashboard.html,用户在聊天窗口中直接看到库存面板------筛选、排序、导出,全部在这个嵌入界面中完成。
用户选中了几个低库存商品,点击"生成采购单",App 通过桥接协议通知 Agent,Agent 再调用 grocery.create-purchase-order 工具。整个过程无缝衔接。
三步配置,零代码改动现有业务系统。
六、展望:Agent 的下一步
从"对话+工具"到"对话+应用"
MCP Apps 代表了一个重要的范式转变。
第一代 AI Agent 是"对话+工具调用"------你说一句话,Agent 调一个函数,返回一段文本。交互是一维的、线性的。适合查天气、算数字、写邮件。
第二代 AI Agent 正在变成"对话+应用嵌入"------对话是主航道,但复杂的任务可以"弹出"一个完整的应用界面,用户在界面里完成操作后无缝回到对话流。Agent 宿主从单纯的"调度器"进化成了"应用容器"。
这个转变的意义不亚于从命令行到图形界面的跃迁。它让 AI 助手真正具备了处理复杂业务的能力------不是通过更长的 prompt 或更贵的模型,而是通过与既有业务系统的深度整合。
当 AI 遇到复杂业务时,最好的交互不是让 AI 学会所有业务逻辑,而是让 AI 知道什么时候该把界面交给专业的业务系统。
SaaS 厂商的三阶段落地路径
对于 SaaS 厂商来说,拥抱 MCP Apps 不需要一步到位:
阶段一:高频查询场景 (1-2 个月)
把最常用的数据查询界面包装成 MCP Apps。让用户在 AI 对话中直接看报表、选数据、点筛选,而不是在聊天窗口里翻 JSON。这是投入最小、用户感知最明显的起点。
阶段二:扩展能力开放 (3-6 个月)
把 PaaS 表单设计器、审批流配置界面、低代码仪表盘逐步暴露为 ui:// 资源。AI 助手终于能理解客户的客制化配置了------客户用了三年的自定义表单,原封不动地出现在 AI 对话里。
阶段三:MCP App 生态市场 (6-12 个月)
开放第三方开发者基于你的 PaaS 能力构建 MCP Apps。行业伙伴可以开发垂直领域的 MCP App,ISV 可以在你的平台上交付定制化解决方案。从 SaaS 厂商变成平台,从卖软件变成卖生态位。
成熟度模型
MCP Apps 的落地可以按四个成熟度来评估:
- 协议兼容------能注册、能发现、工具能调用,Agent 和 App 说同一种语言
- 视觉兼容------UI 在 Agent 宿主中正确渲染,不崩不卡,样式不突兀
- 行为兼容------桥接协议通信正常,数据能进能出,操作有反馈
- 体验兼容------用户感觉"这本来就是 AI 的一部分",没有割裂感,用完还想用
大多数 SaaS 厂商做到阶段二就已经能创造显著的用户价值。而 MCP Apps 的美妙之处在于:你不需要推翻现有的技术架构,不需要重写前端,只需要在正确的位置插上一根标准化的引线。
AI 不会取代 SaaS,但会用 MCP 连上 SaaS 的 AI 将重新定义 SaaS。MCP Apps 不是未来时,而是现在进行时------问题是,你的 SaaS 准备好被"切片"了吗?