Tool schemas
Your get_current_datetime function exists. Claude still can't call it, because Claude has no idea it exists. The thing that bridges the gap is a tool schema — a structured description of the function that you ship alongside your messages when you call the API. This lesson covers what schemas look like, how to write good ones, and the cheat you should use every time you build a new tool.
JSON Schema, briefly
JSON Schema is a widely-used data validation language that predates tool calling. It describes the shape of a JSON object — types, required fields, constraints. The AI community adopted it for tool use because it's mature, well-specified, and good at exactly the thing you need: telling another system what a function's parameters look like.
A tool schema for Claude has three required pieces at the top level:
name— a clear, descriptive identifier matching your Python function.description— natural-language documentation: what it does, when to use it, what it returns.input_schema— a JSON Schema object describing the function's arguments.
Writing an effective description
The description is the single most important part of the schema — it's the text Claude reads to decide whether and when to call the tool. A good description covers three things in 3–4 sentences:
- What the tool does. "Returns the current date and time..."
- When Claude should use it. "...when the user asks about the current time, or when any other tool requires a starting timestamp."
- What it returns. "...as a string formatted according to the provided
date_format."
A bad description is one sentence: "Gets the time." Technically correct, operationally useless. Claude can't disambiguate a bad description from a better tool, can't decide whether it applies in edge cases, and can't predict the return shape to use downstream.
Every argument inside input_schema.properties also gets its own description. Apply the same rigour: what is this parameter, what values are valid, what happens at the edges.
The easy way: let Claude write the schema for you
Writing JSON Schema by hand is tedious and error-prone. The cheat is to have Claude write the schema from your function code. Paste your function into a Claude conversation along with the Anthropic tool-use documentation and ask for a schema. You'll get a properly structured result in seconds.
A prompt that works:
Here is a Python function:
<code>
[paste function]
</code>
Write a valid JSON schema spec for tool calling with this function.
Follow the best practices in the attached Anthropic documentation.
Review what Claude gives you, tweak the description, and drop it into your codebase. For a three-tool project, this is the difference between half an hour of schema wrangling and three minutes of editing.
The schema for get_current_datetime
After running the generation pass, here's the schema:
get_current_datetime_schema = {
"name": "get_current_datetime",
"description": (
"Returns the current date and time as a formatted string. "
"Use this tool when the user asks about the current time or date, "
"or when another tool needs a current timestamp as a starting point. "
"Returns a string formatted according to the provided date_format."
),
"input_schema": {
"type": "object",
"properties": {
"date_format": {
"type": "string",
"description": (
"A Python strftime format string specifying how to format "
"the returned datetime (e.g. '%Y-%m-%d %H:%M:%S' for "
"'2024-05-22 14:30:25')."
),
"default": "%Y-%m-%d %H:%M:%S",
},
},
"required": [],
},
}
Walk through the pieces:
namematches the Python function exactly. You will need to look this name up by string to dispatch tool calls later — mismatches here are painful.descriptioncovers all three criteria: what, when, what it returns.input_schema.typeis always"object"for tool calls.propertieshas one entry per parameter. Each has atype, adescription, and optionally adefault.requiredis an empty array becausedate_formathas a default — Claude can call the tool with no arguments.
Naming convention: function_name_schema
Use the pattern function_name and function_name_schema to keep schemas organised. Every tool function has a matching schema variable with the same name plus _schema. When you build multiple tools (as you will in the next lessons), this convention means you never have to hunt for the schema that goes with a given function — it's right next to it in the file, with a predictable name.
This also pays off when you're building the dispatch table later: you can iterate over your tools with a simple pattern instead of memorising which schema pairs with which function.
Type safety with ToolParam
The Anthropic SDK ships a ToolParam TypedDict for schema shapes. Wrapping your schema in ToolParam() gives you static type checking from mypy or Pyright:
from anthropic.types import ToolParam
get_current_datetime_schema = ToolParam({
"name": "get_current_datetime",
"description": "...",
"input_schema": {
"type": "object",
"properties": {...},
"required": [],
},
})
ToolParam isn't strictly required — the API accepts plain dicts — but it catches typos and structural mistakes at the type-check stage instead of at runtime. For a three-tool project it's nice to have. For a thirty-tool production system it's the kind of small ergonomic fix that saves real debugging time.
What the schema unlocks
With a well-written schema in hand, you can pass tools into the API alongside your messages:
response = client.messages.create(
model=model,
max_tokens=1000,
messages=messages,
tools=[get_current_datetime_schema],
)
Claude now knows the tool exists, when to use it, and how to format arguments. What it sends back in the response is different from what you've seen so far — instead of a plain text block, the response may contain a tool_use block with the name and arguments of the function it wants you to run. Handling that multi-block response shape is the next lesson.
Key Takeaways
- 1A tool schema has three pieces: a name matching the function, a 3–4 sentence description covering what, when, and what-it-returns, and an input_schema describing the arguments.
- 2The description is the single most important field — it is the text Claude reads to decide whether and when to call the tool.
- 3The fastest way to write a schema is to have Claude generate it from your function code, then review and tweak the description.
- 4Use the naming convention function_name and function_name_schema so every schema sits next to its function with a predictable name.
- 5Wrap schemas in ToolParam from anthropic.types for static type checking — not required but catches structural mistakes at type-check time.
- 6Pass tools into client.messages.create() via the tools parameter alongside messages; Claude then knows what functions exist and how to call them.