Client Tools
Overview
A client tool runs inside the user's browser. When the LLM decides to call the tool, the agent invokes the handler you registered in your frontend code and waits for its result. Use client tools when the action belongs on the user's device — reading browser state, calling a device API, or touching a service that should not transit your server.
When to Use Client over Server
| Use a client tool when… | Use a server tool when… |
|---|---|
| The data lives in the browser (form fields, route, local storage). | The data lives in your backend or a third-party API. |
| You need to trigger a UI action (open a modal, navigate, scroll). | You need to write to a database, queue, or external system. |
| The credential must not leave the user's device. | The credential is a server-held API key. |
Define the Tool
Create the tool with config.type: "client". The configuration is minimal — the LLM only needs the schema and description; the actual code lives in your frontend.
curl -X POST "https://api.d-id.com/tools" \
-H "Authorization: Basic <YOUR KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "get_user_cart",
"description": "Return the items currently in the visitor'\''s shopping cart. Use this when the user asks about their cart contents or checkout total.",
"provider": "Storefront",
"schema": {
"parameters": {
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"config": {
"type": "client"
}
}'{
"id": "tool_cart_lookup",
"name": "get_user_cart",
"config": { "type": "client" },
"created_at": "2026-05-20T12:00:00.000Z"
}Attach it to an agent the same way as a server tool — see Attaching Tools to Agents.
Register the Handler
Two registration paths cover both integration modes. The handler signature is identical in both.
With the Client SDK
Call registerClientTool on the AgentManager instance. Handlers can be registered before or after connect(); pre-connect handlers are buffered and attached when the session opens.
import { createAgentManager } from '@d-id/client-sdk';
const agentManager = createAgentManager('agt_xyz789', {
auth: { type: 'key', clientKey: '<CLIENT_KEY>' },
callbacks: { /* ... */ },
});
agentManager.registerClientTool('get_user_cart', async (args) => {
const cart = readCartFromStore();
return JSON.stringify({
items: cart.items,
total: cart.total,
currency: cart.currency,
});
});
await agentManager.connect();To remove a handler, call agentManager.unregisterClientTool(name).
With the Agents UI Embed
The embed exposes the same API on window.DID_AGENTS_API.functions. Wait for the connection to be ready before registering — calling too early throws "Agent manager is not initialized."
<script src="https://agent.d-id.com/index.js"
data-name="did-agent"
data-agent-id="agt_xyz789"
data-client-key="<CLIENT_KEY>">
</script>
<script>
window.DID_AGENTS_API.events.on('connection', ({ state }) => {
if (state !== 'Connected') return;
window.DID_AGENTS_API.functions.registerClientTool(
'get_user_cart',
async (args) => {
const cart = readCartFromStore();
return JSON.stringify({
items: cart.items,
total: cart.total,
currency: cart.currency,
});
}
);
});
</script>Handler Contract
| Field | Type | Description |
|---|---|---|
args | object | The parsed JSON arguments the LLM produced from the tool's schema. |
| Return | Promise<string> | A JSON-serialized string. Max 15 KiB. |
Rules:
- The handler must be async (or return a Promise).
- The return value must be a string. Stringify objects with
JSON.stringifybefore returning. - Throwing inside the handler propagates as a tool error — the LLM sees the error message and can respond accordingly.
- If the agent invokes a tool name that has no registered handler, the call fails with
No handler registered for client tool: <name>.
Observability
Tool lifecycle is surfaced through SDK callbacks and embed events.
Client SDK
Pass an onToolEvent callback when creating the agent manager:
const agentManager = createAgentManager('agt_xyz789', {
auth: { /* ... */ },
callbacks: {
onToolEvent: (event) => {
// event.type: 'ToolCallStarted' | 'ToolCallDone' | 'ToolCallError'
// event.payload: { call_id, name, input, output, duration_ms, ... }
console.log(event.type, event.payload);
},
},
});Agents UI Embed
Subscribe to agent activity to drive UI affordances (e.g. show a "running tool" indicator):
window.DID_AGENTS_API.events.on('agentActivity', ({ state }) => {
if (state === 'TOOL_ACTIVE') {
// a tool is currently executing
}
});