Why AI Agent Tool Calling Depends on JSON Schema: Parameter Errors, Type Errors, and Validation

Why Tool Calling uses JSON Schema as its contract, how to classify parameter and type errors in tool arguments, and a practical validation pipeline with ajv, strict mode, and error feedback to the model.

Earlier posts in this series set the stage: the evolution of JSON Schema, Function Calling, and MCP explains why they exist; JSON data flow from Tool Calling to MCP traces where bytes move; whether JSON Schema is becoming the standard Agent contract covers ecosystem convergence. This article focuses on a practical question: why Tool Calling almost inevitably depends on JSON Schema, and how to classify and validate parameter vs. type errors.

When a model picks a tool and fills parameters, the host cannot “trust luck”—it must fail-fast against the same Schema before execution. One hallucinated argument can delete data, send the wrong email, or poison the next turn. Bottom line: JSON Schema is the only parameter contract understood by model APIs, MCP, and host runtimes alike; validate after parse and before execute, and feed structured errors back for retry.

Why Tool Calling depends on JSON Schema

Tool Calling (same data flow as Function Calling) means: the model chooses a tool and outputs JSON arguments that match a contract. Three parties must agree:

  • Model APIs: OpenAI, Gemini, and Anthropic Tools APIs describe parameters with JSON Schema; some vendors also constrain decoding with Schema.
  • MCP: each Tool’s inputSchema is JSON Schema; Hosts often pass it through or trim to a subset when mapping to model APIs.
  • Host programs: need machine-readable, versionable, CI-checkable contracts—ajv, Python jsonschema, etc. beat “JSON format in the prompt” by orders of magnitude.

Without Schema, hosts regex-parse or prompt-parse arguments—that breaks at Agent scale. Schema gives shape (which fields), types, and constraints (enum, minimum, pattern)—everything you need syntactically before calling HTTP/DB/MCP. Business rules (“does priority=high violate SLA?”) still need code; Schema blocks most hallucinations at the syntax layer.

User intent → model reads JSON Schema in tools[]
           → outputs tool_calls[].function.arguments (JSON string)
           → host JSON.parse + Schema validate
           → only then call MCP / HTTP / DB

Three places Schema sits on the call chain

StageSchema roleTypical failure
Tool registration (tools / MCP list)Tells the model what tools exist and what args they needInvalid Schema, draft mismatch, misleading description
Model output (tool_calls.arguments)Constrains generated parameter JSONMissing required, wrong types, invented fields
Tool result (messages)Optional: constrain result shape before contextNon-JSON response, field drift

Vs. Structured Output: Structured Output constrains the final user-facing JSON reply; Tool Calling Schema constrains execution parameters. You can share one Schema source (Pydantic / Zod), but validate Tool arguments on every tool_calls before execute.

Parameter errors: missing, extra, wrong names, syntax

Parameter errors mean JSON may parse (or fails before parse) but violates Schema keys and required rules:

ErrorExampleSchema keywordMitigation
Missing requiredSchema needs title, args only have priorityrequiredFeed error back; clarify required in description
Extra fieldsModel invents urgent: trueadditionalProperties: falseOpenAI strict often enforces; otherwise strip or reject
Wrong key spellingtitel vs titleproperties keysConsistent naming; strong descriptions
JSON syntaxTrailing comma, single quotes(parse layer)JSON.parse first; Structured Output reduces syntax errors
Empty arguments{} but Schema has requiredrequired, minPropertiesZero-arg tools: explicit properties: {}
// Schema fragment
{
  "type": "object",
  "properties": {
    "ticket_id": { "type": "string", "description": "Ticket ID" },
    "note": { "type": "string" }
  },
  "required": ["ticket_id"],
  "additionalProperties": false
}

// Model output (missing ticket_id) → validation fails
{ "note": "Please handle ASAP" }

Type errors: mismatches, enum, nesting, coercion

Type errors: fields exist but values’ JSON type or format violates Schema:

ErrorExampleCommon cause
Primitive typelimit: "10" should be numberModels often stringify numbers
enum violationpriority: "urgent", enum is low/medium/highDescription didn’t list allowed values
Nested array/objectExpected tags: [], got stringSchema too complex for model subset
format stringemail fails format: emailHallucinated email or date formats
oneOf/anyOfPolymorphic arg matches no branchOver-complex Schema for target API

Coercion: some validators coerce "10" to 10. In production Agents, prefer coercion off—silent fixes hide systematic drift. If you must coerce, document it and lock behavior in CI samples.

// Type error example
Schema: { "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }
Model:  { "limit": " fifty " }  // string, not numeric → fail

Validation: syntax, arguments, strict mode

1. Validate the Schema itself

Before registering tools, meta-validate parameters / inputSchema (draft 2020-12, etc.). JSON Toolbox in the browser works locally—don’t ship invalid Schema to model APIs.

2. Validate arguments against Schema

After JSON.parse(arguments), validate with the same Schema used at registration:

  • JavaScript / TypeScript: ajv (mind draft and strict options)
  • Python: jsonschema, Pydantic (model_validate after JSON parse)
  • Codegen: Zod / Pydantic → JSON Schema single source

3. Vendor strict mode

OpenAI strict: true requires a stricter subset (e.g. all objects with additionalProperties: false). That reduces model-side errors but does not replace host validation—dialects differ by vendor; see the Contract article.

4. Sample-driven CI

Per tool: valid argument samples + intentional failures in CI. Schema changes are breaking API changes—version them.

End-to-end pipeline and error feedback

Minimal pipeline (extends the data-flow article):

1. tools/list or static register → validate each inputSchema syntax
2. On tool_calls → JSON.parse(arguments)
   ├─ parse fail → tool message "JSON syntax error: …" → model retry
   └─ parse ok → ajv/jsonschema validate
        ├─ fail → structured errors (missing, type, enum) → feed back
        └─ ok → execute + optional business rules
3. Tool result → optional result Schema before append to messages
4. Log: schema version, raw arguments, error codes (no secrets)

Error feedback must be machine-readable: “ticket_id is required” beats “bad params, retry”. Many frameworks format validation errors as JSON in the tool role for self-correction.

Execution still needs auth and idempotency—Schema guarantees shape, not “this ticket_id belongs to the user”.

Practical recommendations

  • Single Schema source: Pydantic / Zod → MCP inputSchema + OpenAI tools.
  • Descriptions are prompts: they drive enum and required compliance—review Schema like API code.
  • Simple Schema, strict validation: trim oneOf/$ref depth to target API subset; fail fast, no silent fixes.
  • Two mandatory checkpoints: after tool_calls before execute; after MCP return before context (if results feed the model).
  • Validate locally first: paste Schema + sample arguments in JSON Toolbox before production.
  • Separate from Structured Output: user reply Schema vs. tools Schema—don’t merge.

FAQ

Can Tool Calling skip JSON Schema and use natural language for parameters?

Prototypes yes; production no. Natural language can’t fail-fast or CI-version; models omit fields and drift types. Mainstream APIs and MCP default to Schema.

Are arguments a string or object?

Most Chat Completions APIs use a JSON string—hosts JSON.parse then validate. Some newer APIs return objects; either way, validate with the same Schema.

How many retries on validation failure?

Often 1–3 with structured error feedback, then clarify or escalate. Infinite retry burns tokens and can loop on hallucinations.

ajv vs Pydantic?

Node/TS hosts: ajv on JSON Schema directly. Python with Pydantic models: generate Schema + model_validate at runtime. Same source as the model-facing Schema.

With strict mode on, still validate on the host?

Yes. strict reduces model errors; it doesn’t stop dirty MCP results, Schema/code drift, or business rule violations.

How to validate Schema and arguments locally?

Paste Schema and sample JSON in JSON Toolbox—browser-local validation, nothing uploaded.

Summary and next steps

Tool Calling depends on JSON Schema because it is the shared, verifiable parameter contract for models, MCP, and hosts. Classify parameter errors (missing, extra, wrong name, syntax) and type errors (types, enum, nesting); intercept before execute and feed structured errors for self-correction.

Next: pick one real tool (e.g. ticket create), write Schema + valid/invalid samples, validate locally in JSON Toolbox, then wire the Agent. Series order: evolution → data flow → contract → this article (validation).