A tool is a function the model can ask you to run, described by a name, a clear description, and a JSON-schema for its inputs. Write a tool the right way so the model calls it correctly.
Why: the model never runs your code — it reads the tool description, decides to call it, and hands you the arguments to execute. Where: the schema (name, description, input_schema) is what the model sees; the Python function is what you run when it asks.
# 1. What the MODEL sees — the schema
weather_tool = {
"name": "get_weather",
"description": "Get the current weather for a city. "
"Call this whenever the user asks about weather.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Paris'"},
},
"required": ["city"],
},
}
# 2. What YOU run when the model calls it
def get_weather(city):
return f"It is 21°C and sunny in {city}."Why: the model decides whether to call a tool almost entirely from its description, so a vague one means missed or wrong calls. When: state explicitly when to call it, not just what it does. Where: describe every parameter too — the model fills them from those hints.
# Weak — the model won't know when to reach for it
{"name": "lookup", "description": "Looks things up."}
# Strong — says WHAT it does and WHEN to call it
{"name": "get_order_status",
"description": "Look up the delivery status of a customer order. "
"Call this whenever the user asks where their order is "
"or mentions an order number."}Why: the model-supplied arguments are untrusted input — it can hallucinate an id or omit a field. When: validate inside the function and return a clear error string instead of crashing; the model reads the error and self-corrects. Where: mark only truly-required fields in the schema so optional ones can be omitted.
def get_order_status(order_id):
if not order_id or not order_id.startswith("ORD-"):
# Returned to the model, not raised — it will ask the user or retry.
return "Error: order_id must look like 'ORD-12345'."
order = DB.get(order_id)
if order is None:
return f"Error: no order found with id {order_id}."
return f"Order {order_id} is {order['status']}."Why: for tools with enums, dates, or odd formats, one concrete example in the description removes ambiguity faster than more prose. When: add an example whenever the model keeps formatting an argument wrong.
book_meeting_tool = {
"name": "book_meeting",
"description": (
"Schedule a meeting. Times are 24-hour ISO 8601. "
"Example: book_meeting(title='Standup', "
"start='2026-07-01T09:00', minutes=15)."
),
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "description": "ISO 8601, e.g. 2026-07-01T09:00"},
"minutes": {"type": "integer", "description": "Duration in minutes"},
},
"required": ["title", "start", "minutes"],
},
}