Skip to content

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) });
  }
}
  1. Send the conversation and the tools.
  2. If the reply has tool_calls (and finish_reason is tool_calls), run each one.
  3. Append the assistant message, then one tool message per call with the matching tool_call_id.
  4. 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_choiceEffect
"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.