Client Tools
Let a managed Agent call functions implemented by its host application.A client tool is a function contract stored on an Agent whose implementation lives in the application that calls it. Use one when the application must own the action or render a domain-specific interface, such as product cards, an approval dialog, or a checkout summary.
"Client" means the Responses API client. The function normally runs in your server or application backend, not inside Rebyte and not necessarily in a web browser.
Client tools and MCP tools
| Client tool | MCP tool | |
|---|---|---|
| Definition | Stored on the Agent | MCP server reference stored on the Agent |
| Implementation | Host application | MCP server |
| Execution | Host application receives a function_call | Rebyte calls the MCP server |
| Result | Host submits function_call_output | Rebyte records the MCP result automatically |
Define the tool once
Add the definition when creating or updating the Agent:
{
"clientTools": [
{
"type": "function",
"name": "present_products",
"description": "Render product cards in the host application.",
"parameters": {
"type": "object",
"properties": {
"product_ids": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["product_ids"],
"additionalProperties": false
},
"strict": true
}
]
}
The equivalent agent.toml is:
[[client_tools]]
type = "function"
name = "present_products"
description = "Render product cards in the host application."
strict = true
[client_tools.parameters]
type = "object"
required = ["product_ids"]
additionalProperties = false
[client_tools.parameters.properties.product_ids]
type = "array"
minItems = 1
maxItems = 20
items = { type = "string" }
Every field is required. name must be unique within the Agent and contain
1–64 letters, numbers, underscores, or hyphens. parameters is a JSON object;
Rebyte stores the schema without choosing or hosting its implementation.
The Agent API also accepts the function object produced by the official SDK's Zod helper, provided it has a description:
import { zodResponsesFunction } from "openai/helpers/zod";
import { z } from "zod";
const presentProducts = zodResponsesFunction({
name: "present_products",
description: "Render product cards in the host application.",
parameters: z.object({ product_ids: z.array(z.string()).min(1).max(20) }),
});
// Include `presentProducts` in `clientTools` when creating or updating the Agent.
Strict parameter schemas
Client tools use the OpenAI strict function-schema subset. The root must have
type: "object". Every object schema, including nested objects and object
branches inside anyOf, must define properties, list every property exactly
once in required, and set additionalProperties: false.
Every nested schema uses exactly one structural form: type, anyOf, or a
local $ref to #, a root $defs entry, or a root draft-07 definitions
entry. Array schemas must define items.
To make a value optional, keep its property name in required and use exactly
one value type plus null, for example type = ["string", "null"] in TOML.
Non-nullable schemas use a single string for type.
The supported schema keywords are:
| Purpose | Keywords |
|---|---|
| Structure | type, properties, required, additionalProperties, items |
| Dialect, composition, and reuse | $schema, anyOf, $defs, definitions, $ref |
| Values and descriptions | enum, const, description |
| Strings | minLength, maxLength, pattern, format |
| Numbers | multipleOf, minimum, maximum, exclusiveMinimum, exclusiveMaximum |
| Arrays | minItems, maxItems |
format accepts date-time, time, date, duration, email, hostname,
ipv4, ipv6, or uuid.
$schema accepts the draft-07 URI emitted by the official OpenAI SDK's Zod
helper. minLength, maxLength, minItems, and maxItems are supported.
default, allOf, oneOf, not, conditional schemas, and other unlisted
keywords are rejected instead of being silently ignored.
A schema may nest up to 10 levels and contain up to 5,000 object properties
and 1,000 enum values. Property names, $defs names, string enum values, and
string constants may contain at most 120,000 characters in total. An enum with
more than 250 values may contain at most 15,000 characters across its string
values.
Execution lifecycle
Host application Rebyte Agent
│ │
│ Response A: user input │
├─────────────────────────────────────►│
│ function_call(name, call_id, args) │
│◄─────────────────────────────────────┤
│ │
│ execute function locally │
│ │
│ Response B: function_call_output │
├─────────────────────────────────────►│
│ continued Agent output │
│◄─────────────────────────────────────┤
Response A and Response B use the same Conversation. When Response A contains
multiple calls, execute all of them and submit all results together. Each
result identifies its pending call by call_id; in Rebyte's focused Responses
subset, function_call_output.output must be a string, so serialize structured
results with JSON.stringify:
const first = await rebyte.responses.create({
model: process.env.REBYTE_AGENT_ID,
input: "Find a tent and show me the choices.",
});
const calls = first.output.filter((item) => item.type === "function_call");
if (calls.length === 0) throw new Error("No client tool call returned");
const outputs = await Promise.all(calls.map(async (call) => ({
type: "function_call_output",
call_id: call.call_id,
output: JSON.stringify(
await executeClientTool(call.name, JSON.parse(call.arguments)),
),
})));
const continued = await rebyte.responses.create({
model: process.env.REBYTE_AGENT_ID,
conversation: first.conversation.id,
input: outputs,
});
Use the official OpenAI SDK for both calls. It already supports the standard
function_call and function_call_output objects; there is no Rebyte
Responses SDK to install or modify.
Do not send the tool schemas with either Response. They belong to the Agent.
Do not use previous_response_id; the Conversation associates the call and
its result. A continuation containing function_call_output requires the
Conversation returned by the Response that emitted the call.
Streaming
With stream: true, accumulate arguments from
response.function_call_arguments.delta or use the complete JSON string from
response.function_call_arguments.done. Execute only completed calls, collect
every result, and wait for response.completed before submitting the result
batch in the same Conversation.
The host application is responsible for validating arguments before acting, authorizing access to its own resources, handling idempotency for side effects, and returning only the data the Agent needs.