Client tools
Tools that your own app runs.
Some tools cannot run anywhere but in your app: reading the form the user is looking at, highlighting a row in their table, opening the page the agent is talking about. A client registers on a session, declares tools like these, and answers the calls the agent makes to them. A browser tab is a client; so is a backend worker.
The model sees a client’s tools as client__<name>__<tool>, and the console’s conversation shows their calls as coming from that client.
Registering
POST /v1/sessions/{session_id}/clients
{
"name": "web",
"lifetime": { "type": "ttl", "data": { "seconds": 60 } },
"tools": [
{
"name": "highlight_row",
"description": "Highlight a row in the table the user is looking at.",
"parameters": {
"type": "object",
"properties": { "row_id": { "type": "string" } },
"required": ["row_id"]
}
}
]
}
| Field | |
|---|---|
name |
1 to 32 characters: lowercase letters, digits, and -. If the session already spent the name, a counter is appended — the response says which name the client got. |
lifetime |
{"type": "never"} ends only when the client leaves. {"type": "ttl", "data": {"seconds": N}}, 5 to 3600, ends N seconds after the last ping, so a closed tab stops offering tools nothing can answer. |
tools |
Optional, up to 64. Each has a name (1 to 64 characters of A–Z, a–z, 0–9, _, -), a description up to 4,096 characters, and a JSON Schema object for its parameters. |
The answer is the client’s id, its assigned name, and when it expires. A session holds up to 32 live clients. The session’s token reaches these routes, as does a key with Sessions › Manage.
Staying registered
Give a tab a TTL and ping while it is open, with a new Idempotency-Key
each time. When the pings stop, the registration lapses.
POST /v1/sessions/{session_id}/clients/{client_id}/pingA ping that answers 404 means the registration lapsed: register again.
A worker that lives as long as the session can use never and skip the
pings. It leaves when it says so, which frees its name and fails any call
it still owed.
DELETE /v1/sessions/{session_id}/clients/{client_id}Answering a call
Watch the stream for client_tool_dispatched: it names your client_id, the call_id, the tool as you declared it, and the arguments.
Acknowledge it
An ack says the client is working on the call. A call must be acked within 60 seconds; once acked, it has five minutes to be answered.
POST /v1/sessions/{session_id}/clients/{client_id}/calls/{call_id}/ackPost the result
POST /v1/sessions/{session_id}/clients/{client_id}/calls/{call_id}/result{ "text": "Row 42 is highlighted.", "is_error": false }text is up to 65,536 characters; is_error marks a failure the model
should read.
If the client goes away, or misses a deadline, the call settles as client_tool_failed with its reason — client_gone, ack_timeout, result_timeout, or run_gone — and the model gets an error result, so the run carries on instead of stalling.
Changing what you offer
The list a client declares replaces the one before it, so a tool you stop naming is a tool you stopped offering:
PUT /v1/sessions/{session_id}/clients/{client_id}/tools
A page that navigates keeps its tools honest this way: replace the list on every screen change — before answering the call that navigated — and the model is only offered what is there.
The TypeScript SDK’s @subako-ai/tools ships ready-made client tools that drive a page: fill a form, submit it, navigate.