Client Tools
What are client tools
Client tools are functions in your app that the AI can call during a conversation. They execute in the browser. When the AI decides it needs to use a tool, the SDK validates the input against your JSON Schema, runs your execute function, and sends the result back to the AI so it can continue the conversation.
Use client tools for operations that can safely run in the browser: looking up order status, reading from local storage, fetching data from your API, or computing values from client-side state.
Defining a tool
Use defineClientTool to create a tool with a name, description, input schema, and an execute function:
import { defineClientTool } from 'gecx-chat';
const lookupOrder = defineClientTool({
name: 'lookup_order',
description: 'Look up an order by ID',
inputSchema: {
type: 'object',
required: ['orderId'],
properties: {
orderId: { type: 'string' },
},
},
execute: async ({ orderId }) => {
const res = await fetch(`/api/orders/${encodeURIComponent(orderId)}`);
return res.json();
},
});
The description tells the AI when and why to call the tool. The inputSchema is a JSON Schema object that defines what arguments the tool accepts. The execute function receives the validated input and returns a result object.
The execute function also receives a ToolExecutionContext as its second argument, which gives you sessionId, turnIndex, toolCallId, and an AbortSignal:
execute: async ({ orderId }, ctx) => {
ctx.signal.throwIfAborted();
const res = await fetch(`/api/orders/${encodeURIComponent(orderId)}`, {
signal: ctx.signal,
});
return res.json();
},
Registering tools
Pass your tools array in the client config or in the useChatSession hook config:
// With useChatSession (React)
const chat = useChatSession({
config: {
auth: tokenEndpointAuth({ endpoint: '/api/gecx-chat-token' }),
tools: [lookupOrder, checkInventory],
},
});
Unknown tool calls (tools the AI requests that you have not registered) fail closed automatically. The SDK will not execute a tool it does not know about.
Approval workflows
For tools that change state (placing an order, updating a profile, applying a refund), set permissions.requiresUserApproval to require explicit approval before the tool runs:
const cancelOrder = defineClientTool({
name: 'cancel_order',
description: 'Cancel an order',
inputSchema: {
type: 'object',
required: ['orderId'],
properties: { orderId: { type: 'string' } },
},
permissions: { requiresUserApproval: true },
execute: async ({ orderId }) => {
const res = await fetch(`/api/orders/${orderId}/cancel`, { method: 'POST' });
return res.json();
},
});
Wire an approval handler in your client config. The SDK ships three built-in handlers:
import {
createAutoApproveHandler,
createDenyAllHandler,
createSelectiveApprovalHandler,
} from 'gecx-chat';
// Auto-approve every tool call (use only in development)
const handler = createAutoApproveHandler();
// Deny every tool call that requires approval
const handler = createDenyAllHandler();
// Auto-approve only specific tools; deny the rest
const handler = createSelectiveApprovalHandler(['lookup_order', 'check_inventory']);
Pass the handler as approvalHandler in your client config. If a tool requires approval and no handler is set, the tool call is denied.
The richer approval taxonomy is shared with server tools:
| Policy | Use |
|---|---|
auto | Read-only or harmless tools. |
user_confirm | User confirmation in the host UI. requiresUserApproval: true maps here. |
supervisor_approve | Do not run in the browser; use a server tool and supervisor workflow. |
denied | Blocked by policy. |
async_pending | Accepted for asynchronous server-side processing. |
For privileged business actions such as refunds, address updates, subscription changes, order edits, or ticket creation with backend credentials, prefer Server Tools. Client tools should not contain service credentials or privileged secrets.
Timeouts
Every tool has a timeoutMs value. The default is 30 seconds. If the execute function does not resolve within the timeout, the SDK aborts the call and reports TOOL_TIMEOUT:
const slowTool = defineClientTool({
name: 'generate_report',
description: 'Generate a detailed report',
inputSchema: { type: 'object', properties: {} },
timeoutMs: 60_000, // 60 seconds
execute: async () => {
// long-running work
return { status: 'done' };
},
});
Validation
Before running your execute function, the SDK validates the AI's input against your inputSchema. If validation fails, the tool call is rejected with a TOOL_VALIDATION_FAILED error and your execute function never runs.
The validator supports a practical subset of JSON Schema: type, required, properties, enum, minLength, maxLength, pattern, minimum, maximum, and items for arrays.
Tool call lifecycle
Each tool call moves through a status flow. You can subscribe to these events via the tool registry's onEvent method:
requested -> awaiting_approval -> approved -> executing -> completed
-> pending_supervisor
-> async_pending
-> denied
-> duplicate
-> failed
-> timed_out
- requested -- the AI asked to call the tool
- awaiting_approval -- the tool requires user approval (skipped if
requiresUserApprovalis false) - pending_supervisor -- a server-side supervisor policy must approve the action
- async_pending -- a server-side action was accepted for asynchronous completion
- approved / denied -- the approval handler's decision
- executing -- the execute function is running
- completed -- the tool returned a result
- duplicate -- a server-side idempotency key was replayed or rejected
- failed -- the execute function threw an error (
TOOL_EXECUTION_FAILED) - timed_out -- the execute function did not resolve in time (
TOOL_TIMEOUT)
Bundled client-side tooling
A few client tools register automatically when their feature is configured. You do not need to add them by hand:
| Tool | Registered when | See |
|---|---|---|
memory.save, memory.update, memory.delete, memory.recall | ChatClientConfig.memory is set | Memory |
| Signal adapter tools (model-tool flavour) | ChatClientConfig.signals.adapters includes modelToolAdapter | Sentiment and Intent |
The computer_use capability is a server tool, not a client tool — it dispatches through /chat/tool-call to a proxy-hosted ComputerUseSession. See Computer-use.
What's next
- Server Tools -- tools that execute on your backend
- Error Handling -- structured errors and retry logic
- Computer-use -- sandboxed agent browsing as a server tool
docs/guides/client-tools.md