diff --git a/fern/docs.yml b/fern/docs.yml index ba3d04943..0a4f8a9a7 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -185,6 +185,9 @@ navigation: - page: Custom tools path: tools/custom-tools.mdx icon: fa-light fa-screwdriver-wrench + - page: Client-side tools (Web SDK) + path: tools/client-side-websdk.mdx + icon: fa-light fa-browser - page: Tool rejection plan path: tools/tool-rejection-plan.mdx icon: fa-light fa-shield-xmark diff --git a/fern/tools/client-side-websdk.mdx b/fern/tools/client-side-websdk.mdx new file mode 100644 index 000000000..cca52c8e5 --- /dev/null +++ b/fern/tools/client-side-websdk.mdx @@ -0,0 +1,296 @@ +--- +title: Client-side Tools (Web SDK) +subtitle: Handle tool-calls in the browser without a server URL +slug: tools/client-side-websdk +--- + +## Overview + +Use the Web SDK to handle tool-calls entirely on the client. This lets your assistant trigger UI-side effects (like showing notifications or changing state) directly in the browser. + +**In this guide, you'll learn to:** +- Define a client-side tool with the Web SDK +- Receive and handle `tool-calls` events on the client +- Inject extra context during a call with `addMessage` + + +Client-side tools cannot send a tool "result" back to the model. If the model must use the output of a tool to continue reasoning, implement a server-based tool instead. See: Server-based Custom Tools. + + + +To make a tool client-side, simply do not provide a server URL. The tool specification is delivered to the browser, and the Web SDK emits tool-calls messages that your frontend can handle. + + +## Quickstart + +1. Install the Web SDK: + +```bash +npm install @vapi-ai/web +``` + +2. Start a call with your tool defined in the model.tools array and subscribe to clientMessages: ['tool-calls']. +3. Listen for message.type === 'tool-calls' and perform the desired UI update. No response is sent back to the model. +4. (Optional) Inject context mid-call using vapi.addMessage(...). + +## Complete example (React + Web SDK) + +```tsx +import Vapi from '@vapi-ai/web'; +import { useCallback, useState } from 'react'; + +const vapi = new Vapi(''); + +function App() { + const [notification, setNotification] = useState(null); + + const handleUIUpdate = useCallback((message?: string) => { + setNotification(message || 'UI Update Triggered!'); + setTimeout(() => setNotification(null), 3000); + }, []); + + // 1) Listen for client tool-calls and update the UI + vapi.on('message', (message) => { + console.log('Message:', message); + + if (message.type === 'tool-calls') { + const toolCalls = message.toolCallList; + + toolCalls.forEach((toolCall) => { + const functionName = toolCall.function?.name; + let parameters: Record = {}; + + try { + const args = toolCall.function?.arguments; + if (typeof args === 'string') { + parameters = JSON.parse(args || '{}'); + } else if (typeof args === 'object' && args !== null) { + parameters = args as Record; + } else { + parameters = {}; + } + } catch (err) { + console.error('Failed to parse toolCall arguments:', err); + return; + } + + if (functionName === 'updateUI') { + handleUIUpdate((parameters as any).message); + } + }); + } + }); + + // 2) Start the call with a client-side tool (no server URL) + const startCall = useCallback(() => { + vapi.start({ + model: { + provider: 'openai', + model: 'gpt-4.1', + messages: [ + { + role: 'system', + content: + "You are an attentive assistant who can interact with the application's user interface by calling available tools. Whenever the user asks to update, refresh, change, or otherwise modify the UI, or hints that some UI update should occur, always use the 'updateUI' tool call with the requested action and relevant data. Use tool calls proactively if you determine that a UI update would be helpful.", + }, + ], + tools: [ + { + type: 'function', + async: true, + function: { + name: 'updateUI', + description: + 'Call this function to initiate any UI update whenever the user requests or implies they want the user interface to change (for example: show a message, highlight something, trigger an animation, etc). Provide an \'action\' describing the update and an optional \'data\' object with specifics.', + parameters: { + type: 'object', + properties: { + message: { + description: + 'Feel free to start with any brief introduction message in 10 words.', + type: 'string', + default: '', + }, + }, + required: ['message'], + }, + }, + messages: [ + { + type: 'request-start', + content: 'Updating UI...', + blocking: false, + }, + ], + }, + ], + }, + voice: { provider: 'vapi', voiceId: 'Elliot' }, + transcriber: { provider: 'deepgram', model: 'nova-2', language: 'en' }, + name: 'Alex - Test', + firstMessage: 'Hello.', + voicemailMessage: "Please call back when you're available.", + endCallMessage: 'Goodbye.', + clientMessages: ['tool-calls'], // subscribe to client-side tool calls + }); + }, []); + + const stopCall = useCallback(() => { + vapi.stop(); + }, []); + + return ( +
+ {notification && ( +
+ {notification} +
+ )} + +
+

+ Vapi Client Tool Calls +

+ +

+ Start a call and ask the assistant to trigger UI updates +

+ +
+ + + +
+
+
+ ); +} + +export default App; +``` + +## Inject data during the call + +Use addMessage to provide extra context mid-call. This does not return results for a tool; it adds messages the model can see. + +```ts +// Inject system-level context +vapi.addMessage({ + role: 'system', + content: 'Context: userId=123, plan=premium, theme=dark', +}); + +// Inject a user message +vapi.addMessage({ + role: 'user', + content: 'FYI: I switched to the settings tab.', +}); +``` + + +If you need the model to consume tool outputs (e.g., fetch data and continue reasoning with it), implement a server-based tool. See Custom Tools. + + +## Key points + +- **Client-only execution**: Omit the server URL to run tools on the client. +- **One-way side effects**: Client tools do not send results back to the model. +- **Subscribe to events**: Use clientMessages: ['tool-calls'] and handle message.type === 'tool-calls'. +- **Add context**: Use vapi.addMessage to inject data mid-call. + +## Next steps + +- **Server-based tools**: Learn how to return results back to the model with Custom Tools. +- **API reference**: See Tools API for full configuration options.