Skip to content

Tool Use

Define tools, let the model call them, and feed results back. Build agents, function-calling workflows, and structured outputs across every model.

A language model on its own can't check the weather, query your database, or book a meeting — it can only produce text. Tool use (also called function calling) closes that gap: you describe named functions, the model decides which to call and with what arguments, your code runs them, and you feed the results back for a final answer. AnyRouter normalizes tool use to the OpenAI schema, so every tool-capable model in the catalog works through the same request shape — swap the model id and your code is unchanged.

Overview

The loop has four steps: define tools, send them with your request, run whatever the model calls, and return the results as tool messages. The model then composes a natural-language answer from those results.

const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Return the current weather for a city.",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string", description: "City name" },
          unit: { type: "string", enum: ["celsius", "fahrenheit"] },
        },
        required: ["city"],
      },
    },
  },
]

How it works

Sending tools with a request

Pass your tools alongside the messages. tool_choice: "auto" lets the model decide whether to call one.

const response = await client.chat.completions.create({
  model: "openai/gpt-5.4-mini",
  messages: [{ role: "user", content: "What's the weather in Tokyo?" }],
  tools,
  tool_choice: "auto",
})

Handling tool calls

When the model decides to call a tool, the response carries tool_calls instead of plain content, and finish_reason is tool_calls:

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_01ABC",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\":\"Tokyo\",\"unit\":\"celsius\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}

Run the tool, then send the result back as a tool role message. Include the original user message and the assistant's tool-call message so the model has full context:

const toolCall = response.choices[0].message.tool_calls![0]
const args = JSON.parse(toolCall.function.arguments)
const weather = await getWeather(args.city, args.unit)

const followup = await client.chat.completions.create({
  model: "openai/gpt-5.4-mini",
  messages: [
    { role: "user", content: "What's the weather in Tokyo?" },
    response.choices[0].message,
    {
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify(weather),
    },
  ],
  tools,
})

The model sees the tool result and composes a natural-language answer.

Forcing or disabling a tool

tool_choice controls whether and which tool the model may call:

// Force a specific function
tool_choice: { type: "function", function: { name: "get_weather" } }

Use "none" to disable tool use entirely and force plain-text output, or "auto" (the default when tools are present) to let the model decide.

Parallel tool calls

Modern models can emit multiple tool calls in a single turn. Iterate over the full tool_calls array, run them in parallel, and include one tool message per call in your follow-up request.

const calls = response.choices[0].message.tool_calls ?? []
const results = await Promise.all(
  calls.map(async (call) => ({
    role: "tool" as const,
    tool_call_id: call.id,
    content: JSON.stringify(await runTool(call)),
  })),
)

Always match every tool_call_id with exactly one tool message in the follow-up. A missing tool result leaves the model waiting on a call it already made and usually produces an error or a degraded answer.

Structured outputs

When you want JSON back without an actual side effect, use response_format instead of tools:

response_format: { type: "json_object" }

For guaranteed conformance, provide a JSON schema:

response_format: {
  type: "json_schema",
  json_schema: {
    name: "weather_report",
    schema: { /* JSON Schema */ },
    strict: true,
  },
}

Configure

Tool use needs no dashboard setup — it's part of every request. To adopt it:

  1. Define your tools as an array of { type: "function", function: { name, description, parameters } } objects, using JSON Schema for parameters.
  2. Send them with tools and a tool_choice on your chat completion.
  3. Loop on finish_reason: "tool_calls": run each call, append a tool message per tool_call_id, and re-send until the model returns a normal completion.
  4. Swap models freely — any tool-capable model in the catalog accepts the same request shape.
  • Presets — bundle tools and tool_choice into a reusable config.
  • Request Logs — inspect tool-call arguments and results per request.
  • API Reference — the full chat completions schema.