Files
openwork/evals/specs/remote-mcp-apps.e2e.test.ts

1134 lines
52 KiB
TypeScript

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<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function requireRecord(value: unknown, label: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(`${label} was not an object: ${JSON.stringify(value)}`);
return value;
}
async function waitForMountedProjectAtlas(
app: Awaited<ReturnType<typeof desktop>>,
expected: string[] = ["Project Atlas", "Connected through OpenWork Connect"],
timeoutMs = 60_000,
): Promise<boolean> {
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<string> {
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<string, string> {
const headers: Record<string, string> = {};
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<string, unknown>, 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<string, unknown>[]) {
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<string, unknown>) {
return Array.isArray(payload.messages)
? payload.messages.filter((message) => isRecord(message) && message.role === "tool").length
: 0;
}
function projectedToolEnding(payload: Record<string, unknown>, 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, unknown>): 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 <main><p className="eyebrow">REMOTE MCP APP</p><h1>{app.name}</h1><p>Cached immutable revision</p><p>{app.status}</p></main>;
}`,
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(/<meta http-equiv="Content-Security-Policy"[^>]*>/i, "");
function connectedAppHtml(version: string): string {
return portableAppDocument.replace(
"</body>",
`<!-- Portable revision ${version} --></body>`,
);
}
function standardMcpAppRpc(message: Record<string, unknown>): Record<string, unknown> {
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<string, unknown>,
endpoint = "/mcp/agent",
extraHeaders: Record<string, string> = {},
) {
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<ReturnType<typeof desktop>>;
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<string, unknown>) {
return Array.isArray(result.tools) ? result.tools.filter(isRecord) : [];
}
function contentsFrom(result: Record<string, unknown>) {
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<string, string> = {};
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<string, unknown>[] = [];
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<void>((resolve, reject) => {
fixture.once("error", reject);
fixture.listen(0, "127.0.0.1", resolve);
});
onTestFinished(async () => {
await new Promise<void>((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,
);
});