场景引入:当 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 文件:

{
“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。

// 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 信息:

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.”
}

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐