Careeroza
Strict Tools
Guaranteed Valid Arguments
Without extra settings, the model's tool input usually matches your schema, but not always (a missing field, a string instead of a number). Add "strict": True to the tool and set "additionalProperties": False in the schema: the API then guarantees the input matches the schema exactly.
tools = [{
"name": "create_return",
"description": "Create a return request for a delivered order. Use only after the customer confirms the item and reason.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"reason": {"type": "string", "enum": ["damaged", "wrong_item", "size_issue", "not_needed"]},
"pickup": {"type": "boolean"},
},
"required": ["order_id", "reason", "pickup"],
"additionalProperties": False,
},
}]
response = client.messages.create(
model="claude-sonnet-5-5", max_tokens=1000, tools=tools,
messages=[{"role": "user", "content": "Return order SE-10423, wrong size. Use the create_return tool. Pickup please."}],
)
if not any(b.type == "tool_use" for b in response.content):
... # auto does not guarantee a call: re-prompt or ask the customerNote: On current models (Claude Opus 5.5, Sonnet 5.5) forcing a tool with tool_choice {"type": "any"} or {"type": "tool", ...} returns a 400 error. Keep the default auto, ask for the tool in the prompt, use strict: True, and check in code whether a tool_use block actually came back.
Business Checks Still Belong in Your Code
Example
A valid schema does not mean the action is allowed. Check the rules and return is_error results so the model can explain or ask again:
import json
ORDERS = {"SE-10423": {"status": "delivered", "days_since_delivery": 3},
"SE-55555": {"status": "delivered", "days_since_delivery": 12}}
def create_return(order_id, reason, pickup):
order = ORDERS.get(order_id)
if order is None:
return {"is_error": True, "content": f"Order {order_id} not found."}
if order["days_since_delivery"] > 7:
return {"is_error": True, "content": "Return window (7 days) has passed. Offer to connect a human agent."}
return {"content": json.dumps({"return_id": "RET-" + order_id[-4:], "pickup": pickup})}
for args in [{"order_id": "SE-10423", "reason": "size_issue", "pickup": True},
{"order_id": "SE-55555", "reason": "damaged", "pickup": True},
{"order_id": "SE-00000", "reason": "damaged", "pickup": False}]:
result = {"type": "tool_result", "tool_use_id": "toolu_x", **create_return(**args)}
print(result)Output
{'type': 'tool_result', 'tool_use_id': 'toolu_x', 'content': '{"return_id": "RET-0423", "pickup": true}'}
{'type': 'tool_result', 'tool_use_id': 'toolu_x', 'is_error': True, 'content': 'Return window (7 days) has passed. Offer to connect a human agent.'}
{'type': 'tool_result', 'tool_use_id': 'toolu_x', 'is_error': True, 'content': 'Order SE-00000 not found.'}