Skip to main content

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:

sandbox-proxy.html
<!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>
main.ts
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:

app.component.ts
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 {}
main.ts
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:

App.tsx
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.

  • 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 mcpApps configuration surface