When an LLM needs to interact with the outside world — fetching weather data, querying a database, or sending an email — it cannot execute code directly. Instead, it emits a structured JSON object describing which function to call and what arguments to pass. JSON Schema is the formal language that defines the contract between the model and your application: it specifies exactly what parameters each tool accepts, their types, constraints, and which fields are required.
Without JSON Schema, tool calling would be a guessing game. The model would have no reliable way to know that a get_weather function expects a location string and an optional unit enum. JSON Schema provides the structured vocabulary that turns natural language intent into deterministic, machine-parseable function invocations. Every major LLM provider — OpenAI, Anthropic, Google, and open-source frameworks — uses JSON Schema as the foundation of their tool-calling APIs.
For AI engineers, understanding JSON Schema for tools is not optional. It is the interface layer between what the model “wants to do” and what your code actually executes. A poorly defined schema leads to hallucinated parameters, type mismatches, and failed tool calls. A well-defined schema constrains the model’s output so tightly that errors become nearly impossible.
A tool definition tells the LLM three things: the function’s name, a natural language description of what it does, and a parameters (or input_schema) object defined using JSON Schema. The model reads the name and description to decide when to call the tool, then uses the schema to construct valid arguments.
JSON Schema is a declarative vocabulary for annotating and validating JSON documents. It defines data types (string, number, boolean, object, array), constraints (enum, minimum, maxLength), required fields, and nested structures. The current standard is Draft 2020-12.
OpenAI wraps tool definitions inside a tools array, with each entry specifying type: "function" and a nested function object containing the name, description, and parameters schema:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current temperature for a given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g. 'San Francisco, CA'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["location"]
}
}
}
When the model decides to call this tool, it emits a tool_calls array in its response containing the function name and a JSON string of arguments matching this schema.
Anthropic uses a slightly different structure. Tools are defined with name, description, and input_schema at the top level — no wrapping function key:
{
"name": "get_weather",
"description": "Get the current weather in a given location.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
Despite the structural differences, both providers use identical JSON Schema syntax for the parameter definitions themselves. If you can write one, you can write the other with minimal adaptation.
The quality of your tool schema directly impacts how reliably the model calls your tools. Research and production experience have identified several critical best practices:
city, state, zip as top-level properties.description field to understand what each parameter means. Vague descriptions like “the input” lead to incorrect arguments. Specific descriptions like “ISO 8601 date string for the booking start date” dramatically improve accuracy."celsius" or "fahrenheit"), use an enum constraint rather than a free-form string. This eliminates an entire class of errors.required array ensures the model always provides critical parameters. Without it, the model may omit fields it considers optional.OpenAI introduced strict: true mode for tool definitions, which guarantees that the model’s output conforms exactly to the provided schema. When strict mode is enabled, the model uses constrained decoding — at each token generation step, only tokens that could produce valid JSON according to the schema are considered. This eliminates malformed JSON, missing required fields, and type errors entirely.
Instead of sampling from the full vocabulary at each step, constrained decoding restricts the token probability distribution to only tokens that keep the output on a valid path through the JSON Schema. This converts a “best effort” generation into a guaranteed-valid output, at the cost of slightly increased latency (roughly 5-15% on first call, with schema caching reducing subsequent calls).
Strict mode has limitations: it requires additionalProperties: false on all objects, does not support certain schema features like oneOf with mixed types, and all fields at every level must be listed in the required array (optional fields should use a type: ["string", "null"] union instead).
Understanding the full lifecycle clarifies where JSON Schema fits:
tool message for final synthesis.Even with strict mode, you should validate tool call arguments in your application layer. Libraries like ajv (JavaScript), jsonschema (Python), or pydantic (Python) can validate against the same schema you sent to the API. This is defense-in-depth: if you ever switch providers or use a model that doesn’t support strict mode, your validation layer catches malformed calls.
# Python example with Pydantic
from pydantic import BaseModel
from typing import Optional, Literal
class GetWeatherArgs(BaseModel):
location: str
unit: Optional[Literal["celsius", "fahrenheit"]] = None
# Validate tool call arguments
args = GetWeatherArgs.model_validate_json(tool_call.arguments)
result = get_weather(args.location, args.unit)
Pydantic models can also auto-generate JSON Schema via GetWeatherArgs.model_json_schema(), keeping your tool definitions and validation logic in perfect sync.
In production AI systems, you typically define a library of 5-50 tools that the model can choose from. Each tool has a JSON Schema definition. The model’s ability to select the right tool depends heavily on the quality of tool names and descriptions. Real-world tool libraries include database query tools, API integration tools, file manipulation tools, and communication tools (email, Slack, etc.).
Advanced systems generate tool schemas dynamically based on context. For example, a CRM agent might register different tools depending on the user’s permissions — an admin sees delete_account while a regular user does not. The JSON Schema definitions are constructed at runtime and passed to the API call.
Because all major providers use JSON Schema for parameter definitions, you can build a provider-agnostic tool layer. Define your tools once using JSON Schema, then wrap them in the appropriate envelope (OpenAI’s function wrapper vs. Anthropic’s input_schema format) at the API call boundary. Libraries like LiteLLM and LangChain abstract this translation layer automatically.
In practice, most teams don’t write JSON Schema by hand. They define tool functions with type annotations in Python or TypeScript, then auto-generate the schema. Frameworks like Pydantic, Zod (TypeScript), and Instructor provide decorators or utility functions that produce valid JSON Schema from typed function signatures, reducing boilerplate and preventing schema drift.
enum constraints, descriptive description fields, and explicit required arrays to maximize tool-calling reliability.