Tool calling
Tool calling lets a model ask your code to do something — look up an order, run a query, call an API — and then use the result in its answer. You describe the tools; the model decides when to call them and with which arguments; your code runs them and sends back the results. The model never runs anything itself.
Use a model whose capabilities.tools is true in the model list. Most current text models qualify.
Define a tool
Each tool is a name, a description the model reads to decide when to use it, and a JSON Schema for its arguments:
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Current weather for a city.",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name, e.g. Berlin" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
additionalProperties: false,
},
},
},
];
The loop
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.cloakray.com/v1", apiKey: process.env.CLOAKRAY_API_KEY });
const messages = [{ role: "user", content: "Do I need a jacket in Berlin today?" }];
while (true) {
const completion = await client.chat.completions.create({ model: "gpt-5.5", messages, tools });
const message = completion.choices[0].message;
messages.push(message);
if (!message.tool_calls?.length) {
console.log(message.content);
break;
}
for (const call of message.tool_calls) {
const args = JSON.parse(call.function.arguments);
const result = await getWeather(args.city, args.unit); // your code
messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });
}
}
- Send the conversation and the tools.
- If the reply has
tool_calls(andfinish_reasonistool_calls), run each one. - Append the assistant message, then one
toolmessage per call with the matchingtool_call_id. - Call again. Repeat until the model answers in text.
A model can ask for several calls in one turn. Run them, in parallel if you like, and send every result back before the next request.
Choosing whether a tool is used
tool_choice | Effect |
|---|---|
"auto" (default) | The model decides. |
"none" | No tools this turn, even if they are listed. |
"required" | The model must call at least one tool. |
{"type": "function", "function": {"name": "get_weather"}} | The model must call that tool. |
On the Messages endpoint
The Anthropic format works the same way with different names: tools take an input_schema, the reply contains tool_use blocks with an id and input, and you answer with a user message holding tool_result blocks that reference that tool_use_id. See Anthropic Messages.
On the Responses API, tools are {"type": "function", "name": ..., "parameters": ...}, calls come back as function_call output items, and you return function_call_output items with the call_id.
Tips
- Validate arguments before acting on them. A model can produce arguments that match the schema but make no sense; treat them like user input.
- Keep descriptions specific. The description is the model's only guide to when a tool applies.
- Tool definitions count as input tokens on every request. With a long tool list, prompt caching keeps that cheap.
- When streaming, tool calls arrive in fragments — see streaming.