Frontend Integration Overview
Ozwell is an embeddable AI assistant that runs inside an iframe on your website. Users chat with it. The AI can call tools you define β JavaScript functions that read from or write to your page. Conversations are private by default; your page only sees tool calls and lifecycle events, never message content.
Try it live: See Ozwell in action at the demo site.
What You're Buildingβ
There are two parts: define tools on your page so the AI knows what it can do, then handle tool calls when the AI invokes them.
1. Define Tools and Load the Widgetβ
Your page declares the tools the AI can call β their names, descriptions, and parameter schemas. By default, an agent can call any tool the page presents. No server-side configuration needed.
Here's a page that exposes two tools: one that reads the current email (get) and one that updates it (set):
<script>
window.OzwellChatConfig = {
apiKey: 'agnt_key-your-agent-key',
tools: [
{
type: 'function',
function: {
name: 'get_user_email',
description: 'Reads the current email address from the page',
parameters: { type: 'object', properties: {} }
}
},
{
type: 'function',
function: {
name: 'set_user_email',
description: 'Updates the email address on the page',
parameters: {
type: 'object',
properties: {
email: { type: 'string', description: 'The new email address' }
},
required: ['email']
}
}
}
]
};
</script>
<script src="https://ozwell-dev-refserver.opensource.mieweb.org/embed/ozwell-loader.js"></script>
2. Handle Tool Callsβ
When the AI calls a tool, the loader dispatches an ozwell-tool-call event. Listen for it and call respond() with the result:
<script>
document.addEventListener('ozwell-tool-call', (e) => {
const { name, arguments: args, respond } = e.detail;
if (name === 'get_user_email') {
// READ from your page
respond({ email: document.getElementById('email').value });
} else if (name === 'set_user_email') {
// WRITE to your page
document.getElementById('email').value = args.email;
respond({ success: true });
}
});
</script>
You must call respond(). The AI waits for the result β if you don't respond, the conversation hangs.
Where Tools Can Be Definedβ
| Approach | Tools defined in | Best for |
|---|---|---|
| Page-defined (above) | OzwellChatConfig.tools | Pages that declare their own capabilities |
| Agent-defined | Agent definition server-side | Centrally managed tools with full schemas and descriptions |
| Both | Agent definition + OzwellChatConfig.tools | Agent owns core tools; page adds context-specific tools |
How it works:
- If the agent has no tools in its server-side definition, it can call any tool the page provides. This is the default.
- If the agent has a tools list defined server-side, those tools are always available. The page can provide additional tools alongside them.
- Agent-defined tool schemas take priority β if the same tool name appears in both, the agent's definition wins.
Tool Namespacingβ
Page-provided tools are automatically prefixed with postMessage: on the wire so they can't collide with tools the agent server implements natively. This is transparent to your page β the prefix is added by the loader when the widget discovers tools, and stripped before your ozwell-tool-call handler fires. You never see the prefix in your code.
Controlling Page Toolsβ
By default, agents accept all page-provided tools. The agent definition can restrict this with the pageTools field:
# Allow all page tools (default β can be omitted)
pageTools: all
# Only allow specific page tools
pageTools:
restricted:
- get_user_email
- set_user_email
# Allow all page tools except these
pageTools:
blocked:
- dangerous_tool
See Agent Registration API β pageTools for details.
The widget runs in a sandboxed iframe β it cannot touch your DOM, read your cookies, or access your JavaScript directly. Only tool calls and their responses cross the iframe boundary.
β‘οΈ Full tutorial with tool calling β the CDN embed guide walks through this step by step.
Integration Approachesβ
Quick Comparisonβ
| Method | Setup Time | Build Required | Best For |
|---|---|---|---|
| CDN Embed | ~5 min | No | Static sites, quick prototypes |
| Framework | ~15 min | Yes | Production SPAs |
| Iframe | ~10 min | Optional | Custom implementations |
CDN Embed (Fastest)β
Add Ozwell to any website with a single script tag. No build step required. Supports tool calling out of the box β define what your AI can do, then handle tool calls in a simple event listener.
<script>
window.OzwellChatConfig = { apiKey: 'agnt_key-your-agent-key' };
</script>
<script src="https://ozwell-dev-refserver.opensource.mieweb.org/embed/ozwell-loader.js"></script>
β‘οΈ Full CDN documentation with tool calling tutorial
Framework Integrationβ
For production applications using modern JavaScript frameworks, we provide dedicated integration guides:
| Framework | Guide | Status |
|---|---|---|
| React | React Integration | β |
| Next.js | Next.js Integration | β |
| Vue 3 | Vue 3 Integration | β |
| Vue 2 | Vue 2 Integration | β |
| Svelte | Svelte Integration | β |
| Vanilla JS | Vanilla JS Integration | β |
All framework integrations render Ozwell within an isolated iframe, ensuring:
- π Security isolation from your host page
- π¨ Consistent styling that won't conflict with your CSS
- π± Responsive behavior out of the box
Standards-inspired: Ozwell's iframe architecture implements an inverted MCP postMessage transport, drawing from proposals by Josh Mandel and the W3C WebMCP community. Learn more in MCP postMessage Standard.
Security Modelβ
Privacy Firstβ
Ozwell is built on a foundation of user privacy and control. When a user opens Ozwell, their conversation is privateβthe host site cannot see, intercept, or log what is said. This creates a safe space where users can:
- Ask any question without embarrassment
- Explore topics freely without surveillance
- Trust that their dialogue stays between them and Ozwell
Sharing is always opt-in. Only when a user explicitly chooses to share information does it become visible to the host site.
Scoped API Keysβ
Frontend integrations use scoped API keys which are:
- β Agent-specific: Tied to a single agent configuration
- β Permission-limited: Only allows operations the agent is authorized for
- β Rate-limited: Protected against abuse
- β Revocable: Can be rotated or disabled without affecting other keys
Iframe Isolationβ
All frontend integrations run inside an iframe with:
- Sandboxed execution β No access to parent page DOM
- Origin isolation β Separate security context
- CSP compliance β Strict content security policies
- No message relay β Conversation content stays in the iframe
- User-controlled sharing β Only explicit user actions can share data
β‘οΈ Learn more about iframe security
Customization Optionsβ
Appearanceβ
| Option | Description | Default |
|---|---|---|
theme | Light or dark mode | auto |
primaryColor | Accent color for buttons/links | #4f46e5 |
position | Widget position (bottom-right, bottom-left, etc.) | bottom-right |
width | Chat window width | 400px |
height | Chat window height | 600px |
Behaviorβ
| Option | Description | Default |
|---|---|---|
autoOpen | Open chat on page load | false |
greeting | Initial message to display | Agent's default |
placeholder | Input field placeholder text | "Type a message..." |
Privacy by Designβ
The conversation between Ozwell and the user is private by default.
Why Privacy Mattersβ
- No message relay: Conversation content is never sent to the host site
- Safe space: Users can ask any question without fear of judgment or surveillance
- User control: Only the user decides if and when to share conversation details
- Trust: The Ozwell brand stands for privacy and user empowerment
What the Host Site Can Seeβ
The host site receives only lifecycle events, never message content:
// β
Allowed: Lifecycle events (no content)
window.addEventListener('ozwell:ready', () => {
console.log('Ozwell widget loaded');
});
window.addEventListener('ozwell:open', () => {
console.log('Chat opened');
});
window.addEventListener('ozwell:close', () => {
console.log('Chat closed');
});
// β Not available: Message content is private
// window.addEventListener('ozwell:message', ...) β Does not exist
User-Initiated Sharingβ
If the user explicitly chooses to share information with the host site, they can do so through in-chat actions:
// Only triggered when user clicks "Share with site" in chat
window.addEventListener('ozwell:user-share', (event) => {
// User explicitly consented to share this specific data
console.log('User shared:', event.detail);
});
This ensures users always feel comfortable asking questionsβeven ones they might consider "dumb"βknowing the conversation stays between them and Ozwell.
Next Stepsβ
- Quick start: Try the CDN embed first
- Production app: Follow your framework guide above
- Custom needs: Review iframe integration
- Standards context: Read about the MCP postMessage Standard that inspired Ozwell's architecture
- Security deep-dive: Understand the iframe security model