


























你让 AI 查一下库存数据,它给你吐了三页文字表格。想按品类筛选?重新说一句话。想看个饼图?再描述一遍。如果 AI 能直接给你一个可交互的仪表盘——按钮能点、图表能拖、数据实时刷新——那会怎样?
这不是想象中的未来,而是正在发生的现实。
2026 年 1 月,MCP 协议官方发布了 MCP Apps(扩展标识符 io.modelcontextprotocol/ui),正式将"交互式 UI"纳入 MCP 的能力版图。三个月后,OpenClaw.NET 在 PR #168 中引入了原生的 MCP App 支持,成为 .NET 生态中率先对 MCP Apps 协议提供完整实现的框架。
今天,我们就来聊聊 MCP Apps 究竟是什么,以及 OpenClaw.NET 是如何为 .NET 开发者铺就这条通往"交互式 AI 应用"的道路的。
相信你已经深有体会:今天的 AI 助手,无论是 Claude、ChatGPT 还是基于大模型的各种 Agent,核心交互方式都是文本对话。你问一句,AI 答一段。需要数据分析?AI 给你生成一段 Markdown 表格。需要可视化?它最多画一张静态的 ASCII 图表。
这种模式在简单场景下够用,但一旦涉及复杂操作——筛选数据、调整参数、实时预览——"对话"就成了效率的瓶颈。
开发者们很早就意识到这个问题。既然 MCP 协议已经让 AI 能够调用外部工具,为什么不进一步让工具返回的不仅是文本,而是一套完整的交互界面呢?
MCP Apps(仓库名 ext-apps)是 MCP 协议的官方扩展,由 Anthropic 于 2026 年 1 月 26 日正式发布。它的核心能力可以用一句话概括:
MCP 工具可以返回交互式 UI,直接嵌在 Claude、ChatGPT 的对话窗口里。
具体来说,对话窗口中会嵌入一个真实的 iframe,里面跑的是由 MCP Server 提供的前端代码。按钮能点、图表能拖、表单能填,数据在前后端之间双向流动。
最关键的是它的渐进增强设计哲学:支持渲染的客户端显示交互界面;不支持的客户端(比如终端工具)照样收到纯文本,不会破坏任何现有功能。
MCP Apps 的架构由三个核心角色组成:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP Server │ │ Host │ │ View │
│ │ │ │ │ │
│ • 注册工具 │◄────┤ • Claude Desktop │◄────┤ • iframe 沙箱 │
│ • 暴露资源 │ │ • ChatGPT │ │ • 前端渲染 │
│ • UI 资源声明 │ │ • VS Code │ │ • 用户交互 │
│ │ │ • 数据中转 │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
▲ │ │
└───────────────────────┴────────────────────────┘
所有通信经 Host 中转
| 角色 | 职责 | 实例 |
|---|---|---|
| MCP Server | 注册工具时在 _meta.ui.resourceUri 里指向 ui:// 资源 |
Grocery Inventory API |
| Host | 创建 iframe、负责数据搬运、通信中转 | Claude Desktop、ChatGPT、VS Code Insiders |
| View | 跑在 iframe 沙箱里的前端代码,渲染界面,反向调用 Server 工具 | 仪表盘、地图、图表组件 |
一个关键的架构约束:View 不能直接跟 MCP Server 通信,所有通信必须经过 Host 中转。这确保了安全性和可审计性。
View 并不是简单的静态页面,它拥有一整套与 MCP Server 和 Host 交互的能力:
| 能力 | 作用 |
|---|---|
tools/call |
调用 Server 的工具 |
resources/read |
读取 Server 的资源 |
ui/message |
往对话里插入消息 |
ui/update-model-context |
静默更新模型上下文(核心能力,用户在 UI 上的操作实时反馈给 LLM) |
ui/open-link |
打开外部链接 |
ui/request-display-mode |
切换显示模式:inline / fullscreen / pip |
visibility |
工具可见性控制,["app"] 标记仅 View 可调 |
其中,ui/update-model-context 是实现 Human-in-the-Loop 的核心机制。用户在 UI 上的每一次操作——点了哪个按钮、改了哪个筛选条件、选了哪个时间范围——都可以通过这条通道实时反馈给大模型,形成一个用户操作 → 更新上下文 → LLM 重新决策 → 刷新界面的完整闭环。
MCP Apps 的出现,本质上是在回答一个 AI 产品设计的核心命题:如何让 AI 干活,同时在关键节点上让人确认?
传统的 Agent 模式是"AI 决定 → 执行 → 告诉你结果",用户全程被动。而 MCP Apps 开启了一种新的范式:"AI 决定调什么工具 → 给你一个交互界面 → 你在界面上确认/调整 → 结果实时反馈给 AI 继续处理"。
这不仅仅是"更好看的输出",而是人机协作范式的升级。
MCP Apps 协议发布后,各路 SDK 和框架开始跟进。但在 .NET 生态中,率先给出完整答案的是 OpenClaw.NET。
PR #168(作者:geffzhang,已合并至 main 分支)为 OpenClaw.NET 增加了原生的 MCP App 支持。这个 PR 的规模充分体现了其工程完整度:
这个数字很有意思:4,000+ 行新代码,却没有任何破坏性变更。这说明 OpenClaw.NET 的架构设计从一开始就为扩展预留了足够的空间。
要理解 OpenClaw.NET 的 MCP App 支持,我们需要先看看它已有的 MCP 相关架构。
在 PR #168 之前,OpenClaw.NET 已经在两个维度上支持了 MCP:
| 层级 | 模块 | 方向 | 用途 |
|---|---|---|---|
| MCP Server | OpenClaw.Gateway/Mcp |
对外暴露 | 把 OpenClaw 自身作为 MCP Server,对外提供工具 |
| MCP Client | OpenClaw.Agent/Plugins |
对内消费 | 连接外部 MCP Server,消费第三方工具 |
| MCP App (新增) | OpenClaw.McpApp |
托管管理 | 发现、管理、托管第三方 MCP 应用 |
前两层解决的是"如何连接"的问题:作为 Server 被连,或作为 Client 去连别人。而新增的第三层 OpenClaw.McpApp 解决的是一个更上层的问题:如何在一个统一的框架内,大规模地管理、运行、协调多个 MCP 应用?
这不是简单的"再加一个客户端",而是一个完整的应用生命周期管理层。
让我们把三个层级放在一起,看看 OpenClaw.NET 的 MCP 架构全景:
┌─────────────────────────────────────────────────────────────────────┐
│ OpenClaw.NET 生态 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ │
│ │ OpenClaw.Gateway │ ◄── 外部 MCP Client(如 Claude Desktop) │
│ │ (MCP Server 层) │ 通过 MCP 协议连接 │
│ │ │ │
│ │ 暴露 OpenClaw 工具 │ │
│ │ 作为标准 MCP Server │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ ┌──────────────────────────────────┐ │
│ │ OpenClaw.McpApp │ │ OpenClaw.Agent/Plugins │ │
│ │ (MCP App 托管层) │ │ (MCP Client 层) │ │
│ │ │ │ │ │
│ │ • 发现 MCP App │ │ • 连接到外部 MCP Server │ │
│ │ • 管理生命周期 │ │ • 消费外部工具 │ │
│ │ • 维护连接状态 │ │ • 集成到 Agent 工作流 │ │
│ │ • 桥接为 ITool │◄────┤ │ │
│ │ │ │ 方向:Client → 外部 Server │ │
│ │ 方向:Host → 托管 App│ │ │ │
│ └──────────┬──────────┘ └──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 第三方 MCP App 生态 │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │
│ │ │库存管理 │ │系统监控 │ │地图服务 │ │PDF 查看器 │ │ │
│ │ │(grocery) │ │(system) │ │(map) │ │(pdf) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
这个架构的精妙之处在于方向性的清晰划分:
OpenClaw.McpApp 项目包含 7 个核心组件,它们共同构成了一个完整的 MCP 应用托管平台。让我们逐一拆解。
每个 MCP App 都需要一个 openclaw.mcpapp.json 清单文件,它定义了应用的基本信息、传输方式和能力声明。
{
"id": "grocery-inventory",
"name": "Grocery Inventory Manager",
"description": "Multi-store inventory management system",
"version": "1.0.0",
"transport": "http",
"url": "https://localhost:5001/mcp",
"hasUi": true,
"uiResourceUri": "ui://grocery/store-dashboard.html",
"toolNamePrefix": "grocery.",
"capabilities": ["tools", "resources", "prompts", "completions"]
}
这个设计体现了几个关键考量:
transport 支持 stdio(子进程)、http(远程端点)、inprocess(进程内),覆盖了从本地工具到远程服务的全部场景toolNamePrefix 避免了命名冲突——grocery.inventory.search 和 sales.inventory.search 可以共存hasUi + uiResourceUri 声明了交互式 UI 的能力和资源地址capabilities 显式声明支持的功能,便于 Host 做能力协商每个 MCP App 从被发现到最终运行,会经历一个严格定义的状态机:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Discovered │───►│ Validated │───►│ Loaded │
│ (被发现) │ │ (已验证) │ │ (已加载) │
└─────────────┘ └──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Disabled │ │ Running │
│ (禁用) │ │ (运行中) │
└─────────────┘ └──────┬──────┘
│
┌────────────────────┘
▼
┌─────────────┐ ┌─────────────┐
│ Stopped │◄─────│ Failed │
│ (已停止) │ │ (失败) │
└─────────────┘ └─────────────┘
| 状态 | 含义 | 转换条件 |
|---|---|---|
| Discovered | 清单文件被扫描发现 | 自动扫描触发 |
| Validated | 清单格式验证通过 | JSON Schema 校验 |
| Disabled | 被配置显式禁用 | Enabled: false |
| Loaded | 应用配置已加载 | 验证通过后自动进入 |
| Running | 连接已建立,工具可用 | McpAppServer 成功连接 |
| Stopped | 已停止运行 | 框架关闭或手动停止 |
| Failed | 连接或运行出错 | 超时、网络错误、协议错误 |
这个状态机的价值在于可观测性和可控性。在生产环境中,你可以精确知道每个 MCP App 处于什么状态,出了什么问题,而不是面对一个"连不上"的黑盒。
McpAppDiscovery 是一个递归清单扫描器,负责在指定路径下自动发现 MCP App:
// Discovery 配置示例
{
"DiscoveryPaths": ["./mcpapps"], // 扫描哪些目录
"Allow": ["grocery-*", "monitor*"], // 白名单,支持 glob 模式
"Deny": ["*-test", "deprecated-*"] // 黑名单,优先于 Allow
}
扫描器会递归搜索 **/openclaw.mcpapp.json 文件,反序列化后进行验证,再通过 allow/deny/glob 过滤规则筛选。这套机制让 MCP App 的部署变得极其简单——把应用目录扔进去,框架自己找到并加载。
McpAppServer 负责管理单个 MCP App 的连接生命周期。它的核心职责包括:
transport 类型选择连接方式(stdio 子进程 / HTTP 端点 / 进程内)tools、resources、promptsIMcpAppInfoProvider 供上层使用// 伪代码示意
class McpAppServer
{
// 幂等启动:如果已经在运行,直接返回
async Task StartAsync()
{
if (State == ConnectionState.Connected) return;
// 根据 transport 创建连接
var transport = transportType switch
{
"stdio" => new StdioTransport(executablePath),
"http" => new HttpTransport(endpointUrl),
"inprocess" => new InProcessTransport(instance),
_ => throw new NotSupportedException()
};
// 连接、握手、枚举能力
await transport.ConnectAsync();
var capabilities = await EnumerateCapabilitiesAsync();
State = ConnectionState.Connected;
}
}
这是整个架构中最关键的"胶水层"。McpAppNativeTool 将远程 MCP App 提供的工具,桥接到 OpenClaw 内部的 ITool 接口:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP App │ │ McpAppNativeTool │ │ OpenClaw Agent │
│ (远程) │◄───────►│ (桥接层) │◄───────►│ (ITool 接口) │
│ │ MCP │ │ ITool │ │
│ • search │ 协议 │ 封装远程调用 │ 协议 │ Agent 无感调用 │
│ • update │ │ 统一错误处理 │ │ │
│ • delete │ │ 参数/结果映射 │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
对于 Agent 来说,调用一个 MCP App 的工具和调用一个原生插件的工具没有任何区别。Agent 不需要知道 grocery.inventory.search 是来自一个本地函数还是一个远程 HTTP 服务——这一切都被桥接层透明地处理了。
McpAppRegistry 是整个 MCP App 子系统的"指挥中心":
McpAppDiscovery 和 McpAppServer最后,McpAppServiceExtensions 提供了一行代码完成所有注册的体验:
// Program.cs
builder.Services.AddOpenClawMcpApps(builder.Configuration.GetSection("McpApps"));
框架内部会依次注册 Discovery → Registry → Server Factory → NativeTool Factory,完全不需要手动干预。
在 appsettings.json 中新增 McpApps 配置节:
{
"McpApps": {
"Enabled": true,
"DiscoveryPaths": ["./mcpapps"],
"Allow": ["*"],
"Deny": [],
"Entries": {
"grocery-inventory": {
"Enabled": true,
"Url": "https://localhost:5001/mcp",
"ToolNamePrefix": "grocery.",
"StartupTimeoutSeconds": 15
}
}
}
}
配置分为两部分:
Enabled 总开关、DiscoveryPaths 扫描路径、Allow/Deny 过滤规则Entries 下每个 MCP App 的具体连接参数零开销设计:
McpApps.Enabled默认为false。如果你不启用这个功能,整个OpenClaw.McpApp模块不会被激活,对性能和启动时间没有任何影响。
OpenClaw Gateway 启动时,MCP App 子系统会按以下顺序执行:
appsettings.json / GatewayConfig.McpApps
│
▼
McpAppDiscovery
扫描 DiscoveryPaths(默认 ./mcpapps/)
查找 **/openclaw.mcpapp.json
反序列化 + 验证 + allow/deny 过滤
│
▼
McpAppInstallState(每个 App 一个实例)
生命周期: Discovered → Validated → [Disabled|Failed] → Loaded → Running → Stopped
│
▼
McpAppRegistry
管理全部 App 的集中注册表
调用 McpAppServer 建立连接
│
▼
McpAppServer
通过 stdio 子进程 / HTTP 端点连接 MCP App
枚举 tools、resources、prompts
生成 IMcpAppInfoProvider
│
▼
McpAppNativeTool(实现 ITool)
注册进 NativePluginRegistry
Agent 可像调用内置工具一样调用 MCP App 工具
PR #168 中包含了一个完整的示例项目 GroceryInventory.Api,这是一个多店铺库存管理 MCP App:
text/html;profile=mcp-app 规范的实时库存仪表盘// openclaw.mcpapp.json
{
"id": "grocery-inventory",
"name": "Grocery Inventory Manager",
"description": "Multi-store inventory management system",
"version": "1.0.0",
"transport": "http",
"url": "https://localhost:5001/mcp",
"hasUi": true,
"uiResourceUri": "ui://grocery/store-dashboard.html",
"toolNamePrefix": "grocery.",
"capabilities": ["tools", "resources", "prompts", "completions"]
}
当 Agent 调用 grocery.inventory.search 时,OpenClaw 会自动将请求路由到 GroceryInventory.Api 的 HTTP 端点。如果用户需要交互式操作,Agent 会返回 Dashboard 的 UI 资源 URI,由客户端渲染为可交互的 iframe 界面。
回顾整个 PR #168,有几个值得称道的工程实践:
4,000+ 行新代码,2,308 个现有测试全部通过。这说明:
McpApps.Enabled 默认为 false,模块默认不激活。这是一种成熟框架的设计哲学:新功能不应该是用户的负担。
新模块自带 43 个单元测试,覆盖了 Discovery、Lifecycle、Registry、Tool Bridging 等核心流程。这不是"为了测试而测试",而是确保一个多步骤、异步、有状态的系统在各种边界条件下都能正确工作。
技术文档的完整度直接决定了功能的采纳率。PR #168 包含了中英文双语的技术文档,降低了国内外开发者的使用门槛。
text/html;profile=mcp-app 交互式 UI 规范根据公开数据,MCP Apps 的生态正在快速扩张:
但同时也存在挑战:
OpenClaw.NET 的 MCP App 支持,为 .NET 开发者打开了一扇新的大门:
首先,你可以用熟悉的技术栈构建 MCP Apps。
ASP.NET Core 服务、Blazor 组件、SignalR 实时通信——这些你早已熟练的技术,现在可以直接用于构建面向 AI 的交互式应用。你的库存管理系统、监控仪表盘、数据可视化平台,稍加改造就能成为 MCP App。
其次,MCP App 是"AI-Native 应用"的新形态。
传统的 SaaS 是"人登录网页 → 操作系统"。MCP App 是"AI 决定调用什么工具 → 给人一个交互界面 → 人的操作反馈给 AI"。这不是简单的交互方式变化,而是应用架构的范式转移。
最后,先发优势。
OpenClaw.NET 是 .NET 生态中率先完整支持 MCP Apps 的框架。这意味着你现在就可以开始构建和实验,在这个新兴领域积累经验和影响力。
让我们回到文章开头的问题:当 AI 给你一个可交互的仪表盘,而不只是文字描述时,什么发生了变化?
答案是:决策权的分配发生了变化。
在传统 Agent 模式中,AI 是"黑盒决策 → 执行 → 输出",用户只能接受或拒绝最终结果。而在 MCP Apps 模式下:
用户提出问题
│
▼
LLM 分析意图,决定调用哪个 MCP App 工具
│
▼
MCP App 返回交互式 UI(不是文本,是界面!)
│
▼
用户在 UI 上操作:筛选、调整、确认
│
▼
ui/update-model-context 将用户操作实时反馈给 LLM
│
▼
LLM 基于更新后的上下文,继续下一步决策
│
▼
循环往复,直到任务完成
在这个循环中,AI 负责"理解意图和编排流程",人类负责"在关键节点做判断和微调"。这正是 Human-in-the-Loop 的理想形态——不是人类被 AI 替代,也不是人类全程手动操作,而是人机协作,各取所长。
MCP Apps 的出现标志着 MCP 协议从"工具调用"向"应用托管"的进化。而 OpenClaw.NET 在 PR #168 中的实现,不仅是一次功能更新,更是 .NET 生态在 AI 应用架构领域的一次重要布局。
对于 .NET 开发者来说,这是一个值得关注的信号:AI 原生应用的时代正在加速到来,而你手中的技术栈,完全有能力参与其中。
正如 MCP Apps 的设计理念——渐进增强,而非颠覆替换。最好的技术演进,永远是在已有基础上开辟新的可能。
io.modelcontextprotocol/uitext/html;profile=mcp-app关于作者:关注 .NET 生态与 AI Agent 架构,致力于用通俗易懂的方式解读前沿技术。如果你也对 OpenClaw.NET 或 MCP Apps 感兴趣,欢迎在评论区交流讨论。
版权声明:本文系原创技术文章,转载请注明出处。文章中的架构图和代码示例基于 OpenClaw.NET 开源项目,遵循项目原始许可证。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。