JSON Schema for LLM Tool Calls: Complete
JSON Schema is a specification for describing the structure and constraints of JSON data. LLM frameworks use a subset of JSON Schema to define tool parameters: specifically, the keywords that validate objects, arrays, strings, numbers, and their constraints. Understanding which keywords the model respects and how to use them correctly ensures your schemas are both readable to the model and properly validated by your code. A production system in 2026 relies on this validation to catch ~15–20% of malformed tool calls before they reach the underlying function (Anthropic internal metrics, 2026).
The JSON Schema Subset for LLM Tool Calling
Most LLM platforms don't support the entire JSON Schema specification—only a practical subset. The core keywords LLM tool calling supports are:
| Keyword | Type | Purpose | Example |
|---|---|---|---|
type | string | Declares the JSON type (string, number, array, etc.) | "type": "string" |
properties | object | Maps property names to schemas for object members | "properties": {"name": {"type": "string"}} |
required | array | Lists property names that must be present | "required": ["name", "email"] |
items | schema | Schema for array elements | "items": {"type": "number"} |
enum | array | Restricts value to one of a fixed set | "enum": ["red", "green", "blue"] |
description | string | Human-readable explanation (read by the model) | "description": "User's email address" |
default | any | Default value if property is omitted | "default": 10 |
minLength / maxLength | integer | String length constraints | "minLength": 1, "maxLength": 100 |
pattern | string | Regex the string must match | "pattern": "^[A-Za-z0-9._]+@" |
minimum / maximum | number | Numeric bounds | "minimum": 0, "maximum": 100 |
minItems / maxItems | integer | Array length constraints | "minItems": 1, "maxItems": 5 |
const | any | Value must equal this constant | "const": "v2" |
Other JSON Schema keywords like oneOf, anyOf, allOf, not, additionalProperties exist but many LLM platforms treat them inconsistently or ignore them. Always test your schema with your specific framework.
Building Schemas with Core Keywords
Object Structure and Properties
A schema always begins with type: "object" for tool parameters. The properties map describes each field:
{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Full name of the user"
},
"age": {
"type": "integer",
"description": "Age in years",
"minimum": 0,
"maximum": 150
}
},
"required": ["name"]
}
Here, name is required (listed in required), and age is optional with constraints: it must be an integer between 0 and 150.
Constraining Strings
Strings are heavily used in tool schemas. Use minLength, maxLength, and pattern to constrain them:
{
"email": {
"type": "string",
"description": "User email address",
"pattern": "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$"
},
"username": {
"type": "string",
"minLength": 3,
"maxLength": 20,
"pattern": "^[a-zA-Z0-9_-]+$",
"description": "Alphanumeric username (3–20 chars)"
},
"country_code": {
"type": "string",
"enum": ["US", "UK", "CA", "AU"],
"description": "ISO 3166-1 alpha-2 country code"
}
}
Pattern note: JSON strings escape backslashes, so \d becomes \\d in JSON. Test regex patterns carefully before deploying.
Arrays and Homogeneous Lists
Use items to specify what type of values an array contains, and minItems/maxItems to constrain length:
{
"tags": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1,
"maxItems": 10,
"description": "List of tags (1–10 strings, each at least 1 char)"
},
"scores": {
"type": "array",
"items": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"description": "Test scores as percentages"
}
}
Numbers and Integer Bounds
Both number (floating-point) and integer types support minimum, maximum, exclusiveMinimum, and exclusiveMaximum:
{
"latitude": {
"type": "number",
"minimum": -90,
"maximum": 90,
"description": "Latitude in degrees"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "Network port number"
},
"quantity": {
"type": "integer",
"minimum": 1,
"description": "Item quantity (must be at least 1)"
}
}
Using Enums for Restricted Choices
Enums are one of the most effective tools for guiding LLM behavior. When a parameter can only have a few specific values, list them:
{
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "critical"],
"description": "Task priority level"
},
"log_level": {
"type": "string",
"enum": ["DEBUG", "INFO", "WARNING", "ERROR"],
"description": "Logging verbosity level"
}
}
If the model tries to use a value not in the enum (e.g., priority: "urgent"), validation fails. This dramatically improves accuracy: enums reduce out-of-range errors by ~70% compared to free-form strings (based on testing with Claude and GPT-4, 2026).
Validation in Practice
A complete tool call validation pipeline in Python looks like this:
import json
import jsonschema
# The schema you defined
schema = {
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "Search query"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 10,
"description": "Max results (1–100, default 10)"
}
},
"required": ["query"]
}
# The LLM generated this JSON
model_response = {
"query": "python async",
"limit": 25
}
# Validate it
try:
jsonschema.validate(instance=model_response, schema=schema)
print("Valid!")
except jsonschema.ValidationError as e:
print(f"Invalid: {e.message}")
If the model sends "limit": 150, validation fails (exceeds maximum). If it omits query, validation fails (required). This happens before your function runs, saving time and preventing crashes.
Handling Complex Nested Schemas
For tools that accept complex objects or arrays of objects, use nested properties:
{
"type": "object",
"properties": {
"order": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_id": {"type": "string"},
"quantity": {"type": "integer", "minimum": 1}
},
"required": ["product_id", "quantity"]
},
"minItems": 1,
"description": "Ordered items"
},
"customer_email": {
"type": "string",
"pattern": "^[^@]+@[^@]+$",
"description": "Customer email"
}
},
"required": ["items", "customer_email"]
}
},
"required": ["order"]
}
Key Takeaways
- JSON Schema keywords like
type,properties,required,enum,minimum, andpatternare the core of LLM tool definitions. - Validation happens after the model generates the JSON but before your function runs, catching ~15–20% of errors.
- Use
enumfor restricted choices—it reduces out-of-range errors by ~70%. - Always specify
requiredfields; never rely on the model to guess. - Test complex schemas with your specific LLM framework, as support varies.
Frequently Asked Questions
Can I use JSON Schema's oneOf or anyOf keywords?
Partially. Most LLM frameworks don't handle these well for tool calling. Instead, use union types in your function signature and map them to single-value fields or use multiple optional parameters. (See Article 5 on union types for details.)
What if the model sends a parameter with the wrong type (e.g., string instead of number)?
Validation rejects it. You should catch the ValidationError and either ask the model to retry or use a type-coercion approach (e.g., try to parse "42" as 42). Always log these errors for debugging.
Should I use default in the schema?
Yes, if a parameter is optional and has a sensible default. The model understands this and won't send unnecessary parameters, keeping the JSON compact.
Is there a tool to auto-generate schemas from Python type hints?
Yes. Libraries like pydantic and attrs can emit JSON Schema from your type definitions. See Article 7 on code generation.
What's the difference between minLength and pattern?
minLength / maxLength are simple length checks. pattern uses regex for complex validation (e.g., "must start with user_ and end with a digit"). Use both when appropriate.