Examples
Proxy Page
A minimal page that renders the proxy and, in workspace mode, calls IOBrowser() so the element has an io instance to discover:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>MCP Apps Sandbox Proxy</title>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
mcp-apps-proxy {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<mcp-apps-proxy></mcp-apps-proxy>
<script type="module" src="./main.js"></script>
</body>
</html>
import "@interopio/mcp-apps-proxy";
const params = new URLSearchParams(window.location.search);
const isWorkspaceMode = params.get("mode") === "workspace";
if (isWorkspaceMode) {
const { default: IOBrowser } = await import("@interopio/browser");
await IOBrowser();
}
Inline mode needs no io.Connect call at all — the element relays messages over window.postMessage as soon as it is connected to the DOM. Only call IOBrowser() when ?mode=workspace is present, since that is the only mode that needs io.Connect interop.
Framework Usage
<mcp-apps-proxy> is a standard web component, so you can render it from Angular or React instead of a static HTML page.
Angular
Add CUSTOM_ELEMENTS_SCHEMA so Angular allows the unknown element, and call IOBrowser() (e.g. via @interopio/ng's provideIoConnect) somewhere in the app - the element discovers that io instance on its own:
import "@interopio/mcp-apps-proxy";
import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
@Component({
selector: "app-root",
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<mcp-apps-proxy></mcp-apps-proxy>`,
})
export class AppComponent {}
import { bootstrapApplication } from "@angular/platform-browser";
import IOBrowser from "@interopio/browser";
import { provideIoConnect } from "@interopio/ng";
import { AppComponent } from "./app/app.component";
const isWorkspaceMode = new URLSearchParams(window.location.search).get("mode") === "workspace";
const providers = isWorkspaceMode ? [provideIoConnect({ browser: { factory: IOBrowser } })] : [];
bootstrapApplication(AppComponent, { providers });
React
React does not know the element's property types, so declare a JSX intrinsic type for it. No ref is needed anymore - just call IOBrowser() in workspace mode:
import { useEffect } from "react";
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"mcp-apps-proxy": React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
}
}
}
const initWorkspaceIo = async (): Promise<void> => {
const isWorkspaceMode = new URLSearchParams(window.location.search).get("mode") === "workspace";
if (!isWorkspaceMode) return;
const { default: IOBrowser } = await import("@interopio/browser");
await IOBrowser();
};
export const App = () => {
useEffect(() => {
initWorkspaceIo();
}, []);
return <mcp-apps-proxy />;
};
Wiring the Host
Point your host's sandboxProxyUrl at wherever you serve the page above:
const aiWeb = await IoAiWebFactory(io, {
mcp: {
mcpApps: {
sandboxProxyUrl: "/sandbox-proxy.html",
displayMode: "workspace", // or "inline", or omit for auto-detection
},
},
});
The host appends ?mode=workspace to this URL itself when it opens the proxy as a workspace window — you do not add it manually.
Related Documentation
- Overview for installation and workspace-mode io discovery
- Sandbox Proxy for why a sandbox proxy is required and how display modes map to transports
- AI Web Configuration for the full
mcpAppsconfiguration surface