Not every tool an agent can call runs in the browser. A tool declares where it executes with kind:
kind | Who runs it | Studio's part |
|---|---|---|
client (default) | Studio, in the page | Advertises the schema and executes it |
server | The agent's own harness | Advertises the schema only |
provided | The LLM provider | Passes the provider's declaration through |
Only client tools have an execute. The other two are declared here and run elsewhere.
Server Tools Copy Link
Use kind: 'server' when the client should advertise a schema that your server executes. That is a narrow case:
- If your agent runs its own loop and the server owns a tool entirely, the server declares it and Studio never sees it. You need nothing here.
- If the client is the one that knows the schema, because it depends on live dashboard state, but the work must happen server-side, declare it here.
const lookupCustomer = api.defineAiTool({
kind: 'server',
name: 'lookup_customer',
description: 'Fetch a customer record from the CRM.',
params: (s) => s.object({ customerId: s.string({ description: 'CRM customer id.' }) }),
});Studio advertises the schema to the model. When the model calls it, Studio executes nothing and returns a placeholder response in its place, so the loop keeps moving and your harness can do the work.
Provider Tools Copy Link
Some providers host tools themselves - web search, code execution, file retrieval. These are never called back to your code: the provider runs them inside the same request and returns the result.
Declare one with kind: 'provided' and hand over the provider's own declaration:
const webSearch = api.defineAiTool({
kind: 'provided',
name: 'web_search',
description: 'Search the web for current information.',
provider: { type: 'web_search' },
});provider is opaque to Studio. It is attached to the tool's schema as-is and reaches your adapter, which turns it into whatever the provider expects - see Tool Calls.
Because the shape is provider-specific, a provided tool ties that agent to that provider. Keep the declaration next to the adapter it belongs with.
A provider-side tool your adapter injects without Studio knowing about it needs no declaration at all, and never appears in a Studio tool list. Declare it only when you want it visible alongside the rest, in the panel's message list and in the telemetry stream.
Listing Them Copy Link
External tools are listed exactly like any other:
directLlmRunner({
id: 'analyst',
adapter,
tools: () => [studio.viewSchema(), studio.executeQuery(), lookupCustomer, webSearch],
}); Next Copy Link
- Direct LLM Runner - where a provider tool's declaration is translated
- Client Tool Runner - when the loop runs on your server
- Custom Tools - client tools