Skip to content

Integration Tutorial

This guide demonstrates how to integrate the RTC Agent Web Component into your web application, covering both quick CDN setup and full NPM integration with SharedWorker configuration.

MethodUse CaseProsCons
CDN (quick start)Documentation sites, demos, prototypesZero config, Workers resolve automaticallyDepends on external CDN, limited customization
NPM + createRtcAgent() (recommended)Production applications, full controlType safety, tree-shaking, SharedWorker for multi-tabRequires SharedWorker setup (one-time)

For documentation sites or quick demos, load via CDN and use the createRtcAgent() factory function:

<!-- Import and configure in one step -->
<script type="module">
import { createRtcAgent } from 'https://cdn.jsdelivr.net/npm/@rtc-agent/component@0.3.0-rc.4/dist/index.js';
const agent = createRtcAgent({
server: { url: 'https://rtc-agent.cherish.chat' },
});
document.body.appendChild(agent);
</script>

The component automatically connects to the Server on the current page’s domain.

File: astro.config.mjs

import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
site: 'https://your-site.github.io',
base: '/docs',
integrations: [
starlight({
title: 'Your Site',
head: [
// Load and configure RTC Agent using factory function
{
tag: 'script',
attrs: { type: 'module' },
content: `
import { z, withMeta, createRtcAgent } from 'https://cdn.jsdelivr.net/npm/@rtc-agent/component@0.3.0-rc.4/dist/index.js';
const initRtcAgent = async () => {
if (document.querySelector('#rtc-agent-global')) return;
const agent = createRtcAgent({
appLabel: 'RTC Agent Assistant',
theme: 'system',
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: '/docs/auth/callback.html',
},
window: {
defaultMode: 'minimized',
draggable: true,
resizable: true,
bubblePosition: {
corner: 'bottom-right',
offset: { x: -24, y: 24 },
},
},
agentName: 'DocsAssistant',
persona: 'You are a helpful documentation assistant.',
groups: [{
name: 'docs',
description: 'Documentation functions',
functions: [
{
name: 'searchDocs',
description: 'Search documentation by keywords',
zodSchema: z.object({
query: withMeta(z.string(), { example: 'RTC Agent setup' })
.describe('Search keywords')
}),
handler: async ({ query }) => {
// Your search implementation
return [{ path: '/getting-started/', title: 'Quick Start' }];
},
},
],
}],
});
const container = document.createElement('div');
container.id = 'rtc-agent-global';
container.appendChild(agent);
document.body.appendChild(container);
// Global styles
const style = document.createElement('style');
style.textContent = \`
#rtc-agent-global {
position: fixed;
bottom: 0;
right: 0;
z-index: 9999;
pointer-events: none;
}
#rtc-agent-global rtc-agent {
pointer-events: auto;
}
\`;
document.head.appendChild(style);
};
document.addEventListener('DOMContentLoaded', () => initRtcAgent());
`,
},
],
}),
],
});

Key differences from the old pattern:

  • ✅ Import createRtcAgent, z, withMeta from CDN
  • ✅ Use factory function instead of manual element creation
  • ✅ All configuration in one declarative object
  • ✅ No need to wait for rtc-agent-ready event
  • ✅ Function registration uses zodSchema with withMeta examples

If you need GitHub login (or other OAuth providers), create a callback page to handle the OAuth redirect.

File: public/auth/callback.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Authorization Complete</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
background: white;
border-radius: 16px;
padding: 48px;
text-align: center;
max-width: 400px;
}
.icon { font-size: 48px; margin-bottom: 16px; }
</style>
</head>
<body>
<div class="container">
<div class="icon" id="icon">⏳</div>
<h1 id="title">Processing...</h1>
<p id="message">Please wait, completing authorization</p>
</div>
<script>
(function() {
var params = new URLSearchParams(window.location.search);
var code = params.get('code');
var state = params.get('state');
var error = params.get('error');
var icon = document.getElementById('icon');
var title = document.getElementById('title');
var message = document.getElementById('message');
if (error) {
icon.textContent = '✕';
title.textContent = 'Authorization Failed';
message.textContent = error;
return;
}
if (code && state) {
var target = window.opener || window.parent;
if (target) {
target.postMessage({
type: 'oauth-callback',
code: code,
state: state,
}, window.location.origin);
icon.textContent = '✓';
title.textContent = 'Authorization Successful';
message.textContent = 'Returning to app...';
setTimeout(function() { window.close(); }, 2000);
}
}
})();
</script>
</body>
</html>

In your GitHub OAuth App (or other provider), set the callback URL:

  • Development: http://localhost:4321/docs/auth/callback.html
  • Production: https://your-site.github.io/docs/auth/callback.html

Start the development server:

终端窗口
pnpm dev

Visit http://localhost:4321/docs/ — you should see the RTC Agent floating widget in the bottom-right corner.

CDN loading ensures SharedWorker file paths resolve correctly without additional build tool configuration.

2. Use the createRtcAgent() factory function

Section titled “2. Use the createRtcAgent() factory function”

The factory function consolidates all configuration in a single declarative object — no manual element creation or waiting for rtc-agent-ready events:

const agent = createRtcAgent({
appLabel: 'My AI Assistant',
server: { url: 'https://rtc-agent.cherish.chat' },
window: { defaultMode: 'minimized', draggable: true },
});
<!-- Skip SSR, render client-side only -->
<rtc-agent client:only="astro"></rtc-agent>

The component uses Shadow DOM for style isolation — no conflicts with your Starlight theme.

This project’s docs site has already completed the integration. Refer to these files:

Production Integration: NPM + createRtcAgent()

Section titled “Production Integration: NPM + createRtcAgent()”

For production applications, use the createRtcAgent() factory function with full type safety and SharedWorker support for multi-tab synchronization.

终端窗口
pnpm add @rtc-agent/component
# or
npm install @rtc-agent/component
# or
yarn add @rtc-agent/component

Step 2: Setup SharedWorker (Required for Multi-Tab)

Section titled “Step 2: Setup SharedWorker (Required for Multi-Tab)”

RTC Agent uses a SharedWorker to multiplex WebSocket connections across browser tabs. The worker file must be accessible from the browser’s public directory.

Run the CLI tool to automatically copy SharedWorker files to the correct location:

终端窗口
npx rtc-agent-setup

The tool automatically:

  • Detects your project type (Vite, Webpack, SvelteKit, etc.)
  • Copies SharedWorker files to the correct directory (static/rtc-agent/ or public/rtc-agent/)
  • Creates a stable filename shared-worker.js for easy reference
  • Generates manifest.json with version information
  • Cleans up old worker files to prevent accumulation

Automatic Setup on Install (Best Practice)

Section titled “Automatic Setup on Install (Best Practice)”

Add the setup tool to your postinstall script to run automatically after npm install:

{
"scripts": {
"postinstall": "rtc-agent-setup"
}
}

This ensures:

  • SharedWorker files are set up automatically for new developers
  • Worker files are updated when upgrading @rtc-agent/component
  • No manual steps required after dependency changes

💡 If you already have a postinstall script, append && rtc-agent-setup to it:

{
"scripts": {
"postinstall": "your-existing-script && rtc-agent-setup"
}
}

If the CLI tool doesn’t work for your setup, manually copy the worker files:

终端窗口
# Create target directory
mkdir -p public/rtc-agent
# Copy worker file and rename to stable filename
cp node_modules/@rtc-agent/component/dist/assets/shared-worker-*.js public/rtc-agent/shared-worker.js

Use the factory function to create and configure the component:

import { z, withMeta, createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
// Create the agent instance
const agent: RtcAgentWithLifecycle = createRtcAgent({
// Basic configuration
appLabel: 'My AI Assistant',
theme: 'system',
lang: 'en-US',
// Server configuration
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: '/auth/callback.html',
},
// SharedWorker URL (required for multi-tab support)
workerURL: '/rtc-agent/shared-worker.js',
// Scenario documentation
scenariosURL: '/scenarios/',
// Window configuration
window: {
defaultMode: 'normal',
bubblePosition: {
corner: 'bottom-right',
offset: { x: -24, y: 24 },
},
},
// Function registration (using Zod schema - recommended)
agentName: 'MyApp',
agentDescription: 'AI assistant for my application',
persona: 'You are a helpful assistant...',
groups: [
{
name: 'editor',
description: 'Editor operations',
functions: [
{
name: 'getCode',
description: 'Get the current code',
handler: () => window.editorAPI.getCode(),
returns: { zodSchema: z.string().describe('Current code in the editor') },
},
{
name: 'setCode',
description: 'Set the editor code',
zodSchema: z.object({
code: withMeta(z.string(), { example: 'console.log("hello")' }).describe('The code to set'),
}),
handler: (params) => {
window.editorAPI.setCode(params.code);
return { success: true };
},
},
],
},
],
// Event handlers
on: {
ready: () => {
console.log('RTC Agent is ready');
},
},
});
// Append to DOM
document.body.appendChild(agent);
// Cleanup when done
agent.destroy();

The auth property in createRtcAgent() controls how the component authenticates with the server. Choose the mode that fits your application:

Pass a fixed access token directly. Simple but not suitable for production — the token expires and cannot be refreshed.

const agent = createRtcAgent({
server: { url: 'https://rtc-agent.example.com' },
workerURL: '/rtc-agent/shared-worker.js',
auth: {
accessToken: 'your-jwt-token',
userId: 'user-123',
},
});
Section titled “Mode 2: Dynamic Token (Recommended for Production)”

Provide a getToken callback (required) and an optional refreshToken callback so the component can fetch and renew tokens on demand. This is the recommended approach for most production applications.

const agent = createRtcAgent({
server: { url: 'https://rtc-agent.example.com' },
workerURL: '/rtc-agent/shared-worker.js',
auth: {
userId: 'user-123',
getToken: async () => {
const res = await fetch('/api/auth/token');
const data = await res.json();
return data.accessToken;
},
refreshToken: async () => {
const res = await fetch('/api/auth/refresh', { method: 'POST' });
return res.json();
},
},
});

Mode 3: Auth Provider (Advanced / Multi-Tenant)

Section titled “Mode 3: Auth Provider (Advanced / Multi-Tenant)”

Implement a full auth interface with isLoggedIn and logout hooks. Use this when you have an existing authentication system or need multi-tenant support.

const agent = createRtcAgent({
server: { url: 'https://rtc-agent.example.com' },
workerURL: '/rtc-agent/shared-worker.js',
auth: {
getToken: async () => myAuthProvider.getToken(),
refreshToken: async () => myAuthProvider.refresh(),
isLoggedIn: () => myAuthProvider.isLoggedIn(),
logout: async () => myAuthProvider.logout(),
getUserId: () => myAuthProvider.getUserId(), // returns the current user's unique ID (strongly recommended)
},
});

See Authentication & Authorization for full details on the login flow, token lifecycle, and security best practices.

<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { base } from '$app/paths';
import { createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
let rtcAgent: RtcAgentWithLifecycle | null = null;
onMount(() => {
rtcAgent = createRtcAgent({
appLabel: 'My App',
theme: 'system',
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: `${base}/auth/callback.html`,
},
workerURL: `${base}/rtc-agent/shared-worker.js`,
scenariosURL: `${base}/scenarios/`,
// ... other configuration
});
document.body.appendChild(rtcAgent);
});
onDestroy(() => {
if (rtcAgent) {
rtcAgent.destroy();
rtcAgent = null;
}
});
</script>
import { useEffect, useRef } from 'react';
import { createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
function RtcAgentWrapper() {
const agentRef = useRef<RtcAgentWithLifecycle | null>(null);
useEffect(() => {
agentRef.current = createRtcAgent({
appLabel: 'My App',
theme: 'system',
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: '/auth/callback.html',
},
workerURL: '/rtc-agent/shared-worker.js',
// ... other configuration
});
document.body.appendChild(agentRef.current);
return () => {
if (agentRef.current) {
agentRef.current.destroy();
agentRef.current = null;
}
};
}, []);
return null;
}
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
const agentRef = ref<RtcAgentWithLifecycle | null>(null);
onMounted(() => {
agentRef.value = createRtcAgent({
appLabel: 'My App',
theme: 'system',
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: '/auth/callback.html',
},
workerURL: '/rtc-agent/shared-worker.js',
// ... other configuration
});
document.body.appendChild(agentRef.value);
});
onUnmounted(() => {
if (agentRef.value) {
agentRef.value.destroy();
agentRef.value = null;
}
});
</script>
PropertyTypeDefaultDescription
appLabelstring"RTC Agent"Title bar text and tooltip
theme"light" | "dark" | "system""system"Theme mode
lang"zh-CN" | "en-US""zh-CN"UI language
databaseNamestring"rtc-agent"IndexedDB name prefix
PropertyTypeDefaultDescription
server.urlstring""Server address (falls back to current domain)
server.redirectUristring"/auth/callback.html"OAuth callback URL
PropertyTypeDefaultDescription
workerURLstringundefinedURL to the SharedWorker file (required for multi-tab support)
PropertyTypeDefaultDescription
auth.accessTokenstring—Static JWT token (Mode 1)
auth.userIdstring—User identifier
auth.getToken() => Promise<string>—Async callback to obtain an access token (Mode 2/3)
auth.refreshToken() => Promise<object>—Async callback to refresh the token (optional in Mode 2, required in Mode 3)
auth.isLoggedIn() => boolean—Check whether the user is authenticated (Mode 3)
auth.logout() => Promise<void>—Log the user out (Mode 3)
auth.getUserId() => string—Return the current user’s unique identifier (Mode 3, strongly recommended for per-user IndexedDB isolation)
PropertyTypeDefaultDescription
window.defaultMode"normal" | "maximized" | "minimized""normal"Initial window mode
window.embeddedbooleanfalseEmbedded mode (no drag/resize/buttons)
window.draggablebooleantrueWhether window can be dragged
window.resizablebooleantrueWhether window can be resized
window.bubblePosition.corner"top-left" | "top-right" | "bottom-left" | "bottom-right""bottom-right"Bubble position corner
window.bubblePosition.offset.xnumber-20Horizontal offset
window.bubblePosition.offset.ynumber20Vertical offset
PropertyTypeDefaultDescription
agentNamestring—Application name
agentDescriptionstring—Application description
personastring—AI persona/instructions
groupsFunctionGroup[][]Function groups for tool registration
PropertyTypeDescription
on.ready() => voidCalled when component is ready
on.beforeDestroy() => voidCalled when the component is about to be destroyed (during disconnectedCallback, before cleanup)

When you upgrade @rtc-agent/component, re-run the setup tool to update the SharedWorker files:

终端窗口
npx rtc-agent-setup

The tool will:

  1. Copy the new worker files
  2. Update manifest.json with the new version
  3. Overwrite the old shared-worker.js

For production deployments, use version-based cache busting:

// Read manifest.json
const manifest = await fetch('/rtc-agent/manifest.json').then(r => r.json());
const workerURL = `/rtc-agent/shared-worker.js?v=${manifest.version}`;
// Use in createRtcAgent()
const agent = createRtcAgent({
workerURL,
// ...
});

Or configure your web server to disable caching for the worker file:

location /rtc-agent/shared-worker.js {
add_header Cache-Control "no-cache, must-revalidate";
}

Symptom: Console shows 404 error for SharedWorker file

Solution:

  1. Verify worker files exist: ls public/rtc-agent/ or ls static/rtc-agent/
  2. Check workerURL path matches the actual file location
  3. Restart the dev server
  4. Re-run npx rtc-agent-setup

Symptom: Changes in one tab don’t appear in other tabs

Solution:

  1. Ensure workerURL is configured in createRtcAgent()
  2. Check browser console for SharedWorker errors
  3. Verify all tabs are on the same origin (protocol + domain + port)
  4. Check that SharedWorker is not blocked by browser extensions

Symptom: Cannot find module '@rtc-agent/component'

Solution:

  1. Verify package is installed: pnpm list @rtc-agent/component
  2. Check node_modules/@rtc-agent/component exists
  3. Reinstall: pnpm install