Whetstone.
Building the LoopTool definitions, tool_choice, and strict
Module 2, Lesson 325 min

Tool definitions, tool_choice, and strict

Three fields, and the exam’s favourite move in this whole domain is asking which object each one hangs off. Get the placement wrong and everything else you know is worth nothing.

It is the same class of knowledge as remembering that strict in a tsconfig goes under compilerOptions and not at the root. Nobody finds that interesting, everybody has been bitten by it, and being wrong about it invalidates a file that is otherwise perfect. Placement is not trivia. It is the difference between a request that works and a request that silently does not do what you meant.

The tool definition

Name, description, and an input_schema in JSON Schema. That is the contract.

A tool definition
{
  "name": "get_weather",
  "description": "Get the current weather for a city. Use when the user asks about conditions right now.",
  "input_schema": {
    "type": "object",
    "properties": { "city": { "type": "string" } },
    "required": ["city"]
  }
}

The description is not documentation, it is the routing logic. The model chooses tools by reading descriptions, so a vague one is a bug, and “gets weather” versus “use when the user asks about conditions right now” is the difference between a tool that fires correctly and one that fires whenever anybody mentions the sky.

tool_choice, and what it forces

Four shapes:

  • auto: the model decides whether to use a tool at all. The default when tools are present.
  • any: it must call some tool, its pick.
  • tool with a name: it must call that tool.
  • none: no tool calls this turn, even though the definitions are right there in the request.

none is the one people forget and it is quietly the most useful in a workflow. It leaves the model aware that the capability exists while forbidding it on this turn, which is exactly what you want on a summarisation step at the end of a run.

any and tool are how you convert a chat model into a structured-output machine: force a call, and the arguments are your parsed object.

strict, and the placement trap

strict: true goes on the tool definition. Next to name, next to description, next to input_schema.

Where strict lives
{
  "name": "get_weather",
  "description": "...",
  "input_schema": { "type": "object", "properties": { "city": { "type": "string" } } },
  "strict": true
}

And the second half of the same fact: strict is not available on mcp_toolset. Tools arriving through the MCP connector do not get strict tool use. Not “set it somewhere else”, not “on by default”: the capability is not there. That is a real constraint on architecture, not exam trivia, because it means moving a tool from your own definitions into an MCP server can silently cost you a guarantee you were relying on.

Parallel tool use, and its off switch

The model can request several tools in one turn, and it is on by default. Your loop handles it the way Lesson 2 said: one user message, several tool_result blocks, ids matched.

To turn it off you set disable_parallel_tool_use, and it lives on tool_choice.

Which is the placement fact worth writing down: tool_choice controls two different things. Whether a tool must be called, and whether more than one may be called at once. It does not control strict. If you remember only “strict on the definition, disable_parallel_tool_use on tool_choice”, you have the whole lesson.

Practice

Try it yourself

Quiz

Where strict goes

You want guaranteed schema conformance on one of your tools. Where does strict: true actually go?

  1. AOn tool_choice, alongside its type field, where tool behaviour is configured
  2. BOn the tool definition itself, next to name, description and input_schema
  3. CAt the top level of the request, applying to every tool at once
  4. DInside input_schema, as a JSON Schema keyword the validator enforces
Show answer

Correct answer: B — On the tool definition itself, next to name, description and input_schema

strict is a property of the tool definition. The tool_choice answer is the designed trap and it is a good one, because tool_choice is the other object in the request with the word tool in it and it genuinely does control tool behaviour, just not this. The JSON Schema answer is tempting for anyone who knows schema validation, but strict is Anthropic's field, not a JSON Schema keyword.

Quiz

One field, wrong object

One field in this request is on the wrong object. The tool description is also weak, but that is not what this card is testing.

Find the placement defect
{
  "tools": [
    {
      "name": "create_invoice",
      "description": "Create an invoice",
      "input_schema": {
        "type": "object",
        "properties": { "amount": { "type": "number" } }
      }
    }
  ],
  "tool_choice": { "type": "any", "strict": true }
}
  1. Astrict belongs on the tool definition, next to name, description and input_schema
  2. Btool_choice type any is invalid when only one tool is defined
  3. Cinput_schema is missing a required array, which is what strict reads
  4. Dstrict should sit at the top level of the request so it covers every tool at once
Show answer

Correct answer: A — strict belongs on the tool definition, next to name, description and input_schema

strict is a property of an individual tool definition, so it moves up into the object alongside name, description and input_schema. The top-level answer is the sharpest trap here, because it is the design you would choose yourself: per-request conformance is a perfectly coherent idea and it would be less repetitive. It is simply not the shape Anthropic chose, and on this paper the shape is the answer. tool_choice type any is fine with a single tool, and an absent required array is a schema-quality issue rather than the placement defect the question is about.

Recall

The tool_choice values

Four shapes, and one of them is the least obvious but most useful in a workflow.

Name the tool_choice options and say what each one forces.

Reveal answer

auto lets the model decide whether to call a tool at all, and is the default when tools are present. any forces it to call some tool, but leaves the choice open. tool with a name forces one specific tool. none forbids tool use entirely while leaving the definitions in the request, which is how you keep the model aware of a capability without letting it reach for it on this turn.

Quiz

strict on an MCP tool

You liked strict so much you want it on the tools coming in through an mcp_toolset.

  1. ASet strict: true on the mcp_toolset entry, which applies it to every tool the server exposes
  2. BSet strict: true on the mcp_servers entry instead
  3. CYou cannot. Strict tool use is not available on mcp_toolset
  4. DIt is on by default for MCP tools, because the server already publishes a schema
Show answer

Correct answer: C — You cannot. Strict tool use is not available on mcp_toolset

Strict tool use is unavailable on mcp_toolset, so this is a capability gap rather than a placement question. The on-by-default answer is the seductive one because the reasoning sounds sound: MCP servers do publish schemas, so surely conformance is handled. Publishing a schema and guaranteeing the model's output conforms to it are different guarantees, and only the second one is what strict means.

Recall

Turning parallel tool use off

A small placement fact, and placement facts are what this whole lesson is about.

Parallel tool use is on by default. Which object carries the switch that turns it off?

Reveal answer

disable_parallel_tool_use lives on tool_choice, not on the tool definition and not at the top level of the request. So tool_choice ends up controlling two separate things: whether a tool must be called, and whether several may be called at once.

Do

Write one definition, and place both fields

Two minutes in a scratch file, no network. This is placement practice rather than schema practice, because placement is what the marks hang on.

  • Write one complete tool definition for a tool you would genuinely use, with name, description and input_schema.
  • Make the description say when to reach for the tool, not what the tool is.
  • Add strict: true, and put it on the correct object.
  • Beside it, write a tool_choice object that forces this specific tool by name and forbids parallel calls.
Done whenstrict sits inside the tool definition, disable_parallel_tool_use sits inside tool_choice, and neither has wandered into the other.
Sign in to track your progress →