JSON Schema for Tool Parameters: A Reference Guide
Tool parameters are almost always a JSON Schema object describing one JSON object of arguments.
Providers differ on which keywords they honor strictly. This cheatsheet covers the conventions that work well for agent tools across OpenAI-compatible APIs, OpenRouter, and common frameworks (verify vendor support at build).
How to Use This Cheatsheet
- Start from the minimal object skeleton, then add constraints only where they prevent bad calls.
- Prefer closed enums and required arrays over prose-only rules.
- Validate the same schema in the host before execution; never trust the model alone.
- When a keyword is ignored by a provider, enforce it in host code anyway.
A - Minimal Skeleton
{
"type": "object",
"properties": {
"order_id": {
"type": "integer",
"description": "Numeric order id, e.g. 1042"
}
},
"required": ["order_id"],
"additionalProperties": false
}| Field | Role |
|---|---|
type: "object" | Root arguments are always an object for tools |
properties | Named parameters |
required | Must-present keys |
additionalProperties: false | Reject surprise keys (support varies; enforce in host) |
per-property description | Steers argument fill quality |
B - Scalar Types
| JSON Schema type | Use for | Notes |
|---|---|---|
string | Names, ids-as-text, free text | Add enum, pattern, or format notes in description |
integer | Counts, numeric ids | Models sometimes emit strings; coerce carefully in host |
number | Floats, money if not integer cents | Prefer integer cents for currency |
boolean | Flags | Avoid tri-state; use optional bool or enum |
null | Rare | Many tool stacks dislike nullable unions; prefer omit optional |
{
"email": {
"type": "string",
"description": "Customer email, e.g. ada@example.com"
},
"limit": {
"type": "integer",
"description": "Page size",
"minimum": 1,
"maximum": 100
},
"dry_run": {
"type": "boolean",
"description": "If true, validate only and do not commit"
}
}C - Enums and Closed Sets
{
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
}| Guidance | Why |
|---|---|
| Keep enums small | Large enums bloat tokens and confuse selection |
| Use stable wire values | Display labels belong in descriptions, not alternate enum spellings |
| Prefer enum over free string | Cuts invented synonyms (C, celcius) |
D - Arrays and Nested Objects
{
"type": "object",
"properties": {
"item_ids": {
"type": "array",
"description": "Product ids to include",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 20
},
"address": {
"type": "object",
"description": "Shipping address",
"properties": {
"line1": { "type": "string" },
"city": { "type": "string" },
"country": {
"type": "string",
"description": "ISO 3166-1 alpha-2, e.g. PT"
}
},
"required": ["line1", "city", "country"],
"additionalProperties": false
}
},
"required": ["item_ids"],
"additionalProperties": false
}| Pattern | Prefer when |
|---|---|
| Flat parameters | Simple tools; easier for models |
| One nested object | Natural record (address, money amount+currency) |
| Deep trees | Avoid; split tools or accept host-side assembly |
E - Optional Fields, Defaults, and Required
| Approach | Schema | Host behavior |
|---|---|---|
| Required | list key in required | Reject if missing |
| Optional omit | not in required | Apply default in code |
| Optional with default keyword | "default": 5 | Helpful docs; still default in host |
| Sentinel empty string | discouraged | Prefer omit or null policy you document |
Do not list a key as required if the model rarely has the value. Inject server-side context (tenant, actor id) in the host instead of asking the model to guess.
F - Formats, Patterns, and Constraints
| Keyword | Example | Caveat |
|---|---|---|
minimum / maximum | integers, numbers | Enforce in host too |
minLength / maxLength | strings | Good guard against dumps |
minItems / maxItems | arrays | Cap blast radius |
pattern | "^[A-Z]{2}$" | Models may still fail; return clear errors |
format | date-time, email | Support varies widely |
{
"sku": {
"type": "string",
"description": "SKU like ABC-1234",
"pattern": "^[A-Z]{3}-[0-9]{4}$"
},
"ship_by": {
"type": "string",
"description": "ISO-8601 date (YYYY-MM-DD), UTC calendar date"
}
}G - Unions and anyOf (use sparingly)
{
"identifier": {
"description": "Order id as int or external string key",
"anyOf": [
{ "type": "integer" },
{ "type": "string", "minLength": 1 }
]
}
}| Advice | Reason |
|---|---|
| Prefer two tools or one clear type | Unions increase invalid args |
| If you must union, document decision rule | "Use int when numeric; else external key" |
| Validate host-side exhaustively | Model-produced unions are messy |
H - Provider Packaging Wrapper
Many OpenAI-compatible APIs wrap the schema:
tool = {
"type": "function",
"function": {
"name": "get_order_status",
"description": "Fetch live status for one order id.",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "integer", "description": "e.g. 1042"}
},
"required": ["order_id"],
"additionalProperties": False,
},
},
}Other stacks (Anthropic-style tool specs, MCP inputSchema, Pydantic AI, LangGraph) carry the same object schema under slightly different keys.
Keep an internal canonical schema and adapt at the edge.
I - Generate From Pydantic (optional)
from pydantic import BaseModel, Field
class GetOrderArgs(BaseModel):
order_id: int = Field(description="Numeric order id, e.g. 1042")
model_config = {"extra": "forbid"}
schema = GetOrderArgs.model_json_schema()
# use schema as the tool parameters object; verify $defs inlining for your provider| Benefit | Watch-out |
|---|---|
| Single source for validate + schema | Some exporters emit $refs providers dislike |
| Field descriptions stay close to code | Still write tool-level description separately |
J - Host Validation Checklist
| Check | On failure |
|---|---|
| JSON parse of arguments string | tool error invalid_json |
| Schema validate | tool error with path + expected type |
| Business rules | tool error invalid_argument with fix hint |
| Authz | tool error or hard refuse without leaking secrets |
| Size caps | truncate or reject oversized strings/arrays |
FAQs
Is full JSON Schema supported for tools?
No universal guarantee. Stick to a conservative subset: object, properties, required, type, enum, basic numeric/string bounds. Verify at build for each model/provider.
Should I set additionalProperties to false?
Yes when the provider honors it, and always enforce in host validation. Surprise keys are a common model failure mode.
Integer vs string ids?
Use the type your backend truly uses. If systems are mixed, document one preferred type and normalize in the host.
How do I represent money?
Prefer integer minor units (amount_cents) plus a currency enum. Floating number invites rounding bugs.
Can parameters be empty objects?
Yes for no-arg tools: "properties": {} with "required": []. Some APIs still want type: object.
What about output schemas?
Tool parameters are inputs. Output shape is your observation contract; some platforms also support structured final answers separately.
Why did the model omit a required field?
Weak description, overloaded context, or model limits. Return a clear error and tighten the schema/description; consider forcing the tool only when appropriate.
Are $ref and $defs safe?
Often problematic for tool calling. Inline small nested objects unless your provider documents $ref support.
Related
- Tool Use & Function Calling Basics - first schema in a loop
- Writing Tool Descriptions the Model Will Actually Use Correctly - wording that fills args well
- How Function Calling Actually Works Under the Hood - where schemas sit in the protocol
- Handling Tool Errors Gracefully Inside an Agent Loop - invalid args as observations
- Tool Use & Function Calling Best Practices - naming and scope habits
- Versioning and Deprecating Agent Tools Without Breaking Agents - schema evolution
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.