Demonstrates dynamic client-defined tools — the SDK/platform pattern where tools are registered at runtime by the embedding application, not known by the server at deploy time.
This example shows how to build a chat agent where:
- The server is generic infrastructure — it accepts whatever tools the client sends
- The client defines tools dynamically (schemas + execute functions)
- Tool schemas are automatically sent to the server via the WebSocket protocol
- The LLM calls the tools, and results are routed back to the client for execution
This is the same architecture you would use when building an SDK or platform where third-party developers define tools in their embedding application.
The server uses createToolsFromClientSchemas() to convert client-provided schemas into AI SDK tools:
import { createToolsFromClientSchemas } from "@cloudflare/ai-chat";
async onChatMessage(_onFinish, options) {
const result = streamText({
model: workersai("@cf/moonshotai/kimi-k2.6"),
tools: createToolsFromClientSchemas(options?.clientTools),
// ...
});
return result.toUIMessageStreamResponse();
}The client passes tools via the tools option on useAgentChat:
import { useAgentChat, type AITool } from "@cloudflare/ai-chat/react";
const tools: Record<string, AITool> = {
getPageTitle: {
description: "Get the current page title",
parameters: { type: "object", properties: {} },
execute: async () => ({ title: document.title })
}
};
const { messages, sendMessage } = useAgentChat({
agent,
tools
});npm install && npm start- Server-side tools (
tool()from"ai"): Best for most apps. Full Zod type safety, simpler code, tools defined in one place. UseonToolCallfor client-side execution. - Dynamic client tools (this pattern): Best for SDKs, platforms, and multi-tenant systems where the tool surface is determined by the embedding application at runtime.
ai-chat— Server-side tools withtool(), approval, andonToolCallplayground— Kitchen-sink showcase of all SDK features