Share model context across iframe boundaries
The Assistant Frame API enables iframes to provide model context (tools and instructions) to a parent window's assistant. This is particularly useful for embedded applications, plugins, or sandboxed components that need to contribute capabilities to the main assistant.
Overview
The Assistant Frame system consists of two main components:
- AssistantFrameProvider: Runs inside the iframe and provides model context
- AssistantFrameHost: Runs in the parent window and consumes context from iframes
Basic Usage
In the iframe (Provider)
The iframe acts as a provider of model context using AssistantFrameProvider:
// iframe.tsx
import { AssistantFrameProvider } from "@assistant-ui/react";
import { ModelContextRegistry } from "@assistant-ui/react";
import { z } from "zod";
// Create a registry to manage your model context
const registry = new ModelContextRegistry();
// Expose the registry to the parent window
AssistantFrameProvider.addModelContextProvider(
registry,
"https://parent-app.com",
);
// Add tools that will be available to the parent assistant
registry.addTool({
toolName: "searchProducts",
description: "Search for products in the catalog",
parameters: z.object({
query: z.string(),
category: z.string().optional(),
}),
execute: async ({ query, category }) => {
// Tool implementation runs in the iframe
const results = await searchAPI(query, category);
return { products: results };
},
});
// Add system instructions
const instructionHandle = registry.addInstruction(
"You are a helpful assistant.",
);
// update the instruction
instructionHandle.update("You have access to a product catalog search tool.");In the parent window (Host)
The parent window consumes the iframe's context using AssistantFrameHost:
// parent.tsx
import { useAssistantFrameHost } from "@assistant-ui/react";
import { useRef } from "react";
function ParentComponent() {
const iframeRef = useRef<HTMLIFrameElement>(null);
// Connect to the iframe's model context
useAssistantFrameHost({
iframeRef,
targetOrigin: "https://trusted-iframe-domain.com",
});
return (
<div>
<Thread /> {/* Your assistant-ui */}
<iframe
ref={iframeRef}
src="https://trusted-iframe-domain.com/embed"
title="Embedded App"
/>
</div>
);
}Advanced Usage
ModelContextRegistry
The ModelContextRegistry provides a flexible way to manage model context dynamically:
const registry = new ModelContextRegistry();
// Add a tool with handle for updates
const toolHandle = registry.addTool({
toolName: "convertCurrency",
description: "Convert between currencies",
parameters: z.object({
amount: z.number(),
from: z.string(),
to: z.string(),
}),
execute: async ({ amount, from, to }) => {
const rate = await fetchExchangeRate(from, to);
return { result: amount * rate, currency: to };
},
});
// Update the tool later
toolHandle.update({
toolName: "convertCurrency",
description: "Convert between currencies with live rates", // Updated description
parameters: z.object({
amount: z.number(),
from: z.string(),
to: z.string(),
includesFees: z.boolean().optional(),
}),
execute: async ({ amount, from, to, includesFees }) => {
const rate = await fetchExchangeRate(from, to);
const fee = includesFees ? 0.02 : 0; // 2% fee
return {
result: amount * rate * (1 - fee),
currency: to,
fee: includesFees ? amount * rate * fee : 0,
};
},
});
// Remove the tool when no longer needed
toolHandle.remove();
// Add multiple instructions
const instruction1 = registry.addInstruction("Be helpful and concise.");
const instruction2 = registry.addInstruction("Use metric units.");
// Remove instructions
instruction1.remove();Multiple Providers
You can register multiple model context providers in the same iframe:
const catalogRegistry = new ModelContextRegistry();
const analyticsRegistry = new ModelContextRegistry();
// Add different tools to each registry
catalogRegistry.addTool({
/* ... */
});
analyticsRegistry.addTool({
/* ... */
});
// Register both providers
const unsubscribe1 =
AssistantFrameProvider.addModelContextProvider(
catalogRegistry,
"https://parent-app.com",
);
const unsubscribe2 =
AssistantFrameProvider.addModelContextProvider(
analyticsRegistry,
"https://parent-app.com",
);
// Later, unsubscribe if needed
unsubscribe1();
unsubscribe2();Security Considerations
Origin Validation
Both the provider and host validate message origins. They default to the current page's origin, so cross-origin frames must specify the exact trusted origin on both sides:
// In iframe - only accept messages from specific parent
AssistantFrameProvider.addModelContextProvider(
registry,
"https://parent-app.com",
);
// In parent - only accept messages from specific iframe
useAssistantFrameHost({
iframeRef,
targetOrigin: "https://iframe-app.com",
});Tool Execution
Tools are executed in the iframe's context, keeping sensitive operations sandboxed:
registry.addTool({
toolName: "accessDatabase",
description: "Query the database",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
// This runs in the iframe with iframe's permissions
// Parent cannot directly access the database
const results = await db.query(query);
return results;
},
});API Reference
AssistantFrameProvider
Static class that manages model context providers in an iframe.
Methods
addModelContextProvider(provider, targetOrigin?)
Registers a model context provider to share with parent windows.
const unsubscribe = AssistantFrameProvider.addModelContextProvider(
registry,
"https://parent-domain.com",
);When targetOrigin is omitted, the provider only communicates with window.location.origin. All providers in a frame share one origin policy. Registering an explicit origin tightens an existing wildcard policy, while registering two different explicit origins throws an error. Unsubscribing drops that registration's requirement. The wildcard value * is available as an explicit opt-in, but it should not be used for contexts that expose tools or sensitive instructions.
dispose()
Cleans up all resources and removes all providers.
AssistantFrameProvider.dispose();AssistantFrameHost
Class that connects to an iframe's model context providers.
Constructor
const host = new AssistantFrameHost(
iframeWindow,
targetOrigin?, // Defaults to window.location.origin
);Methods
getModelContext()
Returns the current merged model context from the iframe.
const context = host.getModelContext();
// { system: "...", tools: { ... } }subscribe(callback)
Subscribes to model context changes.
const unsubscribe = host.subscribe(() => {
console.log("Context updated:", host.getModelContext());
});dispose()
Cleans up the connection to the iframe.
host.dispose();useAssistantFrameHost
React hook that manages the lifecycle of an AssistantFrameHost.
useAssistantFrameHost({
iframeRef: RefObject<HTMLIFrameElement>,
targetOrigin?: string, // Defaults to window.location.origin
});ModelContextRegistry
A flexible registry for managing model context with dynamic updates.
Methods
addTool(tool)
Adds a tool and returns a handle for updates/removal.
const handle = registry.addTool({
toolName: string,
description?: string,
parameters: ZodSchema | JSONSchema,
execute: (args, context) => Promise<any>,
});
handle.update(newTool); // Update the tool
handle.remove(); // Remove the tooladdInstruction(instruction)
Adds a system instruction and returns a handle.
const handle = registry.addInstruction("Be concise.");
handle.update("Be detailed."); // Update instruction
handle.remove(); // Remove instructionaddProvider(provider)
Adds another model context provider.
const handle = registry.addProvider(anotherProvider);
handle.remove(); // Remove providerUse Cases
Embedded Analytics Dashboard
An analytics iframe can provide data query tools to the parent assistant:
// In analytics iframe
registry.addTool({
toolName: "queryMetrics",
description: "Query analytics data",
parameters: z.object({
metric: z.string(),
timeRange: z.string(),
}),
execute: async ({ metric, timeRange }) => {
const data = await analyticsAPI.query(metric, timeRange);
return { data, visualization: createChart(data) };
},
});Plugin System
Third-party plugins can extend the assistant's capabilities:
// In plugin iframe
registry.addTool({
toolName: "translateText",
description: "Translate text to another language",
parameters: z.object({
text: z.string(),
targetLanguage: z.string(),
}),
execute: async ({ text, targetLanguage }) => {
return await pluginAPI.translate(text, targetLanguage);
},
});Data Visualization
Provide data visualization tools in an iframe:
// In visualization iframe
registry.addTool({
toolName: "createChart",
description: "Generate a chart from data",
parameters: z.object({
data: z.array(
z.object({
label: z.string(),
value: z.number(),
}),
),
chartType: z.enum(["bar", "line", "pie"]),
title: z.string().optional(),
}),
execute: async ({ data, chartType, title }) => {
// Generate chart using a library like Chart.js or D3
const chartUrl = await generateChart(data, chartType, title);
return {
chartUrl,
summary: `Created ${chartType} chart with ${data.length} data points`,
};
},
});