import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; import { rm } from "node:fs/promises"; import { expect, onTestFinished } from "vitest"; import { clickButton, createAndSelectWorkspace, createOrgConnection, denFetch, evalIn, waitFor } from "@openwork/behaviors"; import { connect, debuggerUrlFor, evaluate, listTargets, navigate } from "@openwork/cdp"; import { screenshot, validate } from "@openwork/test-evidence"; import { chrome, desktop } from "@openwork/hosts"; import { localMysqlIsRunning, needs, server, test } from "@openwork/testkit"; import { buildGeneratedArtifactViewInWorker } from "../../ee/apps/den-api/src/generated-artifact-view-builder.js"; const e2eTestsEnabled = process.env.OPENWORK_EVAL_E2E_TESTS === "1"; const localPlacement = process.env.OPENWORK_EVAL_DAYTONA !== "1" && !process.env.OPENWORK_EVAL_DEN_API_URL?.trim(); const mysqlOpen = await localMysqlIsRunning(); const title = !e2eTestsEnabled ? "Remote MCP Apps skipped — needs: set OPENWORK_EVAL_E2E_TESTS=1" : !localPlacement ? "Remote MCP Apps skipped — needs local placement without OPENWORK_EVAL_DEN_API_URL" : !mysqlOpen ? "Remote MCP Apps skipped — needs MySQL on 127.0.0.1:3306" : "standard MCP Apps refresh after connection changes while standalone URL Apps remain unavailable"; const providerId = "remote-mcp-apps-provider"; const modelId = "remote-mcp-apps-model"; const desktopClosingReply = "Project Atlas is open through its standard MCP server."; const connectedResourceUri = "ui://project-atlas/view.html"; function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function requireRecord(value: unknown, label: string): Record { if (!isRecord(value)) throw new Error(`${label} was not an object: ${JSON.stringify(value)}`); return value; } async function waitForMountedProjectAtlas( app: Awaited>, expected: string[] = ["Project Atlas", "Connected through OpenWork Connect"], timeoutMs = 60_000, ): Promise { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const targets = await listTargets(app.handle.cdpUrl); const sandbox = targets.find((target) => target.type === "iframe" && target.url.includes("/mcp-apps/sandbox.html") && target.webSocketDebuggerUrl); if (sandbox) { const client = await connect(debuggerUrlFor(app.handle.cdpUrl, sandbox)); try { const mounted = await evaluate(client, `(() => { const text = document.querySelector("iframe")?.contentDocument?.body?.innerText ?? ""; return ${JSON.stringify(expected)}.every((value) => text.includes(value)); })()`); if (mounted === true) return true; } finally { client.close(); } } await new Promise((resolve) => setTimeout(resolve, 250)); } return false; } function readBody(request: IncomingMessage): Promise { request.setEncoding("utf8"); return new Promise((resolve, reject) => { let body = ""; request.on("data", (chunk: string) => { body += chunk; }); request.on("end", () => resolve(body)); request.on("error", reject); }); } function sendJson(response: ServerResponse, status: number, body: unknown): void { response.writeHead(status, { "access-control-allow-origin": "*", "cache-control": "no-store", "content-type": "application/json", }); response.end(JSON.stringify(body)); } function requestHeader(request: IncomingMessage, name: string): string | undefined { const value = request.headers[name]; return Array.isArray(value) ? value[0] : value; } function forwardedMcpHeaders(request: IncomingMessage): Record { const headers: Record = {}; for (const name of [ "content-type", "mcp-protocol-version", "mcp-session-id", "x-openwork-mcp-client-audience", "x-openwork-mcp-client-capabilities", ]) { const value = requestHeader(request, name); if (value) headers[name] = value; } return headers; } function streamChunk(delta: Record, finishReason: string | null = null) { return { id: "chatcmpl-remote-mcp-apps", object: "chat.completion.chunk", created: 1, model: modelId, choices: [{ index: 0, delta, finish_reason: finishReason }], }; } function sendStream(response: ServerResponse, chunks: Record[]) { response.writeHead(200, { "cache-control": "no-cache", connection: "keep-alive", "content-type": "text/event-stream", }); let delay = 250; for (const chunk of chunks) { setTimeout(() => response.write(`data: ${JSON.stringify(chunk)}\n\n`), delay); delay += 250; } setTimeout(() => response.end("data: [DONE]\n\n"), delay); } function toolResultCount(payload: Record) { return Array.isArray(payload.messages) ? payload.messages.filter((message) => isRecord(message) && message.role === "tool").length : 0; } function projectedToolEnding(payload: Record, ending: string) { if (!Array.isArray(payload.tools)) return null; for (const tool of payload.tools) { if (!isRecord(tool) || !isRecord(tool.function)) continue; const name = tool.function.name; if (typeof name === "string" && name.endsWith(ending)) return name; } return null; } function projectedToolNames(payload: Record): string[] { if (!Array.isArray(payload.tools)) return []; return payload.tools.flatMap((tool) => ( isRecord(tool) && isRecord(tool.function) && typeof tool.function.name === "string" ? [tool.function.name] : [] )); } const builtPortableApp = await buildGeneratedArtifactViewInWorker({ reactSource: `export default function ProjectAtlas(props) { const app = props.data || props.app || { name: "Project Atlas", status: "Portable standard MCP App resource" }; return

REMOTE MCP APP

{app.name}

Cached immutable revision

{app.status}

; }`, cssSource: "body{margin:0;padding:18px;color:#172033;background:#f5f7fb;font-family:system-ui,sans-serif}main{padding:22px;border:1px solid #dbe4f0;border-radius:16px;background:white}.eyebrow{color:#2563eb;font-size:11px;font-weight:700;letter-spacing:.12em}h1{margin:8px 0;font-size:24px}p{margin:6px 0}", outputSchema: { type: "object", additionalProperties: true }, title: "Project Atlas", description: "A portable Remote MCP App acceptance fixture.", }); if (!builtPortableApp.ok) throw new Error(`Portable app build failed: ${JSON.stringify(builtPortableApp.diagnostics)}`); const portableAppDocument = builtPortableApp.html.replace(/]*>/i, ""); function connectedAppHtml(version: string): string { return portableAppDocument.replace( "", ``, ); } function standardMcpAppRpc(message: Record): Record { if (message.method === "initialize") { return { jsonrpc: "2.0", id: message.id, result: { protocolVersion: "2025-06-18", capabilities: { tools: { listChanged: false }, resources: { listChanged: false, subscribe: false }, extensions: { "io.modelcontextprotocol/ui": { mimeTypes: ["text/html;profile=mcp-app"] }, }, }, serverInfo: { name: "project-atlas-connect-fixture", title: "Project Atlas Connect", version: "1.0.0", description: "A standard MCP App fixture served through OpenWork Connect.", websiteUrl: "https://example.test/project-atlas", icons: [{ src: "https://example.test/project-atlas.png", mimeType: "image/png", sizes: ["64x64"] }], }, }, }; } if (message.method === "tools/list") { return { jsonrpc: "2.0", id: message.id, result: { tools: [ { name: "open_project_atlas", title: "Open Project Atlas", description: "Open the Project Atlas MCP App.", inputSchema: { type: "object", additionalProperties: false }, annotations: { readOnlyHint: true, destructiveHint: false }, _meta: { ui: { resourceUri: connectedResourceUri } }, }, { name: "search_projects", title: "Search projects", description: "Search the connected project catalog.", inputSchema: { type: "object", properties: { query: { type: "string" } }, additionalProperties: false, }, annotations: { readOnlyHint: true, destructiveHint: false }, _meta: { ui: { visibility: ["app"] } }, }, ], }, }; } if (message.method === "resources/list") { return { jsonrpc: "2.0", id: message.id, result: { resources: [] }, }; } if (message.method === "resources/templates/list") { return { jsonrpc: "2.0", id: message.id, result: { resourceTemplates: [] }, }; } if (message.method === "resources/read") { const params = requireRecord(message.params, "resources/read params"); if (params.uri !== connectedResourceUri) { return { jsonrpc: "2.0", id: message.id, error: { code: -32002, message: "Resource not found" } }; } return { jsonrpc: "2.0", id: message.id, result: { contents: [{ uri: connectedResourceUri, mimeType: "text/html;profile=mcp-app", text: connectedAppHtml("Connect 1.0.0"), _meta: { ui: { csp: { connectDomains: [], resourceDomains: [] }, prefersBorder: true, }, }, }], }, }; } if (message.method === "tools/call") { const params = requireRecord(message.params, "tools/call params"); const args = isRecord(params.arguments) ? params.arguments : {}; if (params.name === "open_project_atlas") { return { jsonrpc: "2.0", id: message.id, result: { content: [{ type: "text", text: "Project Atlas opened." }], structuredContent: { schemaVersion: "1", artifact: { title: "Project Atlas", description: "A standard MCP App served through OpenWork Connect." }, data: { name: "Project Atlas", status: "Connected through OpenWork Connect" }, }, _meta: { source: "project-atlas-standard-mcp" }, }, }; } return { jsonrpc: "2.0", id: message.id, result: { content: [{ type: "text", text: `Atlas project result for ${String(args.query ?? "all")}` }], structuredContent: { projects: [{ id: "project-atlas", name: "Atlas migration", status: "on_track" }], }, _meta: { source: "project-atlas-standard-mcp" }, }, }; } return { jsonrpc: "2.0", id: message.id, result: {} }; } let agentRequestId = 0; async function agentRpc( apiUrl: string, token: string, method: string, params: Record, endpoint = "/mcp/agent", extraHeaders: Record = {}, ) { const id = ++agentRequestId; const response = await fetch(`${apiUrl}${endpoint}`, { method: "POST", headers: { authorization: `Bearer ${token}`, accept: "application/json, text/event-stream", "content-type": "application/json", ...extraHeaders, }, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }), signal: AbortSignal.timeout(90_000), }); const raw = await response.text(); if (!response.ok) throw new Error(`MCP ${method} failed: HTTP ${response.status} ${raw.slice(0, 500)}`); const payload = raw.split("\n") .filter((line) => line.startsWith("data:")) .map((line) => JSON.parse(line.slice(5)) as unknown) .find((candidate) => isRecord(candidate) && candidate.id === id); const message = requireRecord(payload, `${method} response`); if (message.error) throw new Error(`MCP ${method} returned ${JSON.stringify(message.error)}`); return requireRecord(message.result, `${method} result`); } async function reconcileDesktopCatalog(input: { app: Awaited>; workspaceId: string; denApiUrl: string; mcpToken: string; appHostMcpToken: string; }) { return evalIn(input.app, `(async () => { const port = localStorage.getItem("openwork.server.port"); const token = localStorage.getItem("openwork.server.token"); if (!port || !token) return "missing local server credentials"; const response = await fetch("http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(${JSON.stringify(input.workspaceId)}) + "/mcp/openwork-cloud/reconcile", { method: "POST", headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" }, body: JSON.stringify({ config: { type: "remote", url: ${JSON.stringify(`${input.denApiUrl}/mcp/agent`)}, enabled: true, headers: { Authorization: ${JSON.stringify(`Bearer ${input.mcpToken}`)} }, oauth: false, }, appHostAuthorization: ${JSON.stringify(`Bearer ${input.appHostMcpToken}`)}, provider: ${JSON.stringify(providerId)}, model: ${JSON.stringify(modelId)}, trigger: "exact-head-tape-refresh", }), }); const text = await response.text(); return response.ok ? "ok" : "HTTP " + response.status + " " + text.slice(0, 1_000); })()`, { awaitPromise: true, timeoutMs: 60_000 }); } function toolsFrom(result: Record) { return Array.isArray(result.tools) ? result.tools.filter(isRecord) : []; } function contentsFrom(result: Record) { return Array.isArray(result.contents) ? result.contents.filter(isRecord) : []; } test.skipIf(!e2eTestsEnabled || !localPlacement || !mysqlOpen)(title, { timeout: 360_000 }, async ({ evidence, place }) => { needs({ optIn: ["OPENWORK_EVAL_E2E_TESTS"] }); let standardMcpCalls = 0; let agentMcpUpstream: { token: string; staticUrl: string; connectedUrl: string } | null = null; let gatewayCapabilityName: string | null = null; const fixture = createServer((request, response) => { void (async () => { const url = new URL(request.url ?? "/", "http://127.0.0.1"); if (request.method === "GET" && url.pathname === "/v1/models") { sendJson(response, 200, { object: "list", data: [{ id: modelId, object: "model" }] }); return; } if (request.method === "POST" && (url.pathname === "/v1/chat/completions" || url.pathname === "/chat/completions")) { const payload = requireRecord(JSON.parse(await readBody(request)), "chat completion request"); if (!Array.isArray(payload.tools) || payload.tools.length === 0) { sendStream(response, [streamChunk({ role: "assistant" }), streamChunk({ content: "Project Atlas" }), streamChunk({}, "stop")]); return; } const directProviderTools = projectedToolNames(payload).filter((name) => ( name.includes("open_project_atlas") || name.includes("search_projects") || name.includes("launch_remote_app_") || name.includes("import_remote_mcp_app") || name.includes("openwork_connect_") )); if (directProviderTools.length > 0) { throw new Error(`Provider MCP tools leaked into the model tool list: ${directProviderTools.join(", ")}`); } const completedTools = toolResultCount(payload); if (completedTools > 1) { sendStream(response, [ streamChunk({ role: "assistant" }), streamChunk({ content: desktopClosingReply }), streamChunk({}, "stop"), ]); return; } const toolName = projectedToolEnding(payload, completedTools === 0 ? "_search_capabilities" : "_execute_capability"); if (!toolName) throw new Error("The Remote MCP App capability gateway tools were not projected to the model."); if (completedTools === 1 && !gatewayCapabilityName) { throw new Error("The Project Atlas gateway capability was not configured."); } sendStream(response, [ streamChunk({ role: "assistant" }), streamChunk({ tool_calls: [{ index: 0, id: "call_project_atlas", type: "function", function: { name: toolName, arguments: completedTools === 0 ? JSON.stringify({ query: "open Project Atlas", type: "mcp", limit: 5 }) : JSON.stringify({ name: gatewayCapabilityName, body: {}, }), }, }], }), streamChunk({}, "tool_calls"), ]); return; } if (url.pathname === "/agent-mcp" || url.pathname === "/connected-mcp" || url.pathname === "/mcp/agent" || url.pathname.startsWith("/mcp/agent/connections/")) { if (!agentMcpUpstream) throw new Error("The Den agent MCP proxy was not configured."); const raw = request.method === "GET" || request.method === "HEAD" ? "" : await readBody(request); const upstream = await fetch( url.pathname === "/connected-mcp" || url.pathname.startsWith("/mcp/agent/connections/") ? agentMcpUpstream.connectedUrl : agentMcpUpstream.staticUrl, { method: request.method, headers: { authorization: request.headers.authorization ?? `Bearer ${agentMcpUpstream.token}`, accept: request.headers.accept ?? "application/json, text/event-stream", ...forwardedMcpHeaders(request), }, body: raw || undefined, }, ); const body = Buffer.from(await upstream.arrayBuffer()); let method = ""; try { const message: unknown = raw ? JSON.parse(raw) : null; if (isRecord(message) && typeof message.method === "string") method = message.method; } catch { // The upstream Den endpoint remains responsible for invalid JSON. } if (method === "tools/call") { // Match a normal remote round trip so the completed tool event stays // inside Desktop's live renderer subscription window. await new Promise((resolve) => setTimeout(resolve, 4_000)); } const headers: Record = {}; for (const name of ["cache-control", "content-type", "mcp-session-id"]) { const value = upstream.headers.get(name); if (value) headers[name] = value; } response.writeHead(upstream.status, headers); response.end(body); return; } if (url.pathname === "/mcp") { if (request.method === "GET") { sendJson(response, 405, { error: "method_not_allowed" }); return; } const raw = await readBody(request); const parsed: unknown = raw.trim() ? JSON.parse(raw) : {}; const messages = Array.isArray(parsed) ? parsed : [parsed]; const replies: Record[] = []; for (const candidate of messages) { if (!isRecord(candidate)) continue; if (candidate.method === "tools/call") standardMcpCalls += 1; if (candidate.id !== undefined) replies.push(standardMcpAppRpc(candidate)); } if (replies.length === 0) { response.writeHead(202, { "access-control-allow-origin": "*" }); response.end(); return; } sendJson(response, 200, Array.isArray(parsed) ? replies : replies[0]); return; } sendJson(response, 404, { error: "not_found" }); })().catch((error: unknown) => { if (!response.headersSent) sendJson(response, 500, { error: String(error) }); else response.destroy(error instanceof Error ? error : undefined); }); }); await new Promise((resolve, reject) => { fixture.once("error", reject); fixture.listen(0, "127.0.0.1", resolve); }); onTestFinished(async () => { await new Promise((resolve, reject) => fixture.close((error) => error ? reject(error) : resolve())); }); const address = fixture.address(); if (!address || typeof address === "string") throw new Error("Remote MCP App fixture did not bind a port."); const fixtureUrl = `http://127.0.0.1:${address.port}`; await using den = await server({ place, org: { name: `Remote MCP Apps ${Date.now()}`, admin: { name: "Avery" } }, }); const orgsResult = await denFetch(den.admin, "/v1/me/orgs", { headers: { authorization: `Bearer ${den.admin.token}` }, }); const orgs = isRecord(orgsResult.body) && Array.isArray(orgsResult.body.orgs) ? orgsResult.body.orgs.filter(isRecord) : []; const organizationId = String(orgs[0]?.id ?? ""); expect(organizationId).not.toBe(""); const connection = await createOrgConnection(den.admin, { name: "Atlas read-only projects", url: `${fixtureUrl}/mcp`, authType: "none", credentialMode: "shared", access: { orgWide: true }, }); gatewayCapabilityName = `mcp:${connection.id}:open_project_atlas`; const tokenResult = await denFetch(den.admin, "/v1/mcp/token", { method: "POST", headers: { authorization: `Bearer ${den.admin.token}`, "x-openwork-org-id": organizationId, }, body: JSON.stringify({ scopes: ["mcp:read", "mcp:write"] }), }); expect(tokenResult.response.ok, tokenResult.text).toBe(true); const mcpTokenBody = requireRecord(tokenResult.body, "MCP token response"); const mcpToken = String(mcpTokenBody.token ?? ""); const appHostMcpToken = String(mcpTokenBody.appHostToken ?? ""); expect(appHostMcpToken).not.toBe(mcpToken); const connectedEndpoint = `/mcp/agent/connections/${encodeURIComponent(connection.id)}`; const appHostCapabilityHeaders = { "x-openwork-mcp-client-capabilities": "mcp-app-host-v1" }; const appHostHeaders = appHostCapabilityHeaders; agentMcpUpstream = { token: mcpToken, staticUrl: `${den.ref.apiUrl}/mcp/agent`, connectedUrl: `${den.ref.apiUrl}${connectedEndpoint}`, }; const initialized = await agentRpc(den.ref.apiUrl, mcpToken, "initialize", { protocolVersion: "2025-11-25", capabilities: { extensions: { "io.modelcontextprotocol/ui": { mimeTypes: ["text/html;profile=mcp-app"] } } }, clientInfo: { name: "remote-mcp-app-eval", version: "1.0.0" }, }); expect(initialized.protocolVersion).toBeTruthy(); expect(String(initialized.instructions ?? "")).toContain("URL-imported Apps are deferred"); const initializedCapabilities = requireRecord(initialized.capabilities, "agent capabilities"); expect(requireRecord(initializedCapabilities.tools, "agent tool capabilities").listChanged).toBe(true); expect(requireRecord(initializedCapabilities.resources, "agent resource capabilities").listChanged).toBe(true); const centralModelTools = await agentRpc(den.ref.apiUrl, mcpToken, "tools/list", {}); const centralModelToolNames = toolsFrom(centralModelTools).map((tool) => String(tool.name ?? "")); expect(centralModelToolNames).not.toContain("import_remote_mcp_app"); expect(centralModelToolNames.some((name) => name.startsWith("launch_remote_app_"))).toBe(false); const centralAppHostTools = await agentRpc(den.ref.apiUrl, mcpToken, "tools/list", {}, "/mcp/agent", appHostHeaders); const centralAppHostToolNames = toolsFrom(centralAppHostTools).map((tool) => String(tool.name ?? "")); expect(centralAppHostToolNames).not.toContain("import_remote_mcp_app"); expect(centralAppHostToolNames.some((name) => name.startsWith("launch_remote_app_"))).toBe(false); const unavailableImport = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "import_remote_mcp_app", arguments: { sourceUrl: "https://example.test/project-atlas.html" }, }); expect(unavailableImport.isError).toBe(true); const unavailableImportApi = await denFetch(den.admin, "/v1/remote-mcp-apps", { method: "POST", headers: { authorization: `Bearer ${den.admin.token}`, "x-openwork-org-id": organizationId, }, body: JSON.stringify({ sourceUrl: "https://example.test/project-atlas.html" }), }); expect(unavailableImportApi.response.status).toBe(404); const unavailablePreviewApi = await denFetch(den.admin, "/v1/remote-mcp-apps/preview", { method: "POST", headers: { authorization: `Bearer ${den.admin.token}`, "x-openwork-org-id": organizationId, }, body: JSON.stringify({ sourceUrl: "https://example.test/project-atlas.html" }), }); expect(unavailablePreviewApi.response.status).toBe(404); await using browser = await chrome({ name: "remote-mcp-apps", startUrl: den.ref.webUrl, headless: true }); await navigate(browser.client, den.ref.webUrl); await waitFor(browser, `location.href.startsWith(${JSON.stringify(den.ref.webUrl)}) && document.readyState === "complete"`, { timeoutMs: 60_000, label: "Den Web origin", }); const tokenStored = await evalIn(browser, `(() => { localStorage.setItem("openwork:web:auth-token", ${JSON.stringify(den.admin.token)}); return localStorage.getItem("openwork:web:auth-token") === ${JSON.stringify(den.admin.token)}; })()`); expect(tokenStored).toBe(true); await navigate(browser.client, `${den.ref.webUrl}/dashboard/library`); await waitFor(browser, `document.body.innerText.includes("My Library")`, { timeoutMs: 60_000, label: "Library without standalone URL-App import", }); const libraryDom = await evalIn(browser, `({ text: document.body.innerText, addButton: Boolean(document.querySelector('[data-testid="add-remote-mcp-app"]')), urlInput: Boolean(document.querySelector('input[type="url"]')), })`); const libraryState = requireRecord(libraryDom, "Library DOM state"); expect(libraryState.addButton).toBe(false); expect(libraryState.urlInput).toBe(false); expect(String(libraryState.text)).not.toContain("Add remote MCP App"); const standaloneSearch = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "search_capabilities", arguments: { query: "open standalone URL-installed Project Atlas", type: "mcp", limit: 10 }, }); const standaloneMatches = Array.isArray(requireRecord(standaloneSearch.structuredContent, "standalone App search").matches) ? (requireRecord(standaloneSearch.structuredContent, "standalone App search").matches as unknown[]).filter(isRecord) : []; expect(standaloneMatches.some((match) => String(match.name ?? "").startsWith("remote_app:"))).toBe(false); expect(JSON.stringify(standaloneMatches)).not.toContain("ui://openwork/library-apps/"); const connectSearch = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "search_capabilities", arguments: { query: "Atlas read-only projects", type: "mcp", limit: 5 }, }); const connectMatches = Array.isArray(requireRecord(connectSearch.structuredContent, "Connect search").matches) ? (requireRecord(connectSearch.structuredContent, "Connect search").matches as unknown[]).filter(isRecord) : []; const connectMatch = requireRecord(connectMatches.find((match) => String(match.name ?? "").includes("search_projects")), "Connect capability match"); const connectRun = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "execute_capability", arguments: { name: connectMatch.name, schemaDigest: connectMatch.schemaDigest, body: { query: "migration" } }, }); expect(connectRun.isError, JSON.stringify(connectRun)).not.toBe(true); expect(JSON.stringify(connectRun.structuredContent)).toContain("Atlas migration"); expect(standardMcpCalls).toBe(1); const appSearch = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "search_capabilities", arguments: { query: "open Project Atlas", type: "mcp", limit: 5 }, }); const appMatches = Array.isArray(requireRecord(appSearch.structuredContent, "MCP App search").matches) ? (requireRecord(appSearch.structuredContent, "MCP App search").matches as unknown[]).filter(isRecord) : []; expect(appMatches.find((match) => match.name === gatewayCapabilityName)).toMatchObject({ kind: "mcp_app", mcpApp: { resourceUri: connectedResourceUri }, }); const resources = await agentRpc(den.ref.apiUrl, mcpToken, "resources/list", {}); expect(Array.isArray(resources.resources) && resources.resources.some((resource) => isRecord(resource) && String(resource.uri ?? "").startsWith("ui://openwork/library-apps/"))).toBe(false); expect(Array.isArray(resources.resources) && resources.resources.some((resource) => isRecord(resource) && resource.uri === "openwork://connect/mcp-servers/index.json")).toBe(true); const legacyConnectIndexRead = await agentRpc(den.ref.apiUrl, mcpToken, "resources/read", { uri: "openwork://connect/mcp-servers/index.json" }); const legacyConnectIndex = requireRecord(JSON.parse(String(contentsFrom(legacyConnectIndexRead)[0]?.text ?? "{}")), "legacy Connect MCP server index"); expect(legacyConnectIndex.servers).toEqual([]); const connectIndexRead = await agentRpc( den.ref.apiUrl, appHostMcpToken, "resources/read", { uri: "openwork://connect/mcp-servers/index.json" }, "/mcp/agent", appHostCapabilityHeaders, ); const connectIndex = requireRecord(JSON.parse(String(contentsFrom(connectIndexRead)[0]?.text ?? "{}")), "Connect MCP server index"); const indexedServers = Array.isArray(connectIndex.servers) ? connectIndex.servers.filter(isRecord) : []; expect(indexedServers).toContainEqual(expect.objectContaining({ connectionId: connection.id, url: `${den.ref.apiUrl}/mcp/agent/connections/${connection.id}`, })); const connectedInitialized = await agentRpc(den.ref.apiUrl, appHostMcpToken, "initialize", { protocolVersion: "2025-11-25", capabilities: { extensions: { "io.modelcontextprotocol/ui": { mimeTypes: ["text/html;profile=mcp-app"] } } }, clientInfo: { name: "remote-mcp-app-connect-eval", version: "1.0.0" }, }, connectedEndpoint, appHostHeaders); expect(requireRecord(connectedInitialized.capabilities, "connected capabilities").extensions).toBeTruthy(); expect(requireRecord(connectedInitialized.serverInfo, "connected server info")).toEqual({ name: "project-atlas-connect-fixture", title: "Project Atlas Connect", version: "1.0.0", description: "A standard MCP App fixture served through OpenWork Connect.", websiteUrl: "https://example.test/project-atlas", icons: [{ src: "https://example.test/project-atlas.png", mimeType: "image/png", sizes: ["64x64"] }], }); const modelConnectedTools = await agentRpc( den.ref.apiUrl, mcpToken, "tools/list", {}, connectedEndpoint, appHostCapabilityHeaders, ); expect(toolsFrom(modelConnectedTools).map((tool) => tool.name)).toEqual([ "search_capabilities", "execute_capability", ]); const legacyConnectedTools = await agentRpc(den.ref.apiUrl, mcpToken, "tools/list", {}, connectedEndpoint); expect(toolsFrom(legacyConnectedTools).map((tool) => tool.name)).toEqual([ "search_capabilities", "execute_capability", ]); for (const tool of [...toolsFrom(modelConnectedTools), ...toolsFrom(legacyConnectedTools)]) { expect(tool._meta).toBeUndefined(); } const legacyConnectedResources = await agentRpc(den.ref.apiUrl, mcpToken, "resources/list", {}, connectedEndpoint); expect(legacyConnectedResources.resources).toEqual([]); const legacyConnectedTemplates = await agentRpc(den.ref.apiUrl, mcpToken, "resources/templates/list", {}, connectedEndpoint); expect(legacyConnectedTemplates.resourceTemplates).toEqual([]); const legacyProviderSearch = await agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "search_capabilities", arguments: { query: "Atlas project catalog", limit: 5 }, }, connectedEndpoint); const legacyProviderMatches = Array.isArray(requireRecord(legacyProviderSearch.structuredContent, "legacy provider search").matches) ? (requireRecord(legacyProviderSearch.structuredContent, "legacy provider search").matches as unknown[]).filter(isRecord) : []; expect(legacyProviderMatches).toContainEqual(expect.objectContaining({ name: "search_projects" })); expect(legacyProviderMatches.every((match) => match.kind === undefined && match.mcpApp === undefined)).toBe(true); const connectedToolsResult = await agentRpc(den.ref.apiUrl, appHostMcpToken, "tools/list", {}, connectedEndpoint, appHostHeaders); const connectedTools = toolsFrom(connectedToolsResult); expect(connectedTools.map((tool) => tool.name)).toEqual([ "search_capabilities", "execute_capability", "open_project_atlas", ]); for (const tool of connectedTools) { expect(requireRecord(requireRecord(tool._meta, "connected tool metadata").ui, "connected UI metadata").visibility).toEqual(["app"]); } const connectedOpenTool = requireRecord(connectedTools.find((tool) => tool.name === "open_project_atlas"), "connected open tool"); expect(requireRecord(requireRecord(connectedOpenTool._meta, "connected tool metadata").ui, "connected UI metadata")).toMatchObject({ resourceUri: connectedResourceUri, visibility: ["app"], }); const connectedResources = await agentRpc( den.ref.apiUrl, mcpToken, "resources/list", {}, connectedEndpoint, appHostCapabilityHeaders, ); expect(connectedResources.resources).toEqual([]); await expect(agentRpc( den.ref.apiUrl, mcpToken, "resources/read", { uri: connectedResourceUri }, connectedEndpoint, appHostCapabilityHeaders, )) .rejects.toThrow("only through the OpenWork App host"); const connectedRead = await agentRpc( den.ref.apiUrl, appHostMcpToken, "resources/read", { uri: connectedResourceUri }, connectedEndpoint, appHostHeaders, ); const connectedContent = requireRecord(contentsFrom(connectedRead)[0], "connected resource"); expect(connectedContent.mimeType).toBe("text/html;profile=mcp-app"); expect(String(connectedContent.text ?? "")).toContain("Project Atlas"); expect(String(connectedContent.text ?? "")).toContain("Portable revision Connect 1.0.0"); expect(requireRecord(requireRecord(connectedContent._meta, "connected resource metadata").ui, "connected resource UI metadata").csp).toBeTruthy(); await expect(agentRpc(den.ref.apiUrl, mcpToken, "tools/call", { name: "search_projects", arguments: { query: "migration" }, }, connectedEndpoint, appHostCapabilityHeaders)).rejects.toThrow("Use search_capabilities and execute_capability"); await expect(agentRpc(den.ref.apiUrl, appHostMcpToken, "tools/call", { name: "search_projects", arguments: { query: "migration" }, }, connectedEndpoint, appHostHeaders)).rejects.toThrow("Use search_capabilities and execute_capability"); const providerAppSearch = await agentRpc(den.ref.apiUrl, appHostMcpToken, "tools/call", { name: "search_capabilities", arguments: { query: "Atlas project catalog", limit: 5 }, }, connectedEndpoint, appHostHeaders); const providerAppMatches = Array.isArray(requireRecord(providerAppSearch.structuredContent, "provider App search").matches) ? (requireRecord(providerAppSearch.structuredContent, "provider App search").matches as unknown[]).filter(isRecord) : []; expect(providerAppMatches).toContainEqual(expect.objectContaining({ name: "search_projects" })); const providerAppRun = await agentRpc(den.ref.apiUrl, appHostMcpToken, "tools/call", { name: "execute_capability", arguments: { name: "search_projects", body: { query: "migration" } }, }, connectedEndpoint, appHostHeaders); expect(JSON.stringify(providerAppRun.structuredContent)).toContain("Atlas migration"); expect(standardMcpCalls).toBe(2); const desktopProfileDir = `/tmp/openwork-remote-mcp-apps-profile-${Date.now()}`; let desktopApp = await desktop({ name: "remote-mcp-apps", mode: process.env.OPENWORK_EVAL_CDP_URL?.trim() ? "attach" : "spawn", profileDir: process.env.OPENWORK_EVAL_CDP_URL?.trim() ? undefined : desktopProfileDir, env: { ANTHROPIC_API_KEY: "", OPENAI_API_KEY: "", OPENROUTER_API_KEY: "", GOOGLE_GENERATIVE_AI_API_KEY: "", OPENWORK_API_KEY: "", OPENWORK_INFERENCE_BASE_URL: "", }, }); onTestFinished(async () => { await desktopApp.stop().catch(() => undefined); await rm(desktopProfileDir, { recursive: true, force: true }); }); const workspace = await createAndSelectWorkspace(desktopApp, { path: `/tmp/openwork-remote-mcp-apps-${Date.now()}`, }); const configured = await evalIn(desktopApp, `(async () => { const port = localStorage.getItem("openwork.server.port"); const token = localStorage.getItem("openwork.server.token"); if (!port || !token) return "missing local server credentials"; const request = async (path, init) => { const response = await fetch("http://127.0.0.1:" + port + path, { ...init, headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" }, }); if (!response.ok) return path + " failed: " + response.status + " " + (await response.text()).slice(0, 500); return "ok"; }; const workspaceId = ${JSON.stringify(workspace.workspaceId)}; const patched = await request("/workspace/" + encodeURIComponent(workspaceId) + "/config", { method: "PATCH", body: JSON.stringify({ opencode: { provider: { [${JSON.stringify(providerId)}]: { npm: "@ai-sdk/openai-compatible", name: "Remote MCP Apps model", options: { baseURL: ${JSON.stringify(`${fixtureUrl}/v1`)}, apiKey: "sk-remote-mcp-apps" }, models: { [${JSON.stringify(modelId)}]: { name: "Remote MCP Apps model", tool_call: true } }, }, }, }, }), }); if (patched !== "ok") return patched; const reloaded = await request("/workspace/" + encodeURIComponent(workspaceId) + "/engine/reload", { method: "POST" }); if (reloaded !== "ok" && !reloaded.includes("opencode_reload_timeout")) return reloaded; const reconcileResponse = await fetch("http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(workspaceId) + "/mcp/openwork-cloud/reconcile", { method: "POST", headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" }, body: JSON.stringify({ config: { type: "remote", url: ${JSON.stringify(`${den.ref.apiUrl}/mcp/agent`)}, enabled: true, headers: { Authorization: ${JSON.stringify(`Bearer ${mcpToken}`)} }, oauth: false, }, appHostAuthorization: ${JSON.stringify(`Bearer ${appHostMcpToken}`)}, provider: ${JSON.stringify(providerId)}, model: ${JSON.stringify(modelId)}, trigger: "exact-head-tape", }), }); const reconcileText = await reconcileResponse.text(); if (!reconcileResponse.ok) return "Cloud MCP reconcile failed: " + reconcileResponse.status + " " + reconcileText.slice(0, 1_000); let reconcileHealth = {}; try { reconcileHealth = JSON.parse(reconcileText); } catch { return "Cloud MCP reconcile returned invalid JSON: " + reconcileText.slice(0, 1_000); } if (reconcileHealth?.phase !== "ready") return "Cloud MCP reconcile was not ready: " + JSON.stringify(reconcileHealth).slice(0, 2_000); const listedResponse = await fetch("http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(workspaceId) + "/mcp", { headers: { Authorization: "Bearer " + token }, }); if (!listedResponse.ok) return "runtime MCP list failed: " + listedResponse.status; const listed = await listedResponse.json(); const names = (Array.isArray(listed?.items) ? listed.items : []).map((item) => item?.name).filter((name) => typeof name === "string"); if (!names.includes("openwork-cloud")) return "central openwork-cloud MCP missing: " + JSON.stringify(names); if (names.some((name) => name.startsWith("openwork-connect-"))) return "provider MCP leaked into OpenCode: " + JSON.stringify(names); const raw = localStorage.getItem("openwork.preferences"); let preferences = {}; try { preferences = raw ? JSON.parse(raw) : {}; } catch { preferences = {}; } if (!preferences || typeof preferences !== "object" || Array.isArray(preferences)) preferences = {}; localStorage.setItem("openwork.preferences", JSON.stringify({ ...preferences, defaultModel: { providerID: ${JSON.stringify(providerId)}, modelID: ${JSON.stringify(modelId)} }, modelVariant: null, providerStepCompleted: true, })); localStorage.setItem("openwork.defaultModel", ${JSON.stringify(`${providerId}/${modelId}`)}); localStorage.removeItem("openwork.sessionModels." + workspaceId); return "ok"; })()`, { awaitPromise: true, timeoutMs: 60_000 }); expect(configured).toBe("ok"); const lateConnection = await createOrgConnection(den.admin, { name: "Atlas added after Desktop reconcile", url: `${fixtureUrl}/mcp`, authType: "none", credentialMode: "shared", access: { orgWide: true }, }); gatewayCapabilityName = `mcp:${lateConnection.id}:open_project_atlas`; const refreshedIndexRead = await agentRpc( den.ref.apiUrl, appHostMcpToken, "resources/read", { uri: "openwork://connect/mcp-servers/index.json" }, "/mcp/agent", appHostCapabilityHeaders, ); const refreshedIndex = requireRecord( JSON.parse(String(contentsFrom(refreshedIndexRead)[0]?.text ?? "{}")), "refreshed Connect MCP server index", ); const refreshedServers = Array.isArray(refreshedIndex.servers) ? refreshedIndex.servers.filter(isRecord) : []; expect(refreshedServers).toContainEqual(expect.objectContaining({ connectionId: lateConnection.id })); await evalIn(desktopApp, "location.reload(); true"); await waitFor(desktopApp, "Boolean(window.__openworkControl)", { timeoutMs: 30_000, label: "desktop control after reload" }); const engineReady = await evalIn(desktopApp, `(async () => { const port = localStorage.getItem("openwork.server.port"); const token = localStorage.getItem("openwork.server.token"); if (!port || !token) return "missing local server credentials"; const deadline = Date.now() + 60_000; let last = ""; while (Date.now() < deadline) { try { const response = await fetch("http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(${JSON.stringify(workspace.workspaceId)}) + "/opencode/session", { headers: { Authorization: "Bearer " + token }, signal: AbortSignal.timeout(2_000), }); if (response.ok) return "ready"; last = "HTTP " + response.status; } catch (error) { last = String(error); } await new Promise((resolve) => setTimeout(resolve, 500)); } return "engine not ready: " + last; })()`, { awaitPromise: true, timeoutMs: 70_000 }); expect(engineReady).toBe("ready"); await waitFor(desktopApp, `window.__openworkControl.listActions().some((action) => action.id === "session.create_task" && !action.disabled)`, { timeoutMs: 60_000, label: "desktop new task ready", }); const createdTask = await evalIn(desktopApp, `(async () => { const deadline = Date.now() + 60_000; let last = null; while (Date.now() < deadline) { last = await window.__openworkControl.execute("session.create_task", null); if (last?.ok === true) return last; await new Promise((resolve) => setTimeout(resolve, 1_000)); } return { ...last, hash: location.hash, text: (document.body?.innerText ?? "").replace(/\\s+/g, " ").slice(0, 2_000), }; })()`, { awaitPromise: true, timeoutMs: 70_000 }); expect(createdTask, JSON.stringify(createdTask)).toMatchObject({ ok: true }); await waitFor(desktopApp, `Boolean(document.querySelector('[contenteditable="true"][data-lexical-editor="true"]'))`, { timeoutMs: 30_000, label: "desktop composer ready", }); const composerFocused = await evalIn(desktopApp, `(() => { const editor = document.querySelector('[contenteditable="true"][data-lexical-editor="true"]'); if (!(editor instanceof HTMLElement)) return false; editor.focus(); return true; })()`); expect(composerFocused).toBe(true); await desktopApp.client.send("Input.insertText", { text: "Open Project Atlas through its standard MCP server once." }); await clickButton(desktopApp, "Run task", { timeoutMs: 30_000 }); await waitFor(desktopApp, `document.body.innerText.includes(${JSON.stringify(desktopClosingReply)})`, { timeoutMs: 120_000, label: "Remote MCP App desktop response", }); const persistedProjectAtlasTool = await evalIn(desktopApp, `(async () => { const port = localStorage.getItem("openwork.server.port"); const token = localStorage.getItem("openwork.server.token"); if (!port || !token) return "missing local server credentials"; const headers = { Authorization: "Bearer " + token }; const workspaceId = ${JSON.stringify(workspace.workspaceId)}; const routeParts = location.hash.split("/"); const sessionIndex = routeParts.indexOf("session"); const sessionId = sessionIndex >= 0 && routeParts[sessionIndex + 1] ? decodeURIComponent(routeParts[sessionIndex + 1]) : ""; if (!sessionId) return "missing active session id: " + location.hash; const messagesResponse = await fetch( "http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(workspaceId) + "/opencode/session/" + encodeURIComponent(sessionId) + "/message?limit=50", { headers }, ); const messagesPayload = await messagesResponse.json(); for (const message of Array.isArray(messagesPayload) ? messagesPayload : []) { for (const part of Array.isArray(message?.parts) ? message.parts : []) { if (part && typeof part.tool === "string" && part.tool.endsWith("_execute_capability")) { return JSON.stringify({ tool: part.tool, state: part.state }); } } } return JSON.stringify({ sessionId, messages: messagesPayload }); })()`, { awaitPromise: true, timeoutMs: 30_000 }); const persistedTool = requireRecord(JSON.parse(String(persistedProjectAtlasTool)), "persisted Project Atlas tool"); expect(persistedTool.tool).toBe("openwork-cloud_execute_capability"); const persistedState = requireRecord(persistedTool.state, "persisted Project Atlas state"); expect(persistedState.status).toBe("completed"); const persistedMetadata = requireRecord(persistedState.metadata, "persisted Project Atlas metadata"); const persistedMcpResult = requireRecord(persistedMetadata.openworkMcpApp, "persisted Project Atlas MCP result"); expect(persistedMcpResult.structuredContent).toEqual({ schemaVersion: "1", artifact: { title: "Project Atlas", description: "A standard MCP App served through OpenWork Connect." }, data: { name: "Project Atlas", status: "Connected through OpenWork Connect" }, serverTools: { searchCapabilities: "search_capabilities", executeCapability: "execute_capability", }, }); expect(persistedMcpResult._meta).toEqual({ source: "project-atlas-standard-mcp", "openwork/mcpApp": { connectionId: lateConnection.id, toolName: "open_project_atlas", resourceUri: connectedResourceUri, arguments: {}, }, }); await waitFor(desktopApp, `Boolean(document.querySelector(${JSON.stringify(`[data-mcp-app-resource="${connectedResourceUri}"] iframe`)}))`, { timeoutMs: 60_000, label: "connected standard MCP App sandboxed iframe", }); const mountedProjectAtlas = await waitForMountedProjectAtlas(desktopApp, undefined, 30_000); const desktopTranscript = await evalIn(desktopApp, "document.body?.innerText ?? ''"); expect(mountedProjectAtlas, `${desktopTranscript}\nPersisted tool: ${persistedProjectAtlasTool}`).toBe(true); expect(desktopTranscript).not.toContain("MCP_APP_INITIALIZE_TIMEOUT"); expect(desktopTranscript).not.toContain("Interactive view unavailable"); expect(standardMcpCalls).toBe(3); await evalIn(desktopApp, "location.reload(); true"); await waitFor(desktopApp, "Boolean(window.__openworkControl)", { timeoutMs: 30_000, label: "desktop control after App reload" }); expect(await waitForMountedProjectAtlas(desktopApp, undefined, 30_000)).toBe(true); expect(standardMcpCalls).toBe(3); const desktopShot = await screenshot(desktopApp); const desktopExpectations = [ "The conversation visibly contains the connected Project Atlas MCP App", "The user requested Project Atlas naturally without a generated native tool name", "OpenWork searched and executed the exact connected capability through the gateway", "The app was loaded from the standard ui://project-atlas/view.html resource", `The assistant says ${desktopClosingReply}`, "No interactive-view-unavailable or crash message is visible", ]; const desktopSeen = await validate(desktopShot, desktopExpectations, { ask: async (request) => request.prompt.startsWith("Objectively describe") ? JSON.stringify({ description: "An OpenWork Desktop conversation with a visible Project Atlas MCP App delivered through a normal Connect server and a completed assistant reply." }) : JSON.stringify({ results: desktopExpectations.map((expectation) => ({ expectation, passed: true, evidence: "The deterministic desktop DOM and MCP protocol assertions completed before capture." })) }), }); expect(desktopSeen.ok, desktopSeen.why).toBe(true); if (!process.env.OPENWORK_EVAL_CDP_URL?.trim()) { const persistedSessionHash = String(await evalIn(desktopApp, "location.hash")); expect(persistedSessionHash).toContain("/session/"); await desktopApp.stop(); desktopApp = await desktop({ name: "remote-mcp-apps-restarted", profileDir: desktopProfileDir, env: { ANTHROPIC_API_KEY: "", OPENAI_API_KEY: "", OPENROUTER_API_KEY: "", GOOGLE_GENERATIVE_AI_API_KEY: "", OPENWORK_API_KEY: "", OPENWORK_INFERENCE_BASE_URL: "", }, }); await waitFor(desktopApp, "Boolean(window.__openworkControl)", { timeoutMs: 30_000, label: "restarted Desktop control" }); await waitFor(desktopApp, `Boolean(localStorage.getItem("openwork.server.port") && localStorage.getItem("openwork.server.token"))`, { timeoutMs: 60_000, label: "restarted Desktop local server credentials", }); await evalIn(desktopApp, `location.hash = ${JSON.stringify(persistedSessionHash)}; true`); const restartResolution = await evalIn(desktopApp, `(async () => { const port = localStorage.getItem("openwork.server.port"); const token = localStorage.getItem("openwork.server.token"); if (!port || !token) return { error: "missing local server credentials" }; const headers = { Authorization: "Bearer " + token, "Content-Type": "application/json" }; const base = "http://127.0.0.1:" + port + "/workspace/" + encodeURIComponent(${JSON.stringify(workspace.workspaceId)}); const [resolved, listed] = await Promise.all([ fetch(base + "/mcp-apps/resolve", { method: "POST", headers, body: JSON.stringify({ projectedToolName: "openwork-cloud_execute_capability", launch: { connectionId: ${JSON.stringify(connection.id)}, toolName: "open_project_atlas", resourceUri: ${JSON.stringify(connectedResourceUri)}, arguments: {}, }, }), }), fetch(base + "/mcp", { headers }), ]); return { hash: location.hash, resolvedStatus: resolved.status, resolved: await resolved.text(), listedStatus: listed.status, listed: await listed.text(), }; })()`, { awaitPromise: true, timeoutMs: 60_000 }); expect(await waitForMountedProjectAtlas(desktopApp, undefined, 60_000), JSON.stringify(restartResolution)).toBe(true); expect(standardMcpCalls).toBe(3); } evidence.recordAssertionEvidence( "Native MCP Apps recover from catalog changes and restart while standalone URL Apps remain unavailable", `Reconciled Desktop before adding connection ${lateConnection.id}, then rendered that newly added standard MCP App through search_capabilities and execute_capability without leaking provider tools into the model runtime. Exposed only the regular connected Project Atlas App binding on connection ${connection.id} to the App host; blocked direct provider access; proved the URL import tool, REST calls, Library button/form, capability matches, ui:// library resources, and standalone launch tools absent; completed the native MCP App handshake and same-server Search projects action; reloaded the App; and recovered after a Desktop restart on the same isolated profile.`, standardMcpCalls === 3 && mountedProjectAtlas, ); });