← Back to Payloads
tutorial2026-05-18

Validate Everything: JSON Schema Patterns for Robust AI Tool Integrations

JSON Schema isn't just for APIs. Learn how to use it as a first-class pattern in your AI agent tool pipelines to catch bad data before it breaks your workflow.
Quick Access
Install command
$ mrt install json-schema
Browse related skills
Validate Everything: JSON Schema Patterns for Robust AI Tool Integrations

Validate Everything: JSON Schema Patterns for Robust AI Tool Integrations

When you're wiring up AI agents to external tools, the gap between "what the model thought it sent" and "what the tool actually expects" will bite you. Every. Single. Time.

The fix isn't prompting discipline — it's validation at the boundary. And the most practical tool for that job is JSON Schema.

Why JSON Schema, Why Now

Most AI agent tutorials hand-wave input validation or skip it entirely. That works until a model decides to pass {"count": "five"} instead of {"count": 5} — and your downstream tool chrashes at 2am.

JSON Schema gives you:

  • Declarative contracts you can version, share, and test
  • Built-in generators for Python (Pydantic), TypeScript (Zod/Zod-like), Go, and Rust
  • Self-documenting behavior — your schema _is_ your docs
  • Runtime + compile-time safety in typed languages

The Core Pattern: Tool Input Schemas

Every tool your agent calls should declare its input schema. Here's a practical example:

json
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Search query, max 200 chars",
      "maxLength": 200
    },
    "limit": {
      "type": "integer",
      "description": "Number of results, 1-50",
      "minimum": 1,
      "maximum": 50,
      "default": 10
    },
    "filters": {
      "type": "object",
      "properties": {
        "source": {"type": "string", "enum": ["web", "news", "images"]},
        "date_range": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "ISO date, e.g. 2024-01-15"
        }
      },
      "additionalProperties": false
    }
  },
  "required": ["query"],
  "additionalProperties": false
}

Key moves here:

  • required marks non-optional fields so missing params fail fast
  • additionalProperties: false catches typos in parameter names
  • pattern on date strings prevents garbage dates like yesterday
  • enum constrains categorical values to known valid options

Validating in Python with Draft-7

python
from jsonschema import validate, ValidationError
import jsonschema
# Use Draft-7 (widely supported, stable)
Validator = jsonschema.Draft7Validator
def validate_tool_input(schema: dict, payload: dict) -> list[str]:
    """Returns list of validation errors, empty if valid."""
    validator = Validator(schema)
    errors = [e.message for e in validator.iter_errors(payload)]
    return errors
# In your tool dispatch
errors = validate_tool_input(tool_schema, agent_output)
if errors:
    raise ToolInputError(f"Invalid input: {'; '.join(errors)}")

Auto-Generating Schemas from Pydantic

Don't hand-write schemas for complex objects — derive them:

python
from pydantic import BaseModel, Field
import json
class SearchRequest(BaseModel):
    query: str = Field(..., max_length=200)
    limit: int = Field(default=10, ge=1, le=50)
    filters: dict | None = None
# One line to schema
schema = SearchRequest.model_json_schema()
print(json.dumps(schema, indent=2))

This gives you validation _and_ a shareable schema for your agent's system prompt.

The Validation Sandwich

A robust tool integration validates at three points:

1. Before tool dispatch — catch bad output from the model before it reaches the tool 2. At the tool boundary — re-validate in the tool adapter (models can hallucinate tool call payloads) 3. At the tool response — validate what the tool returns before passing it back to the model

python
# Middle layer: tool adapter
class ToolAdapter:
    def __init__(self, tool_schema: dict):
        self.validator = jsonschema.Draft7Validator(tool_schema)
    def dispatch(self, validated_input: dict) -> dict:
        errors = [e.message for e in self.validator.iter_errors(validated_input)]
        if errors:
            raise ValidationError(f"Schema violation: {errors}")
        return self._execute(validated_input)

Common Pitfalls

  • **Forgetting additionalProperties: false** — allows extra garbage fields to sneak through
  • **null vs missing** — use type: ["string", "null"] if null is a valid value, not absence
  • Dates as strings — always validate format with pattern or $ref to date-time formats
  • Number coercion — JSON numbers and Python integers are safe; strings pretending to be numbers need explicit pattern or format checks

Related Resources

Next Steps

  • Add JSON Schema validation to one tool in your agent pipeline today
  • Generate schemas from your existing Pydantic/TypeScript types
  • Write a schema for your agent's most failure-prone tool call
Related Dispatches