OpenWork Native MCP Apps 远程接入指南:标准 MCP 服务器驱动的 App 宿主架构与安全边界
【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork
Native MCP Apps 是 OpenWork 中由标准 MCP 服务器交付的应用形态:服务器通过 OpenWork Connect 接入,宿主(Desktop)识别io.modelcontextprotocol/ui扩展与工具上的_meta.ui.resourceUri绑定,经resources/read读取 UI 资源,再通过标准 MCP Apps 桥接通道传递工具输入与结果。本文以 docs/features/remote-mcp-apps/README.md 为核心骨架,结合 apps/server/src/mcp-app-host.ts、ee/apps/den-api/src/mcp/connect-mcp-server-index.ts 等源码,完整讲解其工作链路、凭据模型、滚动发布现状、安全边界,以及一套可复现的本地演示与回归验证流程。读完本文,你将掌握如何基于 Connect 连接接入并验证一个 Native MCP App,理解为何它不依赖独立 URL App 安装,以及 App 宿主凭据如何被严格限定在受控代理面上。
什么是 Native MCP Apps
OpenWork 的 MCP Apps 由标准 MCP 服务器交付,这些服务器通过 OpenWork Connect 接入。服务器声明稳定的io.modelcontextprotocol/ui扩展,工具通过_meta.ui.resourceUri精确绑定一个 UI 资源,宿主使用resources/read读取该资源,工具输入与结果则经由标准 MCP Apps 桥接通道传递。
该功能单元不包含从 HTML URL 安装独立 App 的能力:URL 导入的 MCP Apps 属于另行规划的未来工作,拥有独立的产品、安全、生命周期与发布契约。同样地,Workflow 仍然是可执行的workflow配置对象;Workflow 生成的视图可以是 MCP 资源,但这并不把 Workflow 执行变成资源加载或独立 URL-App 安装路径。
在源码层面,这一协议由 apps/server/src/mcp-app-host.ts 中的常量固定:
const MCP_APP_EXTENSION = "io.modelcontextprotocol/ui"; const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app"; const MCP_PROTOCOL_VERSION = "2025-06-18";工具侧的绑定解析在toolUiResourceUri(apps/server/src/mcp-app-host.ts)中完成:它读取_meta.ui.resourceUri(并兼容历史字段meta["ui/resourceUri"]),强制要求资源 URI 以ui://开头,否则抛出invalid_resource_uri。
标准 MCP 服务器接入路径
Connect 继续负责服务器配置、认证、访问授权、按成员凭据与工具策略。OpenWork Cloud 控制服务器在以下位置发布按成员(member-scoped)的资源索引:
openwork://connect/mcp-servers/index.json这一 URI 在 ee/apps/den-api/src/mcp/connect-mcp-server-index.ts 中定义(CONNECT_MCP_SERVER_INDEX_URI)。索引读取方能看到哪些成员可用连接,取决于成员面向的 MCP 连接是否启用——源码中的memberFacingMcpConnectionsEnabled判断正是这一成员级可见性控制的实现。
双重凭据模型:普通模型凭据 vs App-host 凭据
签名后的 Desktop 会话会额外铸造一枚短时、带非公开mcp:app-hostscope 的 App-host 凭据。Desktop 只把这枚凭据与端点描述符存放在私有的 App-host 状态中,两者都不会投影进 OpenCode。Desktop 使用该凭据读取索引,并对外声明mcp-app-host-v1客户端能力。
Den 只有在以下四个条件同时满足时才返回非空的 provider 索引:
- 服务器校验过的 scope(
mcp:app-host); - 客户端声明的
mcp-app-host-v1能力; - 与两个发布闸门(rollout gates)通过。
普通模型或遗留 MCP 令牌无法通过伪造 audience 或能力头来解锁索引。在源码中,客户端能力头与能力值定义于 apps/server/src/connect-mcp-server-catalog.ts:
export const CONNECT_MCP_APP_HOST_CAPABILITY_HEADER = "x-openwork-mcp-client-capabilities"; export const CONNECT_MCP_APP_HOST_CAPABILITY = "mcp-app-host-v1"; export const CONNECT_MCP_APP_HOST_NAME_PREFIX = "openwork-app-host-connect-";App-host 连接的名字由该前缀加摘要生成(connectMcpAppHostName,见 apps/server/src/connect-mcp-server-catalog.ts),索引请求会带上能力头(同文件第 336 行)。
Desktop从不把openwork-connect-*条目写入 OpenCode 运行时或任何模型可见的 MCP registry。连接在以下路径被代理:
/mcp/agent/connections/{connectionId}模型侧表面:search_capabilities / execute_capability
当前 Desktop 客户端不会在 OpenCode 中注册这些 provider 描述符;模型只能通过中央openwork-cloud的search_capabilities与execute_capability工具发现并调用普通的 provider 操作。这两枚工具的语义在 ee/apps/den-api/src/mcp/agent.ts 中有详细的内置指令约束:只对search_capabilities返回的精确名称调用execute_capability;kind: mcp_app的匹配项代表来自已连接 MCP 服务器的标准 MCP App,应通过execute_capability执行并让兼容宿主渲染其ui://资源,而不要导入或转换成独立 HTML URL。
对于仍保留旧版逐连接条目的历史发布客户端,代理仅暴露名为search_capabilities与execute_capability的兼容对:
- 绝不返回 provider 目录、MCP App 启动工具或元数据、资源或模板;
- 拒绝一切直接 provider 调用;
- 保证普通操作在旧条目被对账(reconciliation)移除期间保持有界。
App 宿主的受限传输面
Desktop 的本地 App 宿主使用私有的带 scope 凭据认证。只有该传输能收到:
- 带合法
_meta.ui.resourceUri、且 provider 声明的可见性包含app的工具; - 限定到发起服务器的、App 可见的
search_capabilities与execute_capability。
因此,Native MCP App 可以渲染并使用来自其常规 MCP 服务器的已授权工具,同时无需把 provider 目录放进模型请求。
App 宿主视图还逐项保留以下语义(见 apps/server/src/mcp-app-host.ts 中的McpAppResource与宿主实现):
- App 工具的精确名称、输入/输出 schema、注解与 UI 绑定;
- 仅针对被暴露 App 工具绑定的资源的具体描述符与
resources/read内容; tools/call返回的content、structuredContent、_meta与isError;- 稳定的 MCP Apps 扩展与
text/html;profile=mcp-app资源; - 每个 Connect 连接一个服务器身份,保持同服务器工具调用的边界。
OpenWork 的访问授权、禁用工具策略与审批规则仍然在代理边界生效。App-host 凭据只授权这个有界的代理面;它不是provider 凭据,不授予任何直接跨服务器访问权限。
发布状态与滚动(Rollout)
Native MCP Apps 对所有部署与组织启用。旧的部署级闸门DEN_REMOTE_MCP_APPS_ENABLED与组织级Native MCP Apps (preview)能力在功能稳定后已被移除,历史存储的组织覆盖值会被忽略。
App 启动元数据(kind: mcp_app、mcpApp.resourceUri、openwork/mcpAppmeta)作为不透明绑定发布在每次有界的 search/execute 结果上;不承载 App 的客户端会忽略它并保留普通工具结果。私有 App-host 索引与逐连接 provider 代理仍以客户端声明的 App-host 能力头为闸门,因此旧版 Desktop 客户端保持有界的 search/execute 表面。对账(reconciliation)同时会移除并断开陈旧的openwork-connect-*OpenCode 条目,同时保留用户自建 MCP 与全部持久的 Connect 记录。
provider 代理声明listChanged: false,因为当前企业连接器打开的是有界请求会话而非持久的 downstream 通知流。目录刷新发生在以下时机之一:
- Connect 对账(reconciliation);
- Desktop 启动;
- 引擎刷新;
- 显式触发 Cloud MCP 刷新。
转发 downstream 的 list-change 通知被列为后续互操作性工作。
安全与兼容性边界(Host 侧)
Desktop 的 App 宿主(apps/server/src/mcp-app-host.ts)执行一系列强约束:
- 协商稳定扩展,解析当前工具定义;
- 即使资源不在
resources/list中,也精确读取对应的ui://资源; - 接受 text 或 base64 编码的 HTML;
- 强制 MIME 与大小限制(源码中的
MAX_RESOURCE_BYTES = 768 * 1024、MAX_RESULT_BYTES = 1024 * 1024); - 校验 CSP 源(
McpAppCsp的connectDomains、resourceDomains、frameDomains、baseUriDomains); - 通过隔离的沙箱代理加载文档;
- 初始化后发送工具输入与保留的工具结果,限制尺寸变化,卸载时拆除桥接;
- 对解析、握手、文档与运行时故障进行隔离处理,同时不隐藏普通工具结果。
App 请求的工具只在发起它的常规 MCP 服务器上解析,并且必须对 App 可见。当工具携带_meta.ui.resourceUri时,Desktop 还要求它与调用 iframe 中加载的资源精确匹配,工作区(Workspace)拒绝策略同样适用:
- 只读的能力搜索直接执行;
- 能力执行使用保守的变更注解(mutation annotations),并要求用户确认;
- provider 授权与审计仍在服务器端执行;
- 不允许跨服务器的 iframe 调用。
启动上下文还受到有界生命周期的约束:MAX_LIVE_LAUNCHES = 256、LAUNCH_TTL_MS = 30 * 60_000(30 分钟 TTL),过期或关闭的启动返回stale_launch_context。凭据与私有修订号只存在于宿主侧,绝不进入资源响应(launchFingerprint使用 sha256 摘要绑定配置、托管身份、运行时修订与私有凭据修订)。
延后项:独立 URL 导入的 Apps
安装来自 URL 的自包含 HTML App 被有意排除在当前变更之外。在当前产品中:
- Den Web 没有 Add MCP App 按钮、URL 表单、已安装 App 详情页或 URL-App 生命周期入口;
- 中央 MCP 服务器不注册
import_remote_mcp_app或任何独立 App 启动工具; - 能力搜索不返回独立 URL-App 匹配;
- 模型与 App-host 目录不包含独立 URL-App 工具;
- 不注册任何
ui://openwork/library-apps/...资源; - 成员服务器索引与启动元数据不包含独立 URL Apps;
/v1/remote-mcp-apps下的 REST 调用未注册,因此不可用。
既有数据库行与早期开发留下的缓存修订会以非破坏方式保留,但在 UI、MCP 目录、能力搜索、资源、启动元数据与 HTTP API 中均不可达、不活跃。该变更不执行任何删除,也不引入破坏性迁移。保留的存储与校验实现不是受支持的运行时表面;未来的独立 URL-App 单元必须自行恢复其 API、UI、安全评审、生命周期、测试与发布契约。
本地演示与验证(demo runbook)
仓库提供了一个确定性本地 fixture 来验证 Native MCP Apps 路径,见 docs/features/remote-mcp-apps/demo-runbook.md。它不依赖托管的画廊、生产环境发布开关、客户连接器或个人凭据。
演示 fixture:Project Atlas
所需 fixture 来自evals/specs/remote-mcp-apps.e2e.test.ts:它是一个本地 Streamable HTTP MCP 服务器,暴露ui://project-atlas/view.html,并提供同服务器的search_projects操作。演示会启动真实的本地 Den API 与 Web 进程、真实的 Electron Desktop/local-server 进程、MySQL、Redis 与一个合成模型服务器。
前置条件
- Node.js 24 与 pnpm 11.4.0(仓库声明的版本);
- Bun、Docker 与一个可用的本地 MySQL 端口(
3306); - shell 中不要有真实的模型/provider 凭据。
从被测分支的全新检出开始:
pnpm install --frozen-lockfile pnpm --dir evals install --frozen-lockfile pnpm --filter @openwork/types build pnpm --filter @openwork-ee/den-db build pnpm --filter @openwork/email build pnpm dev:den:mysql运行精确 HEAD 的演示 tape
OPENWORK_EVAL_E2E_TESTS=1 pnpm evals:e2e remote-mcp-apps一份有效的必需证明以"一个通过、零失败、零跳过"且"verdict":"passed"结尾。请把 runner 打印的 JSON 报告路径与本地验证记录一并保存。
演示 tape 的执行序列:
- 启动隔离的 Den 组织、合成成员、本地 Project Atlas MCP 服务器与 Desktop profile;
- 将 Project Atlas 添加为组织 Connect MCP 服务器并等待其就绪;
- 确认模型可见表面只包含中央
search_capabilities与execute_capability工具; - 确认陈旧的逐连接兼容入口只暴露同样的有界对,且无资源/模板/App 元数据、无直接 provider 工具;
- 确认私有 App 宿主只收到发起服务器的 App 可见工具与精确的
ui://project-atlas/view.html资源; - 通过发起服务器的 App-host 能力对搜索并执行
search_projects,观察合成 Atlas 迁移结果; - 让合成模型返回绑定的工具结果,在 Desktop 中渲染 App、重载并确认再次渲染;
- 使用同一隔离 profile 重启 Desktop、重访会话,确认 App 可恢复。
Native MCP Apps 对所有部署与组织启用,演示无需翻转任何部署或组织闸门。
聚焦的安全与兼容性检查
cd apps/server bun --conditions=development test \ src/connect-mcp-server-catalog.test.ts \ src/mcp-app-host.test.ts \ src/mcp-app-sandbox.test.ts \ src/cloud-mcp-reconcile.e2e.test.ts cd ../app bun test \ tests/den-mcp-url.test.ts \ tests/mcp-app-frame.test.ts \ tests/session-mcp-maintenance.test.ts \ tests/cloud-mcp-maintenance-gate.test.ts cd ../../ee/apps/den-api bun test \ test/remote-mcp-app-rollout.test.ts \ test/external-connection-proxy.test.ts \ test/admin-organization-capabilities.test.ts \ test/organization-capabilities.test.ts这些检查覆盖:精确可信源匹配、跨源拒绝、绑定源的 App-host 凭据、App-host 凭据隐私、沙箱/CSP 执行、同服务器绑定、发布默认值、无 App-host 凭据的旧响应、有界陈旧客户端兼容,以及禁用 Apps 时普通 Connect 的保留。
预期失败类别
- 跳过(skipped)的 tape 是不完整的证明:检查同意(consent)环境变量、本地 placement 与 MySQL 可用性;
missing local server credentials是 Desktop 启动/就绪问题;- 资源错误会指出具体 URI、MIME、大小或 CSP 校验失败;
- 源(origin)错误表示 Den/App-host 端点对不是精确的可信源匹配;
- 工具拒绝表示错误服务器路由、App 可见性、工作区策略、provider 授权或必需的变更确认缺失;
- 不声明私有 App-host 能力的客户端保持有界 search/execute 表面——这是预期行为,不是资源加载失败。
托管的 SOL 画廊可以单独作为外部观察检查,但其可达性不是OpenWork 兼容性证明,也不属于本必需演示。
从文档到源码:关键结论速览
| 主题 | 文档要点 | 源码佐证 |
|---|---|---|
| 协议扩展 | io.modelcontextprotocol/ui与text/html;profile=mcp-app | apps/server/src/mcp-app-host.ts |
| 成员索引 URI | openwork://connect/mcp-servers/index.json | ee/apps/den-api/src/mcp/connect-mcp-server-index.ts |
| App-host 能力 | mcp-app-host-v1客户端能力头 | apps/server/src/connect-mcp-server-catalog.ts |
| 资源 URI 校验 | 必须ui://前缀 | apps/server/src/mcp-app-host.ts |
| 启动生命周期 | 有界启动、TTL、指纹 | MAX_LIVE_LAUNCHES/LAUNCH_TTL_MS/launchFingerprint,apps/server/src/mcp-app-host.ts |
| 模型表面 | 仅search_capabilities/execute_capability | ee/apps/den-api/src/mcp/agent.ts |
结语
Native MCP Apps 的价值单元是"标准 MCP 服务器 + Connect 连接 + 有界 App 宿主"的组合:模型只面对中央search_capabilities/execute_capability,App 宿主只面对发起服务器的 App 可见工具与精确ui://资源,二者之间的凭据与目录永不交叉投影。对读者而言,最重要的边界是:一切独立 URL-App 安装能力当前均不存在,任何"从 URL 导入 App"的路径都不属于本发布范围。如果需要亲手验证,demo-runbook 给出的本地 tape 与聚焦测试是仓库内最直接的证据链。
【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考