useClientTool()
Register a browser tool on the current thread.
A client tool runs in the app shell: pick a file, read the current form, call a native API. The server still records the call; the browser performs it.
The model sees a client tool only while it is mounted. Unmount the page, and the next turn cannot call it.
Assistant.useClientTool("getSelection", {
execute: async () => ({
text: window.getSelection()?.toString() ?? "",
}),
});Register the same tool on the binding when every page should expose it:
export const Assistant = Pai.agent("main", {
clientTools: {
getSelection: {
execute: async () => ({
text: window.getSelection()?.toString() ?? "",
}),
},
},
});Import
Assistant.useClientTool("getSelection", { execute });Signature
Server-declared client-routed tool (no execute on defineTool):
function useClientTool<TName extends ClientToolName<TContract>>(
toolName: TName,
options: ClientToolOptions<TContract, TName>,
): ClientToolRegistrationState;Browser-only tool:
function useClientTool(
definition: ClientDefinedToolOptions<TContract>,
): ClientToolRegistrationState;Identity helper for a clientTools map value:
function clientTool(
definition: ClientDefinedToolOptions<TContract>,
): ClientDefinedToolOptions<TContract>;function ClientTool(props: ClientToolProps<TContract>): null;Parameters
toolName: ClientToolName<TContract>
Registration name of a server-declared client-routed tool.
options: ClientToolOptions
execute: (ctx) => output | Promise<output>
Browser implementation.
render?: ToolRenderFn
Card for this client tool.
key?: string
definition: ClientDefinedToolOptions
Frontend-only tool.
name: string
description: string
inputSchema: z.ZodType
outputSchema: z.ZodType
execute: (ctx) => output | Promise<output>
render?: ToolRenderFn
dataSchemas?: Record<string, z.ZodType>
Returns
A ClientToolRegistrationState.
Mount/unmount bookkeeping. The tool is visible to the model only while mounted.
Package-root useClientTool / clientTool / ClientTool follow the nearest ThreadProvider. Bound Assistant.useClientTool keeps that agent's contract.
Examples
Server-declared tool
The server defineTool omits execute. The browser supplies it:
Assistant.useClientTool("getSelection", {
execute: async () => ({
text: window.getSelection()?.toString() ?? "",
}),
});Browser-only tool
Assistant.useClientTool({
name: "getSelection",
inputSchema: z.object({}),
outputSchema: z.object({ text: z.string() }),
execute: async () => ({
text: window.getSelection()?.toString() ?? "",
}),
});Learn more
- Client tools
createClientToolHarnessdefineTool()(omitexecuteon the server)