Client Tools
Client tools are tools you declare in the browser that the model calls and the browser executes โ no server-side implementation. There are three kinds:
| Helper | Kind | What it does |
|---|---|---|
action() | function | Runs an async handler in the browser; its resolved return value becomes the tool result sent back to the model. |
view() | render-only component | The model fills the component's props from the schema; the card renders inline and the call is auto-acknowledged once it mounts. |
ask() | interactive component | The model fills the component's props; the value the component emits back becomes the tool result (human-in-the-loop). |
Tools are arguments-typed by a Standard Schema (e.g. a Zod object). The catalog is shipped to the model by the adapter; the backend graph binds the client stubs and ends its turn so the browser executes them.
The same declarations work with @threadplane/langgraph and @threadplane/ag-ui โ only the provideAgent/injectAgent imports change.
#Declaring a registry
tools({...}) collects named tools into a frozen registry. Pass it to <chat> via [clientTools]:
The object keys (get_weather, weather_card, confirm_booking) are the tool names the model sees. tools() preserves each tool's precise generic type, so downstream lookups stay typed.
#Typed component props with ViewProps
For view() and ask(), the component's signal inputs are checked against the schema output at compile time โ every field the schema produces must be a declared input() with an assignable type (the component may declare extra inputs the schema doesn't fill). Derive the input types directly from the schema with ViewProps<typeof schema> so the two never drift:
Under strict: true, the typed view/ask overloads report a compile error at the view(...)/ask(...) call site if the component's inputs diverge from the schema โ mismatches become build errors, not silent runtime failures.
#Typed handler args with ToolArgs
For action(), the handler argument type is inferred from the schema automatically. When you want to name that type โ e.g. to write the handler separately โ use ToolArgs<typeof schema> (an alias of the schema's inferred output):
#Typed agent state
Tool handlers and components often read agent state. Pair the registry with a typed AgentRef so agent.state() / agent.value() carry your state shape instead of Record<string, unknown> โ see Typed state via AgentRef:
#API reference
| Export | Purpose |
|---|---|
action(description, schema, handler) | Declare a function tool (handler return โ result) |
view(description, schema, component) | Declare a render-only component tool (auto-acknowledged) |
ask(description, schema, component) | Declare an interactive component tool (emitted value โ result) |
tools(map) | Freeze a name-keyed registry for [clientTools] |
ViewProps<S> | Component input prop bag inferred from a schema |
ToolArgs<S> | Handler argument type inferred from a schema |
ClientToolDef / ClientToolRegistry | The tool-definition union and frozen-registry types |