Skip to content

Function Registration Guide

Function Registration is the bridge that lets AI invoke your business capabilities. Registered functions automatically generate documentation that AI can read and learn to use — no manual prompt engineering, no extra integration code needed.

ApproachUse CaseAdvantages
📋 DeclarativeMost scenarios (recommended)Zero config, just set agentConfig
🔧 ImperativeFine-grained control needed (multiple Registries, dynamic registration)Flexible, manual lifecycle management

The simplest approach — just set the agentConfig attribute:

import { z, withMeta } from '@rtc-agent/component';
const agent = document.querySelector('rtc-agent');
agent.agentConfig = {
name: 'OrderApp',
persona: 'You are an order management assistant, helping users create, query, and process orders.',
groups: [
{
name: 'order',
description: 'Order management operations',
functions: [
{
name: 'create',
description: 'Create a new order',
zodSchema: z.object({
productId: withMeta(z.string(), { example: 'PROD-001' }).describe('Product ID'),
quantity: withMeta(z.number().int().positive(), { example: 2 }).optional().describe('Purchase quantity')
}),
returns: { zodSchema: z.object({ orderId: z.string() }).describe('Order info, including orderId') },
handler: async (params) => {
const res = await api.createOrder(params.productId, params.quantity);
return { orderId: res.id, status: res.status };
}
}
]
}
]
};

💡 After setting agentConfig, the system automatically creates the Registry, registers functions, and generates documentation — completely zero-config.

For fine-grained control, use defineRegistry to register manually:

import { defineRegistry } from '@rtc-agent/component';
const registry = defineRegistry({
name: 'OrderApp',
description: 'Order management application',
persona: 'You are an order management assistant.'
});
const orderGroup = registry.createGroup({
name: 'order',
description: 'Order management operations'
});
orderGroup.register({
name: 'create',
description: 'Create a new order',
handler: async (params) => {
return await api.createOrder(params.productId, params.quantity);
}
});

Each function consists of the following fields:

FieldTypeRequiredDescription
📛 namestring✅Function name; combined with group name to form the full path (e.g., order.create)
📝 descriptionstring✅Function description, written into auto-generated documentation; AI uses this to decide when to call
🎯 zodSchemaZodType—Recommended Zod schema for parameter validation and type inference. Provides runtime validation and automatic TypeScript types
📐 parametersParameterDef[]—Legacy OpenAPI-style parameter definitions. Use zodSchema instead for new code
🔙 returnsReturnDef—Return value definition {schema?, zodSchema?, description?}, helping AI understand the output. zodSchema is recommended. Supports all major Zod types (ZodObject, ZodArray, ZodUnion, etc.) with automatic OpenAPI schema conversion. Nested objects are recursively expanded into property tables in auto-generated docs, matching Parameters behavior
⚡ handlerfunction✅Execution function; supports async, receives params argument
🪝 hooksobject—UI hooks (onStart / onSuccess / onError / onProgress)

Zod schemas provide runtime validation, type safety, and better developer experience:

import { z, withMeta } from '@rtc-agent/component';
{
name: 'createOrder',
description: 'Create a new order',
zodSchema: z.object({
productId: withMeta(z.string(), { example: 'PROD-001' }).describe('Product ID'),
quantity: withMeta(z.number().int().positive(), { example: 2 }).describe('Purchase quantity'),
couponCode: withMeta(z.string(), { example: 'SAVE10' }).optional().describe('Optional coupon code')
}),
handler: async (params) => {
// params is fully typed: { productId: string; quantity: number; couponCode?: string }
return await api.createOrder(params.productId, params.quantity, params.couponCode);
}
}

Benefits of Zod Schema:

  • ✅ Runtime validation: Parameters are automatically validated before handler execution
  • ✅ Type inference: TypeScript automatically infers parameter types from the schema
  • ✅ Composable: Easy to combine and reuse schemas
  • ✅ Descriptive: Use .describe() to add parameter descriptions for documentation
  • ✅ Example values: Use withMeta(schema, { example }) to add example values for auto-generated docs
import { z, withMeta } from '@rtc-agent/component';
{
name: 'refund',
description: 'Initiate a refund for a specified order, supporting full and partial refunds',
zodSchema: z.object({
orderId: withMeta(z.string(), { example: 'ORD-20240101-001' }).describe('Order ID'),
amount: withMeta(z.number().positive(), { example: 99.99 }).optional().describe('Refund amount (omit for full refund)'),
reason: withMeta(z.string(), { example: 'Product defective' }).describe('Refund reason')
}),
returns: {
zodSchema: z.object({
refundId: z.string(),
status: z.string(),
}).describe('Refund result, including refundId and status'),
},
handler: async (params, onProgress) => {
onProgress?.(30);
await validateOrder(params.orderId);
onProgress?.(70);
const result = await processRefund(params.orderId, params.amount);
return { refundId: result.id, status: result.status };
},
hooks: {
onStart: () => showToast('Processing refund...'),
onSuccess: (result) => showToast(`Refund successful: ${result.refundId}`),
onError: (err) => showToast(`Refund failed: ${err.message}`)
}
}
Section titled “Legacy OpenAPI-Style Parameters (Not Recommended)”

For backward compatibility, you can still use OpenAPI-style parameters:

{
name: 'refund',
description: 'Initiate a refund',
parameters: [
{ name: 'orderId', schema: { type: 'string' }, required: true, description: 'Order ID' },
{ name: 'amount', schema: { type: 'number' }, description: 'Refund amount' }
],
handler: async (params) => { /* ... */ }
}

⚠️ Migration Note: We recommend migrating to zodSchema for new functions. Zod provides better type safety, runtime validation, and a more concise syntax.

RuleRecommendedAvoid
📛 Group nameBusiness domain nouns (order, user, payment)Too broad (utils, misc, do)
🔤 Function nameVerbs or verb-object phrases (create, getProfile)Meaningless abbreviations (fn1, doIt)
📐 GranularityEach function does one thingOne function does everything

📌 Full Path: Group name + function name = full invocation path. For example, order.create, payment.refund.

After registration, AI calls functions in a sandbox via the script tool:

AI has two equivalent invocation syntaxes:

// Approach 1: Proxy chain call (natural syntax)
await rtcAgent.order.create({ productId: '123', quantity: 2 });
// Approach 2: callFunction method
await rtcAgent.callFunction('order.create', { productId: '123', quantity: 2 });

💡 Proxy chain calls let AI use registered functions just like calling a regular API — intuitive syntax, hard to get wrong.

Hooks let the host application intervene at various stages of function execution, enabling UI feedback, logging, and more:

HookTriggerDescription
🟦 onStartBefore executionCan throw CancelledError to cancel execution
🟩 onSuccessAfter successExecutes asynchronously, does not block main flow
🟥 onErrorAfter failureExecutes asynchronously, does not block main flow
🟨 onProgressProgress updateTriggered when handler calls onProgress(n)
// Hook example: Show Toast notifications
{
hooks: {
onStart: () => showToast('Operation starting...'),
onSuccess: (result) => showToast(`Operation complete: ${result.id}`),
onError: (err) => showToastError(`Operation failed: ${err.message}`)
}
}
Generated ContentDescription
📄 Function DocsOne Markdown file per function, including description, parameter table, return values (nested objects recursively expanded into property tables), and call examples (with @returns JSDoc comments)
📑 Index FileINDEX.md lists all available functions for easy AI browsing
🧠 Agent GuideAGENT.md summarizes all function capabilities, helping AI understand the overall context

💡 Register and Use Immediately: Documentation is auto-generated at registration time; AI can read it directly to learn function usage — no extra prompt engineering needed.