
OpenWork Desktop MCP Apps 内联宿主运行时流程、安全边界与源码级实现解析【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork导读本文围绕 OpenWork Desktop 的 MCP Apps 内联宿主inline host展开讲解桌面端如何在一条已完成的 MCP 工具调用之后把标准的 MCP Apps UI 资源直接挂载到会话中同时保证普通文本结果在视图缺失、不受支持或初始化失败时仍然可见可用。读完本文你将掌握该宿主的五步运行时流程、io.modelcontextprotocol/ui扩展协商与ui://资源解析机制、完整的沙箱与 CSP 安全边界以及对应的服务端测试与 Testkit 端到端场景验证方式。MCP Apps 内联宿主是什么OpenWork Desktop 是一个由 opencode 驱动的开源桌面工作区。它的 MCP Apps 内联宿主inline host提供了一种附在工具调用之后的交互视图能力当 OpenCode 调用某个配置好的 MCP 工具并返回结果后桌面端会尝试把该工具声明的 HTML 资源以安全沙箱的形式内联渲染到对话中。关键的可用性设计是结果兜底宿主从不依赖视图是否成功渲染。原文案说明normal text result remains visible and usable if the view is absent, unsupported, or fails to initialize——普通文本结果在视图缺失、不受支持或初始化失败时依然可见、可用。前端代码为此准备了明确的兜底文案Interactive view unavailable. The normal tool result is still available.见 mcp-app-frame.tsx并在渲染失败时通过McpAppDiagnosticNotice展示可复制的技术诊断信息。运行时流程从工具调用到内联视图关联文档给出了五步运行时流程下面结合源码逐一展开。第 1 步OpenCode 调用一次配置好的 MCP 工具OpenCode 调用的是经过投影projected命名的工具。宿主使用projectedMcpToolName(serverName, toolName)把服务器名与工具名拼接为${sanitize(serverName)}_${sanitize(toolName)}其中非[a-zA-Z0-9_-]字符会被替换为下划线见 mcp-app-host.ts。投影命名让会话里只有一个扁平的工具标识后续宿主可以据此反查原始工具。第 2 步引擎插件保留标准结果字段OpenWork 捆绑的引擎插件会把该次调用返回的标准content、structuredContent以及结果_meta字段原样保留在已完成的工具 part 中。会话同步侧的toolCallProviderMetadata会从工具 part 的状态元数据中读取openworkMcpResult或兼容的openworkMcpApp并整体写入 provider metadata 的openwork.mcpResult见 parse-tool-parts.ts。前端preservedResult()再从 part 的callProviderMetadata.openwork.mcpResult中还原这三组字段见 mcp-app-frame.tsx。第 3 步桌面端请本地服务端反查原始工具桌面端把投影工具名发给本地 OpenWork 服务端服务端调用resolveMcpAppResource或面向 Connect 能力网关的resolveConnectMcpAppResource、面向同服务器间接启动的resolveSameServerMcpAppResource。解析逻辑会校验投影工具名格式/^[a-zA-Z0-9_-]{1,256}$/从运行时生效的 opencode 配置里枚举 MCP 服务器listMcpFromRuntimeSnapshotreadEffectiveRuntimeOpencodeConfig保证与产生该次工具调用的同一份生效配置一致按投影名前缀筛选候选服务器再对每个候选服务器建立远程客户端并反查tools/list。第 4 步扩展协商与资源解析宿主客户端在 initialize 时声明扩展能力capabilities.extensions[io.modelcontextprotocol/ui] { mimeTypes: [text/html;profilemcp-app] }见 mcp-app-host.ts协议版本固定为2025-06-18。反查工具后宿主通过toolUiResourceUri(tool)读取工具_meta.ui.resourceUri同时兼容旧式_meta[ui/resourceUri]并强制要求以ui://开头否则抛出invalid_resource_uri见 mcp-app-host.ts。随后调用resources/read读取该资源要求返回的资源内容恰好一个且 URI 匹配MIME 类型必须是text/html;profilemcp-appHTML 只能以text或blob严格 base64、UTF-8 校验二选一提供。第 5 步沙箱 iframe 挂载与结果投递服务端把解析出的 HTML、CSP 域列表与prefersBorder等信息组装成McpAppResource返回桌面端。桌面端渲染组件McpAppSandboxView依次经历打开专用沙箱代理页 → 建立AppBridgemodelcontextprotocol/ext-apps/app-bridge的PostMessageTransport→ 注入带 CSP 的 HTML → 等待应用初始化 → 通过官方 MCP Apps 桥接协议把原始工具输入sendToolInput与结果sendToolResult投递给视图见 mcp-app-frame.tsx。要点宿主从不重放replay原始工具调用来重建结构化数据。这一点在源码中体现为视图拿到的输入与结果都来自被保留的 part而不是再次调用工具因此只返回标准文本内容、没有结构化字段的 MCP 工具同样可以挂载视图。启动上下文launch context机制为了保证视图发起的后续动作绑定在正确的会话与服务器上服务端在解析成功后为可写场景创建一次性启动上下文launch上限MAX_LIVE_LAUNCHES 256个并发 launchTTL 为 30 分钟LAUNCH_TTL_MS每个 launch 携带 workspace、session、enginev1/v2、服务器名、工具名、资源 URI 以及一个fingerprintfingerprint 是服务端配置、托管身份localManagedMcpAppIdentity、运行时配置修订号与 Connect 私有授权修订号的 SHA-256 摘要——配置或凭据一旦变化旧 launch 立即失效stale_launch_context防止资源响应被离线复用见 mcp-app-host.ts。只读readOnly场景不会绑定 launch视图只能展示不能发起工具调用。安全边界沙箱、CSP、传输与配额沙箱隔离资源标记运行在不透明的 iframe 中。服务端提供专用沙箱代理页/mcp-apps/sandbox.html其脚本创建内层 iframe并通过srcdoc注入资源 HTML内层 iframe 仅带sandboxallow-scripts见 mcp-app-sandbox.ts。文档所列的禁止项——无 referrer、无表单、无弹窗、无同源访问、无顶层导航、无下载、无设备权限——全部由该 sandbox 属性与代理的 postMessage 白名单转发共同保证只有来自origin null的内层子窗口消息会被转发给父级反之亦然且代理会校验声明的主机源与 referrer 源一致。桌面端外层 iframe 使用sandboxallow-scripts allow-same-origin与referrerPolicyno-referrer见 mcp-app-frame.tsx并强制要求沙箱代理解析出的源与宿主同源时才继续否则以MCP_APP_SANDBOX_ORIGIN_INVALID失败。注入式 CSPdeny-by-default桌面端在把 HTML 写入srcdoc前调用secureMcpAppHtml()把构建好的 CSP 以meta http-equivContent-Security-Policy注入资源文档的head之前并且拒绝任何出现在html根之前、可能先于策略执行的可执行标记拒绝出现在head之前的标记见 mcp-app-frame.tsx。构建出的 CSP 以default-src none打底script-src/style-src允许self unsafe-inline加资源域名connect-src仅允许声明的连接域名否则noneobject-src none、form-action none见 mcp-app-frame.tsx。服务端在解析_meta.ui.csp时同样执行 deny-by-default 校验四类域名列表connectDomains/resourceDomains/frameDomains/baseUriDomains每类最多 16 个 origin且必须是 HTTPS originloopback HTTP 例外带用户名密码、路径、query 或 hash 的 URL 一律拒绝见 mcp-app-host.ts。传输与凭据保护远程 MCP 连接必须使用 HTTPS仅 loopback127.0.0.1、localhost、[::1]允许 HTTP见 mcp-app-host.ts。客户端首选Streamable HTTP传输遇到特定初始化失败HTTP 400/404/405时回退到遗留 SSE见 mcp-app-host.ts。所有请求经过createLocalManagedMcpGuardedFetch()守卫并先执行assertLocalManagedMcpUrl拦截私有地址访问DNS 重绑定防护。配置的 MCP headers 与凭据只存在于本地服务端绝不进入资源响应或 iframe。配额限制项限制源码位置资源 HTML 体积768 KiBMAX_RESOURCE_BYTESmcp-app-host.ts代理的工具结果体积1 MiBMAX_RESULT_BYTESmcp-app-host.ts工具发现数量最多 2,048 个工具mcp-app-host.ts工具发现分页最多 32 页mcp-app-host.tsCSP 域名列表每类最多 16 个 originmcp-app-host.ts提供商错误摘要512 字符去控制字符后mcp-app-host.ts目录探测超时单服务器 10 秒mcp-app-host.ts工具可见性与只读同源仲裁视图只能调用其来源配置的同一个 MCP 服务器上的工具callMcpAppTool会强制校验服务器名、工具、资源 URI、workspace/session/engine 与 launch 上下文完全一致。目标工具必须对 apps 可见_meta.ui.visibility未声明则默认可见需包含app被允许调用前需通过审批规则readOnlyHint ! true || destructiveHint true即视为需要用户批准toolRequiresApproval见 mcp-app-host.ts未批准直接抛tool_requires_approval不被工作区 MCP 工具策略拒绝diagnoseMcpToolDenies若工具声明了新的资源绑定必须与 launch 时记录的resourceUri一致否则按tool_resource_mismatch/stale_launch_context处理。服务端对该宿主切片不授予专用沙箱源、摄像头、麦克风、地理位置、剪贴板访问、采样sampling、宿主消息、外部链接打开、下载以及可写工具调用写调用需经审批且必须走 launch 上下文。当前兼容性资源解析器目前支持✅ 配置好的远程 Streamable HTTP MCP 服务器✅ 遗留远程服务器通过SSE 回退mcp-app-transport-fallback.test.ts专项覆盖见 mcp-app-transport-fallback.test.ts。暂不支持❌ 直接从 command/stdio MCP 条目解析资源会以unsupported_transport报错MCP Apps currently require a configured remote HTTP MCP server⚠️ OpenWork 托管的 OAuth 连接仍需后续适配器以便资源发现复用其加密的服务端凭据路径普通远程连接 配置的服务端 headers 今天即可工作测试中通过serverConfig携带 token、在 MCP config 中配置 headers 验证。验证体系服务端测试mcp-app-host.test.ts987 行用真实内存 HTTP MCP fixture 服务器覆盖扩展协商服务器声明io.modelcontextprotocol/ui扩展与text/html;profilemcp-appMIME投影工具命名projectedMcpToolName的净化与反查资源解析resolveMcpAppResource/resolveConnectMcpAppResource/resolveSameServerMcpAppResource的匹配、歧义ambiguous_tool、资源变更tool_resource_mismatch工具可见性visibility: [model]的工具不能作为 app 目标tool_not_visible只读同源仲裁只读工具同服务器可调用写拒绝非只读/破坏性工具未批准时拒绝tool_requires_approval且对恶意提供商错误文本做 512 字符摘要截断测试用\u0007控制字符 2,000 字符尾部模拟 hostile provider。mcp-app-sandbox.test.ts 在node:vm中执行真实沙箱代理脚本验证只转发被指派的 opaque 子窗口消息、拒绝错误宿主源、拒绝未指派前的资源注入、sandbox 属性恒为allow-scripts等。应用端测试mcp-app-frame.test.ts 覆盖结果保留preservedResult还原content/structuredContent/_meta、会话映射、CSP 构建buildMcpAppCsp与安全 HTML 注入secureMcpAppHtml包括拒绝 html 根之前出现可执行标记。Testkit 端到端场景Testkit 场景mcp-app-inline-host世界定义见 library.ts驱动一次确定性的 OpenCode 工具调用模型依次调用save_artifact_view与声明了_meta.ui.resourceUri的render_cardmock MCP 服务器通过resources/read返回 base64 编码的text/html;profilemcp-app资源端到端验证内联视图挂载后可见的内联卡片与兜底文本转写fallback transcript同时成立。小结OpenWork Desktop 的 MCP Apps 内联宿主把一次工具调用与一个可交互视图解耦结果字段原样保留保证兜底可用ui://资源 官方桥接协议保证标准互通而不重放调用、只读同源仲裁、launch 指纹、注入式 CSP、opaque 沙箱、HTTPS-only 传输共同构成纵深防御。对 MCP 工具开发者而言只需在工具_meta.ui中声明resourceUri并在服务器上实现resources/read返回text/html;profilemcp-app即可让工具在 OpenWork Desktop 中获得内联交互视图对使用者而言即使某个视图不受支持对话中的文本结果也始终可用。【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考