Skip to content
MediumAgentsPython 3

Tool Call Schema Validator

Validate tool call names and arguments before runtime execution.

35m3 sample tests9 hidden tests

Validate model-proposed against a small runtime schema. The model proposes arguments; the application must check names, required fields, and types before any tool executes. This exercise uses its own flat schema format. A required field map and type names such as str are not the standard JSON Schema format.

Requirements

  • Define validate_call(call, schemas).
  • schemas maps tool name to a schema dict.
  • Each schema has required and optional optional field maps.
  • Treat a missing args field as an empty argument dictionary.
  • Field type names can be str, int, float, bool, list, or dict.
  • Return a list of error strings.
  • Return [] when the call is valid.
  • Reject unknown tools.
  • Reject missing required fields.
  • Reject extra fields not in required or optional maps.
  • Reject wrong field types.

Error string formats

Use these exact codes (tests match the strings):

CaseFormatNotes
Unknown toolunknown_tool:{name}Only error returned (early return).
Missing requiredmissing:{field}Required fields in schema map iteration order.
Extra fieldextra:{field}Lexicographically sorted.
Type mismatchtype:{field}:{expected_type}Fields in required-then-optional schema order.

Collect all applicable errors in category order: unknown (early return) → missing → extra → type.

A call can fail in multiple ways at once. For schema {"required": {"query": "str", "limit": "int"}} and arguments {"limit": True, "debug": 1}, the exact result is ["missing:query", "extra:debug", "type:limit:int"]. Missing fields come first, extras are sorted, and type errors follow schema order.

Type acceptance

  • int values are accepted for float fields.
  • bool is never accepted for int or float (even though bool subclasses int in Python).

Example

These calls show valid arguments, a missing required value, and the unknown-tool early return:

python
schemas = {"search": {"required": {"query": "str"}}}
assert validate_call({"name": "search", "args": {"query": "vector db"}}, schemas) == []

schemas = {"fetch": {"required": {"url": "str", "timeout": "float"}}}
assert validate_call({"name": "fetch", "args": {"url": "https://x"}}, schemas) == ["missing:timeout"]
assert validate_call({"name": "delete_all", "args": {}}, {}) == ["unknown_tool:delete_all"]

Constraints

  • Keep error order deterministic.
  • bool isn't accepted for int or float.
  • Don't execute the tool.
  • Inputs are dictionaries. Every call has a string name; when present, args is a dictionary with string field names. Malformed outer input shapes are outside this exercise.
  • Schemas are trusted configuration: required and optional maps have disjoint string field names and supported type names. Map insertion order defines schema order. Names are case-sensitive.
  • Check containers only at the top level: any list satisfies list, and any dictionary satisfies dict, regardless of their contents. Do not coerce values: "3" and 3.0 fail an int field.

An absent optional field produces no error. A present optional field with None is a type error, because none of the supported types accepts None. Required fields with None are also present: report type, not missing.

Passing these shape checks does not authorize a tool call. For example, {"path": "/private/payroll.csv"} can satisfy a string field while requesting a file the caller cannot read. A real dispatcher must apply caller and resource authorization before execution. This validator also leaves enums, numeric bounds, and nested validation to the follow-ups.

Editor