Skip to content

Mapping the "tool_choice" Parameter Between APIs

10 min read · updated August 11, 2026

Every tool-calling API lets you say “decide for yourself”, “do not call anything”, “call something” and “call this one”. They disagree about whether that is a string or an object, about what “call something” is called, and about what happens after the forced call.

Four modes, three spellings

Start from the semantics, because those are stable and the spellings are not:

  • Auto — the model decides whether to call a tool. Almost always the default when tools are present.
  • None — the tools are visible to the model, and it must answer in text without calling one. Note that this is not the same as omitting the tools array: the definitions still occupy context, still cost tokens, and still inform the answer.
  • Required / any — the model must call some tool, its choice which. Useful when a turn has no valid text answer.
  • Specific tool — the model must call the named one. This is the mode that doubles as a structured-output mechanism, which is why it appears in function calling versus structured output.

Two things about the default are worth pinning down before you translate anything. The parameter is optional everywhere, and omitting it is not the same as sending auto on every API — the documented behaviour when tools are present is generally auto, but the behaviour when the tools array is empty or absent is defined separately, and some APIs reject a forcing value with no tools to force. So a converter that always emits a value can turn a previously-valid request into a 400 on exactly the turns where your agent had nothing to offer. Emit the field only when your neutral representation says something other than the default, and drop it entirely when the tool list is empty.

The string-or-object shape

OpenAI’s Chat Completions API takes tool_choice as either a bare string or an object. The strings are "none", "auto" and "required"; naming a specific tool requires the object form:

"tool_choice": "auto"
"tool_choice": "none"
"tool_choice": "required"
"tool_choice": {"type": "function", "function": {"name": "get_order_status"}}

Parallel calls are controlled separately, by a top-level parallel_tool_calls boolean rather than by anything inside tool_choice. The documented default is that tools are called in parallel when the model wants to; setting it false constrains the model to one call per turn. The exact interaction between that flag and the forcing modes is worth reading in the vendor reference before you rely on it, and the emitted format is covered in the parallel tool calls format.

The deprecated predecessor, function_call, took "none", "auto" or {"name": "..."} and had no equivalent of "required". If you are migrating from it, that missing mode is the one behaviour you may have simulated with prompt text and can now express directly.

The tagged-object shape

Anthropic’s Messages API takes tool_choice as an object in every case, discriminated by type:

"tool_choice": {"type": "auto"}
"tool_choice": {"type": "none"}
"tool_choice": {"type": "any"}
"tool_choice": {"type": "tool", "name": "get_order_status"}

The mapping is direct except for one name: any is the equivalent of required, and it is the single most common translation error in this parameter because both words are plausible for both meanings. A converter that passes the string "required" through is sending a value the API does not define; a converter that maps any to auto because “any tool” sounds permissive has removed the forcing entirely, which is silent and produces exactly the wrong-tool symptoms described in why tool-choice behaviour changes after a swap.

Parallelism is controlled here from inside the same object, by a disable_parallel_tool_use boolean, rather than by a sibling top-level parameter. That is a structural difference a field-by-field converter misses: the flag has to move levels, and its polarity is inverted relative to parallel_tool_calls — one says “allow”, the other says “disable”. Passing the same boolean through unchanged gives you precisely the opposite of what you configured.

The config-block shape

Google’s Gemini API expresses the same idea as a nested configuration object rather than a single parameter: a tool configuration containing a function-calling configuration with a mode whose values follow an upper-case convention — AUTO, ANY, NONE — alongside an allowedFunctionNames list.

The list is the interesting part, and it is discussed under gaps below. The structural point for a converter is that the value is not a sibling of tools at the top level but lives in its own config object, so a flat field mapping has nowhere to put it.

Mode names, defaults and the interaction between forcing and parallelism are exactly the surface vendors revise between API versions, and a mode added later will not appear here. Confirm against OpenAI’s chat completions reference, Anthropic’s messages reference and Google’s function calling docs before writing a converter against them.

What has no counterpart

  • Restricting to a subset. The allowed-names list lets you say “call one of these three”. Neither the string-or-object nor the tagged-object shape has that: they offer any tool, or one named tool, with nothing in between. Migrating a config that uses it means either narrowing to a single forced tool, or — usually better — passing only that subset in the tools array for the turn. Doing it that way is portable everywhere and reduces schema tokens as a side effect.
  • The parallelism control changes level and polarity between the two most common shapes, as above. Treat it as a separate field in your neutral representation with an explicit “allow parallel” sense, and let each emitter invert it if it must.
  • none arrived later than the other modes on some APIs. If your target does not accept it, the equivalent is to send no tools array for that turn — not identical, since the model no longer sees the definitions, but usually the intent.
  • What happens after a forced call is not specified identically. Forcing applies to the turn, not to the conversation, so an agent loop that sets a forcing value once and reuses the request body will force a call on every subsequent turn and never terminate. Reset it to auto after the first turn. This is a genuine infinite-loop source, and testing the infinite-loop guard is what catches it before production does.